/* apigaming, Exchange: dark only. The engine's slate is the page, boards sit edge to edge, teal is the
   one action colour and coral the one warning. Tokens first, then base type, page frame, controls,
   surfaces, seats and the arena, then pages. Design notes: docs/DESIGN.md. */

/* Rubik (SIL OFL 1.1, fonts/OFL-rubik.txt) sets headlines and the wordmark, Figtree (OFL 1.1,
   fonts/OFL-figtree.txt) the running text, Martian Mono (OFL 1.1, fonts/OFL-martian-mono.txt) receipts,
   ids, clocks, table figures and code. All three are variable and self-hosted: the pages' CSP is
   default-src 'self'. */
@font-face { font-family: "Rubik"; src: url("fonts/rubik-latin.woff2") format("woff2"); font-weight: 300 900; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Figtree"; src: url("fonts/figtree-latin.woff2") format("woff2"); font-weight: 300 900; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Martian Mono"; src: url("fonts/martian-mono-latin.woff2") format("woff2"); font-weight: 100 800; font-stretch: 75% 112%; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

:root {
  /* Slate: the page and every board, the exact background the engines draw their boards on. */
  --slate: #0b0f19;
  --slate-2: #141a2a;
  --slate-rule: #273149;
  --slate-ink: #e6edf7;
  --slate-ink-2: #9aa7bd;
  --slate-line: #3d4a6b;
  --slate-field-line: #5d6c92;
  --slate-code-bg: rgba(230, 237, 247, 0.08);
  --slate-check: #4ade80;
  --slate-err: #ff6b5e;
  /* Teal: the one action colour, also the accent bar under the current page, the brand mark and the
     numerals of a step. Coral is its opposite: a loss, a decline, an error. */
  --teal: #2ee6c5;
  --teal-hover: #6ff2da;
  --coral: #ff6b5e;
  --action: var(--teal);
  --on-action: #06231e;
  --action-hover: var(--teal-hover);
  --accent: var(--teal);
  --accent-ink: var(--teal);
  --footer-bg: #05070d;
  /* The record: the page is the slate, raised slate carries cards, tables and captions. */
  --paper: #0b0f19;
  --sheet: #141a2a;
  --ink: #e6edf7;
  --ink-2: #9aa7bd;
  --rule: #273149;
  --rule-strong: #5d6c92;
  --hover: #1b2338;
  --row-hover: #182036;
  --field: #0f1524;
  --placeholder: #8b98b0;
  --code-bg: rgba(230, 237, 247, 0.09);
  --numeral: #ffffff;
  --arena-edge: #273149;
  /* Seats: the engines' own piece colours; -ink for text, -deep for a numbered disc. */
  --seat1: #ef4444;
  --seat1-ink: #f87171;
  --seat1-deep: #c0262d;
  --seat2: #3b82f6;
  --seat2-ink: #60a5fa;
  --seat2-deep: #2563eb;
  --seat3: #22c55e; --seat3-deep: #15803d;
  --seat4: #f59e0b; --seat4-deep: #b45309;
  --seat5: #a855f7; --seat5-deep: #7e22ce;
  --seat6: #06b6d4; --seat6-deep: #0e7490;
  --seat7: #ec4899; --seat7-deep: #be185d;
  --seat8: #84cc16; --seat8-deep: #4d7c0f;
  --seat1-dot: var(--seat1); --seat2-dot: var(--seat2);
  --seat3-dot: var(--seat3); --seat4-dot: var(--seat4); --seat5-dot: var(--seat5);
  --seat6-dot: var(--seat6); --seat7-dot: var(--seat7); --seat8-dot: var(--seat8);
  --seat3-ink: #4ade80; --seat4-ink: #fbbf24; --seat5-ink: #c084fc;
  --seat6-ink: #22d3ee; --seat7-ink: #f472b6; --seat8-ink: #a3e635;
  /* Checked: a passing verification, a connected bot, a gain. Always with a tick or a word. */
  --check: #4ade80;
  --usdc: #f5b83d;
  --err: var(--coral);
  --on-err: #1a0b0a;
  --scrim: rgb(5 8 14 / 0.72);
  --shade: rgb(0 0 0 / 0.75);
  --err-wash: #2b1a1e;
  --err-line: #6b3a3f;
  --amber: #f2b84b;
  --violet: #c3b5f5;

  --font: "Figtree", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-head: "Rubik", "Figtree", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --surface: var(--paper);
  --focus: var(--teal);
  --pulse: rgba(230, 237, 247, 0.5);
  color-scheme: dark;

  --wrap: 1200px;
  --gutter: 32px;
  --r-sheet: 8px;
  --r-board: 8px;
  --r-control: 6px;
}

/* ---- base ---------------------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scrollbar-gutter: stable; font: 400 1rem/1.55 var(--font); font-optical-sizing: auto; color: var(--ink); background: var(--paper); -webkit-text-size-adjust: 100%; text-rendering: optimizeLegibility; }
body { margin: 0; min-height: 100vh; display: flex; flex-direction: column; background: var(--paper); overflow-x: clip; }
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding-left: var(--gutter); padding-right: var(--gutter); }
main.wrap { flex: 1; padding-top: 0.5rem; padding-bottom: 3rem; }
/* The footer sits the same distance under every page: the last block's own margin does not add to it. */
main.wrap > :last-child, main.wrap > :last-child > :last-child, main.wrap > :last-child > :last-child > :last-child { margin-bottom: 0; }

a { color: inherit; text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); }
a:hover { text-decoration-color: currentColor; text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 2px; }

h1, h2, h3, .brand, .stat-value, .party { font-family: var(--font-head); }
h1, h2, h3 { margin: 0; line-height: 1.15; font-weight: 700; letter-spacing: -0.015em; }
h1 { font-size: 2.25rem; font-weight: 700; letter-spacing: -0.025em; line-height: 1.05; }
h2 { font-size: 1.75rem; margin: 2.75rem 0 0.85rem; }
h3 { font-size: 1.125rem; margin: 1.25rem 0 0.5rem; }
h1 small, h2 small, h3 small { font-size: max(0.75rem, 0.6em); font-weight: 500; letter-spacing: 0; color: var(--ink-2); }
h2 .count { font-size: 0.875rem; font-weight: 600; color: var(--ink-2); margin-left: 0.5rem; letter-spacing: 0; }
p { margin: 0.6rem 0; }
ul, ol { padding-left: 1.4rem; }
li { margin: 0.25rem 0; }
strong { font-weight: 700; }

code, pre, kbd { font-family: var(--font-mono); }
code { font-size: max(0.8125em, 12px); background: var(--code-bg); padding: 0.05em 0.3em; border-radius: 3px; overflow-wrap: anywhere; }
pre { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 0.9rem 1.1rem; overflow-x: auto; font-size: 0.8125rem; line-height: 1.7; margin: 0.6rem 0 1.1rem; }
pre code { background: none; padding: 0; font-size: inherit; overflow-wrap: normal; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.lead { font-size: 1.125rem; line-height: 1.5; color: var(--ink-2); max-width: 62ch; }
.muted { color: var(--ink-2); }
.pos, .win { color: var(--check); }
.neg { color: var(--err); }
.pts, .rating { font-variant-numeric: tabular-nums; }
.rating { font-weight: 700; }
/* Figures that line up in a column are Martian Mono: it is wide, so a step smaller than the text. */
td.num, td.rank, td.ts, .rating, .pts, .clock, .score { font-family: var(--font-mono); font-size: 0.8125rem; }
.rules p { margin: 0 0 0.9rem; max-width: 70ch; }
.rules p:last-child { margin-bottom: 0; }

/* ---- page frame ---------------------------------------------------------------------------------- */
header.site { border-bottom: 1px solid var(--rule); background: var(--paper); }
/* One row from 1180px: brand, the ten links (they may wrap), Search, then Create a bot or the bell. Below that
   the links drop to a full-width row of their own under brand, Search and the action, so the action never
   falls off the first row. */
header.site .wrap { display: flex; align-items: center; gap: 0.5rem 1rem; min-height: 4rem; padding-top: 0.6rem; padding-bottom: 0.6rem; flex-wrap: nowrap; }
header.site nav { display: flex; flex-wrap: wrap; align-items: center; flex: 1 1 auto; margin-left: 0.5rem; }
header.site nav a { display: inline-flex; align-items: center; min-height: 44px; font-size: 0.9375rem; font-weight: 600; color: var(--ink-2); text-decoration: none; padding: 0.4rem 0.5rem; border-radius: 4px; }
header.site nav a:hover { color: var(--ink); background: var(--hover); }
header.site nav a.on { color: var(--ink); box-shadow: inset 0 -3px 0 var(--accent); border-radius: 0; }
header.site nav a.account { color: var(--ink); margin-left: 0.5rem; }
header.site a.start { display: inline-flex; align-items: center; flex: none; min-height: 44px; white-space: nowrap; background: var(--action); color: var(--on-action); border-radius: var(--r-control); padding: 0 1.1rem; font-weight: 700; font-size: 0.9375rem; text-decoration: none; }
header.site a.start:hover { background: var(--action-hover); }
header.site .palette-open { flex: none; display: inline-flex; align-items: center; gap: 0.5rem; min-height: 44px; padding: 0 0.8rem; color: var(--ink-2); font-weight: 600; }
header.site .palette-open[hidden] { display: none; }
header.site .palette-open:hover { color: var(--ink); }
header.site .palette-open .ic { flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
header.site .palette-open { padding: 0; width: 44px; justify-content: center; }
header.site .palette-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
header.site .header-end { display: flex; align-items: center; flex: none; position: relative; }
header.site .bell { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; min-height: 44px; padding: 0; background: transparent; color: var(--ink-2); border: 1.5px solid transparent; border-radius: var(--r-control); }
header.site .bell:hover { color: var(--ink); background: var(--hover); border-color: transparent; }
header.site .bell:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
header.site .bell-count { position: absolute; top: 3px; right: 1px; min-width: 1.25rem; height: 1.25rem; padding: 0 0.28rem; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 0.75rem; font-weight: 700; line-height: 1; color: var(--on-action); background: var(--teal); border-radius: 0.6rem; }
header.site .bell-count[hidden] { display: none; }
header.site .bell-pop { position: fixed; inset: 4.25rem 1rem auto auto; margin: 0; width: min(23rem, calc(100vw - 1rem)); max-height: min(30rem, calc(100dvh - 5rem)); overflow: auto; padding: 0.5rem 0.75rem 0.75rem; background: var(--sheet); color: var(--ink); border: 1px solid var(--rule-strong); border-radius: 10px; box-shadow: 0 14px 36px rgb(0 0 0 / 0.55); }
header.site .bell-pop:not(:popover-open) { display: none; }
header.site .bell-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.25rem 0 0.5rem; border-bottom: 1px solid var(--rule); }
header.site .bell-head form { margin: 0; }
header.site .bell-head form[hidden] { display: none; }
header.site .bell-list { list-style: none; margin: 0; padding: 0; }
header.site .bell-list li { display: flex; flex-direction: column; gap: 0.1rem; padding: 0.6rem 0 0.6rem 0.75rem; border-bottom: 1px solid var(--rule); border-left: 3px solid transparent; font-size: 0.875rem; }
header.site .bell-list li.unread { border-left-color: var(--teal); }
header.site .bell-title { font-weight: 650; color: var(--ink); }
header.site .bell-list li:not(.unread) .bell-title { font-weight: 500; color: var(--ink-2); }
header.site .bell-body, header.site .bell-when { font-size: 0.8125rem; }
header.site .bell-links { display: flex; gap: 0.9rem; font-size: 0.8125rem; font-weight: 650; }
header.site .bell-links a { padding: 0.2rem 0; }
header.site .bell-empty { margin: 0.75rem 0; font-size: 0.875rem; }
header.site .bell-empty[hidden] { display: none; }
header.site .bell-all { display: inline-block; margin-top: 0.6rem; font-size: 0.875rem; font-weight: 650; }
header.site .bell { transition: background 0.15s, color 0.15s; }
/* The account: a round person button at the end of the header (base.html), with the bell's unread count; on a phone too. */
header.site .acct-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; flex: none; width: 44px; height: 44px; min-height: 44px; margin-left: 0.25rem;
  color: var(--ink); background: var(--sheet); border: 1px solid var(--rule-strong); border-radius: 50%; text-decoration: none; transition: background 0.15s, border-color 0.15s; }
header.site .acct-btn .ic { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
header.site .acct-btn:hover { background: var(--hover); }
header.site .acct-btn.on { border-color: var(--teal); color: var(--teal); }
header.site .acct-btn:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
header.site .acct-badge { position: absolute; top: -4px; right: -6px; min-width: 1.25rem; height: 1.25rem; padding: 0 0.28rem; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  font: 700 0.75rem/1 var(--font-mono); color: var(--on-action); background: var(--teal); border-radius: 0.6rem; box-shadow: 0 0 0 2px var(--paper); }
header.site .acct-badge[hidden] { display: none; }
@media (min-width: 641px) { header.site .acct-badge { display: none; } }   /* the bell beside it carries the count there; a phone hides the bell */
/* "My bots" in the header's nav and its tab (_nav.html): the count of the account's ready bots; amber and pulsing while one plays a
   set, a dot when one plays and none is ready here. */
header.site nav a.bots-link { gap: 0.4rem; }
.nav-badge { min-width: 1.25rem; height: 1.25rem; padding: 0 0.28rem; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  font: 700 0.75rem/1 var(--font-mono); color: var(--on-action); background: var(--teal); border-radius: 0.6rem; }
.nav-badge[hidden] { display: none; }
.nav-badge.is-playing, .tabbar .tab-badge.is-playing { background: var(--amber); }
.nav-badge:empty, .tabbar .tab-badge:empty { min-width: 0; width: 0.6rem; height: 0.6rem; padding: 0; }
@media (prefers-reduced-motion: no-preference) {
  .nav-badge.is-playing, .tabbar .tab-badge.is-playing { animation: bots-pulse 1.6s ease-in-out infinite; }
}
@keyframes bots-pulse { 0%, 100% { box-shadow: 0 0 0 2px var(--sheet), 0 0 0 2px rgb(242 184 75 / 0.6); } 50% { box-shadow: 0 0 0 2px var(--sheet), 0 0 0 7px rgb(242 184 75 / 0); } }
@media (max-width: 1179px) {
  header.site .wrap { flex-wrap: wrap; row-gap: 0; padding-top: 0.5rem; padding-bottom: 0.25rem; }
  header.site .brand { order: 0; margin-right: auto; }
  header.site .palette-open { order: 1; }
  header.site .header-end, header.site a.start { order: 2; }
  header.site nav { order: 3; flex: 1 0 100%; margin: 0 -0.5rem; }
}
@media (max-width: 700px) {
  header.site .bell-pop { inset: 3.75rem 0.5rem auto auto; }
}
/* The primary nav is a popover (base.html): a plain row on wide screens, and on a phone the sheet the Menu button opens,
   so the header stays one row: brand, search, Menu, sign in or bell. Without popover support the nav keeps its own row. */
header.site nav[popover] { position: static; inset: auto; width: auto; height: auto; max-width: none; padding: 0; border: 0; background: transparent; color: inherit; overflow: visible; margin: 0 0 0 0.5rem; }
header.site .menu-btn { display: none; }
@supports selector(:popover-open) {
  @media (max-width: 899px) {
    header.site .wrap { flex-wrap: nowrap; row-gap: 0; padding-top: 0.5rem; padding-bottom: 0.5rem; gap: 0.25rem 0.5rem; }
    header.site .menu-btn { order: 2; display: inline-flex; align-items: center; justify-content: center; flex: none; width: 44px; height: 44px; min-height: 44px; padding: 0; color: var(--ink-2); anchor-name: --site-menu; }
    header.site .menu-btn:hover, header.site nav:popover-open ~ .menu-btn { color: var(--ink); background: var(--hover); }
    header.site nav:popover-open ~ .menu-btn { border-color: var(--teal); }
    header.site .menu-btn .ic { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; }
    header.site .brand > span:last-child { font-size: 1.375rem; }
    header.site a.start { padding: 0 0.9rem; }
    header.site nav[popover]:not(:popover-open) { display: none; }
    header.site nav[popover]:popover-open { position: fixed; inset: 4rem 0.75rem auto; margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0.25rem; padding: 0.5rem; max-height: calc(100dvh - 5rem); overflow: auto; background: var(--sheet); border: 1px solid var(--rule-strong); border-radius: 10px; box-shadow: 0 14px 36px rgb(0 0 0 / 0.55); }
    header.site nav[popover] a { min-height: 48px; padding: 0.5rem 0.75rem; font-size: 1rem; border-radius: var(--r-control); }
    header.site nav[popover] a.on { box-shadow: none; background: var(--hover); color: var(--teal); }
    header.site nav[popover] a.account { margin-left: 0; }
    @supports (position-anchor: --site-menu) {
      header.site nav[popover]:popover-open { position-anchor: --site-menu; inset: auto 0.75rem auto; top: calc(anchor(bottom) + 6px); }
    }
    @media (prefers-reduced-motion: no-preference) {
      header.site nav[popover] { transition: opacity 0.15s ease-out, transform 0.15s ease-out, display 0.15s allow-discrete, overlay 0.15s allow-discrete; }
      header.site nav[popover]:not(:popover-open) { opacity: 0; transform: translateY(-6px); }
      @starting-style { header.site nav[popover]:popover-open { opacity: 0; transform: translateY(-6px); } }
    }
  }
}
@media (max-width: 420px) { header.site .brand > span:last-child { font-size: 1.25rem; } header.site .brand-mark { width: 1.6rem; } }
@media (max-width: 359px) { header.site .brand-mark { display: none; } }
/* The back arrow (base.html's a.hdr-back, back.js, web/nav_up.py): on a phone and in the installed app's frame (html.in-shell, any
   width), on every page but the tab roots; a desktop browser has its own back button and the full nav. A teal chevron, the
   parent's name beside it when the page was landed on directly ("‹ Live"), alone when it goes back (.is-back). It sits in
   the gutter's edge (-12px) so the chevron lines up with the content, and the brand beside it drops to its mark. */
header.site .hdr-back { display: none; order: -1; flex: 0 1 auto; align-items: center; min-width: 44px; height: 44px; min-height: 44px; margin-left: -12px; padding: 0 0.5rem 0 0.5rem;
  color: var(--ink); text-decoration: none; border-radius: var(--r-control); font: 600 1rem/1 var(--font); white-space: nowrap; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
header.site .hdr-back:hover, header.site .hdr-back:active { background: var(--hover); }
header.site .hdr-back:focus-visible { outline: 2px solid var(--teal); outline-offset: 0; }
header.site .hdr-back .ic { flex: none; margin: 0 -2px; color: var(--teal); fill: none; stroke: currentColor; stroke-width: 2.25; stroke-linecap: round; stroke-linejoin: round; }
header.site .hdr-back-lbl { min-width: 0; max-width: 7.5rem; overflow: hidden; text-overflow: ellipsis; margin-left: 0.1rem; }
header.site .hdr-back.is-back .hdr-back-lbl, header.site .hdr-back-lbl:empty { display: none; }
header.site.has-back .brand { min-width: 0; }
header.site.has-back .brand > span:last-child { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }   /* the mark only; the link keeps its name */
@media (max-width: 640px) { header.site .hdr-back { display: inline-flex; } }
@media (min-width: 641px) { html:not(.in-shell) header.site.has-back .brand > span:last-child { position: static; width: auto; height: auto; overflow: visible; clip-path: none; } }
html.in-shell header.site .hdr-back { display: inline-flex; }
@media (max-width: 359px) { header.site.has-back .brand-mark { display: inline-flex; } header.site .hdr-back-lbl { display: none; } }

/* Where you are and where next (flow pass): breadcrumbs on pages deeper than two levels, the next-set links under a set. */
.crumbs { margin: 0.25rem 0 0.75rem; font-size: 0.875rem; color: var(--ink-2); }
@media (max-width: 640px) { .crumbs { display: none; } }      /* a phone's header arrow is the way up there (base.html's a.hdr-back) */
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; margin: 0; padding: 0; list-style: none; }
.crumbs li { display: inline-flex; align-items: center; min-width: 0; }
.crumbs li + li::before { content: "\203A"; margin: 0 0.45rem; color: var(--rule-strong); }
.crumbs a { display: inline-flex; align-items: center; min-height: 32px; color: var(--ink-2); text-decoration: none; }
.crumbs a:hover { color: var(--ink); text-decoration: underline; }
.crumbs [aria-current] { color: var(--ink); font-weight: 600; overflow-wrap: anywhere; }
@media (pointer: coarse) { .crumbs a { min-height: 44px; } }
.flow-nav { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0.75rem 0; }
.flow-nav .flow-note { flex-basis: 100%; margin: 0; font-size: 0.875rem; }
.err-go { margin-top: 1rem; }
.panel-more { display: inline-flex; align-items: center; min-height: 44px; margin-top: 0.25rem; font-weight: 650; text-decoration: none; color: var(--teal); }
.panel-more::after { content: "\00a0\2192"; }
.panel-more:hover { text-decoration: underline; }
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  header.site { view-transition-name: site-head; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.16s; }
}
.brand { display: inline-flex; align-items: center; gap: 0.55rem; text-decoration: none; color: var(--ink); }
.brand > span:last-child { font-size: 1.625rem; font-weight: 800; letter-spacing: -0.035em; line-height: 1; }
.brand:hover > span:last-child { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.2em; }
/* Two pieces, seat 1 and seat 2: the mark is drawn by the stylesheet, the markup is one empty span. */
.brand-mark { display: inline-flex; flex: none; align-items: flex-start; width: 1.9rem; padding-bottom: 0.45rem; background: linear-gradient(var(--accent), var(--accent)) bottom / 100% 3px no-repeat; }
.brand-mark::before, .brand-mark::after { content: ""; width: 1.1rem; height: 1.1rem; border-radius: 50%; background: var(--seat1); }
.brand-mark::after { background: var(--seat2); margin-left: -0.3rem; opacity: 0.92; }
/* On the home page the mark plays its two moves: red drops into place, blue a beat later, then the rule draws. */
@media (prefers-reduced-motion: no-preference) {
  .brand.intro .brand-mark::before, .brand.intro .brand-mark::after { animation: brand-drop 0.6s both, brand-fade 0.15s both; }
  .brand.intro .brand-mark::after { animation-delay: 0.16s; }
  .brand.intro .brand-mark { animation: brand-rule 0.45s 0.62s cubic-bezier(0.16, 1, 0.3, 1) both; }
}
@keyframes brand-drop {
  0% { transform: translateY(var(--brand-drop, -1.5rem)); animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.6); }
  60% { transform: none; animation-timing-function: cubic-bezier(0.2, 0.6, 0.4, 1); }
  78% { transform: translateY(-0.2rem); animation-timing-function: cubic-bezier(0.6, 0, 0.8, 0.4); }
  100% { transform: none; }
}
@keyframes brand-fade { from { opacity: 0; } }
@keyframes brand-rule { from { background-size: 0 3px; } }

