/* px-lms design system.
   Lifted from the approved concept (docs/mockup-americas-number-1-hater.html)
   and turned into tokens, so a second promotion re-themes by overriding the
   custom properties in promotion.json rather than forking this file. */

:root {
  /* Tokens are named for the JOB they do, not the colour they happen to be.
     The three colour-named tokens at the bottom are aliases kept so the ~110
     call sites below — and both shipped promotion.json theme blocks — did not
     all have to change in one commit. New code should use the semantic names.

     ROLES, and why they are three different hues rather than one:

     accent  — brand chrome. The red names the product; it never names a state.
     positive — alive / survived / won / current round / your pick / verified.
     danger  — eliminated / busted / burned / locked / error / warning.

     Red cannot carry both "brand" and "alive": if it did, an alive entrant and
     an eliminated one would render in the same colour in the leaderboard.
     And danger cannot be red either — the brand red against the ember red this
     file used to use measures 1.34:1, i.e. the same colour to the eye, so a red
     danger state would be indistinguishable from a red button. Amber is. */

  /* Brand. #E93A3A is the operator's stated primary and is not up for nudging;
     everything else here moved to make it work. */
  --accent: #E93A3A;
  --accent-bright: #FF5A5A;
  --accent-rgb: 233 58 58;
  /* Label on a filled accent surface. Black is 5.13:1 there; every white,
     #FFFFFF included, is under 4.5. */
  --on-accent: #000000;
  /* Same story on a filled positive surface, at 9.70:1. */
  --on-positive: #000000;

  /* Positive states keep the mint family they already had. */
  --positive: #00C896;
  --positive-bright: #00E5AA;
  --positive-rgb: 0 200 150;

  /* Danger/warning. Already in this file as an unnamed literal at .result-warn. */
  --danger: #FFB020;
  --danger-rgb: 255 176 32;
  --warning: var(--danger);

  /* Grounds. #000000 is the operator's stated secondary, and also the only
     ground the brand red clears AA on: on the old #0A1520 navy it measured
     4.50:1, a hair under. On black it is 5.13:1. The raised surfaces are
     near-black rather than black so a panel still reads as lifted. */
  --bg-deep: #000000;
  --bg-panel: #0B0B0B;
  --bg-panel-2: #151515;
  /* The ground as a channel triple, for the translucent header/menu scrims. */
  --bg-deep-rgb: 0 0 0;

  --white: #F5F7FA;
  --muted: #8A94A6;
  /* 0.08 over the old navy read as 1.23:1; over black the same 0.08 is 1.14:1,
     which loses the hairline. 0.12 puts it back at 1.27:1. */
  --line: rgba(255, 255, 255, 0.12);
  --radius: 10px;
  --wrap: 1180px;

  /* Aliases. --navy and --teal-light are gone: nothing referenced either. */
  --teal: var(--accent);
  --teal-alt: var(--accent-bright);
  --ember: var(--danger);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  background: var(--bg-deep);
  color: var(--white);
  font-family: 'Inter', system-ui, sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
.display { font-family: 'Anton', sans-serif; font-weight: 400; letter-spacing: 0.01em; }
a { color: inherit; text-decoration: none; }
[hidden] { display: none !important; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 32px; }

:where(button, a, input, [tabindex]):focus-visible {
  outline: 2px solid var(--teal-alt);
  outline-offset: 2px;
}

/* ── Nav ──────────────────────────────────────────────────────────────── */

header {
  position: sticky; top: 0; z-index: 50;
  background: rgb(var(--bg-deep-rgb) / 0.85);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 16px 32px; max-width: var(--wrap); margin: 0 auto;
}
/* Only the header's nav, because `.admin-tabs` is a <nav> too and picks up
   everything the bare type selector above sets. This is the containing block
   for the mobile menu panel, which is positioned against it. */
header nav { position: relative; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand span { font-size: 15px; font-weight: 800; letter-spacing: 0.08em; }
.brand small { display: block; font-size: 9px; color: var(--teal); letter-spacing: 0.14em; font-weight: 600; margin-top: -2px; }

/* The lockup is the email's own image now (server/views/layout.js brandLockup),
   so the brand stacks: logo, kicker under it. Before, an X mark sat beside the
   word PROPHETX re-typeset in Inter, which is why the header and the mail did
   not look like the same product. The wordmark is white, and every surface it
   appears on is dark — header the ground at 85%, footer var(--bg-panel),
   and neither shipped theme overrides those. */
.brand-lock { flex-direction: column; align-items: flex-start; gap: 3px; }
.brand-logo { display: block; width: clamp(112px, 12vw, 148px); height: auto; }
.brand-lock small { display: block; font-size: 9px; color: var(--teal); letter-spacing: 0.14em; font-weight: 600; }

/* Hamburger for .nav-links. Off above 760px, where the links are in the bar.
   It sits before .nav-links in the DOM so Tab goes from the button straight
   into the menu it just opened; `order` in the media query below puts it back
   on the right of the row visually. */
.nav-toggle {
  display: none; flex: 0 0 auto; align-items: center; justify-content: center;
  width: 40px; height: 38px; padding: 0; cursor: pointer;
  background: transparent; border: 1px solid var(--line); border-radius: 8px;
  color: var(--white); font: inherit;
}
.nav-toggle:hover { border-color: var(--teal); }
.nav-toggle-bars { position: relative; display: block; width: 17px; height: 2px; background: currentColor; border-radius: 2px; }
.nav-toggle-bars::before, .nav-toggle-bars::after {
  content: ""; position: absolute; left: 0; display: block;
  width: 17px; height: 2px; background: currentColor; border-radius: 2px;
}
.nav-toggle-bars::before { top: -5px; }
.nav-toggle-bars::after { top: 5px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { top: 0; transform: rotate(-45deg); }
/* The button's accessible name, not a decoration: it is what a screen reader
   announces, so it is hidden from sight rather than from the a11y tree. */
.nav-toggle-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.nav-links { display: flex; gap: 32px; font-size: 14px; color: var(--muted); font-weight: 500; }
.nav-links a:hover, .nav-links a.active-link { color: var(--white); }
/* `min-width: 0` on both, because a flex item's default `min-width: auto`
   refuses to shrink below its content and that is what pushed the header past
   the viewport: signed in, the bar is the lockup plus "BrandonMcAllister1994 ·
   0 WK STREAK" plus "Make Your Pick", and at 768 — just above the breakpoint
   where the links leave the bar — that measured 775 in a 768 viewport and took
   the CTA off screen with it. The chip is the item that gives, at every width,
   and it ellipsises rather than wrapping "0 WK STREAK" onto a second line
   mid-phrase. public/js/app.js keeps the untruncated string as the title. */
.nav-right { display: flex; align-items: center; gap: 14px; min-width: 0; }
.playing-as {
  font-size: 11px; color: var(--teal-alt); letter-spacing: 0.06em;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.nav-cta, .btn-primary {
  background: var(--teal); color: var(--on-accent); padding: 10px 20px; border-radius: 6px;
  font-weight: 700; font-size: 13px; letter-spacing: 0.02em;
  box-shadow: 0 0 0 1px rgb(var(--accent-rgb) / 0.3);
  transition: transform .15s ease, box-shadow .15s ease;
  border: none; font-family: inherit; cursor: pointer;
}
.nav-cta:hover:not(:disabled), .btn-primary:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 4px 16px rgb(var(--accent-rgb) / 0.35); }
/* The holding page's call to action: present, legible, and plainly not a
   button yet — "Opening Soon" is a promise, not a control. */
[data-action="opening-soon"]:disabled { opacity: .55; cursor: default; box-shadow: none; transform: none; }
.btn-primary { padding: 14px 28px; font-size: 14px; }
.btn-primary:disabled { opacity: .4; cursor: not-allowed; }
.btn-secondary {
  display: inline-block; padding: 14px 28px; border-radius: 6px; font-size: 14px;
  font-weight: 700; border: 1px solid var(--line); color: var(--white);
}
.btn-secondary:hover { border-color: var(--teal); color: var(--teal-alt); }

/* Below 760px the links do not fit in the bar. They used to be `display: none`
   with nothing in their place — and My Run has no URL of its own and was not in
   the footer either, so that section was unreachable on every phone. It is a
   disclosure panel behind .nav-toggle now. */
@media (max-width: 760px) {
  /* Row one is the logo, the call to action and the menu button. Row two, when
     there is one, is the identity chip on its own — `.nav-right` stops being a
     box here (display: contents) precisely so its two children can land on
     different rows, which a shared parent would not allow. Left as one flex
     item, .nav-right could not fit beside the brand and the button, so it took
     a whole row and pushed the button onto a third: a 165px-tall header on a
     phone, most of it empty. Signed out the chip is `hidden`, so there is no
     second row at all. */
  nav { flex-wrap: wrap; row-gap: 8px; column-gap: 12px; }
  .brand { order: 1; }
  .nav-right { display: contents; }
  .nav-cta { order: 2; margin-left: auto; }
  .nav-toggle { display: inline-flex; order: 3; }
  .playing-as { order: 4; flex: 1 0 100%; }
  .nav-links {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0; padding: 2px 20px 10px; font-size: 15px;
    background: rgb(var(--bg-deep-rgb) / 0.98); border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
  }
  nav.is-nav-open .nav-links { display: flex; }
  .nav-links a { padding: 13px 2px; color: var(--white); border-bottom: 1px solid var(--line); }
  .nav-links a:last-child { border-bottom: 0; }
  .nav-links a.active-link { color: var(--teal-alt); }
}

/* ── Pages ────────────────────────────────────────────────────────────── */

.page { display: none; }
.page.active { display: block; animation: pageIn .35s ease; }
@keyframes pageIn { from { opacity: 0 } to { opacity: 1 } }

/* ── Hero ─────────────────────────────────────────────────────────────── */

.hero { position: relative; padding: 100px 32px 90px; text-align: center; overflow: hidden; }
.hero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(ellipse 700px 400px at 50% -10%, rgb(var(--accent-rgb) / .16), transparent 70%),
    radial-gradient(ellipse 500px 300px at 85% 20%, rgb(var(--accent-rgb) / .08), transparent 70%);
}
.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgb(var(--accent-rgb) / .08); border: 1px solid rgb(var(--accent-rgb) / .3);
  color: var(--teal-alt); font-size: 12px; font-weight: 700; letter-spacing: .12em;
  padding: 7px 14px; border-radius: 100px; margin-bottom: 28px; text-transform: uppercase;
}
.eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px var(--accent); animation: pulse 1.6s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

h1.headline {
  font-family: 'Anton', sans-serif; font-weight: 400;
  font-size: clamp(48px, 9vw, 108px); line-height: .92; text-transform: uppercase;
}
.strike-wrap { position: relative; color: var(--accent); }
.strike-wrap::after {
  content: ""; position: absolute; left: -2%; right: -2%; top: 52%; height: 6px;
  background: var(--accent); transform: rotate(-2.5deg); opacity: .9;
}
.presented { margin-top: 18px; font-size: 13px; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.presented b { color: var(--teal-alt); }
.sub { max-width: 640px; margin: 26px auto 0; color: var(--muted); font-size: 17px; }
.sub strong { color: var(--white); }
.hero-ctas { display: flex; gap: 14px; justify-content: center; margin-top: 34px; flex-wrap: wrap; }
.hero-stats { display: flex; gap: 56px; justify-content: center; margin-top: 60px; flex-wrap: wrap; }
.stat .num { font-family: 'Anton', sans-serif; font-size: 34px; color: var(--teal-alt); }
.stat .lbl { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-top: 4px; }

/* ── Prize ────────────────────────────────────────────────────────────── */

.prize-hero { padding: 80px 32px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--bg-panel); }
.prize-hero-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center; }
.kicker { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--teal); font-weight: 700; display: flex; align-items: center; gap: 10px; }
.kicker-ember { color: var(--accent); }
.kicker .rule { width: 24px; height: 1px; background: currentColor; display: inline-block; }
/* line-height has to clear Anton's descenders: the cash headline breaks as
   "$10,000" / "IN CASH", and at .95 the comma dropped onto the shoulder of the
   C below it and read as a diacritic. 1.05 keeps at least 3px of gap across the
   whole clamp() range, including the fractional sizes near the 860px
   breakpoint, where 1.02 still closed to 1px. */
