:root {
    --bg:        #0d1117;
    --panel:     #161b22;
    --panel-alt: #1c2129;
    --border:    #2b3440;
    --text:      #c9d1d9;
    --muted:     #7d8792;
    --dim:       #5a636e;
    --accent:    #d9a441;
    --grid:      rgba(150, 163, 178, 0.12);
    --good:      #3fb950;
    --warn:      #d29922;
    --bad:       #f05252;
    --mana:      #58a6ff;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
    font-size: 15px;
    line-height: 1.5;
}

/* ---------- shell ---------- */

.app {
    max-width: 1180px;
    margin: 0 auto;
    padding: 20px 16px 40px;
}

/* The sidebar takes whatever the map does not need. The map is 86ch wide (80
   cells plus two 3ch gutters), which at a 9px advance is 774px, and 800px once
   the map-scroll padding and the panel border are counted; that leaves 330px
   here. Widen the sidebar past this and the map starts scrolling instead. */
.layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 330px;
    gap: 18px;
    align-items: start;
}

@media (max-width: 940px) {
    .layout { grid-template-columns: minmax(0, 1fr); }
}

.column {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
}

/* Splits the space under the map. Both halves hold short panels, so pairing
   them recovers the height they would waste stacked. The notes take the larger
   share: their table has five columns to the decision's two. */
.subcolumns {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 18px;
    align-items: start;
}

