BareGit

Refresh the public homepage

Add a NetHack-themed landing page with the agent prompt, spectator
instructions, and recent games. Clarify the model slug in the agent guide.
Author: MetroWind <chris.corsair@gmail.com>
Date: Wed Sep 23 17:46:14 2026 -0700
Commit: 99dbdaa174c0f2a9ecfb1b3a20956e2201ccc8fa

Changes

diff --git a/web/AGENT.md b/web/AGENT.md
index e1db85e..64640e4 100644
--- a/web/AGENT.md
+++ b/web/AGENT.md
@@ -16,8 +16,8 @@ spectator pages cannot control a game.
 ## Start a game
 
 Call `new_game` once with `model_slug` set to a short, printable ASCII
-name for your model or agent. You may also provide a character `name`.
-The result contains `game_id`, `control_token`, `viewer_url`, and the
+name for your model slug. You may also provide a character `name`. The
+result contains `game_id`, `control_token`, `viewer_url`, and the
 first `state`. Keep `game_id` and `control_token` for subsequent tool
 calls. Share the `viewer_url` with the user before continuing, but do
 not publish the control token or put it in a URL. If creation is rate
diff --git a/web/home.css b/web/home.css
index 25fe34e..7f093cc 100644
--- a/web/home.css
+++ b/web/home.css
@@ -1,46 +1,569 @@
-body {
-  color: #222;
-  font: 16px system-ui, sans-serif;
-  margin: 3rem auto;
-  max-width: 1100px;
-  padding: 0 1rem;
+:root
+{
+    color-scheme: dark;
+    --background: #0b1210;
+    --panel: #14201b;
+    --border: #34493c;
+    --text: #f0f4e9;
+    --muted: #a4b6a9;
+    --accent: #c9f767;
 }
 
-table {
-  border-collapse: collapse;
-  width: 100%;
+*
+{
+    box-sizing: border-box;
 }
 
-th,
-td {
-  border-bottom: 1px solid #ccc;
-  padding: .55rem;
-  text-align: left;
+body
+{
+    margin: 0;
+    min-width: 320px;
+    color: var(--text);
+    background:
+        radial-gradient(circle at 75% 12%, #244733 0, transparent 34%),
+        radial-gradient(circle at 2% 55%, #182e23 0, transparent 32%),
+        var(--background);
+    font: 16px/1.5 Inter, system-ui, sans-serif;
+}
+
+body::before
+{
+    position: fixed;
+    inset: 0;
+    z-index: 0;
+    background-image:
+        linear-gradient(#ffffff07 1px, transparent 1px),
+        linear-gradient(90deg, #ffffff07 1px, transparent 1px);
+    background-size: 48px 48px;
+    content: "";
+    pointer-events: none;
+}
+
+a
+{
+    color: inherit;
+}
+
+button,
+input
+{
+    font: inherit;
+}
+
+:focus-visible
+{
+    outline: 2px solid var(--accent);
+    outline-offset: 3px;
+}
+
+.site-shell
+{
+    position: relative;
+    z-index: 1;
+    width: min(100% - 48px, 2000px);
+    margin: 0 auto;
+}
+
+.site-header
+{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 24px;
+    min-height: 92px;
+    border-bottom: 1px solid var(--border);
+}
+
+.brand
+{
+    display: inline-flex;
+    align-items: center;
+    gap: 12px;
+    font-size: 1.25rem;
+    font-weight: 800;
+    letter-spacing: -.04em;
+    text-decoration: none;
+    white-space: nowrap;
+}
+
+.brand-mark
+{
+    display: grid;
+    width: 38px;
+    height: 38px;
+    place-items: center;
+    border: 1px solid var(--accent);
+    border-radius: 8px;
+    color: var(--accent);
+    font: 700 1.5rem/1 ui-monospace, monospace;
+    box-shadow: 0 0 22px #c9f76724;
+}
+
+.brand-accent
+{
+    color: var(--accent);
+}
+
+.header-badge,
+.eyebrow,
+.prompt-heading,
+.connection-label,
+.terminal,
+th
+{
+    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
+}
+
+.header-badge
+{
+    display: inline-flex;
+    align-items: center;
+    gap: 10px;
+    padding: 9px 13px;
+    border: 1px solid var(--border);
+    border-radius: 99px;
+    color: var(--muted);
+    font-size: .75rem;
+    letter-spacing: .02em;
+}
+
+.status-dot
+{
+    width: 7px;
+    height: 7px;
+    border-radius: 50%;
+    background: var(--accent);
+    box-shadow: 0 0 12px var(--accent);
+}
+
+.hero
+{
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: clamp(32px, 5vw, 80px);
+    padding: clamp(32px, 5vw, 80px) 0;
 }
 
-code {
-  overflow-wrap: anywhere;
+.hero-content
+{
+    min-width: 0;
+    flex-grow: 1;
+}
+
+.eyebrow
+{
+    margin: 0 0 22px;
+    color: var(--accent);
+    font-size: .75rem;
+    font-weight: 700;
+    letter-spacing: .18em;
+}
+
+h1
+{
+    margin: 0 0 36px;
+    font-size: min(5rem, 10vw);
+    font-weight: bold;
+    letter-spacing: -.065em;
+    line-height: 1.2;
+}
+
+h1 span,
+h1 strong
+{
+    display: block;
+}
+
+h1 strong
+{
+    color: var(--accent);
+    font-weight: inherit;
+}
+
+.prompt-panel
+{
+    position: relative;
+    max-width: 670px;
+    padding: 24px;
+    padding-bottom: 4px;
+    border: 1px solid #526b49;
+    border-radius: 14px;
+    background: #15221de8;
+    box-shadow: 0 24px 70px #0005, inset 0 1px #ffffff12;
+}
+
+.prompt-panel::before
+{
+    position: absolute;
+    top: -1px;
+    left: 24px;
+    width: 68px;
+    height: 2px;
+    background: var(--accent);
+    content: "";
+}
+
+.prompt-heading
+{
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 14px;
+    color: var(--accent);
+    font-size: .7rem;
+    font-weight: 700;
+    letter-spacing: .13em;
+}
+
+.prompt-panel label
+{
+    display: block;
+    margin-bottom: 12px;
+    font-size: 1.15rem;
+    font-weight: 700;
 }
 
 .agent-prompt
 {
     display: flex;
-    gap: .5rem;
+    gap: 8px;
 }
 
 .agent-prompt input
 {
     flex: 1;
     min-width: 0;
-    padding: .55rem;
+    padding: 12px 14px;
+    border: 1px solid var(--border);
+    border-radius: 7px;
+    color: var(--text);
+    background: #0a120e;
+    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
+    font-size: .85rem;
+    text-overflow: ellipsis;
 }
 
 .agent-prompt button
 {
-    padding: .55rem .9rem;
+    flex: none;
+    padding: 10px 18px;
+    border: 1px solid var(--accent);
+    border-radius: 7px;
+    color: #13210c;
+    background: var(--accent);
+    cursor: pointer;
+    font-size: .85rem;
+    font-weight: 800;
+    transition: background .15s ease, transform .15s ease;
+}
+
+.agent-prompt button:hover
+{
+    background: #e0ff9c;
+    transform: translateY(-2px);
 }
 
 #agent-prompt-status
 {
     min-height: 1.5em;
+    margin: 5px 0 0;
+    color: var(--accent);
+    font-size: .8rem;
+}
+
+.prompt-hint
+{
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    gap: 4px 16px;
+    margin: 4px 0 0;
+    color: var(--muted);
+    font-size: .78rem;
+}
+
+.prompt-hint a
+{
+    color: var(--accent);
+    text-underline-offset: 3px;
+}
+
+.how-it-works
+{
+    max-width: 670px;
+    margin-top: 28px;
+}
+
+.how-it-works h2
+{
+    margin: 0 0 6px;
+    color: var(--accent);
+    font-size: 1.2rem;
+    letter-spacing: 0;
+}
+
+.how-it-works p
+{
+    margin: 0;
+    color: var(--muted);
+}
+
+.terminal
+{
+    min-width: 350px;
+    overflow: hidden;
+    border: 1px solid #536b4d;
+    border-radius: 12px;
+    background: #0a110ed9;
+    box-shadow: 0 28px 70px #0008, 0 0 70px #c9f7670d;
+    transform: rotate(2deg);
+}
+
+.terminal-topbar,
+.terminal-footer
+{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 12px;
+    padding: 13px 17px;
+    color: var(--muted);
+    font-size: .68rem;
+    letter-spacing: .09em;
+}
+
+.terminal-topbar
+{
+    border-bottom: 1px solid var(--border);
+}
+
+.terminal-lights
+{
+    display: flex;
+    gap: 6px;
+}
+
+.terminal-lights i
+{
+    width: 7px;
+    height: 7px;
+    border-radius: 50%;
+    background: #556b58;
+}
+
+.terminal-lights i:first-child
+{
+    background: var(--accent);
+}
+
+.terminal-map
+{
+    overflow: auto;
+    margin: 0;
+    padding: 28px 20px;
+    color: #c4ed9f;
+    font: clamp(.65rem, 1.3vw, .93rem)/1.55 ui-monospace, monospace;
+    text-shadow: 0 0 16px #c9f76745;
+}
+
+.terminal-footer
+{
+    border-top: 1px solid var(--border);
+    color: var(--accent);
+    letter-spacing: 0;
+}
+
+.connection-bar
+{
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: 8px 20px;
+    padding: 18px 0;
+    border-top: 1px solid var(--border);
+    border-bottom: 1px solid var(--border);
+}
+
+.connection-label
+{
+    color: var(--muted);
+    font-size: .7rem;
+    font-weight: 700;
+    letter-spacing: .1em;
+}
+
+.connection-bar code
+{
+    color: var(--accent);
+    font-size: .86rem;
+    overflow-wrap: anywhere;
+}
+
+.connection-note
+{
+    margin-left: auto;
+    color: var(--muted);
+    font-size: .8rem;
+}
+
+.results
+{
+    padding: 72px 0 100px;
+}
+
+.results-heading
+{
+    display: flex;
+    align-items: end;
+    justify-content: space-between;
+    flex-wrap: wrap;
+    gap: 12px 30px;
+    margin-bottom: 26px;
+}
+
+.results-heading .eyebrow
+{
+    margin-bottom: 8px;
+}
+
+h2
+{
+    margin: 0;
+    font-size: clamp(1.5rem, 2.6vw, 2.4rem);
+    letter-spacing: -.045em;
+    line-height: 1.15;
+}
+
+.results-heading > p
+{
+    margin: 0;
+    color: var(--muted);
+    font-size: .86rem;
+}
+
+.table-shell
+{
+    overflow-x: auto;
+    border: 1px solid var(--border);
+    border-radius: 12px;
+    background: #111c17dd;
+}
+
+table
+{
+    width: 100%;
+    min-width: 790px;
+    border-collapse: collapse;
+}
+
+th,
+td
+{
+    padding: 17px 20px;
+    border-bottom: 1px solid var(--border);
+    text-align: left;
+    white-space: nowrap;
+}
+
+th
+{
+    color: var(--muted);
+    background: #1b2b22;
+    font-size: .68rem;
+    font-weight: 700;
+    letter-spacing: .09em;
+    text-transform: uppercase;
+}
+
+td
+{
+    font-size: .87rem;
+}
+
+td:first-child
+{
+    color: var(--accent);
+    font-weight: 700;
+}
+
+tbody tr:last-child td
+{
+    border-bottom: 0;
+}
+
+tbody tr:hover
+{
+    background: #203127;
+}
+
+@media(max-width: 900px)
+{
+    .hero
+    {
+        grid-template-columns: 1fr;
+    }
+
+    .terminal
+    {
+        width: min(100%, 520px);
+        transform: none;
+    }
+}
+
+@media(max-width: 600px)
+{
+    .site-shell
+    {
+        width: min(100% - 32px, 1280px);
+    }
+
+    .site-header
+    {
+        min-height: 75px;
+    }
+
+    .header-badge
+    {
+        display: none;
+    }
+
+    .hero
+    {
+        padding: 54px 0 64px;
+    }
+
+    h1
+    {
+        margin-bottom: 28px;
+    }
+
+    .prompt-panel
+    {
+        padding: 18px;
+        padding-bottom: 4px;
+    }
+
+    .agent-prompt button
+    {
+        padding: 10px 12px;
+    }
+
+    .connection-note
+    {
+        width: 100%;
+        margin-left: 0;
+    }
+
+    .results
+    {
+        padding: 58px 0 80px;
+    }
+}
+
+@media(prefers-reduced-motion: reduce)
+{
+    .agent-prompt button
+    {
+        transition: none;
+    }
 }
diff --git a/web/home.html b/web/home.html
index e112667..628d61d 100644
--- a/web/home.html
+++ b/web/home.html
@@ -3,44 +3,107 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  <title>Public NetHack games</title>
+  <meta name="theme-color" content="#0b1210">
+  <title>NetHack MCP — send your agent into the dungeon</title>
   <link rel="stylesheet" href="/home.css">
 </head>
 <body>
-  <main>
-    <h1>Public NetHack games</h1>
-    <p>Agents play through the shared MCP endpoint. Spectators can watch
-       live games, and recent results appear below.</p>
-    <p>MCP endpoint: <code>{{MCP_URL}}</code></p>
-    <section>
-      <h2>Let an agent play</h2>
-      <p>Paste this into an agent that supports remote MCP servers:</p>
-      <div class="agent-prompt">
-        <input id="agent-prompt" type="text" readonly
-               aria-label="Prompt to paste into an agent"
-               value="{{AGENT_PROMPT}}">
-        <button id="copy-agent-prompt" type="button">Copy</button>
+  <div class="site-shell">
+    <header class="site-header">
+      <a class="brand" href="/" aria-label="NetHack MCP home">
+        <span class="brand-mark" aria-hidden="true">@</span>
+        <span>NetHack <span class="brand-accent">MCP</span></span>
+      </a>
+      <span class="header-badge">
+        <span class="status-dot" aria-hidden="true"></span>
+        A public dungeon for agents
+      </span>
+    </header>
+    <main>
+      <section class="hero" aria-labelledby="hero-title">
+        <div class="hero-content">
+          <p class="eyebrow">WELCOME TO THE DUNGEON</p>
+          <h1 id="hero-title">
+            <span>Do you have tokens to waste?</span>
+            <span>Do you suck at NetHack?</span>
+            <strong>Then this MCP is for you!</strong>
+          </h1>
+          <div class="prompt-panel">
+            <div class="prompt-heading">
+              <span>01 / COPY THE PROMPT</span>
+              <span aria-hidden="true">[ @ ]</span>
+            </div>
+            <label for="agent-prompt">Send your agent into the dungeon</label>
+            <div class="agent-prompt">
+              <input id="agent-prompt" type="text" readonly
+                     value="{{AGENT_PROMPT}}">
+              <button id="copy-agent-prompt" type="button">
+                Copy prompt
+              </button>
+            </div>
+            <p id="agent-prompt-status" role="status" aria-live="polite"></p>
+          </div>
+          <section class="how-it-works" aria-labelledby="how-title">
+            <h2 id="how-title">How does this work?</h2>
+            <p>Paste the prompt into an agent that supports remote MCP. When
+               it starts a game, the agent will give you a spectator URL.
+               Open that link in your browser to watch it play live.</p>
+          </section>
+        </div>
+        <aside class="terminal" aria-hidden="true">
+          <div class="terminal-topbar">
+            <span class="terminal-lights"><i></i><i></i><i></i></span>
+            <span>DUNGEON PREVIEW</span>
+          </div>
+          <pre class="terminal-map">  --------------------------
+  |.............|..........|
+  |....$........+..........|
+  |.............|.....d....|
+  |..@..........|..........|
+  |.............|..........|
+  -------+------------------
+         #
+         ##########</pre>
+          <div class="terminal-footer">
+            <span>&gt; awaiting agent input_</span>
+            <span>DLVL: 01</span>
+          </div>
+        </aside>
+      </section>
+      <div class="connection-bar">
+        <span class="connection-label">REMOTE MCP ENDPOINT</span>
+        <code>{{MCP_URL}}</code>
+        <span class="connection-note">Agents play. You watch.</span>
       </div>
-      <p id="agent-prompt-status" role="status" aria-live="polite"></p>
-    </section>
-    <h2>Ten most recent completed games</h2>
-    <table>
-      <thead>
-        <tr>
-          <th>Character</th>
-          <th>Model</th>
-          <th>Started</th>
-          <th>Ended</th>
-          <th>Outcome</th>
-          <th>Lowest floor</th>
-          <th>End floor</th>
-        </tr>
-      </thead>
-      <tbody>
-        {{RECENT_GAMES}}
-      </tbody>
-    </table>
-  </main>
+      <section class="results" aria-labelledby="results-title">
+        <div class="results-heading">
+          <div>
+            <p class="eyebrow">THE RUN LOG</p>
+            <h2 id="results-title">Ten most recent completed games</h2>
+          </div>
+          <p>Watch live games through the link shared by an agent.</p>
+        </div>
+        <div class="table-shell">
+          <table>
+            <thead>
+              <tr>
+                <th>Character</th>
+                <th>Model</th>
+                <th>Started</th>
+                <th>Ended</th>
+                <th>Outcome</th>
+                <th>Lowest floor</th>
+                <th>End floor</th>
+              </tr>
+            </thead>
+            <tbody>
+              {{RECENT_GAMES}}
+            </tbody>
+          </table>
+        </div>
+      </section>
+    </main>
+  </div>
   <script src="/local-time.js"></script>
   <script src="/copy-prompt.js"></script>
 </body>