.prize-headline { font-family: 'Anton', sans-serif; font-weight: 400; font-size: clamp(38px, 5vw, 62px); line-height: 1.05; text-transform: uppercase; margin: 16px 0 18px; }
.prize-copy { color: var(--muted); max-width: 440px; margin-bottom: 24px; }
.ticket-svg { width: 100%; height: auto; }
@media (max-width: 860px) {
  .prize-hero-inner { grid-template-columns: 1fr; }
  /* One column means the artwork gets the full width, which on a tablet or a
     phone in landscape blows a 420px design up past 1000px. Hold its size. */
  .ticket-svg { max-width: 420px; margin: 0 auto; display: block; }
}

/* ── Sections ─────────────────────────────────────────────────────────── */

section#how, section#board, section#run, section#rules, section#leaderboard, section#home-board { padding: 80px 0; }
.section-head { max-width: 640px; margin-bottom: 44px; }
.section-head-tight { margin-bottom: 36px; }
.section-head h2 { font-family: 'Anton', sans-serif; font-weight: 400; font-size: clamp(30px, 4vw, 46px); text-transform: uppercase; margin: 14px 0 12px; }
.section-head p { color: var(--muted); }

.how-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.how-card { position: relative; background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 28px 22px; }
.how-num { font-family: 'Anton', sans-serif; font-size: 30px; color: var(--teal); opacity: .5; }
.how-card h3 { font-size: 17px; margin: 10px 0 8px; }
.how-card p { color: var(--muted); font-size: 14px; }
@media (max-width: 900px) { .how-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .how-grid { grid-template-columns: 1fr; } }

/* ── Board ────────────────────────────────────────────────────────────── */

.board-frame { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; }
/* No overflow:hidden on the frame — it would make the frame the sticky
   container and the submit bar would never reach the viewport. The rounded
   corners are kept by clipping the top bar and the submit bar themselves. */
.board-frame > .board-topbar { border-radius: 14px 14px 0 0; }
.board-frame > .submit-bar { border-radius: 0 0 14px 14px; }
.board-topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 20px; border-bottom: 1px solid var(--line); background: var(--bg-panel-2); flex-wrap: wrap; }
.board-topbar-right { display: flex; align-items: center; gap: 16px; }
.week { font-size: 12px; font-weight: 700; letter-spacing: .14em; color: var(--muted); }
.week span { color: var(--teal-alt); }
.board-source { font-size: 10px; letter-spacing: .1em; color: var(--muted); border: 1px solid var(--line); padding: 3px 8px; border-radius: 4px; }
.locks-in { font-size: 11px; letter-spacing: .1em; color: var(--ember); }
.locks-in .clock { margin-right: 6px; }

.board-filters { display: flex; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--line); flex-wrap: wrap; align-items: center; }
.board-search {
  flex: 1 1 220px; min-width: 180px; background: var(--bg-deep); border: 1px solid var(--line);
  border-radius: 6px; padding: 9px 12px; color: var(--white); font-family: inherit; font-size: 13px;
}
.board-search::placeholder { color: var(--muted); }
.chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
  background: transparent; border: 1px solid var(--line); color: var(--muted);
  border-radius: 100px; padding: 7px 14px; font-size: 12px; font-weight: 600;
  font-family: inherit; cursor: pointer;
}
.chip:hover { color: var(--white); border-color: var(--teal); }
.chip.is-on { background: rgb(var(--accent-rgb) / .12); border-color: var(--teal); color: var(--teal-alt); }

.rb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(164px, 1fr)); gap: 8px; padding: 14px; align-items: stretch; }
.board-empty { grid-column: 1 / -1; color: var(--muted); text-align: center; padding: 40px 0; font-size: 14px; }

.rb-card {
  position: relative; background: var(--bg-panel-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px 11px; display: flex; flex-direction: column; gap: 7px;
  transition: border-color .15s ease, transform .15s ease;
}
.rb-card:not(.locked):hover { border-color: rgb(var(--accent-rgb) / .5); transform: translateY(-2px); }
.rb-card.selected { border-color: var(--positive); box-shadow: 0 0 0 1px var(--positive), 0 6px 22px rgb(var(--positive-rgb) / .18); }
.rb-card.locked { opacity: .42; }
/* Game pairs on the public board, mirroring the console: the two teams of one
   game share a tint, alternating game to game, so the pair reads as a block
   even when the grid wraps it onto different rows. */
.rb-card.pair-b { background: linear-gradient(rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.035)), var(--bg-panel-2); }

/* A name this entrant spent THIS round: dimmed like any burned card, but edged
   in the positive green and labelled as theirs — not the danger amber of a
   name lost to an earlier round. It stays unpickable; it is simply already
   yours. */
.rb-card.mine { opacity: .62; border-color: rgb(var(--positive-rgb) / .45); }
.rb-card.mine .rb-pick-btn { color: var(--positive); }
.mine-label { color: var(--positive); }
/* The card's identity row is the shared .pick-ident block; this keeps its
   own gap and badge size. */
.rb-top { display: flex; justify-content: flex-start; align-items: center; gap: 10px; }
/* Uniform cards, Patrick's ask: every name block is exactly two lines tall
   (a one-line name leaves the second line empty, a three-line name is clamped),
   so the price row and the button sit in the same place on every card. */
.rb-name {
  font-weight: 700; font-size: 13.5px; line-height: 1.2; min-height: 2.4em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.rb-team { font-size: 10px; color: var(--accent-bright); letter-spacing: .1em; font-weight: 700; }
.rb-matchup { font-size: 10px; color: var(--muted); font-family: 'JetBrains Mono', monospace; white-space: nowrap; }
/* First pitch: the moment this one card stops being pickable. It sits under
   the matchup because the two belong together — which game, and when it
   starts — and stacking them in the corner keeps the whole right column as
   one block of reference detail, clear of the name and the price.
   Smaller than the matchup above it: same column, lower billing. */
/* The right column never gives up width to a long name, and the name column
   is allowed to shrink and wrap instead — `min-width: 0` on a flex child is
   what lets it, and without it a name like "Ronald Acuña Jr." pushes the
   column it should be wrapping inside of. */
.rb-right { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }
.rb-left { min-width: 0; }
.rb-when {
  font-size: 9.5px; color: var(--muted); font-family: 'JetBrains Mono', monospace;
  letter-spacing: .02em; white-space: nowrap; text-align: right;
  font-variant-numeric: tabular-nums;
}
.rb-when.is-locked { color: var(--muted); opacity: .6; }
/* The stadium, under the matchup and countdown: reference detail, quietest of
   the three, truncated rather than wrapping a long name into the card. */
.rb-venue {
  font-size: 9px; color: var(--muted); opacity: .8; text-align: right;
  max-width: 110px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rb-card.locked .rb-when { opacity: .55; }
.mp-when { margin-top: 4px; font-size: 12px; color: var(--muted); }
.rb-odds { display: flex; justify-content: space-between; align-items: flex-end; gap: 8px; min-height: 42px; }
/* The price block keeps its width; the game column takes what is left and
   truncates rather than spilling past the card edge on the narrowest cards. */
.rb-odds > div:first-child { flex: 0 0 auto; min-width: 0; }
.rb-odds .rb-right { flex: 1 1 auto; min-width: 0; max-width: 100%; }
.rb-odds .rb-matchup, .rb-odds .rb-when { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.rb-odds .rb-venue { max-width: 100%; }
/* The team badge sits at the right of the odds row, the way Patrick mocked it:
   a round mark beside the price, above the button. Sized so the price stays
   the loudest thing on the card; smaller on phones so the row does not wrap. */
.rb-logo { width: 42px; height: 42px; flex: 0 0 auto; object-fit: contain; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5)); }
.rb-card.locked .rb-logo { filter: grayscale(1) opacity(.7); }
@media (max-width: 480px) { .rb-logo { width: 35px; height: 35px; } }
.rb-odds .lbl { font-size: 8px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.rb-odds .val { font-family: 'JetBrains Mono', monospace; font-size: 16px; font-weight: 700; color: var(--white); line-height: 1.1; }
/* A price with no offer resting behind it: the last one the market traded at.
   Shown rather than blanked (Patrick, 2026-09-07), but marked, because you
   cannot take a price nobody is offering. */
.rb-odds .val.is-last { color: var(--muted); }
.val-last { font-family: 'JetBrains Mono', monospace; font-size: 8px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-left: 5px; vertical-align: 2px; }
/* An estimate, worked out from the other side of the book. Same size and
   placement as `last` so the two read as one family of caveat, and the number
   itself is dimmed a little further: it is the least firm of the three. */
.val-est { font-family: 'JetBrains Mono', monospace; font-size: 8px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-left: 5px; vertical-align: 2px; opacity: .85; }
.val.is-est { opacity: .72; }
.rb-consensus { font-size: 10px; color: var(--muted); font-family: 'JetBrains Mono', monospace; }
.rb-consensus.hot { color: var(--ember); }
.rb-pick-btn {
  width: 100%; background: rgb(var(--accent-rgb) / .1); border: 1px solid rgb(var(--accent-rgb) / .35);
  color: var(--teal-alt); border-radius: 6px; padding: 6px; font-size: 11px; font-weight: 700;
  font-family: inherit; cursor: pointer;
 margin-top: auto;}
.rb-pick-btn:hover:not(:disabled) { background: rgb(var(--accent-rgb) / .2); }
.rb-pick-btn:disabled { opacity: .5; cursor: not-allowed; border-color: var(--line); color: var(--muted); background: transparent; }
.rb-card.selected .rb-pick-btn { background: var(--positive); color: var(--on-positive); border-color: var(--positive); }

.torch-stamp {
  position: absolute; inset: 0; pointer-events: none; border-radius: var(--radius);
  background: repeating-linear-gradient(-45deg, transparent 0 8px, rgb(var(--danger-rgb) / .06) 8px 16px);
}
.torch-label { font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--ember); }
/* .torch-label and .mine-label are the same specificity and this one is later
   in the file, so a card that is BOTH burned-styled and yours (app.js paints
   "Your pick this round" with both classes) took the danger colour. It is a
   positive state and needs to say so. */
.torch-label.mine-label { color: var(--positive); }

.board-footer { padding: 14px 20px; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); }
.submit-bar {
  display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 12px 20px;
  border-top: 1px solid var(--line); background: var(--bg-panel-2); flex-wrap: wrap;
  /* Pinned to the bottom of the viewport for as long as the board is on screen,
     so a pick can be submitted from anywhere in the grid without scrolling. */
  position: sticky; bottom: 0; z-index: 6;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, .55);
}
.submit-bar.has-pick { border-top-color: rgb(var(--positive-rgb) / .5); }
.submit-status b { color: var(--white); }
.submit-status { font-size: 13px; color: var(--muted); }
.submit-status.ready { color: var(--positive-bright); }
.submit-status.error { color: var(--ember); }