/* Footer D, Broadcast: a band darker than the page under a 4px accent edge. */
footer.site {
  --ink: #e6edf7; --ink-2: #aab5cc; --rule: #273149; --paper: #05070d;
  background: var(--footer-bg); color: var(--ink-2); border-top: 4px solid var(--teal); font-size: 0.875rem; margin-top: 1rem;
}
footer.site .wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; padding-top: 1.75rem; padding-bottom: 1.5rem; }
footer.site .statement { margin: 0 0 0.35rem; color: var(--ink); font-size: 1.0625rem; max-width: 70ch; }
footer.site .links { display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
footer.site .links a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; color: var(--ink-2); text-decoration: none; }
footer.site .links a:hover { color: var(--teal); text-decoration: underline; }
/* The install hint (base.html, pwa.js): one quiet line under the links, with its own dismiss button. */
footer.site .install-hint { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; padding: 0.15rem 0.15rem 0.15rem 0.85rem; max-width: 100%; color: var(--ink); background: var(--paper); border: 1px solid var(--rule); border-left: 3px solid var(--teal); border-radius: var(--r-sheet); }
footer.site .install-hint p { margin: 0; padding: 0.5rem 0; }
footer.site .install-hint [data-install-prompt] { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; }
footer.site .install-hint[hidden], footer.site .install-hint [hidden] { display: none; }
footer.site .install-hint .share { white-space: nowrap; }
footer.site .install-hint .share-ic { display: inline-block; vertical-align: -0.2em; fill: none; stroke: var(--teal); stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
footer.site .install-x { flex: none; margin-left: auto; width: 44px; height: 44px; padding: 0; border-color: transparent; color: var(--ink-2); font-size: 1.25rem; }
main.offline-page { padding-top: 3rem; padding-bottom: 4rem; }

.page-head { margin: 2.25rem 0 1.75rem; }
.page-head .lead { margin-top: 0.75rem; }
.page-head .actions { margin-top: 1.25rem; }
.page-head.with-action { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem 2rem; flex-wrap: wrap; }
.page-head .head-actions { display: flex; align-items: center; gap: 0.75rem 1rem; flex-wrap: wrap; }
.page-head h1 a { color: inherit; text-decoration: none; }
.page-head h1 a:hover { text-decoration: underline; text-decoration-thickness: 3px; }
.subline { margin: 0.6rem 0 0; color: var(--ink-2); }
.subline .tag { margin-right: 0.35rem; }
.subline a + a, .subline span + a { margin-left: 0.6rem; }
.section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem 1rem; margin: 2.75rem 0 0.85rem; flex-wrap: wrap; }
.section-head h2 { margin: 0; display: flex; align-items: center; gap: 0.6rem; }
.more { font-size: 0.9375rem; font-weight: 550; white-space: nowrap; }
details.more { white-space: normal; margin: 0 0 0.75rem; }
details.more > summary { display: flex; align-items: center; min-height: 44px; cursor: pointer; color: var(--teal); text-decoration: underline; text-underline-offset: 3px; }
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: start; }
.cols > * { min-width: 0; }
.cols > section > h2:first-child, .cols > section > .section-head:first-child { margin-top: 0; }
.grid { display: grid; gap: 1rem; }
.prose-page { max-width: 72ch; }
/* Running prose keeps a readable measure; tables, sheets and the arena use the full column. */
main.wrap > p, main.wrap > ul, main.wrap > ol, main.wrap > h2 + p, .doc-page > p, .doc-page > ul, .doc-page > ol, ol.steps > li { max-width: 72ch; }
.prose-page h2 { font-size: 1.3125rem; margin-top: 2rem; }

/* ---- flashes and notices ------------------------------------------------------------------------- */
.flash { padding: 0.75rem 1rem; margin: 1.25rem 0 0; background: var(--sheet); border: 1px solid var(--rule); border-left: 4px solid var(--check); border-radius: var(--r-sheet); }
.flash.error { border-left-color: var(--err); }
.flash.info { border-left-color: var(--ink-2); }
.flash.key { border-left-color: var(--ink); word-break: break-all; }
.notice { padding: 0.8rem 1rem; margin: 0.75rem 0 1rem; background: var(--sheet); border: 1px solid var(--rule); border-left: 4px solid var(--ink); border-radius: var(--r-sheet); display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.notice.blocked, .notice.first-move.stalled { display: block; border-left-color: var(--err); background: var(--err-wash); border-color: var(--err-line); }
.notice.first-move { display: block; }
.notice.first-move p { margin: 0.6rem 0 0.3rem; }
.notice .countdown { font-variant-numeric: tabular-nums; }

/* ---- controls ------------------------------------------------------------------------------------ */
button, .btn {
  font: inherit; font-size: 0.9375rem; font-weight: 650; line-height: 1.2; padding: 0.6rem 1rem; min-height: 2.5rem;
  border-radius: var(--r-control); border: 1.5px solid var(--action); background: var(--action); color: var(--on-action);
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; text-decoration: none; white-space: nowrap;
}
button:hover, .btn.primary:hover, .btn.small:not(.ghost):hover { background: var(--action-hover); border-color: var(--action-hover); text-decoration: none; }
.btn { background: transparent; color: var(--ink); }
[data-keys-open][hidden], [data-shortcuts-open][hidden] { display: none; }   /* .btn's display would show them: keyboard help is for a fine pointer only */
.btn:hover { background: var(--hover); text-decoration: none; }
.btn.primary, .btn.small:not(.ghost) { background: var(--action); color: var(--on-action); }
button.ghost, .btn.ghost { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
button.ghost:hover, .btn.ghost:hover { background: var(--hover); border-color: var(--ink); }
button.danger { background: transparent; color: var(--err); border-color: var(--err); }
button.danger:hover { background: var(--err-wash); }
/* icon button: a bare, borderless control for a number or glyph that acts (move numbers, transport); the quiet end of the shared set. */
button.icon, .btn.icon { background: transparent; color: var(--ink-2); border-color: transparent; min-height: 0; padding: 0.1rem 0.35rem; border-radius: 4px; font-size: 0.8125rem; justify-self: end; }
button.icon:hover, .btn.icon:hover { background: var(--field); border-color: var(--rule); color: var(--ink); }
button.small, .btn.small { font-size: 0.875rem; padding: 0.35rem 0.75rem; min-height: 2rem; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:disabled:hover { background: var(--action); border-color: var(--action); }
button.ghost:disabled:hover, button.danger:disabled:hover { background: transparent; }
.actions { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; }

input, select, textarea { font: inherit; font-size: 0.9375rem; padding: 0.5rem 0.65rem; min-height: 2.5rem; border: 1.5px solid var(--rule-strong); border-radius: 4px; background: var(--field); color: var(--ink); min-width: 0; }
input:focus-visible, select:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-color: var(--ink); }
input::placeholder { color: var(--placeholder); }
input[type="checkbox"], input[type="radio"] { min-height: 0; accent-color: var(--ink); width: 1.05rem; height: 1.05rem; margin: 0; }
input[type="range"] { min-height: 0; }
input:disabled { background: var(--hover); }
label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.875rem; font-weight: 600; color: var(--ink-2); }
label input, label select { font-weight: 400; color: var(--ink); }
/* iOS Safari zooms the page into any focused field under 16px, so touch and phone screens keep fields at 16px. The size is in px because the root font-size drops to 15px on phones. Never use maximum-scale or user-scalable=no for this: it blocks pinch-zoom. */
@media (hover: none) and (pointer: coarse), (max-width: 640px) { input, select, textarea { font-size: 16px; } }
label.check { flex-direction: row; align-items: center; gap: 0.6rem; font-weight: 400; font-size: 0.9375rem; color: var(--ink); }
form.filters, form.inline, .row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; margin: 0.6rem 0; }
form.inline label, form.filters label { flex-direction: row; align-items: center; gap: 0.5rem; }
form.stack { display: flex; flex-direction: column; gap: 0.9rem; max-width: 28rem; }
form.stack.narrow { max-width: 26rem; }
form.stack button { align-self: flex-start; }
form.panel { padding: 1rem 1.25rem; }
fieldset { min-width: 0; }
details summary { cursor: pointer; font-weight: 600; font-size: 0.9375rem; }
details summary:hover { text-decoration: underline; text-underline-offset: 0.2em; }

/* ---- surfaces: sheets, tables, facts -------------------------------------------------------------- */
.panel { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 1.25rem 1.5rem; margin: 0.75rem 0; }
.panel > h2:first-child, .panel > h3:first-child { margin-top: 0; }
.card-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem 1rem; flex-wrap: wrap; }
.card-head h2, .card-head h3 { margin: 0; }
.empty-state { padding: 1.5rem; border: 1.5px dashed var(--rule-strong); border-radius: var(--r-sheet); }
.empty { text-align: center; padding: 1.4rem 1rem !important; }

/* position: relative keeps a visually hidden (.vh) heading inside the scroller, so it can't widen the page. */
.table-wrap { position: relative; overflow-x: auto; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); margin: 0.6rem 0 1.1rem; -webkit-overflow-scrolling: touch; }
.table-wrap table { margin: 0; }
table { border-collapse: collapse; width: 100%; margin: 0.6rem 0 1.1rem; font-size: 0.875rem; }
th, td { text-align: left; padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { font-size: 0.8125rem; font-weight: 600; color: var(--ink-2); white-space: nowrap; border-bottom: 1.5px solid var(--rule-strong); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--row-hover); }
th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td.rank { color: var(--ink-2); font-weight: 650; }
td.ts { white-space: nowrap; font-variant-numeric: tabular-nums; }
tr.aborted td, tr.closed td { color: var(--ink-2); }
tr.revoked td { color: var(--ink-2); text-decoration: line-through; }
tr.on-move td { font-weight: 650; }
table.compact th, table.compact td { padding: 0.4rem 0.6rem; font-size: 0.8125rem; }
table.params { margin: 0.35rem 0 0.75rem; }
td.options { font-family: var(--font-mono); font-size: 0.8125rem; color: var(--ink-2); min-width: 14rem; max-width: 30rem; white-space: normal; line-height: 1.7; }
td.options .opt { white-space: nowrap; }
td code { overflow-wrap: normal; }
td form.inline { margin: 0; }
.panel > p, .panel > ul { max-width: 80ch; }
/* Ids are for the API: kept whole in the markup (select to copy), cut short on screen. */
table.lobby td:first-child code, table.sets td:first-child code, table.keys td:first-child small, table.fair-sets td:last-child code { display: inline-block; max-width: 9.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
table.sets td:nth-child(2) a, table.leaders td:last-child a { white-space: nowrap; }
table.lobby td:first-child code { max-width: 6.5rem; }
table.lobby .clock, table.sets .clock { border: 0; padding: 0; color: inherit; font-weight: 400; }

dl.facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.45rem 1.5rem; margin: 0.6rem 0 1.1rem; }
dl.facts dt { color: var(--ink-2); font-size: 0.875rem; font-weight: 600; padding-top: 0.08rem; }
dl.facts dd { margin: 0; min-width: 0; }
dl.facts.inline { gap: 0.3rem 1rem; font-size: 0.9375rem; margin: 0.5rem 0 0.75rem; }