@media (max-width: 940px) {
    .subcolumns { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- panels ---------- */

.panel {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.panel-head {
    padding: 8px 12px;
    background: var(--panel-alt);
    border-bottom: 1px solid var(--border);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--muted);
}

.panel-body { padding: 12px; }

.panel-body.scroll {
    max-height: 260px;
    overflow-y: auto;
}

/* Roughly what the panels beside it come to, so the two halves under the map
   finish together and neither leaves the other's space blank. */
.panel-body.notes-box { max-height: 460px; }

/* A turn yields two messages at most, and usually none. Hold the box at two
   lines either way, so that the panels below it stay put as the turn's
   messages come and go. The height is the sum of its parts: 6 padding + 18
   line + 4 gap + 18 line + 6 padding. That only stays exact while .message
   pins its own line-height. */
.panel-body.messages-box {
    height: 52px;
    padding-top: 6px;
    padding-bottom: 6px;
    overflow-y: auto;
}

.empty {
    color: var(--dim);
    font-style: italic;
    font-size: 13px;
}

/* ---------- messages ---------- */

.messages {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.message {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 13px;
    line-height: 18px;
    color: var(--text);
    border-left: 2px solid var(--accent);
    padding-left: 9px;
}

/* A system message (flagged with a leading '!'): louder than game text, so the
   user notices it. Colour, weight and a tint carry the emphasis without adding
   vertical padding, which would break the fixed messages-box height. */
.message.system {
    color: var(--warn);
    font-weight: 700;
    border-left-color: var(--warn);
    background: rgba(210, 153, 34, 0.12);
    border-radius: 0 3px 3px 0;
}

/* ---------- map ---------- */

.map-scroll {
    overflow-x: auto;
    padding: 12px;
    background: #090c11;
}

.map {
    font-family: ui-monospace, "Cascadia Mono", Consolas, "DejaVu Sans Mono", monospace;
    font-size: 15px;
    /* pinned to an integer so 10 rows is exactly 180px: the grid overlay's
       horizontal lines stay locked to the row boundaries with no drift */
    line-height: 18px;
    width: max-content;
    user-select: none;
}

/* the ruler and the gutter share the map's font-size so that every column
   stays 1ch wide and the coordinates line up with the cells they label */
.map-ruler { color: var(--dim); }

/* The ruler's digits are .cell spans, to hold the column alignment, so they
   need saying again here: chrome, dim as the gutters, not map content. */
.map-ruler .cell { color: var(--dim); }

.map-row { white-space: pre; }

/* Frames the 80x21 grid, leaving the rulers outside it. Drawn as an overlay
   rather than a border on the rows themselves: a real border would widen every
   row and pull them out of step with the rulers. The insets clear the 1ch gap
   the gutters already leave, so the frame lands beside the cells, not on them. */
.map-grid {
    position: relative;
    padding: 2px 0;
}

.map-grid::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(3ch - 3px);
    right: calc(3ch - 3px);
    border: 1px solid var(--border);
    border-radius: 3px;
    pointer-events: none;
}

/* Light guide lines every 10 squares, drawn as a second overlay inset to the
   cell area (past the 3ch gutters and the 2px top/bottom padding, exactly like
   the frame). Columns are 1ch so the verticals never drift; the pinned 18px
   line-height makes 10 rows exactly 180px for the horizontals. Each gradient
   puts a 1px line at the END of every 10-unit block, so lines fall after
   columns 10,20,...,70 and rows 10,20 — never on the frame edge. */
.map-grid::after {
    content: "";
    position: absolute;
    top: 2px;
    bottom: 2px;
    left: 3ch;
    right: 3ch;
    pointer-events: none;
    background-image:
        repeating-linear-gradient(to right,
            transparent 0 calc(10ch - 1px), var(--grid) calc(10ch - 1px) 10ch),
        repeating-linear-gradient(to bottom,
            transparent 0 calc(180px - 1px), var(--grid) calc(180px - 1px) 180px);
}

.map-gutter {
    display: inline-block;
    width: 3ch;
    padding-right: 1ch;
    color: var(--dim);
    text-align: right;
}

/* the same gutter mirrored, so a row can be read off either edge */
.map-gutter.right {
    padding-right: 0;
    padding-left: 1ch;
    text-align: left;
}

/* Terrain the game gave us no entity for. Lighter than --dim, which is meant
   for chrome on the panels: this is the body of the map, read constantly, and
   it sits on a background darker than any panel. */
.cell {
    display: inline-block;
    width: 1ch;
    text-align: center;
    color: #9aa5b2;
}

.cell.ent { color: var(--text); }

/* Out of sight: recalled from an earlier visit, may be stale. Enough of a drop
   to separate it from what is lit, not so much that it stops being readable. */
.cell.faded { opacity: 0.62; }

/* the top item of a pile — more lies underneath */
.cell.pile { text-decoration: underline dotted; text-underline-offset: 2px; }

/* No meaning beyond "you are here" — just somewhere for the eye to land. The
   glyph keeps the colour the game gave it; the ring is inset rather than an
   outline so that it cannot bleed over the cells either side. */
.cell.hero {
    background: rgba(217, 164, 65, 0.16);
    box-shadow: inset 0 0 0 1px rgba(217, 164, 65, 0.5);
    border-radius: 2px;
    font-weight: 700;
}

/* ---------- prompt ---------- */

.prompt-kind {
    display: inline-block;
    padding: 1px 8px;
    margin-right: 10px;
    border-radius: 999px;
    background: var(--accent);
    color: #17202b;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    vertical-align: 1px;
}

.prompt-kind.over { background: var(--bad); color: #ffffff; }

.prompt-text {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 14px;
}

.menu {
    margin: 10px 0 0;
    padding: 0;
    list-style: none;
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 13px;
}

.menu li {
    padding: 3px 0;
    border-top: 1px solid var(--border);
}

.menu-key {
    display: inline-block;
    width: 2.5ch;
    color: var(--accent);
    font-weight: 700;
}

.menu li.selected { color: var(--good); }

/* ---------- notes ---------- */

.notes {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.notes th {
    padding: 0 8px 5px;
    border-bottom: 1px solid var(--border);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: left;
}

.notes td {
    padding: 5px 8px;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}

.notes tr:last-child td { border-bottom: none; }

/* Text takes whatever the other four columns leave; they are all short and
   fixed, so let them shrink to their contents. */
.notes th:nth-child(2) { width: 100%; }

.notes .note-id,
.notes .note-age {
    font-family: ui-monospace, Consolas, monospace;
    color: var(--muted);
    white-space: nowrap;
}

.notes .note-age { text-align: right; }

.notes .note-check {
    text-align: center;
    white-space: nowrap;
}

.notes .note-check input {
    margin: 0;
    accent-color: var(--accent);
    vertical-align: -1px;
}

/* ---------- action ---------- */

/* One grid for both rows, so the labels share a column and stay aligned. The
   column takes its width from the widest of them: a fixed width would have to
   guess, as these are proportional caps and ch measures a digit. */
/* Aligned by the tops of the rows rather than by baseline: the prediction box
   below scrolls, and a scrolling box reports its own bottom edge as its
   baseline, which would drop the label away from the line it names. The pinned
   line-heights bring the label and its text level instead. */
.acts {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    align-items: start;
    column-gap: 12px;
}

/* the second row, ruled off from the first */
.acts > :nth-child(n+3) {
    margin-top: 7px;
    padding-top: 7px;
    border-top: 1px solid var(--border);
}

.act-label {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
    line-height: 18px;
}

.act-text {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 13px;
    line-height: 18px;
}

/* Matches the note text: the body face, as this is a sentence.

   Held at five lines however long the sentence runs, so that the button below
   stays where the eye left it; a verbose agent needs the room. Sized
   content-box, as the padding-top the rule above adds must not eat into the
   five lines; the height is theirs alone. */
.act-prose {
    font-size: 13px;
    line-height: 18px;
    box-sizing: content-box;
    height: 90px;
    overflow-y: auto;
}

/* ---------- who ---------- */

.who {
    padding-bottom: 10px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--border);
}

.who-title {
    font-size: 15px;
    font-weight: 600;
}

.who-sub {
    color: var(--muted);
    font-size: 12px;
}

/* ---------- meters ---------- */

.meter { margin-bottom: 10px; }

.meter-label {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    margin-bottom: 3px;
}

.meter-label span:first-child {
    color: var(--muted);
    letter-spacing: 0.06em;
}

.meter-label span:last-child {
    font-family: ui-monospace, Consolas, monospace;
}

.meter-track {
    height: 7px;
    border-radius: 999px;
    background: var(--panel-alt);
    overflow: hidden;
}

.meter-fill {
    height: 100%;
    border-radius: 999px;
    transition: width 0.25s ease;
}

.meter-fill.hp-good { background: var(--good); }
.meter-fill.hp-warn { background: var(--warn); }
.meter-fill.hp-bad  { background: var(--bad); }
.meter-fill.pw      { background: var(--mana); }

/* ---------- stats ---------- */

.stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin-bottom: 12px;
}

.stat {
    background: var(--panel-alt);
    border-radius: 5px;
    padding: 5px 0;
    text-align: center;
}

.stat-name {
    font-size: 10px;
    letter-spacing: 0.09em;
    color: var(--muted);
}

.stat-value {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 15px;
    font-weight: 600;
}

/* Two label/value pairs per row: these values are all short, and one pair per
   row left a stripe of blank space down the middle. */
.facts {
    display: grid;
    grid-template-columns: repeat(2, max-content minmax(0, 1fr));
    gap: 3px 8px;
    font-size: 13px;
}

/* space the pairs apart without opening a gap inside either one */
.facts dt:nth-child(4n+3) { padding-left: 10px; }

.facts dt { color: var(--muted); }

.facts dd {
    margin: 0;
    font-family: ui-monospace, Consolas, monospace;
    text-align: right;
}

/* ---------- chips ---------- */

.chips {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.chip {
    padding: 1px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--panel-alt);
    font-size: 12px;
}

.chip.alert {
    border-color: var(--bad);
    color: var(--bad);
}

/* ---------- lists ---------- */

.rows {
    display: flex;
    flex-direction: column;
    gap: 5px;
    font-size: 13px;
}

.row-item {
    display: flex;
    align-items: baseline;
    gap: 9px;
}

.row-key {
    flex: none;
    color: var(--accent);
    font-family: ui-monospace, Consolas, monospace;
    font-weight: 700;
}

/* ---------- footer ---------- */

/* the column's own gap sets it off now, so no rule above it */
.footer {
    display: flex;
    justify-content: center;
    gap: 8px;
}

/* icon-only buttons (rewind, previous): square-ish, no wide letter-spacing */
.button.icon {
    padding: 7px 14px;
    letter-spacing: 0;
    line-height: 1;
}

.button {
    padding: 7px 26px;
    border: 1px solid var(--accent);
    border-radius: 6px;
    background: var(--accent);
    color: #17202b;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.04em;
    cursor: pointer;
}

.button:hover { background: #e8b856; }

.button:active { transform: translateY(1px); }

/* The agent's turn is a round trip to a language model: seconds, not frames,
   and the hero often moves a single square, so the result is easy to miss.
   Wait the whole page, not just the button, as the pointer will not be over it
   for most of that time. The universal selector beats the cursor every other
   rule sets. */
.app.busy,
.app.busy * {
    cursor: wait;
}

.button:disabled {
    background: var(--panel-alt);
    border-color: var(--border);
    color: var(--muted);
}

.button:disabled:hover { background: var(--panel-alt); }

.button:disabled:active { transform: none; }

/* ---------- colophon ---------- */

/* A quiet link out to the project, centred under everything. */
.colophon {
    display: block;
    margin-top: 18px;
    text-align: center;
    font-size: 12px;
    color: var(--muted);
    text-decoration: none;
}

.colophon:hover {
    color: var(--accent);
    text-decoration: underline;
}

/* ---------- states ---------- */

.notice {
    padding: 40px 20px;
    text-align: center;
    color: var(--muted);
}

.notice.error { color: var(--bad); }

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: #3d4855; }