/* ── Rules ────────────────────────────────────────────────────────────── */

.rules-layout { display: grid; grid-template-columns: 1.4fr 1fr; gap: 48px; align-items: start; }
.rule-list { list-style: none; display: flex; flex-direction: column; gap: 22px; }
.rule-list li { display: flex; gap: 16px; }
.rule-mark {
  flex: 0 0 30px; height: 30px; border-radius: 50%; border: 1px solid var(--teal);
  color: var(--teal-alt); display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.rule-list h4 { font-size: 16px; margin-bottom: 5px; }
.rule-list p { color: var(--muted); font-size: 14px; }
.rules-legal { margin-top: 32px; font-size: 12px; color: var(--muted); }

.pot-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 28px; }
.pot-lbl { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--teal); font-weight: 700; }
.pot-num { font-family: 'Anton', sans-serif; font-size: 36px; margin: 10px 0 4px; text-transform: uppercase; }
.pot-sub { font-size: 13px; color: var(--muted); margin-bottom: 22px; }
.pot-row { display: flex; justify-content: space-between; padding: 11px 0; border-top: 1px solid var(--line); font-size: 13px; }
.pot-row .k { color: var(--muted); }
.pot-row .v { font-family: 'JetBrains Mono', monospace; font-weight: 700; }
@media (max-width: 860px) { .rules-layout { grid-template-columns: 1fr; } }

/* ── Leaderboard ──────────────────────────────────────────────────────── */

/* The standings were the only data table in this file with no scroll
   container: five 20px-padded columns need ~600px, so on a phone Status and
   Last Pick were clipped by body{overflow-x:hidden} — unreachable, not
   scrollable. This is the same treatment every admin table already gets from
   .entrants-scroll, and it keeps the table semantics: restacking rows as cards
   would mean inventing per-cell labels in CSS, and user-visible strings live in
   promotions/<slug>/content.json, not here. */
.lb-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.lb-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.lb-table th {
  text-align: left; padding: 14px 20px; font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--line); background: var(--bg-panel-2);
}
.lb-table td { padding: 14px 20px; border-bottom: 1px solid var(--line); }
.lb-table tr.is-me td { background: rgb(var(--accent-rgb) / .06); }
/* Cells stop wrapping now that the table can scroll: "Hidden until kickoff"
   was breaking onto three lines and making a two-word cell the tallest row on
   the page. The entrant's own name is the exception — usernames run to 24
   characters, and that column is the one that should give. */
.lb-table th, .lb-table td { white-space: nowrap; }
/* Column 2 of the share table stacks a pick's name over its matchup, so it is
   the one cell in this file that still wants to wrap. 22ch is the width it had
   before the verified pill briefly took this selector to 25ch; that pill is on
   the standings table, not this one. */
.lb-table.share-picks td:nth-child(2) { white-space: normal; overflow-wrap: anywhere; min-width: 22ch; }

/* The standings' entrant cell holds two things, and only one of them may give
   up space. The entrant's own name can be shortened and still be recognised.
   The verified pill is a logo: it is whole or it is wrong. So the name gets a
   `max-width` and an ellipsis, and the pill keeps the `nowrap` every cell here
   already has.

   The cap is the load-bearing part, and it has to be a `max-width`. This is an
   auto-layout table, and an auto-layout table sizes a column from its content's
   *max-content* width, so the usual flex recipe does not reach it: measured at
   390px, `display: flex` + `min-width: 0` + `overflow: hidden` on the name took
   this column 225px -> 299.77px — wider, not narrower, and nothing truncated. A
   `max-width` works because it caps the very number the column is sized from.

   Why the width matters at all: at 390px the table is wider than its card and
   scrolls (see .lb-scroll), so what is legible at rest is whatever fits the
   first 352px. Taking this column to 25ch/225px pushed the last two letters of
   the STREAK header and the "s" of every "4 wks" past that edge — the header
   read "STRE". 25ch still fits a 24-character username whole, so nothing
   truncates on a desktop; the phone tightens the cap in the narrow block at the
   end of this file, where the 352px is actually spent. */
.lb-name {
  display: inline-block; max-width: 25ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
.lb-table td.board-empty { white-space: normal; }
.rank { font-family: 'Anton', sans-serif; color: var(--teal); }
.streak { font-family: 'JetBrains Mono', monospace; color: var(--muted); }
.status-alive { color: var(--positive-bright); font-weight: 700; font-size: 12px; }
.status-out { color: var(--ember); font-weight: 700; font-size: 12px; }

/* ── Footer ───────────────────────────────────────────────────────────── */

footer { border-top: 1px solid var(--line); padding: 56px 0 32px; background: var(--bg-panel); }
.foot-top { display: grid; grid-template-columns: 1.6fr 2fr; gap: 48px; }
.foot-brand p { color: var(--muted); font-size: 13px; margin-top: 14px; max-width: 380px; }
.foot-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.foot-col h5 { font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--teal); margin-bottom: 12px; }
.foot-col a { display: block; color: var(--muted); font-size: 13px; padding: 4px 0; }
.foot-col a:hover { color: var(--white); }
.foot-bottom { display: flex; justify-content: space-between; gap: 16px; margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); flex-wrap: wrap; }
/* The CFTC risk disclosure sits on its own full-width row rather than in
   .foot-bottom's space-between pair: it is a paragraph, and as a flex sibling
   of the copyright it got squeezed into a ragged column on the right. Capped
   at 100ch so the line length stays readable on a wide desktop. */
.foot-disclosure { margin-top: 16px; max-width: 100ch; font-size: 11px; line-height: 1.7; color: var(--muted); }
.foot-disclosure a { color: var(--muted); text-decoration: underline; }
.foot-disclosure a:hover { color: var(--white); }

@media (max-width: 760px) { .foot-top { grid-template-columns: 1fr; } }

/* ── Modals ───────────────────────────────────────────────────────────── */

.modal-overlay {
  position: fixed; inset: 0; z-index: 100; background: rgba(0, 0, 0, .8);
  backdrop-filter: blur(6px); display: grid; place-items: center; padding: 24px;
}
.modal-card {
  position: relative; background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 16px; padding: 34px 30px; width: min(420px, 100%); text-align: center;
  max-height: 90vh; overflow-y: auto;
}
.modal-x { position: absolute; top: 14px; right: 16px; background: none; border: none; color: var(--muted); font-size: 16px; cursor: pointer; }
.modal-check { font-size: 34px; margin-bottom: 12px; }
.modal-title { font-family: 'Anton', sans-serif; font-weight: 400; font-size: 30px; line-height: 1; text-transform: uppercase; }
.modal-title span { color: var(--teal-alt); }
.modal-sub { color: var(--muted); font-size: 14px; margin: 12px 0 22px; }
.modal-pick { background: var(--bg-panel-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; margin-bottom: 20px; }
/* ONE identity block, used by the board card, the receipt and My Run, so a
   pick reads the same everywhere: the badge on the left, the name and the
   team code beside it, the two vertically centred against each other. A
   one-line name therefore centres on the badge (Patrick, 2026-09-04) and a
   two-line name pushes nothing out of place. */
.pick-ident { display: flex; align-items: center; gap: 12px; min-width: 0; }
.pick-ident > img { flex: 0 0 auto; object-fit: contain; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5)); }
.pick-ident > img[hidden] { display: none; }
.pick-ident-text { min-width: 0; }

/* On the receipt the whole card is centred — tick, headline, matchup, date —
   so the badge and name are centred as a group too, rather than sitting hard
   left against centred text (Patrick, 2026-09-04). */
.modal-pick .pick-ident { justify-content: center; }
.modal-pick .mp-logo { width: 42px; height: 42px; }
.mp-team { font-size: 11px; letter-spacing: .12em; font-weight: 700; color: var(--accent-bright); margin-top: 3px; }
.modal-pick .mp-meta { margin-top: 10px; }
.mp-name { font-size: 20px; font-weight: 700; }
.mp-meta { font-size: 12px; color: var(--muted); font-family: 'JetBrains Mono', monospace; margin-top: 4px; }
.mp-odds-row { display: flex; justify-content: space-between; align-items: center; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.mp-odds-lbl { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); text-align: left; }
.mp-odds-val { font-family: 'JetBrains Mono', monospace; font-size: 22px; font-weight: 700; color: var(--teal-alt); }
.modal-close {
  display: block; width: 100%; background: var(--teal); color: var(--on-accent); border: none;
  border-radius: 6px; padding: 13px; font-weight: 700; font-size: 14px; font-family: inherit; cursor: pointer;
}
.modal-change { display: block; width: 100%; margin-top: 10px; background: none; border: none; color: var(--muted); font-size: 13px; font-family: inherit; cursor: pointer; }
.modal-change:hover { color: var(--white); }