.tabs { display: flex; gap: 0.1rem; flex-wrap: wrap; border-bottom: 1px solid var(--rule); margin: 0.5rem 0 1.25rem; }
.tabs a { padding: 0.55rem 0.8rem; color: var(--ink-2); font-weight: 600; font-size: 0.9375rem; text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs a:hover { color: var(--ink); background: var(--hover); }
.tabs a.on { color: var(--ink); border-bottom-color: var(--accent); }
.tabs.small { border: 1.5px solid var(--ink); border-radius: var(--r-control); padding: 0; margin: 0; gap: 0; overflow: hidden; }
.tabs.small a { padding: 0.3rem 0.85rem; border: 0; margin: 0; font-size: 0.875rem; }
.tabs.small a.on { background: var(--action); color: var(--on-action); }

/* The game picker (_macros.html game_menu_btn): on a phone a row of games (the filter chips of Live, Recent and the lobby,
   the leaderboard's tabs) is the popover of a "Game: <name>" button: a sheet of every game, two to a row, under the button
   (at the foot of the screen where the browser cannot anchor it), so no game is behind a sideways scroll. Wider, or without
   popover support, the button is hidden and the row stays where it is: its popover wrapper (.game-menu) is display:
   contents, so the row lays out as if the wrapper were not there. */
.game-menu-btn { display: none; }
.game-menu[popover] { display: contents; color: inherit; }
@supports selector(:popover-open) {
  @media (max-width: 640px) {
    .game-menu-btn { display: inline-flex; justify-content: flex-start; gap: 0.45rem; min-width: 0; padding: 0 0.85rem; anchor-name: --game-menu; }
    .game-menu-btn .game-menu-k { color: var(--ink-2); font-weight: 600; }
    .game-menu-btn .game-menu-v { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
    .game-menu-btn .chev { flex: none; width: 1rem; height: 1rem; margin-left: auto; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
    .game-menu-btn:has(+ .game-menu:popover-open) { border-color: var(--teal); }
    .game-menu-btn:has(+ .game-menu:popover-open) .chev { rotate: 180deg; }
    .game-menu[popover]:not(:popover-open) { display: none; }
    .game-menu[popover]:popover-open { display: block; position: fixed; inset: auto 0.75rem 0.75rem; margin: 0; padding: 0.5rem; max-height: min(32rem, calc(100dvh - 1.5rem)); overflow: auto; overscroll-behavior: contain; background: var(--sheet); color: var(--ink); border: 1px solid var(--rule-strong); border-radius: 10px; box-shadow: 0 14px 36px rgb(0 0 0 / 0.55); }
    @supports (position-anchor: --a) {
      .game-menu[popover]:popover-open { position-anchor: --game-menu; inset: auto 0.75rem auto; top: calc(anchor(bottom) + 6px); position-try-fallbacks: flip-block; }
    }
    .game-menu[popover] nav.tabs[aria-label="Game"] { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.25rem; margin: 0; padding: 0; border: 0; overflow: visible; }
    .game-menu[popover] nav.tabs::after { content: none; }
    .game-menu[popover] nav.tabs a { justify-content: flex-start; gap: 0.5rem; min-width: 0; padding: 0.5rem 0.75rem; margin: 0; border: 0; border-radius: var(--r-control); background: transparent; color: var(--ink); font-size: 0.9375rem; line-height: 1.25; white-space: normal; }
    .game-menu[popover] nav.tabs a:hover { background: var(--hover); }
    .game-menu[popover] nav.tabs a.on { background: var(--hover); color: var(--teal); }
    .game-menu[popover] nav.tabs .count { margin-left: auto; }
    @media (prefers-reduced-motion: no-preference) {
      .game-menu-btn .chev { transition: rotate 0.15s ease-out; }
      .game-menu[popover] { transition: opacity 0.15s ease-out, translate 0.15s ease-out, display 0.15s allow-discrete, overlay 0.15s allow-discrete; }
      .game-menu[popover]:not(:popover-open) { opacity: 0; translate: 0 -6px; }
      @starting-style { .game-menu[popover]:popover-open { opacity: 0; translate: 0 -6px; } }
    }
  }
}

.pager { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin: 1.25rem 0; }
.pager-side { flex: 1; display: flex; }
.pager .btn { white-space: nowrap; }
.pager-side:last-child { justify-content: flex-end; }
.pager-count { font-size: 0.875rem; text-align: center; font-variant-numeric: tabular-nums; }

/* ---- tags and chips: two components, never mixed ------------------------------------------------
   TAG = the ONE read-only label that states a status or a fact, in every template, area sheet and
   script: <span class="tag ok">. Variants are only these:
     .tag            neutral fact (tier, kind, count, finished); .mute is the quieter grey, .strong the ink outline
     .ok             teal: active, open, verified, connected, ready, beaten, just posted
     .warn           amber: staged, frozen, paused, scheduled, offline, tutorial tier, estimate
     .err            coral: failed, closed, cancelled, a loss reason
     .alt            violet: demo tier
     .solid          filled teal: live now
     .dot            leading dot in the tag colour (.dot.off hollow, .dot.pulse beats while live)
     .dashed         provisional or estimated figure
     .bare           text only, no outline: a quiet inline note
   CHIP = a selectable option that does something when pressed (.chip.option filter and choice chips). */
.tag { --tag-c: var(--ink-2); --tag-line: var(--rule-strong); display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.75rem; font-weight: 600; line-height: 1.35; padding: 0.05rem 0.55rem; border: 1px solid var(--tag-line); border-radius: 999px; color: var(--tag-c); vertical-align: 0.12em; white-space: nowrap; text-decoration: none; }
a.tag:hover { color: var(--ink); text-decoration: none; }
.tag.mute { --tag-line: var(--rule); }
.tag.strong { --tag-c: var(--ink); --tag-line: var(--ink); }
.tag.ok { --tag-c: var(--teal); --tag-line: var(--teal); }
/* USDC prizes (core/cash.py): amber, never the teal of points. */
.usdc { color: var(--usdc); }
.tag.usdc { --tag-c: var(--usdc); --tag-line: var(--usdc); }
.usdc-plus { color: var(--usdc); font-family: var(--font-mono); font-size: 0.8125rem; white-space: nowrap; }
.tag.warn { --tag-c: var(--amber); --tag-line: var(--amber); }
.tag.err { --tag-c: var(--err); --tag-line: var(--err); }
.tag.alt { --tag-c: var(--violet); --tag-line: var(--violet); }
.tag.solid { color: var(--on-action); background: var(--teal); --tag-line: var(--teal); }
.tag.dashed { border-style: dashed; cursor: help; }
.tag.bare { padding: 0; border-color: transparent; font-size: 0.875rem; font-weight: 500; }
.tag.dot::before { content: ""; flex: none; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; }
.tag.dot.off::before { background: transparent; border: 1.5px solid currentColor; box-sizing: border-box; }
.tag.dot.pulse::before { animation: tag-pulse 1.4s ease-in-out infinite; }
@keyframes tag-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .tag.dot.pulse::before { animation: none; } }
h1 .tag { font-size: 0.875rem; vertical-align: 0.4em; letter-spacing: 0; font-weight: 600; }
.chip { display: inline-block; font-size: 0.75rem; font-weight: 650; line-height: 1.35; padding: 0.08rem 0.45rem; border: 1px solid currentColor; border-radius: 4px; color: var(--ink-2); vertical-align: 0.12em; white-space: nowrap; }
.chip.option { font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 500; color: var(--ink); background: var(--sheet); border-color: var(--rule); vertical-align: baseline; }
.set-options { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.85rem 0 0; }
/* The lobby's strip of your bots: each name a status tag, the dot filled when connected. */
.bot-strip { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1.25rem; margin: 0 0 0.75rem; font-size: 0.8125rem; }

/* ---- seats: a disc in the seat's colour, numbered where the markup carries the seat ---------------- */
.badge { --seat-c: var(--rule-strong); --seat-d: var(--ink-2); display: inline-flex; align-items: center; gap: 0.45rem; max-width: 100%; min-width: 0; font-weight: 650; font-size: 0.9375rem; color: inherit; text-decoration: none; vertical-align: middle; }
a.badge:hover .badge-name, a.badge:hover > span:last-child { text-decoration: underline; text-underline-offset: 0.2em; }
.badge::before { content: ""; flex: none; width: 0.75rem; height: 0.75rem; border-radius: 50%; background: var(--seat-c); }
.badge:has(.badge-seat)::before { content: none; }
.badge.s1, .pip.s1 { --seat-c: var(--seat1-dot); --seat-d: var(--seat1-deep); --seat-e: var(--seat1); }
.badge.s2, .pip.s2 { --seat-c: var(--seat2-dot); --seat-d: var(--seat2-deep); --seat-e: var(--seat2); }
.badge.s3, .pip.s3 { --seat-c: var(--seat3-dot); --seat-d: var(--seat3-deep); --seat-e: var(--seat3); }
.badge.s4, .pip.s4 { --seat-c: var(--seat4-dot); --seat-d: var(--seat4-deep); --seat-e: var(--seat4); }
.badge.s5, .pip.s5 { --seat-c: var(--seat5-dot); --seat-d: var(--seat5-deep); --seat-e: var(--seat5); }
.badge.s6, .pip.s6 { --seat-c: var(--seat6-dot); --seat-d: var(--seat6-deep); --seat-e: var(--seat6); }
.badge.s7, .pip.s7 { --seat-c: var(--seat7-dot); --seat-d: var(--seat7-deep); --seat-e: var(--seat7); }
.badge.s8, .pip.s8 { --seat-c: var(--seat8-dot); --seat-d: var(--seat8-deep); --seat-e: var(--seat8); }
/* On the slate the engines' own seat colours read, as they do on the boards. */
.arena .badge { --seat-c: var(--seat-e); }
.badge-seat { position: relative; flex: none; display: inline-grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--seat-d); color: var(--numeral); font-size: 0.75rem; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.badge-name, .badge > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.badge.on-move .badge-seat { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--seat-c); }
.badge.winner { font-weight: 800; }
li.s1 .mover { color: var(--seat1-ink); }
li.s2 .mover { color: var(--seat2-ink); }
li.s3 .mover { color: var(--seat3-ink); } li.s4 .mover { color: var(--seat4-ink); } li.s5 .mover { color: var(--seat5-ink); }
li.s6 .mover { color: var(--seat6-ink); } li.s7 .mover { color: var(--seat7-ink); } li.s8 .mover { color: var(--seat8-ink); }

/* Tables (players > 2). In the lobby: how many seats are taken, in words and as one pip per seat,
   filled in the seat's colour once taken. On a set: the ranking is the result, first place first. */
.seats-taken { font-size: 0.8125rem; color: var(--ink-2); white-space: nowrap; }
.seat-pips { display: inline-flex; gap: 3px; vertical-align: middle; margin-left: 0.35rem; }
.pip { width: 0.6rem; height: 0.6rem; border-radius: 50%; border: 1.5px solid var(--rule-strong); }
.pip.taken { background: var(--seat-c); border-color: var(--seat-c); }
ol.ranking { list-style: none; counter-reset: place; margin: 0; padding: 0; }
ol.ranking li { counter-increment: place; display: grid; grid-template-columns: 2rem minmax(0, 1fr); align-items: baseline; margin: 0; padding: 0.3rem 0; border-bottom: 1px solid var(--rule); }
ol.ranking li:last-child { border-bottom: 0; }
ol.ranking li::before { content: counter(place); font-weight: 800; color: var(--ink-2); font-variant-numeric: tabular-nums; }
ol.ranking li:first-child { font-size: 1.3125rem; font-weight: 800; }
ol.ranking li:first-child::before { color: var(--ink); }

/* The running side of a chess clock is the one lit up. */
.clock { display: inline-block; font-variant-numeric: tabular-nums; font-size: 0.875rem; font-weight: 600; color: var(--ink-2); padding: 0.1rem 0.45rem; border: 1px solid var(--rule); border-radius: 4px; white-space: nowrap; }
.clock.ticking { background: var(--ink); border-color: var(--ink); color: var(--paper); }

.live { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 650; }
.live-dot { flex: none; width: 0.6rem; height: 0.6rem; border-radius: 50%; background: currentColor; animation: pulse 1.8s ease-out infinite; }
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 var(--pulse); }
  70% { box-shadow: 0 0 0 0.55rem rgba(0, 0, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}

/* ---- the arena: a full-bleed slate band wherever games are shown ----------------------------------- */
.arena {
  --surface: var(--slate); --focus: var(--slate-ink); --pulse: rgba(230, 237, 247, 0.55); --row-hover: var(--slate-2);
  background: var(--slate); color: var(--slate-ink);
  /* Edge to edge without widening the page: a border-image drawn outside the box is ink overflow,
     so it never adds a sideways scroll. Its first and last pixel rows are the band's edge. */
  border-image: linear-gradient(to bottom, var(--arena-edge) 0 1px, var(--slate) 1px calc(100% - 1px), var(--arena-edge) calc(100% - 1px)) 0 fill / 0 / 0 100vmax;
  padding: 2rem 0 2.25rem; margin: 2.5rem 0;
}
.arena h2 { color: var(--slate-ink); }
.arena .muted { color: var(--slate-ink-2); }
.arena code { background: var(--slate-code-bg); }
.arena .tag:not(.ok, .warn, .err, .alt, .solid, .strong) { --tag-c: var(--slate-ink-2); --tag-line: var(--slate-rule); }
.arena .clock { color: var(--slate-ink-2); border-color: var(--slate-rule); }
.arena .clock.ticking { background: var(--slate-ink); border-color: var(--slate-ink); color: var(--slate); }
.arena .empty-state { border-color: var(--slate-rule); color: var(--slate-ink-2); }
.arena .section-head { margin-top: 0; }
.arena button, .arena .btn.primary { background: var(--teal); border-color: var(--teal); color: #06231e; }
.arena button:hover { background: var(--teal-hover); border-color: var(--teal-hover); }
.arena button.ghost { background: transparent; color: var(--slate-ink); border-color: var(--slate-rule); }
.arena button.ghost:hover { border-color: var(--slate-ink); background: var(--slate-2); }
.arena button:disabled:hover { background: var(--teal); border-color: var(--teal); }
.arena select { background: var(--slate-2); color: var(--slate-ink); border-color: var(--slate-field-line); }
.arena label { color: var(--slate-ink-2); }

.card-grid { grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); }
.set-card { position: relative; display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; padding: 0.85rem 0.85rem 0.75rem; border: 1px solid var(--slate-rule); border-radius: var(--r-board); background: var(--slate); }
.set-card.is-live { border-color: var(--slate-line); }
.set-card .card-head { align-items: center; font-size: 0.875rem; }
.set-card .game-name { font-size: 1.0625rem; font-weight: 750; color: var(--slate-ink); text-decoration: none; }
.set-card .game-name:hover { text-decoration: underline; }
.set-card .card-state { color: var(--slate-ink-2); font-weight: 600; text-align: right; }
.seat-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.5rem; }
.seat-cell { display: flex; flex-direction: column; gap: 0.35rem; align-items: flex-start; min-width: 0; }
.seat-cell.s2 { align-items: flex-end; }
.seat-cell .badge { font-size: 0.875rem; }
.seat-row.table-seats { grid-template-columns: minmax(0, 1fr); gap: 0.3rem; }
.seat-row.table-seats .seat-cell { flex-direction: row; justify-content: space-between; align-items: center; }
.mini-board { display: block; border-radius: 6px; overflow: hidden; outline-offset: 3px; }
/* The board frame (templates/_macros.html board_slot): the server draws no board. It is a skeleton while a board is
   expected and says why where none can be drawn: Boards need JavaScript (scripting off), or needs WebGL. */
.ag-board { display: grid; place-items: center; box-sizing: border-box; padding: 1rem; text-align: center; color: var(--slate-ink-2); font-size: 0.875rem; line-height: 1.4; }
.board-msg { display: block; max-width: 26rem; }
@media (scripting: enabled) { .board-msg-nojs { display: none; } }
@media (scripting: none) { .board-msg-gl { display: none; } }
.mini-board .ag-board { width: 100%; }
/* One frame for every card's board: 4:3, the board centred inside it, so cards and the page below never change height. */
.board-card .mini-board { aspect-ratio: 4 / 3; display: grid; place-items: center; background: var(--slate); }
.board-card .mini-board .ag-board { width: 100%; height: 100%; }
.mini-board:hover { box-shadow: 0 0 0 1px var(--slate-ink-2); }
.card-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem 0.75rem; flex-wrap: wrap; margin-top: auto; padding-top: 0.15rem; font-size: 0.875rem; color: var(--slate-ink-2); }
.set-card .card-foot .more { font-size: 0.875rem; color: var(--slate-ink); }

/* /recent's and Live's cards (_set_list.html): each bot's set points, with its payout (settled) or clock (live). */
.sets-arena { margin: 0 0 1.25rem; padding: 1.5rem 0 1.75rem; }
.arena .win { color: var(--teal); }
.arena .win strong { color: inherit; }
.board-card .seat-score { display: inline-flex; align-items: baseline; gap: 0.45rem; font-variant-numeric: tabular-nums; }
.board-card .seat-score .points { font-size: 1.0625rem; font-weight: 800; color: var(--slate-ink); }
.board-card .seat-score .payout { font-size: 0.8125rem; color: var(--slate-ink-2); }
.board-card .badge.winner .badge-seat { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--slate-check); }
.board-card.aborted .mini-board { opacity: 0.55; }
.board-card .no-board { display: grid; place-items: center; min-height: 8rem; border: 1px dashed var(--slate-rule); color: var(--slate-ink-2); font-size: 0.875rem; text-decoration: none; }
.board-card .set-options { margin: 0; gap: 0.3rem; }
.arena .chip.option { color: var(--slate-ink-2); background: transparent; border-color: var(--slate-rule); font-size: 0.75rem; }
.board-card .card-foot { gap: 0.35rem 0.75rem; }
.board-card .card-foot > * { white-space: nowrap; }
.board-card .card-foot .more { margin-left: auto; font-weight: 650; }
.board-card .seat-score .clock { font-size: 0.8125rem; }
/* The lobby's challenge cards: an example start (labelled, never the set's real one), the
   creator's rating where a set card has points, and the viewer's actions under the footer. */
.example-board { position: relative; margin: 0; }
.mini-board.example-board:hover { box-shadow: none; }
.example-board .example-label { position: absolute; top: 0.4rem; left: 0.4rem; padding: 0.1rem 0.45rem; border: 1px solid var(--slate-rule); border-radius: 4px; background: var(--slate); color: var(--slate-ink-2); font-size: 0.75rem; font-weight: 650; }
/* Board cards that move (Lobby, Recent, Live: web/card_motion.py, landing.js). A challenge card's
   sample game is a recording's board (.wall-board); a set card's board, while the browser draws
   it, keeps the card's one 4:3 frame and sits inside at its own ratio (--board-ratio), letterboxed on the slate. */
.demo-board .wall-board { border-radius: 6px; }
.anim-box { position: relative; display: grid; place-items: center; container-type: size; background: var(--slate); }
.anim-box > .render-board { width: min(100%, calc(100cqh * var(--board-ratio, 1.25))); pointer-events: none; }
.anim-box > .render-board canvas { display: block; }
/* a board with no WebGL context (off screen, or over the page's budget) leaves its last frame as a 2D canvas, on top of the cell its board takes (wall.ts, stillOf) */
.anim-box > .render-board.is-still, .wall-board > .render-board.is-still { grid-area: 1 / 1; z-index: 1; }
.anim-box .twist-cube-player { display: none !important; }
.anim-box .twist-cube-3d-text, .anim-box .snooker3d-hud { display: none !important; }
.challenge-card .card-state { display: inline-flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; justify-content: flex-end; }
.challenge-card .seats-taken { color: var(--slate-ink-2); }
.challenge-card .seat-pips { margin-left: 0; }
.challenge-card .pip { border-color: var(--slate-line); }
.challenge-card .open-seat { flex-direction: row; align-items: center; justify-content: flex-end; gap: 0.4rem; font-size: 0.875rem; }
.challenge-card .strikes { color: var(--slate-err); }
.challenge-card .card-actions { justify-content: flex-end; gap: 0.5rem; padding-top: 0.15rem; }
.challenge-card .card-actions .btn.small:not(.ghost) { background: var(--teal); border-color: var(--teal); color: #06231e; }
.challenge-card .card-actions .btn.small:not(.ghost):hover { background: var(--teal-hover); border-color: var(--teal-hover); }
.challenge-card .card-actions button.ghost { background: transparent; color: var(--slate-ink); border-color: var(--slate-rule); }
.challenge-card .card-actions button.ghost:hover { background: var(--slate-2); border-color: var(--slate-ink); }
.challenge-card .card-actions button.danger { background: transparent; color: var(--slate-err); border-color: var(--slate-err); }
.challenge-card .card-actions button.danger:hover { background: var(--slate-2); }
.lobby-note { margin: -0.5rem 0 1rem; font-size: 0.875rem; }

/* ---- home ---------------------------------------------------------------------------------------- */
.hero { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 2.5rem 3rem; align-items: center; padding: 3.5rem 0 1.5rem; }
.hero-title { font-size: clamp(2.5rem, 1.4rem + 4.4vw, 4rem); font-weight: 700; line-height: 1.04; letter-spacing: -0.025em; text-wrap: balance; margin: 0 0 1.25rem; }
.hero .lead { margin: 0 0 1.5rem; max-width: 52ch; }
.hero .actions { gap: 0.6rem 0.9rem; }
.hero .btn { min-height: 48px; padding: 0.6rem 1.35rem; font-size: 1rem; }
.agents-note { font-size: 0.9375rem; color: var(--ink-2); max-width: 52ch; margin-top: 1rem; }
.section-intro { margin: 0.35rem 0 0; max-width: 62ch; font-size: 0.9375rem; }
.section-head .more { display: inline-flex; align-items: center; min-height: 44px; font-weight: 700; text-decoration: none; border-bottom: 2px solid var(--accent); }
.section-head .more:hover { color: var(--accent-ink); }
.section-head { align-items: flex-end; }
.section-head h2 + .section-intro { margin-top: 0.35rem; }
#SKIP .home-steps > h2 { margin-top: 3rem; }

/* The memorable element: a move as the HTTP exchange it is. Your bot's side in casual mono, the
   platform's answers in linear sans, two lifelines and the arrows between them. */
.exchange { margin: 0.5rem 0 0; position: relative; }
.exchange .parties { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; padding-bottom: 0.6rem; }
.exchange .party { font-size: 1.5rem; font-weight: 800; line-height: 1.1; }
.exchange .party.server { text-align: right; }
.exchange ol { list-style: none; margin: 0; padding: 0.25rem 0 1.5rem; position: relative; }
.exchange ol::before, .exchange ol::after { content: ""; position: absolute; top: 0; bottom: 0; width: 2px; background: var(--ink); }
.exchange ol::before { left: 0; }
.exchange ol::after { right: 0; }
.exchange li { position: relative; margin: 0; padding: 1.5rem 1.5rem 0.85rem; }
.exchange li::after { content: ""; position: absolute; left: 2px; right: 2px; bottom: 0; height: 2px; background: var(--ink); }
.exchange li::before { content: ""; position: absolute; bottom: -5px; width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }
.exchange li.call::before { right: 2px; border-left: 11px solid var(--ink); }
.exchange li.reply::before { left: 2px; border-right: 11px solid var(--ink); }
.exchange li.call code { display: block; background: none; padding: 0; font-size: 1rem; font-weight: 650; line-height: 1.45; overflow-wrap: anywhere; }
.exchange li.call code + code { font-weight: 450; color: var(--ink-2); }
.exchange li.reply { color: var(--ink-2); font-size: 1rem; line-height: 1.45; padding-left: 2.25rem; }
.exchange li.reply strong { color: var(--ink); font-weight: 650; }
.exchange figcaption { font-size: 0.9375rem; color: var(--ink-2); margin-top: 0.85rem; }

main > .arena:last-child { margin-bottom: -3rem; }

/* A recorded game's board (web/landing.py), on /games, the lobby's sample games and /practice's
   picker: a board on the slate that keeps its own shape inside (letterboxed on the slate, which the
   engines draw on anyway): the empty board frame (.ag-board) fills it, and landing.js sizes the browser's
   board to the box's height (--board-ratio, its width over its height). */
.wall-board { position: relative; display: grid; place-items: center; aspect-ratio: 5 / 4; container-type: size; padding: 0.5rem; overflow: hidden; border: 1px solid var(--slate-rule); border-radius: var(--r-board); background: var(--slate); }
.wall-board > .ag-board:not(.vh) { position: absolute; inset: 0.5rem; padding: 0.25rem; }
.wall-board .board-msg { font-size: 0.75rem; }
.wall-board > .render-board { width: min(100%, calc(100cqh * var(--board-ratio, 1.25))); pointer-events: none; }
.wall-board > .render-board canvas { display: block; }
.wall-board .twist-cube-player { display: none !important; }   /* the solution player's controls: the board plays it */
.wall-board .twist-cube-3d-text, .wall-board .snooker3d-hud { display: none !important; }   /* 3D styles' captions: a tile is too small for them over the board */

/* Standings: one panel per game. Without scripting they stack under their game's name; app.js turns
   them into tabs (the game's name on a button, the others hidden). */
.standings-board { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); }
.standings-board .standing-panel { padding: 0.5rem 1rem 0.75rem; }
.standings-board .standing-panel + .standing-panel { border-top: 1px solid var(--rule); }
.standings-board .panel-title { margin: 0.5rem 0; font-size: 1.0625rem; }
.standings-board .table-wrap { border: 0; border-radius: 0; margin: 0; background: none; overflow-y: hidden; }
.standings-board .empty-state { margin: 0.5rem 0; }
.standings-board.is-tabbed { padding: 0; }
.standings-board.is-tabbed .standing-panel { padding: 0; border: 0; }
.standings-board.is-tabbed .panel-title { display: none; }
.tab-bar { display: flex; gap: 0.25rem; padding: 0.6rem 0.75rem; border-bottom: 1px solid var(--rule); overflow-x: auto; scrollbar-width: thin; }
.tab-bar button { flex: none; background: transparent; color: var(--ink-2); border: 1px solid transparent; border-radius: var(--r-control); font-weight: 600; padding: 0 1rem; white-space: nowrap; }
.tab-bar button:hover { background: var(--hover); color: var(--ink); }
.tab-bar button[aria-pressed="true"] { background: var(--hover); color: var(--ink); border-color: var(--accent); }
table.standings th[scope="row"] { font-family: inherit; font-size: 0.9375rem; font-weight: 600; color: inherit; white-space: normal; border-bottom: 1px solid var(--rule); border-bottom-width: 1px; }
table.standings tbody tr:last-child th { border-bottom: 0; }
table.standings td, table.standings th { vertical-align: baseline; }
table.standings td.rank { width: 3.5rem; text-align: left; }
table.standings td.rating { font-weight: 700; color: var(--ink); }
table.standings tbody tr:first-child td.rating { color: var(--accent-ink); }

