/* Shared styles. Same visual language as docs/brief.html.
 *
 * THE VOCABULARY IS A SCOREBOARD (U9). Amber on dark, a few enormous tabular
 * numerals, yard-line hash marks where a rule would otherwise go, and the
 * faintest cast of mown turf on the page ground. The discipline a real
 * scoreboard has is the point: it shows four numbers the size of a door and
 * nothing else, which is what keeps this from becoming decoration.
 *
 * WHERE THE PERSONALITY GOES. The leaderboard, because that is the page people
 * open to look. The pick page is fifteen games and thirty number boxes typed
 * by thumb on a Saturday, so it gets the palette and nothing louder. The
 * operator console gets the same family and stays utilitarian: it is a working
 * tool on a laptop, and legibility beats personality there.
 *
 * CONTRAST. Amber on dark is easy to get wrong, so every pair below was
 * measured rather than eyeballed. The worst of them is 4.64:1 (--ink-3 on
 * --surface-2, the table head) against the 4.5 AA needs; the scoreboard's
 * amber on its own dark is 10.33:1. Anything darkened here was darkened to
 * clear that bar -- see the note against --ink-3.
 */
:root {
  --paper: #f2f3ef;
  --surface: #ffffff;
  --surface-2: #e7eae4;
  --ink: #14181a;
  --ink-2: #4d5451;
  /* 4.64:1 on --surface-2. The old #858c88 was 4.06 there, which is the
     column head on every table on the site -- a real AA failure, not a
     rounding one. */
  --ink-3: #626965;
  --rule: #d5dad2;
  --accent: #8a5209;
  /* What text on a solid --accent has to be. Hardcoding #fff worked in light
     and was unreadable on the dark theme's pale amber. */
  --on-accent: #ffffff;
  --accent-soft: #f7ead4;
  --win: #1f6b45;
  --loss: #9c3320;
  /* Two more section accents, for the leaderboard's panel headings only --
     decorative bars, not text-on-background pairs, so held to "reads clearly
     against --paper and --surface" rather than a measured AA ratio. */
  --accent-2: #2f6fae;
  --accent-3: #6a3fa0;

  /* The scoreboard slab. These do NOT flip with the colour scheme: a
     scoreboard is dark in daylight too, and the hero and the header read as
     the same object on both themes because of it. */
  --board: #0d1211;
  --board-2: #16211c;
  --board-amber: #ffb020;
  --board-ink: #e8ede9;
  --board-mute: #9aa8a1;
  --board-win: #6ec392;
  --board-loss: #ef8a72;
  --board-line: rgba(255, 176, 32, 0.14);

  /* The two field motifs, as tokens so the dark theme can dim them without a
     second copy of the rules that use them. */
  --turf: rgba(46, 92, 60, 0.022);
  --hash: #b9c1b6;
  --hash-5: #8a5209;

  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --paper: #10120f;
    --surface: #171a17;
    --surface-2: #1f231f;
    --ink: #e9ebe6;
    --ink-2: #a4aba6;
    --ink-3: #878e89;
    --rule: #2c312d;
    --accent: #f0b357;
    --on-accent: #12150f;
    --accent-soft: #3a2e1c;
    --win: #6ec392;
    --loss: #ef8a72;
    --accent-2: #7fb8e8;
    --accent-3: #c9a8e8;

    --turf: rgba(110, 195, 146, 0.022);
    --hash: #39413b;
    --hash-5: #7a5a24;

    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` comes from the user-agent
   stylesheet, so ANY author rule that sets `display` beats it -- and both
   screens toggle panels by setting `.hidden = true` on elements this file
   gives `display: flex`. Without this, the sticky save bar stays on screen for
   a signed-out visitor and the candidate panel's filters show before there is
   anything to filter. One rule, rather than a `:not([hidden])` on every
   flex container, because the next one added would forget. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* The turf.
 *
 * Mown stripes, at two percent. It has to be felt rather than seen -- the
 * moment it is legible AS stripes it is a texture on a website instead of the
 * ground the page sits on, and the first draft at 3.5% was. Opted into with
 * `<body class="turf">` so the operator console never gets it, and painted on
 * the BODY rather than on panels: everything with rows in it (tables, cards,
 * the save bar) has an opaque --surface behind it, which is what keeps the
 * pattern out from under text that has to be read.
 *
 * `background-attachment: fixed` would be prettier and is deliberately absent:
 * it is a known repaint cost while scrolling on mobile Safari, and this is a
 * page people scroll on a phone. */
body.turf {
  background-image: repeating-linear-gradient(
    90deg,
    var(--turf) 0 44px,
    transparent 44px 88px
  );
}

.wrap { max-width: 780px; margin: 0 auto; padding: 0 18px 80px; }

/* --- yard-line hash marks ---
   Where this file would otherwise put a horizontal rule. Ticks every 16px with
   a taller, warmer one every 80px -- five hashes to a yard line, which is what
   makes it read as a field rather than as a dashed border.

   Three gradients on one element, and no extra markup anywhere: the divider is
   a ::before on the thing it divides. */
.panel::before {
  content: '';
  display: block;
  height: 11px;
  background-image:
    repeating-linear-gradient(90deg, var(--hash-5) 0 2px, transparent 2px 80px),
    repeating-linear-gradient(90deg, var(--hash) 0 2px, transparent 2px 16px),
    linear-gradient(var(--rule), var(--rule));
  background-size: 100% 11px, 100% 6px, 100% 1px;
  background-position: 0 100%, 0 100%, 0 100%;
  background-repeat: repeat-x, repeat-x, no-repeat;
}

/* --- the header, as a scoreboard slab ---
   Bled out of the column with negative margins rather than 100vw: a viewport
   unit includes the scrollbar on a desktop browser, which is a horizontal
   scrollbar on the body for the sake of 15px of colour. */
header.bar {
  background: var(--board);
  color: var(--board-ink);
  /* The slab has to read as an OBJECT on the page, and on the dark theme the
     board and the page ground are four hex points apart -- without an edge the
     header simply vanishes into the background there. A hairline at the
     amber's own hue, plus enough shadow to lift it. */
  border: 1px solid rgba(255, 176, 32, 0.16);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
  border-radius: 0 0 10px 10px;
  margin: 0 -18px 26px;
  padding: 22px 18px 18px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  align-items: baseline;
  justify-content: space-between;
  /* Yard lines down the slab, at the amber's own hue and almost invisible. */
  background-image: repeating-linear-gradient(
    90deg,
    transparent 0 63px,
    var(--board-line) 63px 64px
  );
}
header.bar > div { flex: 1 1 auto; min-width: 0; }
/* The mark. Inline SVG rather than a file: no request, no licence question,
   and crisp at any size -- and it is the same geometry as the app icon, drawn
   once in tools/make-icons.mjs. */
.mark {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  align-self: center;
}
.mark-ball { fill: var(--board-amber); }
.mark-lace {
  fill: none;
  stroke: var(--board);
  stroke-width: 13;
  stroke-linecap: round;
}
h1 {
  /* Shrinks rather than wrapping the nav off the slab at 375px. */
  font-size: clamp(1.15rem, 4.6vw, 1.5rem);
  font-weight: 800;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  margin: 0;
  color: var(--board-ink);
}
.sub {
  font-family: ui-monospace, 'IBM Plex Mono', Menlo, Consolas, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--board-mute);
  font-variant-numeric: tabular-nums;
}
nav a {
  font-size: 0.85rem;
  color: var(--board-amber);
  text-decoration: none;
  margin-left: 16px;
  border-bottom: 1px solid transparent;
}
nav a:hover, nav a:focus-visible { border-bottom-color: var(--board-amber); }
nav a:focus-visible, header.bar :focus-visible {
  outline: 2px solid var(--board-amber);
  outline-offset: 3px;
}
/* Sign out lives in the nav row, so it wears the slab rather than the page. */
header.bar .btn-quiet {
  color: var(--board-amber);
  border-color: rgba(232, 237, 233, 0.28);
}
header.bar .btn-quiet:hover:not(:disabled),
header.bar .btn-quiet:focus-visible { border-color: var(--board-amber); }

.mono, td.num, .rank {
  font-family: ui-monospace, 'IBM Plex Mono', Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
}

/* --- pick cards ---
   Fifteen of these is the whole page, and a player types thirty numbers into
   them on a phone. So the CHROME is what gives ground -- padding, the meta
   row's margin, the breathing room around each side -- and the score box only
   ever gets bigger. A card that is 30px shorter is worth nothing if the box
   inside it is too small to hit. */
.game {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 4px 12px 5px;
  margin-bottom: 8px;
}
.game.locked { opacity: 0.62; }
.game .meta {
  font-family: ui-monospace, monospace;
  font-size: 0.68rem;
  line-height: 1.35;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 2px;
}
.game .locktag { color: var(--loss); font-weight: 600; }

/* The betting line (U10, R17). Deliberately the same quiet .meta treatment as
   the conference and kickoff row: it is CONTEXT for the scores being typed
   above it, not a control and not a result. Below the matchup rather than
   above so the first thing read is still the two teams.

   `text-transform: none` is not cosmetic -- KTD13 says the provider's own
   string is shown VERBATIM, and .meta uppercases. 'Alabama -24.5' is what the
   book wrote and is what the player sees. */
.game .meta.line {
  justify-content: flex-start;
  text-transform: none;
  letter-spacing: 0.02em;
  font-size: 0.72rem;
  margin: 6px 0 2px;
  padding-top: 6px;
  border-top: 1px solid var(--rule);
}
.side {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0;
}
.side .team { flex: 1; font-weight: 500; }
.side .team .rk {
  color: var(--ink-3);
  font-size: 0.82em;
  font-weight: 400;
}
/* The most-tapped control in the whole app: thirty of these, by thumb, on a
   Saturday. 44px is the floor, not the goal, and the 1rem type is load-bearing
   too -- iOS zooms the page when it focuses an input under 16px. */
.side input {
  width: 72px;
  min-height: 44px;
  padding: 7px 8px;
  font: 600 1rem ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.side input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.side input:disabled { background: var(--surface-2); color: var(--ink-3); }

/* --- sticky save bar --- */
/* The hash marks again, this time as the bar's top edge. Painted as a
   background rather than a ::before, because ::before on a flex container is a
   flex ITEM -- it would sit in the row beside the status and the button. */
.savebar {
  position: sticky;
  bottom: 0;
  background-color: var(--surface);
  background-image:
    repeating-linear-gradient(90deg, var(--hash-5) 0 2px, transparent 2px 80px),
    repeating-linear-gradient(90deg, var(--hash) 0 2px, transparent 2px 16px),
    linear-gradient(var(--rule), var(--rule));
  background-size: 100% 9px, 100% 5px, 100% 1px;
  background-position: 0 0, 0 0, 0 0;
  background-repeat: repeat-x, repeat-x, no-repeat;
  padding: 18px 18px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
/* 44px is the floor for EVERY button, not just the ones marked .btn-small.
   Measured before this line existed: Save picks, Publish, Finalize, Export and
   Add game all came out at 38-40px -- under the touch target on the two screens
   that are used from a phone. */
button {
  font: 600 0.92rem 'Archivo', system-ui, sans-serif;
  min-height: 44px;
  padding: 9px 20px;
  border: 0;
  border-radius: 5px;
  background: var(--accent);
  /* Not #fff. The dark theme's accent is a pale amber, and white on it is
     1.9:1 -- the button was unreadable on a phone in dark mode. */
  color: var(--on-accent);
  cursor: pointer;
}
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.status { font-size: 0.85rem; color: var(--ink-2); }
.status.err { color: var(--loss); }
.status.ok { color: var(--win); }

/* Destructive. Deliberately the loss colour: the operator should register it
   as the same kind of thing as a loss on the scoreboard before clicking. */
.btn-danger { background: var(--loss); }
/* Secondary, for actions that sit beside a primary one without competing. */
.btn-quiet {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--rule);
}
.btn-quiet:hover:not(:disabled),
.btn-quiet:focus-visible { border-color: var(--accent); }
/* Quiet AND destructive: the outline of a secondary button in the loss colour.
   Delete sits in a row of things used once a season, so a slab of solid red on
   every one of fifteen cards is weight the screen cannot afford -- but it stays
   the only red thing in the row, which is the part that matters. */
.btn-quiet.btn-danger {
  background: transparent;
  color: var(--loss);
  border: 1px solid var(--rule);
}
.btn-quiet.btn-danger:hover:not(:disabled),
.btn-quiet.btn-danger:focus-visible { border-color: var(--loss); }
/* Reorder and row buttons: small type, but never a small target. */
.btn-small { padding: 6px 12px; font-size: 0.82rem; min-height: 44px; }

/* --- operator form controls ---
   Scoped to .field / .check so nothing here reaches the pick card's compact
   score boxes in picks.html, which are deliberately their own size. */
.field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.field > span {
  font-family: ui-monospace, monospace;
  font-size: 0.64rem;
  line-height: 1.25;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.field input,
.field select {
  width: 100%;
  min-height: 44px;
  padding: 9px 10px;
  font: 0.95rem 'Archivo', system-ui, sans-serif;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.field input:focus-visible,
.field select:focus-visible,
.check input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.field input:disabled, .field select:disabled {
  background: var(--surface-2);
  color: var(--ink-3);
}
.check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-size: 0.9rem;
}
.check input[type='checkbox'] {
  width: 22px;
  height: 22px;
  accent-color: var(--accent);
  margin: 0;
}

/* --- sign-in and password forms (U1-U3) ---
   Deliberately thin: these reuse .field and the button rules above, and the
   design pass (U9) owns how any of it looks. What is here is the layout the
   controls cannot do without -- a single column on a phone, a reveal button
   that sits inside the password field's row, and hint text that reads as help
   rather than as an error. */
.stack { display: flex; flex-direction: column; gap: 12px; max-width: 26rem; }
.stack .field + .hint { margin-top: -8px; }
.h2 { font-size: 1.05rem; margin: 0; }
.hint { font-size: 0.85rem; color: var(--ink-2); margin: 0; }
.note {
  font-size: 0.92rem;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  padding: 12px 14px;
  margin: 0;
}
/* The show/hide toggle rides in the field, so the password box keeps the full
   width of the column minus the button rather than wrapping under it. */
.pw { display: flex; gap: 8px; align-items: stretch; }
.pw input { flex: 1; min-width: 0; }
.pw button { white-space: nowrap; }
.actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
/* Sign out is a button, not a link -- it changes state -- but it sits in the
   nav row with the links and needs their spacing. */
nav button { margin-left: 16px; }

/* --- tables ---
   NO TURF UNDER A TABLE. `.tw` carries an opaque --surface for exactly that
   reason: a 94-row standings table read on a phone in sunlight has no margin
   to give away to a pattern, however faint. The field motifs stop at the edge
   of anything with rows in it.

   `.tw` is also the horizontal-scroll container. The six-column season table
   does not fit 375px, and it is the TABLE that scrolls -- the body never
   does. */
table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.tw {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background-color: var(--surface);
  /* Cues that a table too wide for the screen scrolls, without JS: two
     solid covers travel WITH the content (background-attachment: local) and
     hide two shadows that stay put (attachment: scroll) -- each shadow shows
     only where there is more table in that direction, and neither shows at
     all when the table already fits. */
  background-image:
    linear-gradient(to right, var(--surface) 30%, transparent),
    linear-gradient(to right, transparent, var(--surface) 70%),
    linear-gradient(to right, rgba(0, 0, 0, 0.18), transparent),
    linear-gradient(to left, rgba(0, 0, 0, 0.18), transparent);
  background-repeat: no-repeat;
  background-size: 24px 100%, 24px 100%, 10px 100%, 10px 100%;
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-attachment: local, local, scroll, scroll;
}
th, td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--rule); }
th {
  font-family: ui-monospace, monospace;
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--surface-2);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
td.num {
  text-align: right;
  white-space: nowrap;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.rank {
  color: var(--ink-3);
  width: 3ch;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* Gold/silver/bronze for the top three of a standings table -- whichever
   table, and by the row's actual rank (set from JS), never by DOM position:
   a signed-in viewer's "neighbourhood" view can start with rank 1 followed by
   a gap marker, but rank 1 is always the first REAL row when it appears at
   all, so this stays correct without having to special-case the gap. Its own
   background/text pair, so it reads the same on both themes without a dark
   override. */
.rank.medal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-weight: 800;
}
.rank.medal-1 { background: linear-gradient(160deg, #ffe08a, #d4930b); color: #3a2e00; }
.rank.medal-2 { background: linear-gradient(160deg, #e6e6e6, #a8a8a8); color: #2a2a2a; }
.rank.medal-3 { background: linear-gradient(160deg, #d68a5c, #a15a30); color: #fff; }

/* The viewer's own row. The tint alone is easy to miss when the row is halfway
   down a table and the phone is in daylight, so it carries an amber edge as
   well -- the same mark the scoreboard uses for a number that is yours. */
tr.me { background: var(--accent-soft); }
tr.me td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
tr.me td { font-weight: 600; }
/* Measured: --ink-3 on the dark theme's --accent-soft is 3.94:1, an AA failure
   that exists on exactly one row of the table -- the viewer's own. --ink-2
   clears it at 5.64, and a firmer rank on your own row is right anyway. */
tr.me .rank { color: var(--ink-2); }
.star { color: var(--accent); }

/* A player's favourite team, as a left edge on their standings row. On the
   CELL, not the tr: a border declared on a table row is only used to resolve
   the collapsed-border model, not painted on the row itself, so it silently
   does nothing there. --team is set inline per row from a client-side colour
   map (public/index.html) keyed on players.favorite_team; a team not in the
   map falls back to transparent rather than a default colour, so an unlisted
   team is invisible rather than wrong. A border, not tr.me's box-shadow, so
   the two can sit side by side on a row that is both the viewer's own and has
   a team colour, rather than one silently winning the cascade. */
#weekRows tr:not(.gap) td:first-child,
#seasonRows tr:not(.gap) td:first-child {
  border-left: 5px solid var(--team, transparent);
}

.empty { color: var(--ink-2); padding: 30px 0; text-align: center; }

/* --- dashboard panels (U8 structure, U9 surface) ---
   A stack that reads in order on a 375px screen, lists that are not bullets,
   and an empty line that sits where its panel's content would have been rather
   than centred in a void. What U9 added is the divider: each panel opens with
   a row of yard-line hash marks instead of a border, which is the field motif
   doing structural work rather than sitting on top of the page as ornament. */
.panel { margin-bottom: 34px; }
.panel::before { margin-bottom: 18px; }
/* The panel directly under the header slab needs no divider -- the slab's own
   edge already is one, and two lines 26px apart read as a mistake. */
.wrap > .panel:first-of-type::before { content: none; }
.panel h2 {
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 4px;
}
/* A color bar per section, one accent hue each -- NOT the hero's h2, which is
   a tiny caption above the score, not a section title, and already carries
   the scoreboard's own amber. Keyed by section id rather than nth-of-type so
   a hidden panel (yourWeek, signed out) cannot shift which bar lands where. */
#thisWeek > h2, #theSeason > h2, #curiosities > h2 {
  position: relative;
  padding-left: 14px;
}
#thisWeek > h2::before, #theSeason > h2::before, #curiosities > h2::before {
  content: '';
  position: absolute;
  left: 0;
  top: 1px;
  bottom: 2px;
  width: 5px;
  border-radius: 3px;
}
#thisWeek > h2::before { background: var(--accent); }
#theSeason > h2::before { background: var(--accent-2); }
#curiosities > h2::before { background: var(--accent-3); }
.panel h3 {
  font-family: ui-monospace, monospace;
  font-size: 0.68rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 22px 0 6px;
}
.panel h4 { font-size: 0.92rem; margin: 14px 0 4px; }
/* Every panel says "not yet" rather than disappearing (KTD6) -- but an empty
   panel must not SHOUT louder than a full one, and at body size next to a
   one-line list it did. Smaller, and with the paragraph's own default margin
   removed so the line sits under its heading rather than adrift from it. */
.panel .empty {
  margin: 0;
  padding: 2px 0 6px;
  font-size: 0.9rem;
  text-align: left;
}
.list {
  list-style: none;
  margin: 0;
  padding: 0 14px;
  font-size: 0.92rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
}
.list li { padding: 9px 0; border-bottom: 1px solid var(--rule); }
.list li:last-child { border-bottom: 0; }
.list .mono { color: var(--accent); font-weight: 700; }
/* The gap marker (KTD7). It has to READ as skipped rows -- a table that jumps
   from 10 to 31 with nothing between them looks like a bug. */
tr.gap td {
  text-align: center;
  color: var(--ink-3);
  font-size: 0.8rem;
  background: var(--surface-2);
  letter-spacing: 0.14em;
}
.move { font-size: 0.8rem; white-space: nowrap; color: var(--ink-3); }
.move.up { color: var(--win); }
.move.down { color: var(--loss); }

/* --- the hero: your week, as the board itself ---
   The one place on the site that gets an enormous number. A stadium scoreboard
   shows a handful of figures the size of a door and nothing else, and that
   restraint is the whole reason this reads as a scoreboard rather than as a
   website with an orange section. So: ONE huge amber rank, the rest of the
   sentence running small along its baseline. */
.panel.hero {
  background: var(--board);
  background-image: repeating-linear-gradient(
    90deg,
    transparent 0 63px,
    var(--board-line) 63px 64px
  );
  color: var(--board-ink);
  border: 1px solid rgba(255, 176, 32, 0.16);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
  border-radius: 10px;
  padding: 16px 20px 20px;
}
.panel.hero h2 {
  font-family: ui-monospace, 'IBM Plex Mono', Menlo, Consolas, monospace;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--board-mute);
  margin-bottom: 2px;
}
.hero-line {
  margin: 0;
  line-height: 1.25;
  color: var(--board-ink);
}
/* Inline rather than a block of its own, so "of 94 this week" sits on the
   number's baseline the way a caption sits under a scoreboard figure. */
.hero-rank {
  font-size: 3.4rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--board-amber);
  font-variant-numeric: tabular-nums;
  margin-right: 0.06em;
}
.panel.hero .mono { color: var(--board-amber); font-weight: 700; }
.panel.hero .hint { color: var(--board-mute); margin-top: 10px; }
.panel.hero .hint strong { color: var(--board-ink); }
.panel.hero .move { color: var(--board-mute); }
.panel.hero .move.up { color: var(--board-win); }
.panel.hero .move.down { color: var(--board-loss); }
.panel.hero .star { color: var(--board-amber); }

/* Motion. There is none to speak of on these pages -- a border colour on hover
   and nothing else -- but the rule stays so that anything added later inherits
   the answer rather than having to remember the question. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}