.signup-form { text-align: left; display: flex; flex-direction: column; gap: 16px; }
.form-field label, .form-field .label { display: block; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 7px; font-weight: 600; }
.form-field input {
  width: 100%; background: var(--bg-deep); border: 1px solid var(--line); border-radius: 6px;
  padding: 11px 13px; color: var(--white); font-family: inherit; font-size: 14px;
}
.yn-toggle { display: flex; gap: 8px; }
.yn-btn { flex: 1; background: var(--bg-deep); border: 1px solid var(--line); color: var(--muted); border-radius: 6px; padding: 10px; font-size: 13px; font-weight: 700; font-family: inherit; cursor: pointer; }
.yn-btn.is-on { border-color: var(--teal); color: var(--teal-alt); background: rgb(var(--accent-rgb) / .1); }
.offer-box { margin-top: 14px; background: rgb(var(--accent-rgb) / .07); border: 1px solid rgb(var(--accent-rgb) / .3); border-radius: var(--radius); padding: 14px; }
.offer-title { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--accent-bright); font-weight: 700; }
.offer-copy { font-size: 13px; color: var(--muted); margin: 6px 0 10px; }
.offer-link { font-size: 13px; font-weight: 700; color: var(--teal-alt); }
.form-error { color: var(--ember); font-size: 13px; }
.form-fineprint { font-size: 11px; color: var(--muted); text-align: center; }

/* ── Toast ────────────────────────────────────────────────────────────── */

.toast {
  position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 20px);
  background: var(--bg-panel-2); border: 1px solid var(--teal); color: var(--white);
  padding: 12px 20px; border-radius: 100px; font-size: 13px; font-weight: 600;
  opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease; z-index: 200;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast.error { border-color: var(--ember); }

/* ── Admin ────────────────────────────────────────────────────────────── */

.admin { padding: 32px 32px 80px; display: flex; flex-direction: column; gap: 20px; }
.admin-status { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pill { font-size: 11px; padding: 6px 12px; border-radius: 100px; border: 1px solid var(--line); }
.pill-ok { color: var(--positive-bright); border-color: rgb(var(--positive-rgb) / .4); }
.pill-warn { color: var(--ember); border-color: rgb(var(--danger-rgb) / .4); }
.admin-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 12px; padding: 22px; }
.admin-card h2 { font-size: 18px; margin-bottom: 16px; }
.admin-card h2 span { font-size: 12px; color: var(--muted); font-weight: 400; }
.admin-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 18px; }
.admin-grid .k { display: block; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.admin-grid .v { font-size: 15px; font-weight: 700; }
.admin-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.admin-actions label { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
.week-input { width: 64px; background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font-family: inherit; }
.limit-input { width: 72px; background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font-family: inherit; }
/* The Admins panel: an address to grant, and a red chip to revoke. Same
   shapes as the rest of the console, so nothing new to learn. */
.admin-input { background: var(--bg-panel-2); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 10px; font-size: 13px; min-width: 260px; }
.admin-input:focus { outline: none; border-color: rgb(var(--accent-rgb) / .5); }
.chip-danger { color: var(--ember); border-color: rgb(var(--danger-rgb) / .45); }
.chip-danger:hover { background: rgb(var(--danger-rgb) / .12); }
/* The sign-in and sign-out controls are links and a form, not buttons. */
a.chip { display: inline-block; text-decoration: none; }
.admin-who { font-size: 11px; color: var(--muted); display: inline-flex; align-items: center; gap: 10px; }
.admin-who button { background: none; border: 0; padding: 0; color: var(--muted); font: inherit; cursor: pointer; text-decoration: underline; }
.admin-who button:hover { color: var(--white); }
.entrant-input, .reason-input { background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font-family: inherit; }
.entrant-input { width: 150px; }
.reason-input { width: 240px; }
.admin-note { font-size: 12px; color: var(--muted); line-height: 1.6; margin-bottom: 14px; max-width: 78ch; }
.pick-who, .pick-selection { width: 220px; background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font-family: inherit; }
.admin-danger { border: 1px solid rgb(var(--danger-rgb) / .35); border-radius: 8px; padding: 12px 14px; margin-bottom: 14px; }
.admin-danger summary { cursor: pointer; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--ember); }
.admin-danger > *:not(summary) { margin-top: 12px; }
.admin-note strong { color: var(--white); }
.admin-out { background: var(--bg-deep); border: 1px solid var(--line); border-radius: 8px; padding: 14px; font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--muted); max-height: 300px; overflow: auto; white-space: pre-wrap; }
.sigma-frame { width: 100%; height: 720px; border: 1px solid var(--line); border-radius: 8px; }

/* Entrants: a roster is read down, so it is a table rather than a JSON dump. */
.admin-note { font-size: 12px; color: var(--muted); margin: -8px 0 14px; }
.admin-card select.status-input { background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font: inherit; }
.q-input { background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 6px 8px; font-family: inherit; min-width: 200px; }
.entrants-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 8px; }
.entrants-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.entrants-table th { text-align: left; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.entrants-table td { padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.entrants-table tbody tr:last-child td { border-bottom: 0; }
.entrants-table .mono { font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.entrants-count, .entrants-empty { font-size: 12px; color: var(--muted); margin-top: 10px; }
.entrant-detail { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); }
.entrant-detail h3 { font-size: 16px; margin-bottom: 16px; }
.entrant-detail h3 span { font-size: 12px; color: var(--muted); font-weight: 400; }
/* A co-winner's status is still `eliminated`, so the two badges sit together. */
.status-win { color: var(--white); background: rgb(var(--positive-rgb) / .18); border: 1px solid var(--positive); border-radius: 100px; padding: 2px 8px; font-weight: 700; font-size: 11px; }

/* A corrected round holds a live pick and a voided receipt. The receipt is
   dimmed and struck through so the live pick is the one the eye lands on, and
   badged so an operator is told what happened rather than left to infer it from
   a `void` in the result column. The receipt is NOT hidden: it is the burn list.
   These only appear on a round that was actually corrected. */
.entrants-table tr.is-superseded td { color: var(--muted); }
.entrants-table tr.is-superseded td:nth-child(2) { text-decoration: line-through; text-decoration-color: var(--muted); }
.pick-void { display: inline-block; text-decoration: none; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--ember); border: 1px solid rgb(var(--danger-rgb) / .5); border-radius: 100px; padding: 2px 8px; white-space: nowrap; vertical-align: middle; }
.pick-live { display: inline-block; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--positive-bright); border: 1px solid var(--positive); border-radius: 100px; padding: 2px 8px; white-space: nowrap; vertical-align: middle; }

/* Copy editor: a long form, so it is grouped and collapsible rather than one
   wall of inputs. Same controls as the rest of the console, nothing new. */
.content-group { border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; margin-bottom: 12px; }
.content-group summary { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); cursor: pointer; }
.content-field { margin-top: 14px; }
.content-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.content-head label { font-size: 12px; color: var(--white); }
.content-head label span { color: var(--muted); font-family: 'JetBrains Mono', monospace; font-size: 11px; }
.content-edited { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--white); background: rgb(var(--positive-rgb) / .18); border: 1px solid var(--positive); border-radius: 100px; padding: 2px 8px; }
.content-flag { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); border: 1px solid var(--line); border-radius: 100px; padding: 2px 8px; }
.content-input { width: 100%; background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 8px 10px; font: inherit; font-size: 13px; }
textarea.content-input { font-family: 'JetBrains Mono', monospace; font-size: 12px; resize: vertical; }
select.content-input { appearance: none; }
.content-hint { font-size: 11px; color: var(--muted); margin-top: 6px; line-height: 1.5; }

/* New promotion: the same form, plus the generated files. A load-bearing field
   is one a dispute turns on — the resolution answers and the round schedule —
   so it is marked differently from a merely-important one. */
.content-flag.is-load-bearing { color: var(--ember); border-color: rgb(var(--danger-rgb) / .5); }
.newpromo-out { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--line); }
.newpromo-file { margin-bottom: 16px; }
.newpromo-file .content-head label { font-size: 12px; }
.newpromo-file .admin-out { max-height: 420px; color: var(--white); }
.newpromo-todo { font-size: 12px; color: var(--muted); line-height: 1.7; padding-left: 20px; }

.notfound { padding: 120px 32px; text-align: center; }
/* Fixed at 96px this set the word "Unsubscribed" 599px wide inside a 326px
   box. break-word is the belt to the clamp's braces: a longer word in a future
   promotion's copy should wrap, not overflow. */