/* Steps: three cards, the step's number large in the accent. */
ol.steps-3 { list-style: none; padding: 0; margin: 1.25rem 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 1rem; counter-reset: step; max-width: none; }
ol.steps-3 li { counter-increment: step; margin: 0; padding: 1.25rem; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); max-width: none; }
ol.steps-3 li::before { content: counter(step); display: block; font-family: var(--font-mono); font-weight: 700; font-size: 1.75rem; line-height: 1; color: var(--accent-ink); border-bottom: 2px solid var(--rule); padding-bottom: 0.75rem; margin-bottom: 0.75rem; }
ol.steps-3 h3 { margin: 0 0 0.4rem; font-size: 1.125rem; }
ol.steps-3 h3 a { text-decoration: none; }
ol.steps-3 h3 a:hover { text-decoration: underline; text-decoration-thickness: 2px; }
ol.steps-3 p { margin: 0; color: var(--ink-2); font-size: 0.9375rem; }
.command { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; background: var(--inset, var(--field)); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 0.4rem 0.4rem 0.4rem 0.9rem; }
.command pre { flex: 1 1 20rem; min-width: 0; margin: 0; padding: 0.5rem 0; border: 0; background: none; border-radius: 0; font-size: 0.75rem; white-space: pre-wrap; overflow-wrap: anywhere; }
.command button { color: var(--accent-ink); border-color: var(--accent); }
.command button:hover { background: var(--hover); border-color: var(--accent); }
details.one-move { margin-top: 1rem; }
details.one-move .exchange { margin-top: 1rem; max-width: 40rem; }

/* Proof: the receipts chained, then the three moments. */
.receipt-chain { margin: 0 0 1.25rem; }
.receipt-chain ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; padding: 0; margin: 0 0 0.5rem; max-width: none; }
.receipt-chain li { flex: 1 1 14rem; min-width: 0; margin: 0; position: relative; display: flex; flex-direction: column; justify-content: center; padding: 0.6rem 0.85rem; background: var(--sheet); border: 1px solid var(--rule); border-left: 3px solid var(--accent); border-radius: var(--r-control); font-family: var(--font-mono); font-size: 0.75rem; line-height: 1.8; overflow-wrap: anywhere; }
.receipt-chain li:not(:last-child)::after { content: ""; position: absolute; left: 100%; top: 50%; width: 1.5rem; height: 2px; background: var(--accent); }
.receipt-chain figcaption { font-size: 0.8125rem; }
ol.proof-steps { list-style: none; padding: 0; margin: 0.5rem 0 1rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 1rem; max-width: none; }
ol.proof-steps li { margin: 0; padding: 1.25rem; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); max-width: none; }
ol.proof-steps h3 { margin: 0 0 0.4rem; font-size: 1.125rem; }
ol.proof-steps p { margin: 0; color: var(--ink-2); font-size: 0.9375rem; }

/* ---- games gallery (/games, templates/games.html) ----------------------------------------------- */
/* Every enabled game as a tile on the slate, grouped by how it is played: its recorded game on a loop
   (a recording's board: .wall-board, landing.js), then its name, pitch,
   counts, top bot and where to go next. Four tiles to a row, two on a tablet, one on a phone. */
.games-arena { margin-top: 1.75rem; padding-top: 2rem; }
.gallery-group + .gallery-group { margin-top: 2.5rem; padding-top: 2rem; border-top: 1px solid var(--slate-rule); }
.gallery-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; margin: 0 0 1.1rem; }
.gallery-head h2 { margin: 0; font-size: 1.5rem; font-weight: 800; }
.gallery-head p { margin: 0; font-size: 0.9375rem; max-width: 62ch; }
.gallery-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.45rem 1rem; }
/* A tile is six rows of the grid (board, name, pitch, counts, top bot, buttons) so every tile of a row lines up. */
.gallery-tile { display: grid; grid-row: span 6; grid-template-rows: subgrid; row-gap: 0.45rem; min-width: 0; margin: 0; }
.gallery-tile > :first-child { grid-row: 1; margin-bottom: 0.25rem; }
.gallery-name { grid-row: 2; align-self: end; }
.gallery-tile .pitch { grid-row: 3; }
.gallery-counts { grid-row: 4; }
.gallery-top { grid-row: 5; }
.gallery-actions { grid-row: 6; }
.gallery-board { display: block; border-radius: var(--r-board); }
.gallery-board:hover .wall-board { border-color: var(--slate-ink-2); }
.gallery-tile.is-live .wall-board { border-color: var(--slate-line); }
.wall-board.no-board { color: var(--slate-ink-2); font-size: 0.875rem; border-style: dashed; }
.gallery-body { display: contents; }
.gallery-name { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin: 0; font-size: 1.125rem; font-weight: 750; line-height: 1.25; }
.gallery-name a { color: var(--slate-ink); text-decoration: none; min-width: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.gallery-name a:hover { text-decoration: underline; text-underline-offset: 3px; }
.gallery-tile .pitch { margin: 0; font-size: 0.9375rem; line-height: 1.45; color: var(--slate-ink-2); }
.gallery-counts { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0; font-size: 0.875rem; }
.gallery-counts a { color: var(--slate-ink-2); text-decoration: none; }
.gallery-counts a:hover { color: var(--slate-ink); text-decoration: underline; }
.gallery-counts strong { color: var(--slate-ink); font-variant-numeric: tabular-nums; }
.gallery-counts .live { color: var(--slate-ink); font-weight: 650; gap: 0.4rem; }
.gallery-counts .live-dot { width: 0.5rem; height: 0.5rem; }
.gallery-top { margin: 0; font-size: 0.875rem; color: var(--slate-ink-2); overflow-wrap: anywhere; }
.gallery-top a { color: var(--slate-ink); font-weight: 650; }
.gallery-top .rating { font-variant-numeric: tabular-nums; }
.gallery-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1.55rem; padding-top: 0.35rem; align-self: end; }
.gallery-actions .btn.small { background: transparent; color: var(--slate-ink); border-color: var(--slate-rule); }
.gallery-actions .btn.small:hover { background: var(--slate-2); border-color: var(--slate-ink); }
.gallery-actions .btn.small:not(.ghost) { background: var(--slate-ink); border-color: var(--slate-ink); color: var(--slate); }
.gallery-actions .btn.small:not(.ghost):hover { background: #ffffff; border-color: #ffffff; }

/* ---- set page ------------------------------------------------------------------------------------ */
/* Board first: the scoreboard on top (the live parts of _set_parts.html), the chips, the boards
   with their clocks in each header and the replay player under each, then the Moves / Fairness /
   Details tabs. */
.set-top { margin: 0.25rem 0 0; }
.set-notices:empty { display: none; }
.scoreboard { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 1rem 1.25rem 1.1rem; }
.sb-state { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; margin: 0 0 0.75rem; font-size: 0.875rem; }
.sb-duel { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 0.75rem 1.5rem; }
.sb-side { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; min-width: 0; }
.sb-side.right { align-items: flex-end; text-align: right; }
.sb-side .badge { font-size: clamp(1.0625rem, 0.9rem + 0.8vw, 1.5rem); font-weight: 800; }
.sb-side .badge-seat { width: 1.6rem; height: 1.6rem; font-size: 0.8125rem; }
.sb-side.won .badge-name { text-decoration: underline; text-decoration-thickness: 3px; text-underline-offset: 0.25em; text-decoration-color: var(--check); }
.sb-role { font-size: 0.8125rem; }
.sb-rating { font-size: 0.9375rem; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.delta { font-weight: 750; }
.delta.pos { color: var(--check); }
.delta.neg { color: var(--err); }
.delta.zero { color: var(--ink-2); }
.sb-score { display: flex; align-items: baseline; gap: 0.4rem; font-size: clamp(2.5rem, 1.8rem + 2.5vw, 3.75rem); font-weight: 850; line-height: 1; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.sb-dash { color: var(--ink-2); font-weight: 500; }
.sb-result { margin: 0.9rem 0 0; font-size: 1.0625rem; text-align: center; }
.sb-money { margin: 0.25rem 0 0; font-size: 0.875rem; text-align: center; }
.scoreboard.table .sb-result, .scoreboard.table .sb-money { text-align: left; }
.sb-title { margin: 0 0 0.4rem; font-size: 1rem; }
.sb-title .muted { font-weight: 500; font-size: 0.875rem; }
ol.sb-ranking { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr)); gap: 0 1.75rem; }
ol.sb-ranking li { display: grid; grid-template-columns: 2.1rem minmax(0, 1fr) auto auto; align-items: center; gap: 0.6rem; padding: 0.35rem 0; border-bottom: 1px solid var(--rule); min-width: 0; }
ol.sb-ranking li.first { font-size: 1.0625rem; }
ol.sb-ranking li.first .badge { font-weight: 800; }
.sb-place { font-weight: 800; color: var(--ink-2); font-variant-numeric: tabular-nums; }
li.first .sb-place { color: var(--ink); }
.sb-pts { font-weight: 750; font-variant-numeric: tabular-nums; white-space: nowrap; }
.set-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin: 0.75rem 0 0; }
.set-chips .set-options { display: contents; }
.boards.arena { margin-top: 1.25rem; padding-top: 1.5rem; }

.boards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr)); gap: 2rem 2.5rem; }
.board { min-width: 0; }
.board:only-child { max-width: 900px; margin: 0 auto; width: 100%; }
.board-head { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem 1rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.board-head .game-result { flex: 1 0 100%; margin: 0; font-size: 0.875rem; color: var(--slate-ink); }
.board-head .game-result .places { color: var(--slate-ink-2); }
.seat-clock { display: inline-flex; align-items: center; gap: 0.4rem; min-width: 0; max-width: 100%; }
.seat-clock .turn { font-size: 0.75rem; font-weight: 650; color: var(--slate-ink-2); white-space: nowrap; }
.seat-clock.on-move .turn { color: var(--slate-ink); }
.board-head h2 { margin: 0; font-size: 1.125rem; }
.board-head h2 small a { color: var(--slate-ink-2); }
.board-head h2 small code { background: none; padding: 0; }
.board .seats { display: flex; gap: 0.4rem 1rem; flex-wrap: wrap; }
/* The board's frame until its board is up (or for good, saying why none is): the same 4:3 as the board. */
.board .fragment { border-radius: var(--r-board); overflow: hidden; border: 1px solid var(--slate-rule); aspect-ratio: 4 / 3; display: grid; background: var(--slate); }
.board .fragment .ag-board { width: 100%; height: 100%; }
.board.focus .fragment { border-color: var(--slate-ink); box-shadow: 0 0 0 1px var(--slate-ink); }
/* Browser-drawn boards (render-loader.js, docs/frontend-rendering.md): a canvas in place of the
   hidden board frame, framed like it; the controls above the boards. */
.board .render-board { width: 100%; border-radius: var(--r-board); overflow: hidden; border: 1px solid var(--slate-rule); background: var(--slate); }
.board .render-board canvas { display: block; width: 100%; height: auto; }
.board.focus .render-board { border-color: var(--slate-ink); box-shadow: 0 0 0 1px var(--slate-ink); }
.render-bar { margin: 0; font-size: 0.875rem; }
/* The Display menu: the board controls in a popover beside the player. */
.display-menu { position: relative; margin-left: auto; }
.display-menu[hidden] { display: none; }
.display-menu > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 0.35rem; min-height: 2.25rem; padding: 0.35rem 0.8rem; border: 1px solid var(--slate-rule); border-radius: var(--r-control); font-size: 0.875rem; font-weight: 650; color: var(--slate-ink); }
.display-menu > summary::-webkit-details-marker { display: none; }
.display-menu > summary::after { content: ""; width: 0.4rem; height: 0.4rem; border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: rotate(45deg) translateY(-2px); }
.display-menu[open] > summary { background: var(--slate-2); border-color: var(--slate-ink-2); }
/* A closed menu's panel is not laid out: on a phone it is fixed to the screen, and a closed <details> can still place it
   (off its own box), past the right edge, which lets the page slide sideways. */
.display-menu:not([open]) > .display-pop { display: none; }
.display-pop { position: absolute; right: 0; bottom: calc(100% + 0.4rem); z-index: 20; width: min(30rem, calc(100vw - 2rem)); padding: 0.85rem 1rem; background: var(--slate-2); border: 1px solid var(--slate-line); border-radius: var(--r-sheet); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); }
.display-pop .render-controls { flex-direction: column; align-items: stretch; gap: 0.6rem; }
.display-pop .render-controls label { justify-content: space-between; }
.display-pop .render-controls label.render-switch { justify-content: flex-start; gap: 0.5rem; }      /* a checkbox keeps its words beside it */
.display-pop-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin: -0.25rem 0 0.6rem; font-size: 0.875rem; font-weight: 650; color: var(--slate-ink); }
.board-foot { display: flex; margin-top: 0.75rem; }
.render-bar[hidden], .render-custom[hidden] { display: none; }
.render-controls { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: center; }
.render-controls label { flex-direction: row; align-items: center; gap: 0.45rem; }
.render-controls select { min-height: 2.25rem; padding: 0.3rem 0.5rem; max-width: 100%; }
.render-controls select:disabled { opacity: 0.6; }
.render-custom { margin-top: 0.75rem; max-width: 46rem; }
.render-custom textarea { width: 100%; font-family: var(--font-mono); font-size: 0.8125rem; }
@media (hover: none) and (pointer: coarse), (max-width: 640px) { .render-custom textarea { font-size: 16px; } }
.render-custom-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.render-custom-status { margin: 0.4rem 0 0; }
.replay { margin-top: 0.75rem; }
.replay-controls { display: flex; flex-wrap: wrap; gap: 0.5rem 0.75rem; align-items: center; }
.replay-controls .replay-scrub { flex: 1 1 14rem; margin: 0; }
.replay-controls label { flex-direction: row; align-items: center; gap: 0.45rem; font-size: 0.875rem; }
.replay [hidden] { display: none; }
.replay-controls button { min-height: 2.25rem; padding: 0.4rem 0.9rem; }
.replay-controls select { min-height: 2.25rem; padding: 0.3rem 0.5rem; }
.replay-controls .replay-play { min-width: 5.2rem; }
.replay-scrub { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.replay-slider { flex: 1; min-width: 0; margin: 0; padding: 0; border: 0; background: transparent; accent-color: var(--slate-ink); }
.replay-label { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--slate-ink-2); font-size: 0.875rem; }
.replay-status { margin: 0.35rem 0 0; font-size: 0.875rem; }
.perspective { margin: -1.25rem 0 0; display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.perspective a { font-weight: 650; font-size: 0.875rem; padding: 0.3rem 0.75rem; border-radius: var(--r-control); border: 1.5px solid var(--ink); color: var(--ink); text-decoration: none; }
.perspective a:hover { background: var(--hover); }

/* The tabs under the boards: links to sections without scripts (:target picks one, else all show),
   tabs with them (app.js, data-tabs-ready). */
.set-tabs { margin: 1.5rem 0 0; }
.tab-list { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--rule); margin: 0 0 1rem; overflow-x: auto; }
.tab-list .tab { padding: 0.55rem 0.9rem; font-weight: 650; color: var(--ink-2); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.tab-list .tab:hover { color: var(--ink); }
.tab-list .tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--ink); }
.set-tabs:not([data-tabs-ready]):has(.tab-panel:target) .tab-panel:not(:target) { display: none; }
.set-tabs:not([data-tabs-ready]) .tab-panel + .tab-panel { margin-top: 1.5rem; }
.tab-panel[hidden] { display: none; }
.moves-games { display: grid; gap: 1.25rem 2.5rem; }
.moves-games.pair { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }
.game-moves { min-width: 0; }
.game-moves h3 { margin: 0 0 0.4rem; font-size: 1rem; }
.game-moves h3 .muted { font-weight: 500; font-size: 0.875rem; margin-left: 0.35rem; }
ol.move-list { list-style: none; margin: 0; padding: 0; max-height: 26rem; overflow-y: auto; border: 1px solid var(--rule); border-radius: var(--r-sheet); background: var(--sheet); position: relative; }
ol.move-list .mv { display: grid; grid-template-columns: 2.75rem minmax(0, 9.5rem) minmax(0, 1fr) auto auto; align-items: center; gap: 0.6rem; padding: 0.3rem 0.75rem 0.3rem 0.4rem; border-bottom: 1px solid var(--rule); font-size: 0.875rem; }
ol.move-list .mv:last-child { border-bottom: 0; }
ol.move-list .mv.current { background: var(--hover); box-shadow: inset 3px 0 0 var(--ink); }
.mv-n { font-variant-numeric: tabular-nums; font-weight: 650; color: var(--ink-2); text-align: right; }
.mv-seat { font-size: 0.8125rem; font-weight: 600; }
.mv-seat .badge-seat { width: 1.25rem; height: 1.25rem; font-size: 0.75rem; }
code.mv-text, .mv-text > summary { font-family: var(--font-mono); font-size: 0.8125rem; background: none; padding: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
details.mv-text { min-width: 0; }
.mv-text > summary { cursor: pointer; display: block; align-content: center; }   /* centred in a touch screen's 44px tap height, in line with the move's number and seat */
.mv-text > summary::before { content: "+ "; color: var(--ink-2); }
.mv-text[open] > summary::before { content: "- "; }
.mv-json { display: block; margin-top: 0.3rem; max-height: 10rem; overflow: auto; white-space: pre-wrap; word-break: break-all; font-size: 0.75rem; }
.mv-score { font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 650; white-space: nowrap; text-align: right; }
.mv-score:empty { display: none; }
ol.move-list .mv:has(> .mv-score:empty) { grid-template-columns: 2.75rem minmax(0, 9.5rem) minmax(0, 1fr) auto; }
/* Twist Cube 3.0's score (a scored game): each bot's best under its name, a pill, a meter. */
.cube-score { display: flex; flex-direction: column; gap: 0.25rem; width: min(100%, 15rem); margin-top: 0.35rem; }
.sb-side.right .cube-score { align-items: flex-end; }
.cs-value { font-size: 1.75rem; font-weight: 800; line-height: 1.05; font-variant-numeric: tabular-nums; }
.cs-value small { font-size: 0.875rem; font-weight: 600; color: var(--ink-2); }
.cs-line { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.45rem; font-size: 0.8125rem; }
.sb-side.right .cs-line { justify-content: flex-end; }
.pct { font-size: 0.75rem; font-weight: 750; padding: 0 0.5rem; border: 1px solid var(--rule-strong); border-radius: 999px; font-variant-numeric: tabular-nums; }
.pct.best { border-color: var(--check); color: var(--check); box-shadow: inset 0 0 0 1px var(--check); }
.cs-meter { display: block; width: 100%; height: 0.4rem; border-radius: 3px; overflow: hidden; }
.cs-meter .track { fill: var(--rule); }
.cube-score.s1 .fill { fill: var(--seat1); } .cube-score.s2 .fill { fill: var(--seat2); }
.cube-score.s3 .fill { fill: var(--seat3); } .cube-score.s4 .fill { fill: var(--seat4); }
.sb-side.right .cs-meter { transform: scaleX(-1); }
.cs-small { font-family: var(--font-mono); font-size: 0.8125rem; color: var(--ink-2); margin-right: 0.35rem; }
.sb-best { margin: 0.75rem 0 0; text-align: center; font-size: 0.9375rem; }
ul.sb-games { list-style: none; margin: 0.85rem 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr)); gap: 0.5rem; }
ul.sb-games li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; padding: 0.45rem 0.7rem; border: 1px solid var(--rule); border-radius: var(--r-sheet); font-size: 0.875rem; font-variant-numeric: tabular-nums; }
.sb-game-seat { display: inline-flex; align-items: center; gap: 0.2rem; }
.sb-game-seat.won { font-weight: 800; color: var(--check); }
.sb-game-seat .badge-seat { width: 1.25rem; height: 1.25rem; font-size: 0.75rem; }
.mv-gap { font-variant-numeric: tabular-nums; color: var(--ink-2); font-size: 0.8125rem; white-space: nowrap; text-align: right; }
.verified-line { margin: 0 0 0.85rem; padding: 0.6rem 0.85rem; border: 1px solid var(--rule); border-left: 4px solid var(--rule-strong); border-radius: var(--r-sheet); background: var(--sheet); }
.verified-line.ok { border-left-color: var(--check); }
.verified-line.ok > strong:first-child { color: var(--check); }
.verified-line.bad { border-left-color: var(--err); background: var(--err-wash); }
.hash { display: inline-flex; align-items: center; gap: 0.45rem; white-space: nowrap; max-width: 100%; }
.hash code { overflow: hidden; text-overflow: ellipsis; }
.hash button, .cmd button { min-height: 0; padding: 0.15rem 0.55rem; font-size: 0.75rem; }
.cmd { display: flex; align-items: flex-start; gap: 0.5rem; min-width: 0; }
.cmd pre { flex: 1; min-width: 0; margin: 0; padding: 0.6rem 0.8rem; }
.cmd button { margin-top: 0.45rem; }
.set-details dl.facts, .fairness dl.facts { margin-top: 0; }
.set-details dd code { word-break: break-all; }
.operator { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--rule); }
.operator h3 { margin: 0 0 0.5rem; font-size: 1rem; }
.operator > p { margin: 0.2rem 0 0.6rem; font-size: 0.875rem; }