.notfound h1 { font-size: clamp(40px, 13vw, 96px); color: var(--teal); overflow-wrap: break-word; }
.notfound p { color: var(--muted); margin: 12px 0 24px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* Test mode: picks are being taken before the pool opens, so they are not real
   entries. Loud on purpose — quieter than this and someone believes they played. */
.board-source.is-test { color: var(--ember); border-color: rgb(var(--danger-rgb) / 0.5); }
/* Sample data reads as a warning; a pinned real board reads as ordinary
   chrome, because it is the normal, correct state. */
.board-source.is-sample { color: var(--ember); border-color: rgb(var(--danger-rgb) / 0.5); }
[data-board-provenance]:empty { display: none; }

/* ── Sharing ──────────────────────────────────────────────────────────────
   The control on the pool page, and the read-only card a shared link opens.
   Built from the same tokens as everything else, so a re-themed promotion gets
   a re-themed share card for free. */

/* Same 80px rhythm above as the sections before it: on the holding page the
   share box sat flush against the prize panel's bottom border. */
section#home-share { padding: 80px 0; }

.share-box { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 22px; }
.share-box-compact { padding: 16px; background: transparent; }
.share-sub { color: var(--muted); font-size: 14px; margin: 10px 0 16px; max-width: 560px; }
.share-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.share-chip { text-align: center; }
.share-link {
  margin-top: 14px; font-size: 12px; color: var(--muted); word-break: break-all;
  background: var(--bg-deep); border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px;
}
.share-run-btn { margin-top: 16px; }
.share-note { margin: 0 0 14px; }

.share-view { padding: 48px 32px 96px; }
.share-hero { margin-bottom: 32px; }
/* A username is one unbroken token, so it has no wrap opportunity of its own
   and the clamp floor alone cannot save it: "BrandonMcAllister1994" set at the
   30px floor still ran 13px past a 320px card. `anywhere` on the base rule
   rather than in a breakpoint, because the limit is the length of the name,
   not the width of the screen. */
.share-name { font-size: clamp(38px, 7vw, 72px); text-transform: uppercase; margin: 12px 0 16px; overflow-wrap: anywhere; }
.share-badges { display: flex; gap: 8px; flex-wrap: wrap; }
.share-frame { margin-bottom: 28px; }
.share-picks td b { display: block; }
.share-meta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.share-trade {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  background: var(--bg-panel-2); border: 1px solid var(--line); border-radius: 14px; padding: 22px; margin-bottom: 28px;
}
.share-trade h2 { font-family: 'Anton', sans-serif; font-weight: 400; font-size: 28px; text-transform: uppercase; margin: 10px 0 4px; }
.share-trade p { color: var(--muted); font-size: 13px; }
.share-cta { margin-bottom: 28px; }

/* The sign-in button is the only action on /admin's signed-out page, so it is
   styled as one rather than as a chip among chips. Google's guidance wants
   their mark on it, white-on-dark to match the console. */
.btn-google {
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  width: 100%; max-width: 320px; margin-top: 20px;
  padding: 13px 20px; border-radius: 8px;
  background: var(--white); color: #1F1F1F;
  font-family: inherit; font-size: 15px; font-weight: 600;
  border: 1px solid var(--line); cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease;
}
.btn-google:hover { transform: translateY(-1px); box-shadow: 0 6px 20px rgba(0, 0, 0, .35); }
.btn-google svg { flex: 0 0 18px; }

/* ── Operator console layout ──────────────────────────────────────────────
   One pool on screen at a time. Every pool renders the same four cards, so a
   flat list put two identical-looking "Settle week" buttons a scroll apart —
   the exact mistake worth designing out of a console used on a live Sunday. */

.admin-layout {
  display: grid; grid-template-columns: 232px minmax(0, 1fr);
  gap: 28px; max-width: var(--wrap); margin: 0 auto; padding: 28px 32px 80px;
  align-items: start;
}

.admin-nav { position: sticky; top: 84px; display: flex; flex-direction: column; gap: 4px; }
.admin-nav-group {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; margin: 14px 0 6px 12px;
}
.admin-nav-group:first-child { margin-top: 0; }

.admin-nav-item {
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  padding: 10px 12px; border-radius: 8px;
  background: transparent; border: 1px solid transparent; color: var(--muted);
  font-family: inherit; font-size: 14px; cursor: pointer; width: 100%;
}
.admin-nav-item:hover { background: var(--bg-panel); color: var(--white); }
.admin-nav-item.is-on {
  background: var(--bg-panel); border-color: rgb(var(--accent-rgb) / .35); color: var(--white);
}
.admin-nav-name { font-weight: 600; }
.admin-nav-meta { font-size: 10px; color: var(--muted); letter-spacing: .04em; }
.admin-nav-item.is-on .admin-nav-meta { color: var(--teal-alt); }

.admin-panes { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.admin-pane { display: flex; flex-direction: column; gap: 20px; }
.admin-pane[hidden] { display: none; }

@media (max-width: 900px) {
  .admin-layout { grid-template-columns: 1fr; padding: 20px; }
  .admin-nav { position: static; flex-direction: row; flex-wrap: wrap; }
  .admin-nav-group { width: 100%; margin: 8px 0 2px; }
  .admin-nav-item { width: auto; }
}

/* ── Operator console, redesigned ─────────────────────────────────────────
   One pool at a time; five tabs inside it. Buttons carry intent (primary /
   ghost / danger) rather than all being the same grey chip, and every action
   answers into a .result block in words — the raw JSON folds under it. */

.pool-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-bottom: 4px; }
.pool-title { font-size: 26px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }
.pool-meta { font-size: 11px; color: var(--muted); margin-top: 6px; letter-spacing: .04em; }
.pool-facts { display: flex; gap: 22px; flex-wrap: wrap; }
.pool-facts .k { display: block; font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.pool-facts .v { font-size: 15px; font-weight: 700; }

.admin-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.admin-tab {
  background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; padding: 10px 16px; cursor: pointer;
}
.admin-tab:hover { color: var(--white); }
.admin-tab.is-on { color: var(--teal-alt); border-bottom-color: var(--teal); }

.admin-tabpane { padding: 22px; }
.admin-tabpane[hidden] { display: none; }

.tab-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
.tab-toolbar-spacer { flex: 1; }
.tab-toolbar-note { font-size: 12px; color: var(--muted); }
.week-pick { font-size: 12px; color: var(--muted); display: inline-flex; align-items: center; gap: 8px; }
.week-pick input { width: 64px; background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 7px 10px; font: inherit; font-size: 13px; }
.admin-tabpane .board-search { flex: 1 1 260px; }

/* Six tabs are a ~590px row, and .admin-tabs had neither flex-wrap nor
   overflow-x: below ~640px Entrants, Corrections and Copy sat past the right
   edge and body{overflow-x:hidden} clipped them, so an operator on a phone
   could not reach Corrections at all. They wrap. Wrapping rather than scrolling
   because every tab then stays visible — a scroller needs an affordance and
   still hides the tab you have not thought to swipe for. */
@media (max-width: 720px) {
  /* `.admin-tabs` is a <nav>, so it inherits `justify-content: space-between`
     from the bare `nav` rule at the top of this file. On one row that reads as
     a spread-out tab bar; on two it strands Copy against the right edge with a
     190px hole beside Corrections. Wrapped rows pack left. */
  .admin-tabs { flex-wrap: wrap; justify-content: flex-start; }
  .admin-tab { padding: 10px 12px; font-size: 12px; }
}
@media (max-width: 560px) {
  .admin-layout { padding: 16px 14px 60px; gap: 18px; }
  .admin-nav-item { font-size: 13px; padding: 8px 10px; }
  .admin-tab { padding: 9px 10px; font-size: 11px; }
  .admin-tabpane { padding: 16px 14px; }
  .admin-card { padding: 16px 14px; }
  .pool-title { font-size: 21px; }
  .pool-facts { gap: 14px; }
  .admin-who { flex-wrap: wrap; }
  /* Fixed-width inputs. .tab-toolbar wraps them, but a 240px box inside a
     292px column leaves nothing to sit beside it and no room to grow. */
  .entrant-input, .reason-input, .pick-who, .pick-selection, .q-input,
  .week-input, .limit-input, .admin-tabpane select.status-input,
  .admin-tabpane .board-search, .week-pick select.round-select { max-width: 100%; }
}
.admin-tabpane select.status-input { background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 8px 10px; font: inherit; font-size: 13px; max-width: 100%; }

/* Buttons with intent. */
.btn {
  border-radius: 8px; padding: 9px 16px; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--line); background: var(--bg-panel-2); color: var(--white); white-space: nowrap;
}
.btn:hover { border-color: var(--teal); }
.btn:disabled { opacity: .5; cursor: default; }
.btn-primary { background: var(--teal); border-color: var(--teal); color: var(--on-accent); }
.btn-primary:hover { background: var(--teal-alt); border-color: var(--teal-alt); }
.btn-ghost { background: transparent; color: var(--white); }
.btn-danger { background: transparent; border-color: rgb(var(--danger-rgb) / .55); color: var(--ember); }
.btn-danger:hover { background: rgb(var(--danger-rgb) / .12); border-color: var(--ember); }
.btn-sm { padding: 7px 12px; font-size: 12px; }
.btn-xs { padding: 4px 9px; font-size: 11px; font-weight: 600; }

/* The weekly stepper. */
.steps { list-style: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
@media (max-width: 1100px) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .steps { grid-template-columns: 1fr; } }
.step { border: 1px solid var(--line); border-radius: 10px; padding: 14px; background: var(--bg-deep); display: flex; flex-direction: column; gap: 8px; }
.step-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.step-n {
  width: 22px; height: 22px; border-radius: 50%; display: inline-grid; place-items: center;
  font-size: 11px; font-weight: 800; background: var(--bg-panel-2); color: var(--muted); border: 1px solid var(--line); flex: none;
}
.step-title { font-weight: 800; font-size: 14px; }
.step-state { font-size: 12px; color: var(--muted); flex-basis: 100%; }
.step-sub { font-size: 12px; color: var(--muted); line-height: 1.5; flex: 1; }
.step-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.step-actions .btn { padding: 8px 12px; font-size: 12px; }
.step.is-done { border-color: rgb(var(--positive-rgb) / .35); }
.step.is-done .step-n { background: var(--positive); color: var(--on-positive); border-color: var(--positive); }
.step.is-live { border-color: rgb(var(--positive-rgb) / .18); }
.step.is-live .step-n { border-color: var(--positive); color: var(--positive-bright); }
.step.is-warn { border-color: rgb(var(--danger-rgb) / .35); }
.step.is-warn .step-n { border-color: var(--ember); color: var(--ember); }
.ok { color: var(--positive-bright); font-weight: 700; }
.warn { color: var(--ember); }
.muted { color: var(--muted); }

.entry-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px; }
.entry-row .k { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.entry-state { font-size: 13px; font-weight: 700; flex: 1; }
.entry-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* The answer to an action. */
.result { border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; margin-top: 14px; font-size: 13px; line-height: 1.55; background: var(--bg-deep); }
.result-ok { border-color: rgb(var(--positive-rgb) / .4); }
.result-warn { border-color: rgb(var(--danger-rgb) / .5); }
.result-err { border-color: rgb(var(--danger-rgb) / .55); }
.result-muted { color: var(--muted); }
.result-title { font-weight: 600; }
.result-body { margin-top: 8px; color: var(--muted); }
.result-body .k { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin: 10px 0 4px; }
.result-body ul.plain { list-style: none; }
.result-body ul.plain li { padding: 3px 0; }
.result-body ul.warn li::before { content: '⚠ '; color: var(--ember); }
.result-raw { margin-top: 10px; }
.result-raw summary { cursor: pointer; font-size: 11px; color: var(--muted); letter-spacing: .08em; text-transform: uppercase; }
.result-raw pre { margin-top: 8px; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--muted); white-space: pre-wrap; max-height: 320px; overflow: auto; }
.tally { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; }
.tally b { color: var(--white); font-size: 16px; }
.chips-wrap { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.tag { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .04em; padding: 3px 10px; border-radius: 100px; border: 1px solid var(--line); color: var(--white); }
.tag-ok { color: var(--positive-bright); border-color: rgb(var(--positive-rgb) / .5); }
.tag-warn { color: var(--ember); border-color: rgb(var(--danger-rgb) / .5); }
.tag-muted { color: var(--muted); font-weight: 600; text-transform: uppercase; font-size: 9px; letter-spacing: .1em; padding: 2px 7px; }

/* The board tab. */
.board-strip { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; font-size: 13px; color: var(--muted); padding: 10px 0 14px; }
.board-strip b { color: var(--white); font-size: 15px; }
/* Game pairs in the console's team view: the two sides of one game share a
   tint, and the tint alternates game to game, so a pair reads as one block. */
.board-pair-b > tr > td, .board-pair-b > tr > th { background: rgba(255, 255, 255, 0.03); }
/* The pick-requirement control sits in the strip like any other fact, but it
   is a control: give the select enough presence to read as one. */
.picks-required-ctl { display: inline-flex; gap: 8px; align-items: center; }
/* A future round that takes more than one pick announces it in My Run. */
.run-col-picks {
  font-size: 9px; font-weight: 700; letter-spacing: .08em; color: var(--ember);
  border: 1px solid var(--ember); border-radius: 4px; padding: 1px 5px; margin-left: 6px;
}
.picks-required-ctl select {
  background: var(--panel, #10151d); color: var(--white); border: 1px solid var(--line);
  border-radius: 6px; padding: 3px 8px; font: inherit; font-size: 13px;
}
.picks-required-ctl select:disabled { opacity: .55; cursor: not-allowed; }
.picks-required-ctl.is-multi select { border-color: var(--ember); color: var(--ember); font-weight: 700; }
.picks-required-note { font-size: 11px; color: var(--muted); }
.board-problems { font-size: 12px; color: var(--ember); margin-bottom: 12px; line-height: 1.6; }
.board-table .board-group-head th { background: var(--bg-panel-2); text-transform: none; letter-spacing: 0; font-size: 12px; padding: 9px 12px; color: var(--white); border-top: 1px solid var(--line); }
.board-game { font-weight: 800; margin-right: 10px; }
.board-table tr.is-locked td { color: var(--muted); }
.board-table td.num, .board-table th.num { text-align: right; }

/* The Picks count is the way in to WHO picked, so it has to look like a way in
   — a count you can click is only useful if it says so before you click it.
   Styled as the number it already was, underlined on the dotted border, rather
   than as a chip: the column reads as a column of numbers and a row of pills
   down it would drown the board. */
.picks-drill {
  background: none; border: 0; padding: 0; font: inherit; font-weight: 800; color: var(--teal-alt);
  cursor: pointer; border-bottom: 1px dotted currentColor;
}
.picks-drill:hover, .picks-drill:focus-visible { color: var(--white); border-bottom-style: solid; }
.picks-drill-copy { display: block; margin-top: 14px; font-size: 12px; color: var(--muted); }
.picks-drill-copy textarea { margin-top: 6px; resize: vertical; }

/* Corrections + entrant actions. */
.correction-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.entrant-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.entrant-actions .reason-input { flex: 1 1 280px; }

.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.seg-btn { background: transparent; border: 0; color: var(--muted); font: inherit; font-size: 12px; font-weight: 700; padding: 8px 12px; cursor: pointer; }
.seg-btn + .seg-btn { border-left: 1px solid var(--line); }
.seg-btn.is-on { background: rgb(var(--accent-rgb) / .12); color: var(--teal-alt); }

/* ── My Run: the season as columns ───────────────────────────────────── */
.run-frame { background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.run-topbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.run-status { font-size: 12px; color: var(--teal-alt); letter-spacing: .06em; }
.run-track { display: flex; gap: 10px; padding: 18px; overflow-x: auto; scroll-snap-type: x proximity; }
/* Paging for .run-track. Off above 560px, where enough of the season is on
   screen that there is nothing to page through. */
.run-pager { display: none; }
.run-page-btn {
  flex: 0 0 auto; width: 44px; height: 36px; cursor: pointer; font: inherit; font-size: 19px; line-height: 1;
  background: var(--bg-panel-2); border: 1px solid var(--line); border-radius: 8px; color: var(--white);
}
.run-page-btn:hover { border-color: var(--teal); color: var(--teal-alt); }
.run-col {
  flex: 0 0 168px; scroll-snap-align: center; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg-panel-2); padding: 12px; display: flex; flex-direction: column; gap: 10px; min-height: 150px;
}
.run-col.is-past { opacity: .8; }
.run-col.is-future { opacity: .55; }
.run-col.is-current { border-color: var(--positive); box-shadow: 0 0 0 1px var(--positive), 0 8px 26px rgb(var(--positive-rgb) / .16); opacity: 1; }
.run-col.is-out { border-color: rgb(var(--danger-rgb) / .6); }
.run-col-head { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.run-col-week { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.run-col.is-current .run-col-week { color: var(--positive-bright); }
.run-col-now { font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--positive-bright); border: 1px solid rgb(var(--positive-rgb) / .5); border-radius: 100px; padding: 1px 6px; }
.run-pick { display: flex; flex-direction: column; gap: 4px; flex: 1; }
/* A multi-pick round stacks its picks; a hairline between them keeps two names
   from reading as one, and the gap gives the stack room. */
.run-pick-multi { gap: 8px; }
.run-pick-multi .run-pick-line + .run-pick-line { padding-top: 8px; border-top: 1px solid var(--line); }
/* A My Run pick is a board card in miniature: identity row, then the market
   label over the price with the matchup opposite, then the result. */
.run-pick-line { display: flex; flex-direction: column; gap: 8px; }
.run-logo { width: 34px; height: 34px; }
.run-team { font-size: 10px; letter-spacing: .1em; font-weight: 700; color: var(--accent-bright); margin-top: 2px; }
.run-market { display: flex; justify-content: space-between; align-items: flex-end; gap: 8px; }
.run-lbl { font-size: 8px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.run-col.is-out .run-logo { filter: grayscale(1) opacity(.6); }
.run-name { font-weight: 800; font-size: 15px; line-height: 1.15; }
.run-meta { font-size: 10px; color: var(--muted); white-space: nowrap; }
.run-odds { font-size: 16px; font-weight: 700; line-height: 1.1; }
/* The result pill fits its words. In the old grid layout it stretched the
   width of the column; it is now an inline block that shrinks to its text. */
.run-result { margin-top: 2px; align-self: flex-start; width: fit-content; max-width: 100%; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; font-weight: 700; padding: 3px 8px; border-radius: 100px; border: 1px solid var(--line); color: var(--muted); }
.run-result.is-survived { color: var(--positive-bright); border-color: rgb(var(--positive-rgb) / .5); }
.run-result.is-busted { color: var(--ember); border-color: rgb(var(--danger-rgb) / .5); }
.run-result.is-void { text-decoration: line-through; }
.run-empty { flex: 1; display: grid; place-items: center; text-align: center; color: var(--muted); font-size: 12px; border: 1px dashed var(--line); border-radius: 8px; padding: 12px; }
a.run-cta { color: var(--teal-alt); border-color: rgb(var(--accent-rgb) / .45); font-weight: 700; }
a.run-cta:hover { background: rgb(var(--accent-rgb) / .08); }
.run-change { display: inline-block; margin-top: 8px; font-size: 12px; font-weight: 700; color: var(--teal-alt); border-bottom: 1px solid rgb(var(--accent-rgb) / .45); }
.run-change:hover { border-bottom-color: var(--teal-alt); }
.run-col-now { font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--positive-bright); border: 1px solid rgb(var(--positive-rgb) / .5); border-radius: 100px; padding: 2px 7px; white-space: nowrap; }
.run-col-now.is-locked { color: var(--ember); border-color: rgb(var(--danger-rgb) / .5); }

/* Board editor + results sheet */
.board-editor { margin-top: 12px; }
.board-editor-table td { padding: 6px 8px; }
.board-editor input.ed { background: var(--bg-deep); border: 1px solid var(--line); color: var(--white); border-radius: 6px; padding: 7px 9px; font: inherit; font-size: 13px; width: 100%; }
.board-editor input.ed:focus { border-color: var(--teal); outline: none; }
.seg-result .seg-btn { padding: 6px 10px; font-size: 11px; }
.seg-result .seg-btn.is-scored { background: rgb(var(--danger-rgb) / .16); color: var(--ember); }
.seg-result .seg-btn.is-held { background: rgb(var(--positive-rgb) / .14); color: var(--positive-bright); }
.results-table tr.has-picks td:first-child { border-left: 2px solid var(--positive); }

/* The player picker in the board editor. A text input that keeps taking free
   text, with a caret that shows the whole team — a <datalist> filtered itself
   by whatever was already typed, so a filled row offered only itself. */
.combo { position: relative; display: flex; align-items: center; gap: 4px; }
.combo .ed-name { flex: 1; }
.combo-caret {
  flex: none; width: 26px; height: 30px; border-radius: 6px; cursor: pointer;
  background: var(--bg-panel-2); border: 1px solid var(--line); color: var(--muted);
  font: inherit; font-size: 11px; line-height: 1;
}
.combo-caret:hover { color: var(--teal-alt); border-color: var(--teal); }
.combo-list {
  position: fixed; z-index: 80; list-style: none; margin: 0; padding: 4px;
  max-height: 260px; overflow-y: auto; border-radius: 8px;
  background: var(--bg-panel-2); border: 1px solid var(--teal);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .6);
}
.combo-list li {
  padding: 7px 10px; border-radius: 5px; font-size: 13px; cursor: pointer; white-space: nowrap;
}
.combo-list li:hover, .combo-list li.is-active { background: rgb(var(--accent-rgb) / .16); color: var(--teal-alt); }
.combo-list li.combo-empty { color: var(--muted); cursor: default; font-size: 12px; white-space: normal; }
.combo-list li.combo-empty:hover { background: none; color: var(--muted); }

/* The round selector. A list of the rounds that exist, not a number box that
   accepts 6 for a six-round pool and says nothing about which round is which. */
.week-pick select.round-select {
  background: var(--bg-deep); border: 1px solid var(--line); color: var(--white);
  border-radius: 6px; padding: 7px 10px; font: inherit; font-size: 13px; min-width: 168px;
}
.week-pick select.round-select:focus { border-color: var(--teal); outline: none; }

/* An option can carry context beside it — an email, a status. */
.combo-list li { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; }
.combo-value { font-weight: 600; }
.combo-sub { font-size: 11px; color: var(--muted); font-family: 'JetBrains Mono', monospace; }
.combo-list li.is-active .combo-sub, .combo-list li:hover .combo-sub { color: var(--white); }

/* The roster is wider than its box, so the Detail button was scrolled off the
   right edge — the one control in the row. Pin the action column to the edge
   so it is reachable wherever the table is scrolled to. */
.roster-table th:last-child, .roster-table td:last-child {
  position: sticky; right: 0; z-index: 2; background: var(--bg-panel);
  box-shadow: -10px 0 12px -8px rgba(0, 0, 0, .9);
}
.roster-table th:last-child { background: var(--bg-panel); }

/* The verified badge: this entrant holds a registered ProphetX account.

   `inline-flex` with `align-items: center` so the pill centres its own
   contents. The lockup inside it used to be aligned to the text baseline, which
   pins its bottom edge and lets it grow upward: measured across .7/.8/.9/1em,
   the bottom edge sat at a constant 6px off the pill's bottom while the mark's
   centre drifted 0 -> -1.5px above the pill's, so the bigger the mark the more
   top-heavy the pill. `vertical-align: middle` on the image alone improves that
   to a constant but still-not-zero +0.77px, because `middle` aligns to the
   baseline plus half an x-height rather than to the pill. Centring the pill's
   own layout is exactly 0 at every size, and the table row keeps its 50.67px. */
/* Find-your-name, above the table. The pool passed 269 entrants on its first
   weekend and somebody ranked 138th had no way to tell they were even in it. */
/* The pager sits in its own element, above the alive count. They used to share
   one, and loadStandings() overwrote the buttons a moment after they rendered. */
/* The UTC stamp under an ET one. Small and quiet: ET is what an operator
   reads, UTC is what they reconcile against a log when something is wrong. */
.v-sub { display: block; font-size: 9px; color: var(--muted); letter-spacing: .04em; margin-top: 1px; }
.lb-pager { display: flex; align-items: center; gap: 10px; padding: 10px 14px; flex-wrap: wrap; }
.lb-pager .chip[disabled] { opacity: .4; cursor: default; }
/* The line under the countdown explaining that players lock as their games
   start. Quiet: it is context, not an alarm — but it is the difference between
   somebody joining on the Thursday and joining on Monday with two names left. */
.board-locknote {
  margin: 0; padding: 0 14px 10px; font-size: 12px; line-height: 1.5;
  color: var(--muted); border-bottom: 1px solid var(--line);
}
.linkish {
  background: none; border: 0; padding: 0; color: var(--muted);
  font-family: inherit; font-size: inherit; text-decoration: underline; cursor: pointer;
}
.lb-find { padding: 10px 14px 0; }
.lb-find-input {
  width: 100%; max-width: 320px; padding: 9px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--ground); color: var(--ink);
  font-family: inherit; font-size: 13px;
}
.lb-find-input:focus { outline: 2px solid var(--ember); outline-offset: 1px; }
/* Your own row is appended when it falls outside the visible slice, so it needs
   to read as yours rather than as the last place in the pool. */
.lb-you {
  margin-left: 8px; font-family: 'JetBrains Mono', monospace; font-size: 9px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ember);
}
.lb-count { font-size: 12px; color: var(--muted); margin-right: 10px; }

.lb-verified {
  display: inline-flex; align-items: center; margin-left: 6px; font-size: 10px; font-weight: 700;
  letter-spacing: .06em; color: var(--positive-bright); border: 1px solid rgb(var(--positive-rgb) / .5);
  border-radius: 100px; padding: 1px 7px; vertical-align: middle; white-space: nowrap;
}

/* The X mark inside that pill, in place of the full PROPHETX lockup that used
   to sit here. `height` is in `em` so the mark tracks the pill's own 10px type
   instead of being a second, unrelated size — and `width: auto` lets the file's
   169x104.2 viewBox decide the rest, so the mark is never squeezed.

   .9em, up from the .8em the lockup ran at. That .8em was a width decision: at
   .9em the lockup was 9px wider and the phone had no 9px to give (see
   .lb-name). The mark is 15px wide at .9em against the lockup's 71px, so the
   width is no longer the constraint and the size can be chosen for the mark
   itself. Not higher than .9em: this is a 19px pill, and a 10px mark in it
   starts crowding the tick and the border rather than reading as a badge.

   The centring lives on the pill (`.lb-verified` is an `inline-flex` that
   centres its items); `vertical-align: middle` here is the fallback for the day
   someone makes that pill an inline-block again, since `baseline` — what this
   was — pins the mark's bottom edge and lets it grow upward out of centre.

   `margin-left: .25em` is 2.5px of box gap, which reads as a 4.5px gap: the
   tick's own right side bearing adds the rest, and this file has no transparent
   margin of its own to hide behind — its viewBox is cropped to the paths' own
   bounds. At the old .45em the visible gap was 6.5px against the pill's own 7px
   of side padding, so the tick sat as far from the mark as it did from the
   pill's edge and read as detached from it.

   Full strength, no `opacity`. The 85% here was for the lockup, which was pure
   #FFFFFF across most of its area and so out-read the entrant's own name (never
   above #F5F7FA) in its own row. The mark is teal — #1DC29A to #00A7AF, the
   same gradient as the favicon — which sits below the name in luminance on its
   own, and fading a teal that is already dimmer than the text just makes the
   badge muddy.

   Nothing about `image-rendering` any more either: this is a vector, so there
   is no downscale to filter and no aliasing to trade against. That whole
   argument belonged to a 900x101 bitmap shown 8px tall. */