/* ---- recent games -------------------------------------------------------------------------------- */
table.recent th, table.recent td { padding-left: 0.5rem; padding-right: 0.5rem; }
table.recent td { vertical-align: middle; }
table.recent .stack { display: flex; flex-direction: column; gap: 0.3rem; align-items: flex-start; }
table.recent time { white-space: nowrap; font-variant-numeric: tabular-nums; }
table.recent .ago { font-size: 0.8125rem; white-space: nowrap; }
table.recent .game { min-width: 13.5rem; max-width: 18rem; }
table.recent .set-options { margin: 0; gap: 0.25rem; }
table.recent .chip.option { font-size: 0.75rem; color: var(--ink-2); padding: 0.02em 0.4em; }
table.recent .badge { max-width: 12rem; font-size: 0.875rem; }
table.recent .score { font-weight: 800; }
table.recent .result { max-width: 12rem; }
table.recent .result .win { white-space: nowrap; }
/* A table's payouts list every seat ("0 - 0 - 38 - 0 PTS"): let them wrap so Replay stays in view. */
table.recent td[data-label="Payout"], table.recent td[data-label="Points"] { white-space: normal; max-width: 7.5rem; }
table.recent .result strong { display: inline-block; max-width: 8.5rem; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
table.recent tr.aborted .badge::before { opacity: 0.5; }

/* ---- leaderboard --------------------------------------------------------------------------------- */
nav.tabs[aria-label="Game"] { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: thin; }
nav.tabs[aria-label="Game"] a { white-space: nowrap; flex: none; }   /* flex: none: on phones `main nav a` sets a min-width, which would let tabs shrink under their labels */
table.leaders td.rating { font-size: 0.9375rem; font-weight: 800; }
table.leaders tbody tr:first-child td.rank { color: var(--ink); }

/* ---- bot and player pages ------------------------------------------------------------------------ */
.bot-name { overflow-wrap: anywhere; }

/* ---- account: the overview, a bot's page, points and settings ------------------------------------ */
.account-who { margin: 0; font-size: 0.875rem; }
.account-logout { margin-top: 2rem; }
.panel .table-wrap { background: var(--field); }
table.history td { white-space: nowrap; }

/* The overview's three tiles: wallet, bots connected, today. */
.account-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: 1rem; margin: 0 0 2rem; }
.tile { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; padding: 1.1rem 1.25rem; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); }
.tile-label { font-size: 0.875rem; font-weight: 650; color: var(--ink-2); }
.tile-value { font-size: 1.875rem; font-weight: 850; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tile-value .of { font-size: 1rem; font-weight: 650; color: var(--ink-2); }
.tile-note { font-size: 0.875rem; }
.tile.explain h2 { margin: 0; font-size: 1.125rem; }
.tile.explain ul { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.9375rem; }
form.claim { margin: 0.15rem 0 0; }
.claim-done { margin: 0; font-size: 0.875rem; }
.notice.getting-started { justify-content: space-between; }

/* Bot cards: one per bot, then a card that opens the new-bot form. */
.bot-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr)); gap: 1rem; margin-bottom: 2rem; }
.bot-card { display: flex; flex-direction: column; gap: 0.85rem; padding: 1.1rem 1.25rem; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); min-width: 0; }
.bot-card header { display: flex; justify-content: space-between; align-items: baseline; gap: 0.4rem 0.75rem; flex-wrap: wrap; }
.bot-card h3 { margin: 0; font-size: 1.25rem; min-width: 0; overflow-wrap: anywhere; }
.bot-card h3 a { color: inherit; text-decoration: none; }
.bot-card h3 a:hover { text-decoration: underline; }
.bot-card p { margin: 0; }
.bot-card .actions { margin-top: auto; }
.bot-figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; margin: 0; }
.bot-figures dt { font-size: 0.8125rem; color: var(--ink-2); }
.bot-figures dd { margin: 0.1rem 0 0; font-size: 1.125rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.bot-figures dd .muted { font-size: 0.8125rem; font-weight: 550; }
.bot-figures.big dd { font-size: 1.75rem; line-height: 1.1; }
details.new-bot { border-style: dashed; border-color: var(--rule-strong); background: transparent; justify-content: center; min-height: 9rem; }
details.new-bot[open] { justify-content: flex-start; background: var(--sheet); }
details.new-bot summary { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; list-style: none; font-weight: 700; color: var(--ink); text-align: center; }
details.new-bot summary::-webkit-details-marker { display: none; }
details.new-bot .plus { font-size: 1.75rem; font-weight: 400; line-height: 1; color: var(--ink-2); }
details.new-bot form.stack { max-width: none; gap: 0.6rem; }

/* Activity: what the account's bots did and where its points went, one line each. */
.activity { margin-bottom: 1rem; }
/* The activity and the "Ask an AI" box side by side; the box goes under it on a narrow screen. */
.activity-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-start; }
.activity-row > .activity { flex: 999 1 32rem; min-width: 0; margin: 0; }
.activity-row > .ai-box, .welcome-side > .ai-box { flex: 1 1 18rem; max-width: none; margin: 0; }
.activity .card-head h2 { margin: 0; font-size: 1.25rem; }
.activity-list { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.activity-line { display: flex; justify-content: space-between; align-items: baseline; gap: 0.25rem 1rem; flex-wrap: wrap; padding: 0.6rem 0; border-top: 1px solid var(--rule); }
.activity-line .how { display: inline-flex; gap: 0.6rem; align-items: baseline; white-space: nowrap; font-variant-numeric: tabular-nums; }
.activity-line .when { font-size: 0.875rem; }

/* First visit: the steps of /start beside the free points and what this is. */
.welcome > h2 { margin: 0; font-size: 2rem; line-height: 1.15; max-width: 36rem; }
.welcome > .lead { max-width: 42rem; margin: 0.6rem 0 1.5rem; }
.welcome-grid { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-start; }
.welcome-grid .first-steps { flex: 999 1 30rem; min-width: 0; margin: 0; }
.welcome-side { flex: 1 1 17rem; display: flex; flex-direction: column; gap: 1rem; }
.welcome-side .panel { margin: 0; }
.welcome-side h3, .first-steps h3 { margin: 0; font-size: 1.125rem; }
.welcome-side p { margin: 0.4rem 0 0.75rem; font-size: 0.9375rem; }
progress.progress { appearance: none; display: block; width: 100%; height: 0.4rem; border: 0; border-radius: 0.2rem; background: var(--rule); overflow: hidden; margin: 0.85rem 0 0.25rem; color: var(--check); }
progress.progress::-webkit-progress-bar { background: var(--rule); }
progress.progress::-webkit-progress-value { background: var(--check); }
progress.progress::-moz-progress-bar { background: var(--check); }
.new-bot-form { margin: 0.6rem 0 0; align-items: flex-end; }
.new-bot-form label { flex-direction: column; align-items: flex-start; }

/* A bot's page. */
.breadcrumb { margin: 1.75rem 0 -1.25rem; font-size: 0.875rem; color: var(--ink-2); }
.bot-line { margin: 0.4rem 0 0; display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.manage-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr)); gap: 1rem; align-items: start; }
.manage-grid .panel { margin: 0; }
.manage-grid h2 { margin: 0 0 0.6rem; font-size: 1.25rem; }
.manage-grid .card-head h2 { margin: 0; }
.manage-grid p { font-size: 0.9375rem; }
.manage-grid form.inline input[type="text"] { width: 8rem; }
.manage-grid form.inline label { flex-direction: column; align-items: flex-start; }
.manage-grid form.inline { align-items: flex-end; }
.more-actions { margin-top: 0.75rem; }
.more-actions summary { font-size: 0.875rem; color: var(--ink-2); }
.rating-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.6rem; }
.rating-list li { display: flex; flex-direction: column; padding: 0.55rem 0.75rem; border: 1px solid var(--rule); border-radius: var(--r-sheet); font-size: 0.8125rem; }
.rating-list strong { font-size: 1.25rem; font-variant-numeric: tabular-nums; }
details.advanced { margin-top: 1rem; }
details.advanced > summary { font-size: 1rem; }
.advanced-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr)); gap: 1.25rem; margin-top: 1rem; }
.advanced-grid .stack { display: flex; flex-direction: column; gap: 0.45rem; font-size: 0.9375rem; }
.advanced-grid code { overflow-wrap: anywhere; }
.key-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.key-row form.inline { margin: 0; }

/* Settings: a column of cards, a row per setting, its form behind a Change button. */
.settings { max-width: 48rem; display: flex; flex-direction: column; gap: 1rem; }
.settings .panel { margin: 0; }
.setting-row { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem 1rem; flex-wrap: wrap; padding: 0.85rem 0; border-top: 1px solid var(--rule); margin-top: 0.85rem; }
.setting-row + .setting-row { margin-top: 0; }
.setting-sub { display: block; font-size: 0.8125rem; }
.small-note { font-size: 0.8125rem; font-weight: 650; }
details.setting-change { display: contents; }
details.setting-change > summary { list-style: none; margin-left: auto; }
details.setting-change > summary::-webkit-details-marker { display: none; }
details.setting-change[open] > form { flex-basis: 100%; margin-top: 0.5rem; }
.history-section h2 { margin: 0 0 0.25rem; }

/* The admin's view of a user's bots (admin_user.html) keeps the old bot panel. */
section.bot { margin: 0.75rem 0 1.25rem; }
section.bot h3 { font-size: 1.3125rem; }
section.bot h3 a { text-decoration: none; }
section.bot h3 a:hover { text-decoration: underline; }
section.bot .balances { font-size: 0.9375rem; color: var(--ink-2); }
section.bot .balances strong { color: var(--ink); font-size: 1.125rem; font-variant-numeric: tabular-nums; }

/* ---- practice (templates/practice.html) ---------------------------------------------------------- */
/* One form read top to bottom: 1 a game (a small gallery tile: its board on the slate, the radio
   hidden behind it), 2 an opponent (a card per level), 3 a bot (pills with its connection dot), then
   the start bar with the run command and the AI help, and the practice sets. Each choice is a radio,
   so the form works without app.js; the chosen one is outlined in ink, the focused one too. */
.practice-step { margin: 2.25rem 0 0; }
.practice-step fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.step-title { display: flex; align-items: center; gap: 0.6rem; margin: 0 0 0.9rem; font-size: 1.375rem; }
.step-n { display: inline-grid; place-items: center; width: 1.75rem; height: 1.75rem; flex: none; border-radius: 50%; background: var(--action); color: var(--on-action); font-size: 0.9375rem; font-weight: 800; }
.step-note { margin: -0.4rem 0 0.9rem; color: var(--ink-2); font-size: 0.9375rem; max-width: 80ch; }
.step-note strong { color: var(--ink); }
input.pick-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }

.pick-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(10.5rem, 100%), 1fr)); gap: 1rem 0.85rem; }
.pick-grid > li { margin: 0; min-width: 0; }
.pick-tile { position: relative; display: flex; flex-direction: column; gap: 0.45rem; padding: 0.35rem; border: 2px solid transparent; border-radius: calc(var(--r-board) + 4px); color: var(--ink); font-weight: 400; cursor: pointer; }
.pick-tile .wall-board { width: 100%; }
.pick-tile .wall-board .example-label { font-size: 0.75rem; }
.pick-tile:hover .wall-board { border-color: var(--slate-ink-2); }
.pick-tile:has(input:checked) { border-color: var(--ink); background: var(--sheet); }
.pick-tile:has(input:checked)::after { content: "\2713"; position: absolute; top: 0.75rem; right: 0.75rem; display: grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--slate-ink); color: var(--slate); font-size: 0.8125rem; font-weight: 800; }
.pick-tile:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.pick-name { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.45rem; padding: 0 0.15rem 0.1rem; font-size: 0.9375rem; font-weight: 750; line-height: 1.25; }
.pick-game-name { margin-right: auto; min-width: 0; overflow-wrap: anywhere; }