.lb-verified-logo {
  display: inline-block; height: .9em; width: auto; margin-left: .25em;
  vertical-align: middle;
}
.lb-filter-note { font-size: 11px; color: var(--muted); }

/* ── Narrow viewports ─────────────────────────────────────────────────────
   Everything at or below 560px, which until now had no rules of its own at
   all: the ten media queries in this file sat at 1100/900/900/860/860/760/760/
   720/600/560, so the 320–430 band — every phone held in portrait — kept
   desktop gutters and desktop type floors.

   The gutter is the substance of it. `.hero`, `.prize-hero` and `.notfound`
   each wrap a `.wrap`, and `.wrap` carries `padding: 0 32px`, so a phone got
   128px of horizontal padding around a 192px content column. The sections give
   up their horizontal padding here and `.wrap` alone supplies the gutter —
   one source, not two. Vertical padding is set per section and deliberately
   does not touch the shared `section#…` selector list above, which another
   change in flight owns.

   The type floors are lowered, not the whole scale: each rule below meets its
   desktop `clamp()` at exactly 560px, so nothing jumps at the breakpoint. They
   are `clamp()` on `vw` rather than fixed sizes because they have to hold under
   both faces — Anton is condensed, and a fallback sets the same string roughly
   a third wider. Measured with real Anton served locally to the browser, and
   re-checked against the fallback. */
@media (max-width: 560px) {
  .wrap { padding: 0 18px; }
  nav { padding: 14px 16px; }
  /* Row one of the header has 288px to hold the logo, the call to action and
     the menu button at 320. At the sizes they carry above this breakpoint that
     needs 321 and the button drops to a row of its own, so both the lockup and
     the button give a little back here. The lockup's floor is 88px, which is
     where the wordmark stops being legible on this asset. */
  .brand-logo { width: clamp(88px, 27vw, 112px); }
  .nav-cta { padding: 9px 14px; font-size: 12px; }
  /* The kicker, not the logo, is what sets the brand's width: "AMERICA'S #1
     HATER" tracked at 0.14em measures 116px against the lockup's 88. Tightening
     the tracking brings the block back under the logo's own width, which is
     what leaves room for the button beside it. */
  .brand-lock small { font-size: 8px; letter-spacing: .07em; }
  .hero { padding: 64px 0 56px; }
  .prize-hero { padding: 56px 0; }
  .notfound { padding: 76px 18px; }
  footer { padding: 40px 0 24px; }

  h1.headline { font-size: clamp(30px, 9vw, 50px); }
  .prize-headline { font-size: clamp(26px, 7vw, 38px); }
  .section-head h2 { font-size: clamp(24px, 6vw, 30px); }
  .section-head { margin-bottom: 28px; }
  .sub { font-size: 15px; margin-top: 20px; }
  .eyebrow { margin-bottom: 20px; font-size: 11px; }
  .hero-stats { gap: 26px; margin-top: 40px; }
  .share-name { font-size: clamp(30px, 7vw, 39px); }

  /* Long unbreakable strings on a 284px column: a share URL, an email in an
     entrant card, a username in a heading. */
  .share-link, .entrant-detail h3 { overflow-wrap: anywhere; }

  /* My Run was 18 fixed 168px columns scrolling inside a 284px box — about one
     and a half columns visible, and nothing on screen saying the other sixteen
     and a half were there. One round now fills the width bar a 26px sliver of
     the next, which is the affordance: a card cut off at the edge is the one
     thing that reads unambiguously as "there is more this way". Snapping goes
     from proximity to mandatory so a swipe lands on a round instead of between
     two, and the pager below gives the same movement a target to tap and a
     stop for the keyboard. */
  .run-track { padding: 14px; gap: 8px; scroll-snap-type: x mandatory; }
  .run-col { flex: 0 0 calc(100% - 34px); scroll-snap-align: start; }
  .run-pager { display: flex; justify-content: center; gap: 10px; padding: 0 14px 14px; }
  .run-topbar { padding: 12px 14px; }
  .share-run-btn { flex: 1 1 auto; }

  /* The standings table is wider than its card at this width and scrolls, so
     whatever has to be legible without scrolling must fit the first 352px.
     STREAK does. Two numbers buy it the room.

     20px of cell padding is a desktop rhythm: five columns spend 200px of a
     352px card on padding alone. 12px spends 120px, handing 80px back to the
     content — 40px of it ahead of the streak column. Horizontal only: the 14px
     top and bottom are what set the 50.67px row height, and that does not move.

     The name's cap comes down from 25ch to 10ch/90px, and it is sized against
     the *worst* case rather than the current board, because one long username
     widens the column for every row in it. Measured with a verified
     21-character name ("BrandonMcAllister1994") at 390px, one `ch` here is
     exactly 9px, so every step of the cap moves the streak column 9px and the
     clearance falls in 9px steps: 9ch leaves 18.22px, 10ch leaves 9.22px, 11ch
     leaves 0.22px, and 12ch is -8.78px — the "STRE" bug back again.

     10ch, not 11ch, and not 9ch. 11ch does technically still fit, but 0.22px is
     not a margin: it is under the 0.5px tolerance the screenshot harness itself
     allows, so it survives on rounding rather than on room. 10ch is the last
     step with real clearance, and the clearance is what the cap is for.

     It is worth the 9px because the extra character is the point of the column.
     This is a leaderboard people read to find themselves, and the cap is what
     stops them: 9ch renders "BrandonMcAllister1994" as "BrandonM…", 10ch as
     "BrandonMc…". Neither is generous, but the second distinguishes one more
     pair of near-identical usernames, and every name in the seeded board
     (longest: "sundaystew", 69px) is inside either, so the ellipsis still only
     appears where it has to.

     The streak column's own width does not enter into this: it is 73.41px
     whatever the streak says, because the "STREAK" header is wider than any
     cell under it — checked by forcing every cell to the two-digit "12 wks",
     which did not move it. So the clearance above holds for a 99-week streak
     as much as for a 4-week one. */
  .lb-table th, .lb-table td { padding-left: 12px; padding-right: 12px; }
  .lb-name { max-width: 10ch; }
}