.level-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.85rem; }
label.level-card { display: flex; flex-direction: column; gap: 0.3rem; padding: 0.9rem 1rem 1rem; border: 1.5px solid var(--rule-strong); border-radius: var(--r-sheet); background: var(--sheet); color: var(--ink); font-weight: 400; font-size: 0.9375rem; cursor: pointer; }
label.level-card:hover { border-color: var(--ink); }
label.level-card:has(input:checked) { border-color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
label.level-card:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
label.level-card:has(input:disabled) { opacity: 0.55; cursor: not-allowed; }
.level-card input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.level-top { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.level-n { font-size: 0.8125rem; font-weight: 650; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.04em; }
.level-n::before { content: ""; display: inline-block; width: 0.8rem; height: 0.8rem; margin-right: 0.45rem; vertical-align: -0.1em; border: 1.5px solid var(--rule-strong); border-radius: 50%; box-sizing: border-box; }
.level-card:has(input:checked) .level-n::before { border: 4px solid var(--ink); }
.level-top .rating { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.level-title { font-size: 1.1875rem; font-weight: 800; }
.level-about { color: var(--ink-2); }
.level-bot { color: var(--ink-2); font-size: 0.8125rem; font-family: var(--font-mono); overflow-wrap: anywhere; }
.level-record { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; margin-top: 0.35rem; padding-top: 0.55rem; border-top: 1px solid var(--rule); font-size: 0.875rem; }
.rec-score strong { font-variant-numeric: tabular-nums; }
.rec-none { color: var(--ink-2); font-style: italic; }
.rec-aborted { color: var(--ink-2); font-size: 0.8125rem; }

.bot-pills { display: flex; flex-wrap: wrap; gap: 0.6rem; }
label.bot-pill, a.bot-pill { position: relative; display: inline-flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; min-height: 2.5rem; padding: 0.4rem 1rem 0.4rem 0.85rem; border: 1.5px solid var(--rule-strong); border-radius: 1.25rem; background: var(--sheet); color: var(--ink); font-size: 0.9375rem; font-weight: 400; cursor: pointer; max-width: 100%; }
label.bot-pill:hover, a.bot-pill:hover { border-color: var(--ink); text-decoration: none; }
label.bot-pill:has(input:checked) { border-color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink); }
label.bot-pill:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.bot-pill .dot { width: 0.6rem; height: 0.6rem; flex: none; border-radius: 50%; border: 1.5px solid var(--rule-strong); box-sizing: border-box; }
.bot-pill.on .dot { background: var(--check); border-color: var(--check); }
.bot-pill.off .dot { border-color: var(--err); }
.bot-pill-name { font-weight: 750; overflow-wrap: break-word; }   /* break-word, not anywhere: a long state or tag wraps to the next line instead of squeezing the name to a letter a line */
.bot-pill-state { font-size: 0.8125rem; color: var(--ink-2); white-space: nowrap; }
.bot-pill.on .bot-pill-state { color: var(--check); font-weight: 650; }
.bot-pill.off .bot-pill-state { color: var(--err); }
a.bot-pill.add { border-style: dashed; color: var(--ink-2); font-weight: 650; }

.start-step { display: contents; }
.start-step > .start-bar { margin-top: 2rem; position: sticky; bottom: 0.75rem; z-index: 5; box-shadow: 0 -0.5rem 1.25rem -0.5rem rgba(0, 0, 0, 0.55); }
.start-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.5rem; padding: 1rem 1.25rem; border: 1.5px solid var(--ink); border-radius: var(--r-sheet); background: var(--sheet); }
.start-summary { margin: 0; font-size: 1.0625rem; flex: 1 1 22rem; min-width: 0; overflow-wrap: anywhere; }
.start-bar button { flex: none; padding: 0.7rem 1.4rem; font-size: 1rem; }
.practice-help { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1.25rem 2rem; align-items: start; margin-top: 1.25rem; }
.run-block > p { margin: 0 0 0.4rem; max-width: 80ch; }
.practice-stuck .stuck-title { margin: 0 0 0.35rem; font-weight: 750; }
.practice-sets { margin-top: 1rem; }
.practice-sets .sets-arena { margin-top: 0.5rem; }
@media (max-width: 960px) {
  .practice-help { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .pick-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem 0.5rem; }
  .pick-tile { padding: 0.25rem; }
  .pick-tile:has(input:checked)::after { top: 0.55rem; right: 0.55rem; }
  .pick-name { font-size: 0.875rem; }
  .pick-name .tag { display: none; }
  .level-cards { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
  .start-bar { padding: 0.9rem 1rem; }
  .start-bar button { width: 100%; justify-content: center; }
  /* The sticky variant stays one compact row on a phone: two lines of summary, the action beside it. */
  .start-step > .start-bar { flex-wrap: nowrap; gap: 0.75rem; margin-top: 1rem; padding: 0.6rem 0.75rem; bottom: max(0.5rem, env(safe-area-inset-bottom)); }
  .start-step > .start-bar .start-summary { flex: 1 1 0; font-size: 0.875rem; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .start-step > .start-bar button, .start-step > .start-bar .btn { width: auto; padding: 0.65rem 1rem; white-space: nowrap; }
}
pre.run-hint { white-space: pre-wrap; overflow-wrap: anywhere; margin: 0.6rem 0; }
/* Long lists: a row far off screen skips layout and paint; `auto` in the intrinsic size remembers a row's real height once it has
   been seen, so scrolling back never shifts the page (the ledger does the same in account.css). */
.crows > .crow { content-visibility: auto; contain-intrinsic-size: auto 5.5rem; }
table.recent tbody tr, table.leaders tbody tr, table.registrants tbody tr { content-visibility: auto; contain-intrinsic-size: auto 2.75rem; }
.field-error { font-size: 0.8125rem; font-weight: 600; color: var(--err); }
.field-error[hidden] { display: none; }
input[aria-invalid="true"] { border-color: var(--err); }

/* ---- lobby --------------------------------------------------------------------------------------- */
#post { scroll-margin-top: 1rem; }
table.lobby td { vertical-align: middle; }
table.lobby th, table.lobby td { padding-left: 0.55rem; padding-right: 0.55rem; }
table.lobby th { white-space: normal; }
table.lobby td.options { min-width: 11rem; }
table.lobby td:nth-child(2) { min-width: 7.5rem; }
table.lobby td.action .actions { flex-wrap: nowrap; gap: 0.4rem; }
table.lobby td.action form.inline { margin: 0; }
/* The post form (lobby.html, web/post_form.py, app.js lobbyPost): /practice's steps on one form. The
   game tiles and bot pills are practice's; each option is a row with its title and hint beside its
   chips, number field or switch, next to the example start of those options; then the stake (a
   slider with chips once app.js runs, the number field always), how long it stays open, the
   opponent, and the summary bar, which sticks to the bottom of the screen while the form is on it. */
.post-panel { padding-bottom: 0; }
.post-form [hidden] { display: none !important; }
.post-form .practice-step { margin-top: 1.75rem; }
.post-form .practice-step:first-of-type { margin-top: 1rem; }
.post-form .step-title { font-size: 1.1875rem; margin-bottom: 0.75rem; }
.step-sub { font-size: 0.9375rem; font-weight: 600; color: var(--ink-2); }
.post-form fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.post-options-row { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem 1.75rem; align-items: start; }
.post-options { display: flex; flex-direction: column; gap: 0.85rem; }
.post-options > p { margin: 0; }
.opt-row { display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); gap: 0.35rem 1.25rem; align-items: center; padding-bottom: 0.85rem; border-bottom: 1px solid var(--rule); }
.opt-row:last-child { border-bottom: 0; padding-bottom: 0; }
.opt-text { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.opt-title { font-weight: 700; color: var(--ink); }
.opt-hint { font-size: 0.8125rem; color: var(--ink-2); line-height: 1.35; }
.opt-control { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; min-width: 0; }
.opt-control input[type="number"] { width: 7.5rem; }
.opt-control input[type="text"] { width: 100%; max-width: 18rem; }
.opt-range { font-size: 0.8125rem; color: var(--ink-2); }
.opt-fixed { font-family: var(--font-mono); }
/* A chip choice: a radio drawn as a chip (the options, Open for, Opponent) or a button (the stake). */
.chip-set { display: flex; flex-wrap: wrap; gap: 0.35rem; min-width: 0; }
.post-form label.choice { position: relative; display: inline-flex; flex-direction: row; gap: 0; font-size: 0.875rem; font-weight: 600; color: var(--ink); cursor: pointer; }
.post-form label.choice input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.post-form label.choice > span, .choice-btn { display: inline-flex; align-items: center; min-height: 2.1rem; min-width: 2.4rem; justify-content: center; padding: 0.25rem 0.7rem; border: 1.5px solid var(--rule-strong); border-radius: 999px; background: var(--sheet); color: var(--ink); font-size: 0.875rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.post-form label.choice:hover > span, .choice-btn:hover { border-color: var(--ink); background: var(--sheet); }
.post-form label.choice.is-default > span { border-style: dashed; }
.post-form label.choice input:checked + span, .choice-btn.on { background: var(--action); border-color: var(--action); border-style: solid; color: var(--on-action); }
.post-form label.choice input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }
.post-form label.choice input:disabled + span { opacity: 0.45; cursor: not-allowed; }
.choice-btn { min-height: 2rem; padding: 0.2rem 0.65rem; }
/* A boolean option: a switch over its checkbox (a hidden "false" before it). */
.post-form label.switch { position: relative; display: inline-flex; flex-direction: row; cursor: pointer; }
.switch input[type="checkbox"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch-track { position: relative; width: 2.6rem; height: 1.5rem; border-radius: 999px; border: 1.5px solid var(--rule-strong); background: var(--field); }
.switch-track::after { content: ""; position: absolute; top: 0.15rem; left: 0.15rem; width: 0.95rem; height: 0.95rem; border-radius: 50%; background: var(--rule-strong); transition: transform 0.15s; }
.switch input:checked + .switch-track { background: var(--action); border-color: var(--action); }
.switch input:checked + .switch-track::after { transform: translateX(1.1rem); background: var(--paper); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--focus); outline-offset: 2px; }
/* The seats and clock of the chosen options (GET /lobby/preview). */
.post-preview { position: sticky; top: 1rem; display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.preview-slot { border-radius: var(--r-board); background: var(--slate); border: 1px solid var(--slate-rule); padding: 0.5rem; min-height: 8rem; display: grid; place-items: center; transition: opacity 0.15s; }
.preview-slot.loading { opacity: 0.6; }
.post-example { width: 100%; margin: 0; color: var(--slate-ink-2); text-align: center; font-size: 0.9375rem; }
.preview-note { margin: 0; font-size: 0.8125rem; color: var(--ink-2); }
.preview-error { margin: 0; padding: 1rem; color: var(--slate-err); font-size: 0.875rem; text-align: center; }
/* The bots: practice's pills with the available points; one with none is disabled. */
.bot-choice { display: inline-flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; max-width: 100%; }
.bot-pill-pts { font-size: 0.8125rem; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
label.bot-pill.empty { opacity: 0.55; cursor: not-allowed; border-style: dashed; }
label.bot-pill.empty:hover { border-color: var(--rule-strong); }
.move-points { font-size: 0.8125rem; font-weight: 650; }
.post-bot-note { margin: 0.6rem 0 0; }
/* The stake, Open for and the opponent. */
.post-terms { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr); gap: 1rem 1.75rem; align-items: start; }
.term { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
.post-form .term-title { display: block; font-size: 0.9375rem; font-weight: 700; color: var(--ink); }
.term-hint { margin: 0; font-size: 0.8125rem; color: var(--ink-2); }
.stake-row { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.stake-slider { flex: 1 1 8rem; min-width: 0; accent-color: var(--ink); }
.stake-field { display: inline-flex; align-items: center; gap: 0.4rem; font-weight: 650; color: var(--ink-2); white-space: nowrap; }
.stake-field input { width: 6.5rem; font-variant-numeric: tabular-nums; }
.post-form label.expiry-at, .post-form label.to-bot { font-size: 0.8125rem; }
.post-form label.expiry-at .hint { font-weight: 400; }
.post-form label.to-bot input { width: 100%; max-width: 18rem; }
/* The summary bar: what is posted and the button, kept in view at the bottom of the screen. */
.post-bar { position: sticky; bottom: 0; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.5rem; margin: 1.75rem -1.5rem 0; padding: 0.9rem 1.5rem; border-top: 1.5px solid var(--ink); background: var(--sheet); border-radius: 0 0 var(--r-sheet) var(--r-sheet); box-shadow: 0 -6px 14px -10px rgba(0, 0, 0, 0.35); }
.post-summary { margin: 0; flex: 1 1 26rem; min-width: 0; font-size: 0.9375rem; line-height: 1.5; overflow-wrap: anywhere; }
.post-summary strong { color: var(--ink); }
.post-warn { display: block; margin-top: 0.2rem; color: var(--err); font-size: 0.8125rem; font-weight: 600; }
.post-bar button { flex: none; padding: 0.7rem 1.4rem; font-size: 1rem; }
.post-panel > .muted:last-child { margin-bottom: 1.25rem; }
/* The challenge just posted (?posted=): its card or row stands out until the page is left. */
.challenge-card.just-posted { box-shadow: 0 0 0 2px var(--slate-check); }
@media (max-width: 960px) {
  .post-terms { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .post-terms > .term:first-child { grid-column: 1 / -1; }
}
@media (max-width: 800px) {
  .post-options-row { grid-template-columns: minmax(0, 1fr); }
  .post-preview { position: static; order: -1; }
  .opt-row { grid-template-columns: minmax(0, 1fr); }
  .post-terms { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .post-bar { margin: 1.5rem -0.85rem 0; padding: 0.75rem 0.85rem; }
  .post-bar button { width: 100%; }
  .post-summary { flex-basis: 100%; font-size: 0.875rem; }
  .preview-slot { min-height: 6rem; }
}
dl.facts dd .set-options { margin: 0; }

/* ---- fairness, verify, starter, docs ------------------------------------------------------------- */
dl.facts.commitment { margin: 0; }
dl.facts.commitment dd code, table.signing-keys code.public-key { word-break: break-all; }
.cols.fair-cols { gap: 1.25rem; }
.cols.fair-cols > .panel { margin: 0.25rem 0; border-top: 3px solid var(--check); }
.cols.fair-cols > .panel + .panel { border-top-color: var(--ink-2); }
.cols.fair-cols > .panel h3 { margin: 0 0 0.5rem; font-size: 1.0625rem; }
.cols.fair-cols > .panel ul { margin: 0; }
table.signing-keys td { vertical-align: middle; }
table.signing-keys td:nth-child(3) { min-width: 16rem; }
ol.steps > li { margin: 0.6rem 0; padding-left: 0.25rem; }
ol.steps > li::marker { font-weight: 800; }
ol.steps pre { margin: 0.5rem 0; }

/* ---- getting started (/start) ------------------------------------------------------------------- */
.start-where { font-size: 1.0625rem; margin: 0 0 1rem; }
ol.start-steps { list-style: none; padding: 0; margin: 0 0 1.5rem; max-width: 72ch; }
ol.start-steps > li { display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); gap: 0 0.75rem; padding: 0.9rem 0; border-top: 1px solid var(--rule); }
ol.start-steps > li:last-child { border-bottom: 1px solid var(--rule); }
ol.start-steps .mark { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: 50%; border: 2px solid var(--rule-strong); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ink-2); }
ol.start-steps li.done .mark { border-color: var(--check); background: var(--check); color: var(--sheet); }
ol.start-steps li.current .mark { border-color: var(--action); background: var(--action); color: var(--on-action); }
ol.start-steps h2 { margin: 0.2rem 0 0.35rem; font-size: 1.125rem; }
ol.start-steps li.todo h2 { color: var(--ink-2); font-weight: 600; }
ol.start-steps li.current .body { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 0.75rem 1rem 0.9rem; }
ol.start-steps .body > p, ol.start-steps .body > form { margin: 0.5rem 0; }
ol.start-steps .body p.muted:last-child { margin-bottom: 0; }
ol.start-steps ol.steps { padding-left: 1.25rem; }
ol.start-steps pre.run-hint, .start-key pre.run-hint { white-space: pre-wrap; overflow-wrap: anywhere; }
.start-key h2 { margin-top: 0; }
.start-result p { margin: 0.35rem 0; }
form.new-key { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0.5rem 0; }
.start-result.won strong:first-child { color: var(--check); }
ul.start-next { max-width: 72ch; padding-left: 1.25rem; }
.ai-box { max-width: 72ch; margin: 1rem 0; padding: 1rem 1.15rem; background: var(--sheet); border: 1px solid var(--rule); border-left: 4px solid var(--ink); border-radius: var(--r-sheet); }
.ai-box h3 { margin: 0 0 0.4rem; font-size: 1.0625rem; }
.ai-box > p { margin: 0 0 0.75rem; }
.ai-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.ai-app-note { margin: 0.5rem 0 0; font-size: 0.875rem; }
.ai-box > .ai-app-note { margin: 0.6rem 0 0; font-size: 0.875rem; }
.ai-prompt { margin-top: 0.6rem; }
.ai-prompt summary { cursor: pointer; font-size: 0.875rem; color: var(--ink-2); }
.ai-prompt pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 0.5rem 0 0; }
ul.start-next li { margin: 0.5rem 0; }

.verdict { border-left: 4px solid var(--rule-strong); }
.verdict h2 { margin: 0 0 0.25rem; font-size: 1.75rem; font-weight: 850; display: flex; align-items: center; gap: 0.6rem; }
.verdict h2::before { content: "\2013"; flex: none; display: inline-grid; place-items: center; width: 1.75rem; height: 1.75rem; border-radius: 50%; background: var(--ink-2); color: var(--sheet); font-size: 1rem; font-weight: 800; }
.verdict.verified { border-left-color: var(--check); }
.verdict.verified h2 { color: var(--check); }
.verdict.verified h2::before { content: "\2713"; background: var(--check); }
.verdict.failed { border-left-color: var(--err); }
.verdict.failed h2 { color: var(--err); }
.verdict.failed h2::before { content: "\2717"; background: var(--err); }
.verdict p { max-width: 70ch; }
table.checks td.mark { width: 1.5em; font-weight: 800; }
table.checks tr.ok td.mark { color: var(--check); }
table.checks tr.fail td.mark, table.checks tr.fail td:nth-child(2) { color: var(--err); }
details.receipts { margin: 0.6rem 0 1.75rem; }
table.receipts td { white-space: nowrap; }
table.receipts td code { background: none; padding: 0; }

table.starter td { vertical-align: middle; }
table.starter td.summary { min-width: 16rem; max-width: 34rem; }
table.starter td:last-child { min-width: 12rem; }
details.files { margin-top: 0.4rem; font-size: 0.8125rem; }
details.files summary { color: var(--ink-2); font-weight: 600; font-size: 0.8125rem; }
details.files ul { list-style: none; padding: 0; margin: 0.3rem 0 0; }
table.endpoints td:nth-child(2), table.errors td:nth-child(2) { white-space: nowrap; }
.doc-page h2 { font-size: 1.3125rem; scroll-margin-top: 1rem; }
.toc { display: flex; flex-wrap: wrap; gap: 0.35rem 1.25rem; margin: 1.5rem 0 0; padding: 0.75rem 0; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); font-size: 0.9375rem; font-weight: 600; max-width: 72ch; }

.error-page { padding: 3rem 0 2rem; }
.error-page .code { font-size: 0.9375rem; font-weight: 650; color: var(--ink-2); margin: 0 0 0.5rem; }

/* ---- admin --------------------------------------------------------------------------------------- */
.admin-tabs { margin-top: 0; }
form.admin-search input[type="search"] { flex: 1; min-width: min(20rem, 100%); max-width: 32rem; }
form.reason-form { gap: 0.5rem; margin: 0.4rem 0; }
form.reason-form input.reason { flex: 1; min-width: min(14rem, 100%); max-width: 26rem; }
form.reason-form input.amount { width: 7rem; }
table.running form.reason-form, table.keys form.reason-form { margin: 0; flex-wrap: nowrap; }
table.running form.reason-form input.reason, table.keys form.reason-form input.reason { min-width: 11rem; }
section.admin-actions > p.muted { margin: 0.2rem 0 0.6rem; font-size: 0.875rem; }
form.ledger-filters input[type="text"], form.audit-filters input[type="text"] { width: 12rem; }
table.journals td.postings { min-width: 18rem; }
table.journals .posting { display: block; white-space: nowrap; font-size: 0.8125rem; }
table.audit td.audit-json { max-width: 22rem; }
table.audit td.audit-json code { display: block; white-space: pre-wrap; word-break: break-word; font-size: 0.75rem; color: var(--ink-2); background: none; padding: 0; }
section.confirm .notice p { margin: 0.2rem 0; }

/* ---- responsive ---------------------------------------------------------------------------------- */
@media (max-width: 960px) {
  .hero { grid-template-columns: 1fr; gap: 1.75rem; padding-top: 2.5rem; }
  .receipt-chain li:not(:last-child)::after { content: none; }
  .exchange { max-width: 34rem; }
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cols { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* The recent list as one card per set: the header row is read out but not drawn, the figures
     carry their column name, and the time, the game and the bots speak for themselves. */
  .recent-wrap { border: 0; background: none; overflow: visible; }
  table.recent, table.recent tbody { display: block; }
  table.recent thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  table.recent tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem 0.75rem; align-items: start; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 0.8rem 0.9rem; margin: 0 0 0.75rem; }
  table.recent td, table.recent td.num { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; padding: 0; border: 0; text-align: left; white-space: normal; }
  table.recent tbody tr:hover td { background: none; }
  table.recent td[data-label]::before { content: attr(data-label); font-size: 0.75rem; font-weight: 650; color: var(--ink-2); }
  table.recent .when, table.recent .game, table.recent .bots { grid-column: 1 / -1; }
  table.recent td.when::before, table.recent td.game::before, table.recent td.bots::before { content: none; }
  table.recent .when { grid-column: 1 / 3; grid-row: 1; align-self: center; }
  table.recent .replay-link { grid-column: 3; grid-row: 1; align-items: flex-end; }
  table.recent .when .stack, table.recent .bots .stack { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.9rem; }
  table.recent .game, table.recent .result { min-width: 0; max-width: none; }
  table.recent .result { grid-column: span 2; }
  table.recent .badge { max-width: 100%; }
  table.recent .result strong { max-width: 11rem; }

}
@media (max-width: 600px) {
  :root { --gutter: 16px; }
  html { font-size: 15px; }
  h1 { font-size: 1.875rem; }
  h2 { font-size: 1.3125rem; margin-top: 2.25rem; }
  .page-head { margin-top: 1.75rem; }
  .hero { padding-top: 2rem; }
  .exchange li { padding-left: 1rem; padding-right: 1rem; }
  .exchange li.reply { padding-left: 1.4rem; }
  .exchange li.call code { font-size: 0.8125rem; }
  .exchange li.reply { font-size: 0.9375rem; }
  .exchange .party { font-size: 1.3125rem; }
  /* The home page's cards become a row to swipe through, so eight boards do not make the page 4,000 px long. */
  .arena .card-grid { grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: 84%; overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; margin: 0 calc(-1 * var(--gutter)); padding: 0 var(--gutter) 0.5rem; scroll-padding: 0 var(--gutter); }
  .arena .card-grid { align-items: start; }
  .arena .card-grid > .set-card { scroll-snap-align: start; }
  /* Except /recent's and Live's, which are the page itself: their cards stack and the pager follows them. */
  .sets-arena .card-grid { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); overflow-x: visible; scroll-snap-type: none; margin: 0; padding: 0; }
  .seat-row { grid-template-columns: 1fr; }
  .seat-cell { flex-direction: row; justify-content: space-between; align-items: center; }
  .seat-cell.s2 { align-items: center; }
  dl.facts { grid-template-columns: 6.5rem minmax(0, 1fr); gap: 0.4rem 0.75rem; }
  dl.facts dt { font-size: 0.8125rem; }
  .panel, .scoreboard { padding: 0.85rem; }
  .sb-duel { gap: 0.5rem 0.75rem; }
  ol.move-list .mv, ol.move-list .mv:has(> .mv-score:empty) { grid-template-columns: 2.25rem auto minmax(0, 1fr) auto auto; gap: 0.4rem; }
  .mv-seat .badge-name { display: none; }
  .replay-controls .replay-scrub { flex-basis: 100%; order: 3; }
  .display-pop { right: auto; left: 50%; transform: translateX(-50%); position: fixed; bottom: 1rem; }
  .arena { padding: 1.5rem 0 1.75rem; }
  .wall-board { padding: 0.3rem; border-radius: 8px; }
  .wall-board > .ag-board:not(.vh) { inset: 0.3rem; }
  .gallery-grid { grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
  .boards { gap: 1.75rem; }
  .perspective { margin-top: -0.75rem; }
  footer.site .links { gap: 0.25rem 1rem; }
  nav.tabs[aria-label="Game"] { margin-left: calc(-1 * var(--gutter)); margin-right: calc(-1 * var(--gutter)); padding: 0 var(--gutter); }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
  *, *::before, *::after { scroll-behavior: auto !important; }
}

/* ---- sign-in pages: log in, sign up, forgot and reset password ---------------------------------- */
.auth { max-width: 27rem; margin: 1.5rem auto 3rem; }
.auth-head { margin-bottom: 1.1rem; }
.auth-head h1 { margin: 0; font-size: 2rem; }
.auth-head .lead { margin-top: 0.5rem; font-size: 1rem; }
.auth-card { padding: 1.5rem; margin: 0; }
.auth-card form.stack { max-width: none; gap: 1.1rem; }
.auth-card input[type="email"], .auth-card input[type="password"], .auth-card input[type="text"] { width: 100%; padding: 0.65rem 0.75rem; }
.auth-card input.short { width: 6rem; text-transform: uppercase; }
.auth-card button[type="submit"] { align-self: stretch; justify-content: center; padding: 0.7rem 1rem; margin-top: 0.25rem; }
.auth-card .flash { margin: 0; }
.field { display: flex; flex-direction: column; gap: 0.3rem; }
.field > label { display: block; font-size: 0.9rem; }
.field-row { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.field-row a { font-size: 0.82rem; }
.hint { font-size: 0.8rem; color: var(--ink-2); }
.faint { color: var(--ink-2); }
.auth-card label.check { align-items: flex-start; font-size: 0.9rem; }
.auth-card label.check input { margin-top: 0.2rem; }
.auth-card > .hint { margin: 1rem 0 0; }
.auth-alt { text-align: center; margin: 1.1rem 0 0; font-size: 0.92rem; }
@media (max-width: 480px) { .auth { margin-top: 0.5rem; } .auth-card { padding: 1.1rem; } }

/* ==== Interaction layer (static/ui.js): dialogs, toasts, menus, tab indicator, ring, palette, skeleton ====
   Everything here is an enhancement: without ui.js the pages work as plain links and forms. */
dialog.dlg { padding: 0; border: 1px solid var(--rule-strong); border-radius: 14px; background: var(--sheet); color: var(--ink); width: min(480px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto; box-shadow: 0 30px 80px rgb(0 0 0 / 0.75);
  opacity: 0; transform: scale(0.5); transform-origin: calc(50% + var(--dx, 0px)) calc(50% + var(--dy, 0px));
  transition: opacity 0.2s ease, transform 0.28s cubic-bezier(0.2, 0.9, 0.3, 1), overlay 0.28s allow-discrete, display 0.28s allow-discrete; }
dialog.dlg[open] { opacity: 1; transform: none; }
@starting-style { dialog.dlg[open] { opacity: 0; transform: scale(0.5); } }
dialog.dlg::backdrop { background: rgb(5 8 14 / 0.72); backdrop-filter: blur(3px); opacity: 0; transition: opacity 0.25s ease, overlay 0.28s allow-discrete, display 0.28s allow-discrete; }
dialog.dlg[open]::backdrop { opacity: 1; }
@starting-style { dialog.dlg[open]::backdrop { opacity: 0; } }
html:has(dialog.dlg:modal) { overflow: hidden; }
.dlg-title { margin: 0; padding: 1.25rem 1.5rem 0; font-size: 1.25rem; line-height: 1.25; }
.dlg-body { padding: 0.75rem 1.5rem 0; margin: 0; color: var(--ink-2); }
.dlg-body .facts { margin: 0.5rem 0; }
.dlg-body .notice { margin: 0.75rem 0; }
.dlg-form { display: block; margin: 0; padding: 1rem 1.5rem 1.25rem; }
.dlg-copy { display: block; width: 100%; min-height: 10rem; margin: 0.5rem 0 0.75rem; font-family: var(--font-mono); font-size: 0.8125rem; resize: vertical; }   /* app.js copyDialog */
.dlg-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: flex-end; margin-top: 0.25rem; }
.dlg-form[data-danger] button[type="submit"]:not([formmethod]) { background: var(--err); border-color: var(--err); color: var(--on-err); }
.dlg-form[data-danger] button[type="submit"]:not([formmethod]):hover { background: #ff857a; border-color: #ff857a; }

/* Toasts: live region at the foot of the page, each item a manual popover stacked by ui.js through --off. */
/* Toasts stack in the bottom right corner, out of the page's way, the newest at the foot; each slides in from the right, and a bar
   along its foot empties over the time left before it goes (ui.js; it holds while the toast is hovered or focused). */
.toasts { position: fixed; inset: auto 0 0 0; height: 0; z-index: 100; pointer-events: none; }
.toast { position: fixed; inset: auto; margin: 0; right: 16px; bottom: 16px; width: min(400px, calc(100vw - 24px)); box-sizing: border-box; display: flex; align-items: center; gap: 0.75rem; justify-content: space-between;
  padding: 0.7rem 0.8rem 0.7rem 1rem; background: var(--sheet); color: var(--ink); border: 1px solid var(--rule-strong); border-left: 4px solid var(--accent); border-radius: 10px; box-shadow: 0 14px 36px rgb(0 0 0 / 0.55); pointer-events: auto;
  overflow: hidden; translate: 0 calc(var(--off, 0px) * -1); opacity: 1; transition: translate 0.25s ease, opacity 0.2s ease, overlay 0.25s allow-discrete, display 0.25s allow-discrete; }
@starting-style { .toast:popover-open { opacity: 0; translate: 32px calc(var(--off, 0px) * -1); } }
.toast.out { opacity: 0; translate: 32px calc(var(--off, 0px) * -1); }
.toast.error { border-left-color: var(--err); }
.toast.info { border-left-color: var(--ink-2); }
.toast-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--accent); transform-origin: left center; opacity: 0.85; pointer-events: none; }
.toast.info .toast-bar { background: var(--ink-2); }
@media (max-width: 480px) { .toast { right: 12px; } }
.toast-text { overflow-wrap: anywhere; }
.toast-x { flex: none; min-height: 1.75rem; padding: 0 0.5rem; font-size: 1.1rem; line-height: 1; }

/* Popover menus: <button popovertarget=ID> + <div popover id=ID data-menu>. Anchored by the browser when it can, else placed by ui.js. */
[data-menu] { margin: 0; padding: 0.25rem; min-width: 11rem; background: var(--sheet); color: var(--ink); border: 1px solid var(--rule-strong); border-radius: 10px; box-shadow: 0 14px 36px rgb(0 0 0 / 0.55); transition: opacity 0.15s ease, translate 0.15s ease, overlay 0.15s allow-discrete, display 0.15s allow-discrete; }
[data-menu]:not(:popover-open) { opacity: 0; translate: 0 -6px; }
@starting-style { [data-menu]:popover-open { opacity: 0; translate: 0 -6px; } }
[data-menu].menu-placed { inset: auto; }
[data-menu] > a, [data-menu] > button { display: flex; width: 100%; min-height: 2.25rem; align-items: center; padding: 0.35rem 0.7rem; border: 0; border-radius: 6px; background: transparent; color: var(--ink); font-weight: 600; text-decoration: none; justify-content: flex-start; }
[data-menu] > a:hover, [data-menu] > button:hover, [data-menu] > a:focus-visible, [data-menu] > button:focus-visible { background: var(--hover); }
@supports (position-anchor: auto) {
  [data-menu] { position-area: bottom span-right; inset: auto; margin-top: 4px; position-try-fallbacks: flip-block, flip-inline; }
}

/* Tabs: an indicator that slides between tabs where anchor positioning exists; else the plain underline stays. */
@supports (anchor-name: --a) {
  .tabs:not(.small), .tab-list { position: relative; }
  .tabs:not(.small) a.on, .tab-list .tab[aria-selected="true"] { anchor-name: --tab-on; border-bottom-color: transparent; }
  .tabs:not(.small)::after, .tab-list::after { content: ""; position: absolute; position-anchor: --tab-on; left: anchor(left); right: anchor(right); bottom: -1px; height: 2px; background: var(--accent); border-radius: 2px; transition: left 0.25s ease, right 0.25s ease; pointer-events: none; }
  .tab-list::after { background: var(--ink); }
}

/* Countdown ring: --t is the fraction left, set by ui.js. */
@property --t { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
.ring { --t: 100%; display: inline-block; flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%; vertical-align: middle;
  background: conic-gradient(var(--accent) var(--t), var(--rule) 0); -webkit-mask: radial-gradient(circle, transparent 54%, #000 56%); mask: radial-gradient(circle, transparent 54%, #000 56%); transition: --t 1s linear; }
.ring.low { background: conic-gradient(var(--err) var(--t), var(--rule) 0); }

/* Command palette */
kbd { font-family: var(--font-mono); font-size: 0.75rem; padding: 0.05rem 0.35rem; border: 1px solid var(--rule-strong); border-radius: 4px; color: var(--ink-2); background: var(--field); }
dialog.pal { width: min(620px, calc(100vw - 32px)); margin-top: 12vh; overflow: hidden; }
dialog.pal input { display: block; width: 100%; box-sizing: border-box; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; background: transparent; padding: 1rem 1.25rem; font-size: 1.0625rem; min-height: 3.5rem; }
@media (hover: none) and (pointer: coarse), (max-width: 640px) { dialog.pal input { font-size: 16px; } }
dialog.pal input:focus { outline: none; box-shadow: inset 0 -2px 0 var(--accent); }
.pal-list { list-style: none; margin: 0; padding: 0.4rem; max-height: min(50dvh, 26rem); overflow: auto; }
.pal-opt { display: flex; justify-content: space-between; gap: 1rem; padding: 0.55rem 0.8rem; border-radius: 8px; cursor: pointer; }
.pal-opt[aria-selected="true"] { background: var(--hover); box-shadow: inset 3px 0 0 var(--accent); }
.pal-kind { color: var(--ink-2); font-size: 0.8125rem; }
.pal-none { padding: 1rem 1.25rem; margin: 0; }

.empty { padding: 2rem 1rem; text-align: center; color: var(--ink-2); border: 1px dashed var(--rule-strong); border-radius: var(--r-sheet); }
.empty strong { display: block; color: var(--ink); font-family: var(--font-head); margin-bottom: 0.25rem; }

@media (prefers-reduced-motion: reduce) {
  dialog.dlg, dialog.dlg::backdrop, .toast, [data-menu], .ring, .tabs::after, .tab-list::after { transition: none; transform: none; translate: none; }
  .toast, .toast.out { translate: 0 calc(var(--off, 0px) * -1); }
  @starting-style { dialog.dlg[open], .toast:popover-open, [data-menu]:popover-open { opacity: 1; transform: none; } }
}

/* ---- one board frame for every game (set page) ---------------------------------------------------- */
/* Every game's board sits in the same 4:3 frame, the board centred and letterboxed inside it, so the page
   below never changes height when you switch games. The renderers fit their camera to the frame. */
.board .render-board { aspect-ratio: 4 / 3 !important; background: var(--slate); }
.board .render-board canvas { width: 100%; height: 100%; }

/* ==== Boards: the browser draws them, 3D first (landing.js, wall.ts, render-loader.js, host.ts) ====
   The server draws no board: each is an empty frame. With scripting on, the browser draws the board
   in it, so the frame's message stays out of sight (visibility: hidden keeps its box: nothing
   shifts) behind a calm skeleton until the script marks the board: data-board-state is "pending"
   (skeleton), "mounted" / "still" (the board is up), "failed" / "static" (no board can be drawn: the
   frame's message shows). A page script that never runs leaves the attribute off; the message then
   shows after 8 s on its own (board-late). html[data-boards="static"] (landing.js: no WebGL or
   canvas, the bundle failed) shows every frame's message of the page. */
@media (scripting: enabled) {
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) :is([data-clip], [data-live-board], [data-demo-url]):is(:not([data-board-state]), [data-board-state="pending"])  .ag-board { visibility: hidden; }
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) :is([data-clip], [data-live-board], [data-demo-url]):not([data-board-state])  .ag-board { animation: board-late 1ms 8s forwards; }
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) .mini-board:is([data-clip], [data-live-board]) { position: relative; }
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) :is(.mini-board, .wall-board):is([data-clip], [data-live-board], [data-demo-url]):is(:not([data-board-state]), [data-board-state="pending"])::after,
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) .gallery-board[data-demo-url]:is(:not([data-board-state]), [data-board-state="pending"]) .wall-board::after,
  html:not([data-boards="static"]) [data-render-root]:is(:not([data-board-state]), [data-board-state="pending"]) .board .fragment:not([data-shown], .vh)::after {
    content: ""; position: absolute; inset: 0; visibility: visible; pointer-events: none; border-radius: inherit;
    background: linear-gradient(100deg, transparent 30%, rgba(230, 237, 247, 0.05) 50%, transparent 70%) 0 0 / 200% 100%;
    animation: board-shimmer 1.6s linear infinite;
  }
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) :is(.mini-board, .wall-board):is([data-clip], [data-live-board], [data-demo-url]):not([data-board-state])::after,
  html:not([data-boards="static"]) :is([data-animated-boards], [data-arena-wall]) .gallery-board[data-demo-url]:not([data-board-state]) .wall-board::after,
  html:not([data-boards="static"]) [data-render-root]:not([data-board-state]) .board .fragment:not([data-shown], .vh)::after {
    animation: board-shimmer 1.6s linear infinite, board-gone 1ms 8s forwards;
  }
  /* A set page: the board's frame waits the same way until its board is up or fails (host.ts shows its message). */
  html:not([data-boards="static"]) [data-render-root]:is(:not([data-board-state]), [data-board-state="pending"]) .board .fragment:not([data-shown], .vh) { visibility: hidden; position: relative; background: var(--slate); }
  html:not([data-boards="static"]) [data-render-root]:not([data-board-state]) .board .fragment:not([data-shown], .vh) { animation: board-late 1ms 8s forwards; }
}
@keyframes board-late { to { visibility: visible; } }
@keyframes board-gone { to { visibility: hidden; } }
@keyframes board-shimmer { to { background-position: -200% 0; } }
@keyframes board-in { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  .anim-box > .render-board:not(.is-still), .wall-board > .render-board:not(.is-still) { animation: board-in 0.3s ease-out; }
}
@media (prefers-reduced-motion: reduce) {
  :is(.mini-board, .wall-board, .gallery-board .wall-board, .board .fragment)::after { animation: none !important; }
}

/* ---- cross-cutting UX (skip link, system states, busy buttons, shortcuts help, touch targets, page transitions) ---- */
.skip { position: fixed; left: 0.75rem; top: 0.5rem; z-index: 200; transform: translateY(-200%); background: var(--action); color: var(--on-action); font-weight: 700; padding: 0.7rem 1rem; border-radius: var(--r-control); text-decoration: none; }
.skip:focus { transform: none; outline: 2px solid var(--ink); outline-offset: 2px; }
main:focus { outline: none; }
button.linklike { background: none; border: 0; padding: 0; min-height: 44px; color: var(--ink-2); font-weight: 600; border-radius: 0; }
button.linklike:hover { background: none; color: var(--teal); text-decoration: underline; }
.btn[aria-disabled="true"], button[aria-busy="true"] { opacity: 0.55; cursor: progress; pointer-events: none; }
button[aria-busy="true"]::after { content: ""; width: 0.8em; height: 0.8em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: busy-spin 0.8s linear infinite; }
@keyframes busy-spin { to { rotate: 1turn; } }
.error-page [data-retry] { font-variant-numeric: tabular-nums; }
.error-page .actions { margin: 1.25rem 0 1rem; }
.net-banner { position: sticky; top: 0; z-index: 60; background: var(--sheet); border-bottom: 2px solid var(--err); color: var(--ink); text-align: center; padding: 0.55rem var(--gutter, 1rem); font-size: 0.9375rem; font-weight: 600; }
.net-banner[hidden] { display: none; }
dl.keys { display: grid; grid-template-columns: max-content 1fr; gap: 0.5rem 1.25rem; margin: 0; align-items: center; }
dl.keys dt { display: flex; gap: 0.3rem; flex-wrap: wrap; }
dl.keys dd { margin: 0; color: var(--ink-2); }
kbd { font-family: var(--font-mono); font-size: 0.8125rem; padding: 0.15rem 0.45rem; background: var(--hover); border: 1px solid var(--rule-strong); border-bottom-width: 2px; border-radius: 4px; color: var(--ink); }
.dlg-keys .dlg-body { padding: 0 1.25rem 0.5rem; }
@media (prefers-reduced-motion: reduce) { button[aria-busy="true"]::after { animation: none; } }
/* One page-to-page transition: the header stays put, the content fades quickly (browsers without cross-document transitions just navigate). */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  header.site { view-transition-name: site-head; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 140ms; }
}
/* Touch: every control and list link is at least 44 px tall (links inside running text keep their size). */
@media (pointer: coarse), (max-width: 640px) {
  button, .btn, button.small, .btn.small, input:not([type="checkbox"], [type="radio"], [type="range"], [type="hidden"]), select, summary,
  .tabs a, .tab-list .tab, a.more, a.chip, a.badge, .toast-x { min-height: 44px !important; }
  main nav a, main :is(td, th) a, a:is([href^="/bots/"], [href^="/players/"], [href^="/sets/"]):not(p a, h1 a, h2 a, h3 a) { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; }
  .toast-x { min-width: 44px; }
  .tabs a, .tab-list .tab, .tabs.small a, a.more, a.chip, a.badge, summary, .count-live, .count-open { display: inline-flex; align-items: center; }
  :is(td, th, li, .card-actions, .seat-strip, .hm-seats, .set-chips) > a:not(.btn, .brand),
  a:is(.game-name, .bot-link, .hm-sn, .hm-gname, .hm-cname, .lobby-practice, .count-live, .count-open) { display: inline-flex; align-items: center; min-height: 44px; }
  summary { display: flex; }
  :is(h2, h3) > a:not(.btn) { display: inline-block; padding-block: 0.9rem; margin-block: -0.9rem; }      /* a title link: a taller hit area, same layout */
  .tourney-teaser .tt-name, .hero-sub a, .area-lobby .crow-title a { display: inline-block; min-height: 0; padding-block: 0.9rem; margin-block: -0.9rem; }  /* the same for short links in a line */
  .bot-strip a.tag { position: relative; }
  .bot-strip a.tag::after { content: ""; position: absolute; inset: -0.9rem 0; }   /* a pill keeps its size; the hit area grows */
  header.site .brand { min-height: 44px; display: inline-flex; align-items: center; }
  header.site.has-back .brand { min-width: 44px; justify-content: center; }      /* the mark alone beside the back arrow: still a 44 px target */
  [data-menu] > a, .pal-opt, label.bot-pill, a.bot-pill, label.check { min-height: 44px; }
  .pal-opt { align-items: center; }
  td > a.bot-link, td > a { padding-block: 0; }
}
/* The salesman duel's two boards (renderers/tsp-duel-3d.ts): each seat's caption over its own board, on the canvas. */
.board-seat-tag { position: absolute; z-index: 2; top: 0.4rem; left: 0.4rem; max-width: calc(100% - 0.8rem); display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.15rem 0.5rem; border-radius: 999px; background: rgba(15, 23, 42, 0.78); color: var(--slate-ink); font-size: 0.75rem; font-weight: 600; line-height: 1.3; pointer-events: none; }
.board-seat-tag > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-seat-tag .seat-dot { flex: none; width: 0.5rem; height: 0.5rem; }
@container (max-width: 150px) { .board-seat-tag { display: none; } }
.duel-panes { position: absolute; inset: 0; display: grid; pointer-events: none; container-type: size; }
.duel-panes[data-layout="side"] { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.duel-panes[data-layout="stack"] { grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); }
.duel-pane { align-self: start; display: flex; flex-direction: column; gap: 0.1em; min-width: 0; padding: 2.4cqw 3cqw; font-size: clamp(0.5rem, 2cqw, 0.875rem); line-height: 1.3; color: var(--slate-ink-2); font-variant-numeric: tabular-nums; }
.duel-panes[data-layout="stack"] .duel-pane { font-size: clamp(0.75rem, 3.3cqw, 0.875rem); padding: 1.6cqw 3cqw; }
.duel-pane .dp-head { display: flex; align-items: center; gap: 0.45em; color: var(--slate-ink); }
.duel-pane .dp-swatch { display: inline-block; width: 0.8em; height: 0.8em; border-radius: 2px; background: var(--slate-line); }
.duel-pane.s1 .dp-swatch { background: #ef5a4f; }
.duel-pane.s2 .dp-swatch { background: #4d8ef7; }
.duel-pane .dp-title { font-weight: 750; }
.duel-pane .dp-badge { margin-left: auto; font-weight: 700; color: var(--slate-ink-2); }
.duel-pane[data-state="improving"] .dp-badge, .duel-pane[data-state="won"] .dp-badge { color: var(--teal); }
.duel-pane[data-state="won"] .dp-title { color: var(--teal); }
.duel-pane .dp-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@container (max-width: 420px) { .duel-pane .dp-cities, .duel-pane .dp-best { display: none; } }
/* Per-seat boards on the set page (frontend/src/renderers/seat-boards.ts, RendererStyle.seatBoards): the game's seats as
   separate framed boards like a swap pair's games (side by side from 900px, stacked below), a title above each and a caption
   bar under it, the page's one transport bar below both. */
.board .render-board.seat-boards { aspect-ratio: auto !important; background: transparent; border: 0; border-radius: 0; overflow: visible; box-shadow: none; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.seat-board { min-width: 0; }
.seat-board .seat-board-frame { aspect-ratio: 4 / 3; position: relative; display: grid; place-items: center; overflow: hidden; background: var(--slate); border: 1px solid var(--slate-rule); border-radius: var(--r-board, 8px); }
.seat-board .seat-board-frame > .render-board, .seat-board .seat-board-frame > canvas { width: 100%; }
.seat-board .seat-board-frame canvas { max-height: 100%; }
.seat-board .seat-board-title { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.5rem; font: 650 0.9375rem var(--font-head); color: var(--ink); }
.seat-dot { display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--slate-line); }
.seat-dot.s1 { background: var(--seat1-dot); }
.seat-dot.s2 { background: var(--seat2-dot); }
.seat-dot.s3 { background: var(--seat3-dot); } .seat-dot.s4 { background: var(--seat4-dot); } .seat-dot.s5 { background: var(--seat5-dot); }
.seat-dot.s6 { background: var(--seat6-dot); } .seat-dot.s7 { background: var(--seat7-dot); } .seat-dot.s8 { background: var(--seat8-dot); }
.seat-board .seat-board-caption { display: block; margin: 0.6rem 0 0; background: var(--sheet); border: 1px solid var(--rule); border-radius: 10px; padding: 0.6rem 0.9rem; font-size: 0.9375rem; line-height: 1.4; font-variant-numeric: tabular-nums; color: var(--ink); min-height: 2.75rem; }
@media (min-width: 56.25rem) {
  .board .render-board.seat-boards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
  .area-set .set-layout:not(.is-pair) .boards.arena:has(.seat-boards) { max-width: none; width: 100%; margin-inline: 0; }
}
/* A table of 5 to 8 seats (Polyfit): 3 boards a row from 900px, 4 from 1200px (polyfit-3d-model.ts columnsFor); the boards
   are smaller there, so the title and caption tighten. 2 to 4 seats keep the two columns above. */
@media (min-width: 56.25rem) {
  .board .render-board.seat-boards:is([data-seats="5"], [data-seats="6"], [data-seats="7"], [data-seats="8"]) { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; }
  .seat-boards:is([data-seats="5"], [data-seats="6"], [data-seats="7"], [data-seats="8"]) .seat-board-caption { padding: 0.45rem 0.7rem; font-size: 0.8125rem; min-height: 2.25rem; }
}
@media (min-width: 75rem) {
  .board .render-board.seat-boards:is([data-seats="5"], [data-seats="6"], [data-seats="7"], [data-seats="8"]) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Wide tables on a phone (admin Games, a tournament's registrants and waitlist): each row becomes a card, every cell
   under its column's name (data-label), so nothing is clipped or scrolled sideways. */
@media (max-width: 720px) {
  .table-wrap:has(> table.admin-games, > table.registrants) { overflow: visible; background: none; border: 0; }
  table.admin-games, table.registrants { display: block; }
  table.admin-games thead, table.registrants thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  table.admin-games tbody, table.registrants tbody { display: grid; gap: 0.75rem; }
  table.admin-games tr, table.registrants tr { display: grid; gap: 0.5rem; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r-sheet); padding: 0.75rem 0.9rem; }
  table.admin-games td, table.registrants td { display: block; padding: 0; border: 0; min-width: 0; overflow-wrap: anywhere; }
  table.admin-games td:empty { display: none; }
  table.admin-games td[data-label]::before, table.registrants td[data-label]::before { display: block; margin-bottom: 0.15rem; content: attr(data-label); font-size: 0.75rem; font-weight: 650; color: var(--ink-2); }
  table.admin-games td.num, table.registrants td.num { text-align: left; }
  table.admin-games td:first-child, table.registrants td:first-child { font-weight: 650; }
  table.admin-games td[colspan], table.registrants td[colspan] { display: block; }
  table.admin-games td[colspan]::before, table.registrants td[colspan]::before { content: none; }
}

/* ---- phone navigation ------------------------------------------------------------------------------- */
/* base.html, _nav.html, nav.js. At 640px and narrower the header is the brand and Search only, sticky, and the way round is
   a tab bar at the foot of the screen (Home, Play, Live, Account or Sign in, More) whose More opens a sheet of every page,
   grouped. Wider, none of it shows and the header is as it was. viewport-fit=cover (base.html) lets the page run under the
   notch and the home indicator, so the header, the gutters and the tab bar pad themselves by the safe-area insets; each
   inset is 0 on a device without one, so a desktop browser lays out exactly as before. */
/* --safe-bottom: the home indicator's height. In the installed app's shell (/app) the page sits in a frame whose insets
   read 0, so the shell passes it in as --shell-safe-bottom; --botbar-h is the shell's bot bar floating above the tab bar. */
:root { --tabbar-h: 0px; --safe-bottom: env(safe-area-inset-bottom, 0px); --botbar-h: 0px; }
html.in-shell { --safe-bottom: var(--shell-safe-bottom, 0px); }
/* No rubber-band in the installed app: iOS bounced the whole frame, its fixed tab bar with it, past the page's ends, so the
   tab bar slid up under the shell's bot bar (which stays put) and the page slid sideways. */
html.in-shell, html.in-shell body { overflow-x: clip; overscroll-behavior: none; }
/* The page's own scroll bar: hidden in the installed app (iOS draws an indicator for the shell's frame) and on touch phones.
   The page still scrolls; inner scrollers (tab strips, tables) keep theirs. */
html.in-shell { scrollbar-width: none; }
html.in-shell::-webkit-scrollbar, html.in-shell body::-webkit-scrollbar { display: none; width: 0; height: 0; }
@media (pointer: coarse) and (max-width: 640px) {
  html { scrollbar-width: none; }
  html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; width: 0; height: 0; }
}
/* The Lobby's "Your bots": a computer opens the bot's public profile, a phone or the installed app its Run in browser panel. */
.bot-strip .bot-link-run { display: none; }
@media (max-width: 640px) { .bot-strip .bot-link-profile { display: none; } .bot-strip .bot-link-run { display: inline-flex; } }
html.in-shell .bot-strip .bot-link-profile { display: none; }
html.in-shell .bot-strip .bot-link-run { display: inline-flex; }
/* A dialog in the shell keeps clear of the shell's bot bar, which floats over the page (--botbar-h; 0 without it). */
html.in-shell dialog:modal:not(.sheet) { bottom: var(--botbar-h); max-height: calc(100dvh - var(--botbar-h) - 2rem); }
html.in-shell dialog.sheet { bottom: var(--botbar-h); max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 0.75rem - var(--botbar-h)); }
header.site { padding-top: env(safe-area-inset-top, 0px); }
.wrap { padding-left: max(var(--gutter), env(safe-area-inset-left, 0px)); padding-right: max(var(--gutter), env(safe-area-inset-right, 0px)); }
.tabbar { display: none; }

@media (max-width: 640px) {
  /* At least 20px under the tabs: clear of the home indicator and the screen's rounded corners, inset or not. */
  :root { --tabbar-pad: max(var(--safe-bottom), 20px); --tabbar-h: calc(56px + var(--tabbar-pad)); }
  body { padding-bottom: calc(var(--tabbar-h) + var(--botbar-h)); }
  /* The header: brand, Search and the account (the person button, or Sign in), one row, kept on screen. The primary nav, the
     Menu button and the bell are the tab bar's and the sheet's now; display: none in every state, so the nav can never widen
     the page either. */
  header.site { position: sticky; top: 0; z-index: 50; }
  /* so a jump to an anchor (#new-bot, the docs' and game pages' sections) lands below that header, not under it:
     the header's 3.5rem row and the notch's inset, plus 0.5rem of air */
  html { scroll-padding-top: calc(3.5rem + env(safe-area-inset-top, 0px) + 0.5rem); }
  header.site .wrap { min-height: 3.5rem; padding-top: 0.375rem; padding-bottom: 0.375rem; gap: 0.25rem; flex-wrap: nowrap; }
  header.site #site-menu, header.site .menu-btn, header.site .bell, header.site .bell-pop { display: none; }
  header.site .brand { min-width: 0; }
  header.site .header-end { order: 2; }
  header.site .acct-btn { margin-left: 0.25rem; }
  header.site a.start { order: 2; padding: 0 0.85rem; font-size: 0.875rem; }
  /* The mark's two pieces drop in from inside the header, not from above the screen's edge. */
  header.site .brand { --brand-drop: -0.9rem; }

  .tabbar { position: fixed; z-index: 50; left: 0; right: 0; bottom: 0; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); height: var(--tabbar-h);
    padding: 0 env(safe-area-inset-right, 0px) var(--tabbar-pad) env(safe-area-inset-left, 0px); background: var(--sheet); border-top: 1px solid var(--rule); box-shadow: 0 -8px 24px rgb(0 0 0 / 0.35); }
  .tabbar .tab { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; min-width: 0; height: 56px; min-height: 0; margin: 0; padding: 0 2px;
    border: 0; border-radius: 0; background: transparent; color: var(--ink-2); font: 600 0.75rem/1.1 var(--font); letter-spacing: 0.01em; text-decoration: none; white-space: nowrap;
    -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
  .tabbar .tab > span:not(.tab-badge):not(.vh) { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
  .tabbar .tab:hover { background: transparent; color: var(--ink); }
  .tabbar .tab:focus-visible { outline-offset: -4px; border-radius: 8px; }
  .tabbar .tab .ic { flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
  .tabbar .tab.on, .tabbar .tab[aria-expanded="true"] { color: var(--ink); }
  .tabbar .tab.on .ic, .tabbar .tab[aria-expanded="true"] .ic { color: var(--teal); }
  .tabbar .tab.on::before { content: ""; position: absolute; top: -1px; left: 50%; width: 28px; height: 3px; margin-left: -14px; border-radius: 0 0 3px 3px; background: var(--accent); }
  .tabbar .tab-badge { position: absolute; top: 5px; left: calc(50% + 5px); min-width: 1.25rem; height: 1.25rem; padding: 0 0.28rem; display: inline-flex; align-items: center; justify-content: center;
    font: 700 0.75rem/1 var(--font-mono); color: var(--on-action); background: var(--teal); border-radius: 0.6rem; box-shadow: 0 0 0 2px var(--sheet); }
  .tabbar .tab-badge[hidden] { display: none; }

  /* Things that stick or float at the foot of the screen sit above the tab bar. */
  .toast { bottom: calc(12px + var(--tabbar-h) + var(--botbar-h)); }
  .start-step > .start-bar { bottom: calc(0.75rem + var(--tabbar-h) + var(--botbar-h)); }
  .post-bar { bottom: calc(var(--tabbar-h) + var(--botbar-h)); }
  :root .area-set .pair-bar .replay-controls { bottom: calc(0.5rem + var(--tabbar-h) + var(--botbar-h)); }
  :root .area-rank .you-row { bottom: calc(0.75rem + var(--tabbar-h) + var(--botbar-h)); }
}
/* From 600px down the Display panel opens just above the bar that holds its button (the replay controls, sticky in a pair),
   the bar's width and centred on it, so the button and the replay controls stay in view; half the screen tall at most. */
@media (max-width: 600px) {
  :is(.replay-controls, .board-foot):has(> .display-menu) { position: relative; }      /* a sticky pair bar stays sticky (more specific) */
  .display-menu { position: static; }
  .display-pop { position: absolute; left: 50%; right: auto; bottom: calc(100% + 0.4rem); transform: translateX(-50%); max-height: 50dvh; overflow-y: auto; }
}
/* While the on-screen keyboard is up (focus in a text field) the tab bar steps aside. */
@media (max-width: 640px) {
  body:has(:is(input:not([type="checkbox"], [type="radio"], [type="range"], [type="button"], [type="submit"], [type="reset"], [type="color"], [type="file"], [type="hidden"]), textarea, [contenteditable=""], [contenteditable="true"]):focus) .tabbar { display: none; }
}

/* The More sheet: a dialog that rises from the foot of the screen over a dimmed page, the account first, then the pages in
   groups. nav.js opens it as a modal; without the script it is the same element as a popover. */
dialog.sheet { position: fixed; inset: auto 0 0 0; margin: 0 auto; width: 100%; max-width: 40rem; height: auto; max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 0.75rem);
  padding: 0; overflow: hidden; color: var(--ink); background: var(--sheet); border: 1px solid var(--rule-strong); border-bottom: 0; border-radius: 16px 16px 0 0; box-shadow: 0 -18px 48px rgb(0 0 0 / 0.6); }
dialog.sheet:is([open], :popover-open) { display: flex; flex-direction: column; }
dialog.sheet::backdrop { background: var(--scrim); }
html:has(dialog.sheet[open]) { overflow: hidden; }
.sheet .ic { flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.sheet-head { position: relative; flex: none; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 14px 8px 4px 20px; touch-action: none; }
.sheet-grab { position: absolute; top: 6px; left: 50%; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--rule-strong); }
.sheet-head h2 { margin: 0; font-size: 1.25rem; }
.sheet-x { flex: none; width: 44px; height: 44px; min-height: 44px; padding: 0; color: var(--ink-2); background: transparent; border: 0; border-radius: 50%; }
.sheet-x:hover { color: var(--ink); background: var(--hover); border-color: transparent; }
.sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px 12px calc(16px + var(--safe-bottom)); }
.sheet-me { margin: 4px 0 6px; padding: 14px; background: var(--paper); border: 1px solid var(--rule); border-radius: 12px; }
.sheet-who { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: 12px; align-items: center; color: inherit; text-decoration: none; }
.sheet-avatar { grid-row: span 2; display: grid; place-items: center; width: 44px; height: 44px; color: var(--teal); background: var(--hover); border: 1px solid var(--rule-strong); border-radius: 50%; }
.sheet-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 700 1.0625rem/1.25 var(--font-head); }
.sheet-mail { grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8125rem; color: var(--ink-2); }
.sheet-points { grid-column: 3; grid-row: 1 / span 2; padding: 0.3rem 0.6rem; font: 600 0.75rem/1 var(--font-mono); white-space: nowrap; color: var(--ink); background: var(--sheet); border: 1px solid var(--rule-strong); border-radius: 999px; }
.sheet-who:hover .sheet-name { text-decoration: underline; text-underline-offset: 0.2em; }
.sheet-run { width: 100%; min-height: 48px; margin-top: 12px; }
.sheet-pitch { margin: 0 0 12px; font-size: 0.9375rem; color: var(--ink-2); }
.sheet-auth { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.sheet-auth .btn { min-height: 48px; padding-left: 0.5rem; padding-right: 0.5rem; }
@media (max-width: 360px) { .sheet-auth { grid-template-columns: minmax(0, 1fr); } }      /* too narrow for two: one above the other */
.sheet-group h3 { margin: 16px 10px 2px; font: 700 0.75rem/1.2 var(--font); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.sheet-group ul { margin: 0; padding: 0; list-style: none; }
.sheet-group li { margin: 0; }
.sheet-form { margin: 0; }
.sheet .sheet-group .sheet-row { display: grid; grid-template-columns: 22px minmax(0, 1fr); column-gap: 14px; align-items: center; justify-content: stretch; width: 100%; min-height: 48px; margin: 0; padding: 5px 10px;
  color: var(--ink); background: transparent; border: 0; border-radius: 10px; font: inherit; text-align: left; text-decoration: none; white-space: normal; }
.sheet .sheet-group .sheet-row .ic { grid-row: span 2; color: var(--ink-2); }
.sheet-lbl { font-size: 1rem; font-weight: 650; line-height: 1.2; }
.sheet-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8125rem; line-height: 1.25; color: var(--ink-2); }
.sheet .sheet-group .sheet-row:hover, .sheet .sheet-group button.sheet-row:hover { color: var(--ink); background: var(--hover); border-color: transparent; }
.sheet .sheet-group .sheet-row.on { background: var(--hover); box-shadow: inset 3px 0 0 var(--accent); }
.sheet .sheet-group .sheet-row.on .ic, .sheet .sheet-group .sheet-row.on .sheet-lbl { color: var(--teal); }
@media (prefers-reduced-motion: no-preference) {
  dialog.sheet { translate: 0 100%; transition: translate 0.28s cubic-bezier(0.2, 0.8, 0.2, 1), display 0.28s allow-discrete, overlay 0.28s allow-discrete; }
  dialog.sheet:is([open], :popover-open) { translate: 0 0; }
  @starting-style { dialog.sheet:is([open], :popover-open) { translate: 0 100%; } }
  dialog.sheet::backdrop { opacity: 0; transition: opacity 0.28s, display 0.28s allow-discrete, overlay 0.28s allow-discrete; }
  dialog.sheet:is([open], :popover-open)::backdrop { opacity: 1; }
  @starting-style { dialog.sheet:is([open], :popover-open)::backdrop { opacity: 0; } }
  dialog.sheet.dragging { transition: none; }
}