/* ── Standings on a phone ──────────────────────────────────────────────
   The narrowest breakpoint in this file was 560px, and the standings table
   is the same width at 559px as it is at 1200px minus its cell padding, so
   every phone held in portrait got the desktop table with the scroll
   container above as its only concession. Measured at 390px on a seeded
   board of eleven — that is a 616.2px table in a 354px card, 245.2px of it
   past the right edge of the viewport and 264px unreachable without a
   sideways swipe. RANK, HATER and STREAK fit whole; LAST PICK gets 68px of
   its 226px; STATUS gets 0 of its 87px.
   Which is exactly the screenshot: the table appears to end at STREAK, and
   the two facts a survivor pool is read for — who is still alive, and who
   they picked — are the two that are not on the screen.

   Horizontal scroll was the wrong answer to that, and it is worth saying
   why, because it is already here and it is the answer that looks free.
   A leaderboard is scanned down, not across. Every row's LAST PICK sits at
   the same hidden x-offset, so recovering that column is not one swipe —
   it is a swipe, a read down eleven rows, and a swipe back to see whose
   row you were on, with the rank and the name you were tracking pushed off
   the left while you look. The column is reachable and still unread.
   Hiding LAST PICK and STATUS below a breakpoint reaches the same end
   state with the swipe removed, which is worse: a column nobody can reach
   is a column nobody reads.

   So the row becomes the unit instead of the column. Each `tr` is a
   two-line card on a 3-column grid — identity and pick down the left,
   state and streak down the right:

       01  sundaystew ✓X               Alive
           Aaron Judge                 9 ser

   Nothing is dropped, nothing is off-screen, and no sideways scroll
   survives.

   WHICH TRACK HOLDS WHAT IS THE LOAD-BEARING PART, and it is why the pick
   sits under the name rather than beside the streak. Column 2 is
   `minmax(0, 1fr)` and column 3 is `auto`, so column 3 is sized from its
   own max-content and column 2 takes what is left. The two strings with no
   bound — a 24-character username and a player's full name — therefore
   both go in column 2, where a `min-width: 0` flex item can be made to
   ellipsis. The two that are bounded by copy in content.json — "Alive" /
   "Out ser 4", and "{n} ser" — go in column 3, which then settles at about
   60px and does not move from row to row.

   Putting the pick in column 3 instead looks tidier and does not work:
   "Gunnar Henderson" is 130px of max-content, so column 3 sizes to that
   and takes 70px straight out of the name. The first draft of this block
   made the mirror-image mistake with the name — name in an `auto` track,
   capped with `max-width: 100%` — and "BrandonMcAllister1994" blew the
   track out and drew its verified pill straight through the STATUS cell.
   A percentage cap cannot constrain a track that is sized from the thing
   being capped; that is the same circularity the 25ch/10ch caps upstream
   exist to work around. In grid the flexible track is what breaks it, so
   the caps come off here and the ellipsis does the work.

   It needs no new copy. The objection to restacking recorded above — that
   per-cell labels would have to be invented in CSS while every
   user-visible string lives in promotions/<slug>/content.json — assumed
   the cells need labelling. These do not. "9 ser" carries its own unit,
   "Alive" and "Out ser 4" are their own labels in the two colours this
   file already gives them, and a player's name under an entrant's name on
   a hater pool's standings is the pick. So this block adds no strings and
   touches no markup and no JS: it is CSS only.

   EVERY SELECTOR IS SCOPED TO `#leaderboard`, which is not tidiness. The
   share page draws `<table class="lb-table share-picks">` inside its own
   `.lb-scroll` (server/views/share.js), so bare `.lb-table` and
   `.lb-scroll` rules here would restack that table too and take away the
   scroll it still needs. `#leaderboard` is the standings section and
   nothing else — server/views/site.js, leaderboardPage().

   560px, matching the block above rather than adding a narrower one, and
   the measurements are the reason. The tempting breakpoint was 480: RANK +
   HATER + STREAK + LAST PICK measure 518.28px, so LAST PICK looks safe
   until the card falls under about 556. But the card is `.wrap` minus its
   gutters, and at 481px that is 443px — LAST PICK is cut by 75px there,
   which is the same defect this block exists to fix, just less of it. A
   breakpoint that leaves a 481–556 band with a clipped LAST PICK is a
   breakpoint chosen for tidiness over the phone. The cards do cost height
   — 11 rows go from 594px to 809px. A phone scrolls down for free. It does
   not scroll sideways for free.

   Above 560 the table gets wider still (759.34px at 561px, where the 20px
   cell padding comes back), and `.lb-scroll` carries it as it always has.
   That band is a tablet with room to swipe, and it is not this change.

   The `display: block` on the table elements drops the table role, and the
   header-to-cell association goes with it. Reading order is unchanged and
   per-row — rank, name, streak, pick, status — so what a screen reader
   gets is still one entrant at a time in that order; note that this is the
   DOM order and the grid places streak visually after the pick. If the
   association is wanted back, the way to do it is an `aria-label` per cell
   from renderStandings() in public/js/app.js, built from the same
   content.json strings the `thead` already draws. That is a JS change, and
   not this one. */
@media (max-width: 560px) {
  /* The scroller stays in the markup and stops being a scroller: there is
     nothing wider than the box any more, and leaving `auto` here keeps a
     scrollbar gutter on the engines that reserve one. */
  #leaderboard .lb-scroll { overflow-x: visible; }
  #leaderboard .lb-table { display: block; font-size: 14px; }
  #leaderboard .lb-table thead { display: none; }
  #leaderboard .lb-table tbody { display: block; }

  #leaderboard .lb-table tbody tr {
    display: grid;
    /* Rank is fixed so every card's name starts on the same left edge — a
       ragged first column is what makes a list of cards read as noise.
       2.6em holds "01" through "50" set in Anton without reflowing. */
    grid-template-columns: 2.6em minmax(0, 1fr) auto;
    grid-template-areas:
      'rank name status'
      'rank pick streak';
    align-items: baseline;
    row-gap: 4px;
    column-gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--line);
  }
  /* The row owns its rule and its highlight now that the cells are blocks
     — left on the cells, each of the five would draw its own.

     `background: none` on the cell is the load-bearing half of that, not
     tidying. `.lb-table tr.is-me td` upstream paints the same 6% teal on every
     cell, and it does not stop applying just because this block adds the
     colour to the row: both would paint, so the five cell rectangles would
     come out at about 12% while the row's own 14px of padding and the 4px
     row-gap between the two lines stayed at 6%. Your own row would read as
     five pale patches on a slightly darker card rather than as one
     highlighted card. Clearing the cell leaves exactly one layer. */
  #leaderboard .lb-table tbody td { display: block; padding: 0; border-bottom: 0; background: none; }
  #leaderboard .lb-table tbody tr.is-me { background: rgb(var(--accent-rgb) / .06); }

  #leaderboard .lb-table tbody td:nth-child(1) { grid-area: rank; font-size: 18px; }
  /* Flex, and `min-width: 0` on both the cell and the name inside it: the
     name is the one thing here allowed to give, and the pill is a logo —
     whole or wrong — so it keeps its `nowrap` and does not shrink. */
  #leaderboard .lb-table tbody td:nth-child(2) {
    grid-area: name; display: flex; align-items: center; min-width: 0; font-weight: 600;
  }
  #leaderboard .lb-table tbody td:nth-child(2) .lb-verified { flex: 0 0 auto; }
  #leaderboard .lb-table tbody td:nth-child(3) { grid-area: streak; justify-self: end; font-size: 12px; }
  #leaderboard .lb-table tbody td:nth-child(4) {
    grid-area: pick; font-size: 13px; color: var(--white);
    font-family: 'Inter', system-ui, sans-serif;
    overflow: hidden; text-overflow: ellipsis;
  }
  #leaderboard .lb-table tbody td:nth-child(5) { grid-area: status; justify-self: end; }

  /* The 10ch cap above was rationing the entrant column against the STREAK
     header a few px to its right. There is no neighbouring column to protect
     now — the name has line one to itself — so the cap comes off and the flex
     line above decides the width instead.

     Which is also why nothing here had to be re-tuned when #77 replaced the
     PROPHETX lockup in the pill with the X mark alone. The name is the flex
     item that gives, so the pill's 98.77px -> 42.08px went straight into the
     name: measured at 320px, the same name track goes 56.7px -> 113.4px with
     no change to this block.

     What still ellipsises, measured across the band on a verified 21-character
     username: nothing at 430px and above; 1px of it at 390px, which is
     rounding rather than a lost glyph; 31px at 360px; and at 320px it is down
     to 113 of 184, where a 13-character name loses its last glyph too. That
     last one is the intended give. 320px is the narrowest phone this file
     supports, the name is the only thing in the row that can be shortened and
     still be recognised, and the alternative is a column nobody can reach. */
  #leaderboard .lb-name { max-width: none; min-width: 0; flex: 0 1 auto; }

  /* Spans the card rather than sitting in the rank column. */
  #leaderboard .lb-table tbody td.board-empty { grid-column: 1 / -1; grid-row: 1; text-align: center; padding: 8px 0; }
}
