:root {
  --bg: #fafafa;       /* page ground */
  --surface: #ffffff;  /* panels, cards, inputs */
  --ink: #14161a;      /* primary text */
  --muted: #63676e;    /* meta, labels, secondary text */
  --rule: #e5e5e3;     /* the only border weight in this design */
  --accent: #0b4fd1;   /* links, focus ring, CPU badge, active state */
  --win: #0e7c4a;
  --loss: #c33a2e;
  --claimed: #f4f2ee;  /* ground of a taken game's claim stamp — see .claim-stamp */
  --lock: #6d28d9;     /* Dan Driscoll Lock of the Week — the app's one purple, echoing the retro
                           visited-link idiom without literally recoloring links */
  --lock-bg: #f5f0fc;  /* light tint of --lock, ground of a whole highlighted locked pick */

  --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-pixel: 'Silkscreen', var(--font-mono);
}

* { box-sizing: border-box; }

body {
  background: var(--bg);
  color: var(--ink);
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15.5px;
  line-height: 1.55;
}

/* Every number in the app — records, spreads, lines, percentages, timestamps — is tabular mono. */
.mono, .rank, .value, .pick-row, .game-meta, .run-row, .status-line,
.panel-label, .field label {
  font-variant-numeric: tabular-nums;
}

a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
a:hover { text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* -- topbar --
 * Two rows (topbar.js's build()): .topbar-top carries the brand and the one session action
 * (Sign in/out), pinned to opposite ends; .topbar-nav below it is destinations only (Home, Picks,
 * Admin) — so the nav row never mixes a page link with a session action. */
.topbar {
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
  padding: 14px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.topbar-top { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
.topbar-brand {
  font-family: var(--font-pixel);
  font-size: 17px;
  letter-spacing: 0.03em;
  color: var(--ink);
  text-decoration: none;
}
.topbar-nav { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.topbar-link {
  font-family: var(--font-mono);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
.topbar-link:hover { color: var(--accent); }

#app { min-height: 100vh; display: flex; flex-direction: column; }
#outlet { flex: 1; max-width: 680px; margin: 0 auto; width: 100%; padding: 32px 20px 64px; }

/* -- panel: the single chokepoint for section chrome (see render.js's panel()) -- */
.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 22px 22px 24px;
  margin-bottom: 40px;
}
.panel-label {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding-bottom: 10px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--rule);
}
.section-note { margin: 0 0 16px; font-size: 13.5px; color: var(--muted); }
.section-note:last-child { margin-bottom: 0; }

/* The admin console's scope indicator (render.js's scopeStamp) — every panel opens with one of
 * these, in the same spot, so "what does this affect" answers itself in one glance rather than a
 * paragraph. The left rule is the same --accent used for the odds-staleness notice below — both
 * are "read this before you trust what's under it." */
.scope {
  font-family: var(--font-mono); font-size: 12px; color: var(--muted);
  padding-left: 10px; margin-bottom: 16px; border-left: 2px solid var(--accent);
}

/* -- hero -- */
.hero { text-align: center; padding: 48px 20px 8px; }
.wordmark {
  font-family: var(--font-pixel);
  font-size: 32px;
  line-height: 1.2;
  margin: 0;
  letter-spacing: 0.02em;
  color: var(--ink);
}
.wordmark .vs { color: var(--accent); }
.tagline { font-size: 15px; color: var(--muted); margin: 16px 0 0; }
.status-line {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  margin: 12px 0 0;
}
.status-line::before { content: '> '; color: var(--accent); }

/* Standings reuses .picks-board/.player-card/.pick-row below — NFL/CFB/TOTAL stacked as their
 * own lines per participant, same shape as a week card's pick status. First place is called out
 * by weight and color alone — no medal icon. */
.who .rank { font-family: var(--font-mono); font-size: 13px; color: var(--muted); margin-right: 4px; }
.who .rank-first { color: var(--accent); font-weight: 700; }

.badge-bot {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  vertical-align: 1px;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 2px;
}
/* A currently-open week — distinct from .badge-bot (Claude/"CPU") even though both are small
 * mono pills, so the two stop reading as the same kind of label. */
.badge-current {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  vertical-align: 1px;
  color: var(--win);
  border: 1px solid var(--win);
  border-radius: 2px;
}
/* The Dan Driscoll Lock of the Week — a little like Claude's reasoning treatment (.reasoning-text
 * below): the pick reads as set apart, not just a small tag on the end of it. `.lock-label` is the
 * full-text second line (render.js's lockedPickRow, game-row.js's claim stamp, picks.js's
 * yourPickCard/theirPickCard); `.pick-locked` boxes just the pick + that label — NOT the league
 * label beside it (render.js's lockedPickRow deliberately keeps "NFL"/"CFB" outside this box, in
 * .pick-row's normal league column, so a locked row still lines up with every other row) — for a
 * surface that has no bordered card of its own to highlight (home page/week-history's compact
 * grid). Where one already exists (.your-pick-card, .claim-stamp) its own `.locked` modifier does
 * the highlighting instead, so a pick is never boxed twice. */
.lock-label {
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--lock);
}
.pick-locked {
  min-width: 0; /* it's a flex item in .pick-row now — let its .value ellipsis do the shrinking */
  border: 1px solid var(--lock);
  background: var(--lock-bg);
  border-radius: 2px;
  padding: 4px 8px;
  margin: 2px 0;
}

/* -- picks board: one card per player, each stat/pick on its own stacked line. Shared by the
 * per-week Picks panel (render.js's picksByPlayer), the home page's standings, and its week-card
 * pick status — one visual language for "a block per person" everywhere it's needed. */
.picks-board { border-top: 1px solid var(--rule); }
.player-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--rule);
}
.player-card:last-child { border-bottom: none; }
/* Fixed floor so every card's name column is the same width — otherwise a short name ("Dudu")
 * and a long one ("Shumputer") leave the NFL/CFB/TOTAL column starting at a different x per row.
 * A longer name than the floor still grows the row on its own rather than truncating. */
.player-card .who { flex: 0 0 auto; min-width: 132px; font-weight: 600; display: flex; align-items: center; }
.player-card.machine .who { color: var(--accent); }
.player-card .pick-rows { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pick-row { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 14px; }
.pick-row .league { flex: none; width: 3.4em; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
/* A long team name still fits one line instead of blowing out the card's fixed-width column. */
.pick-row .value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-row .value.revealed.win { color: var(--win); }
.pick-row .value.revealed.loss { color: var(--loss); }
.pick-row .value.revealed.push { color: var(--muted); }
.pick-row .value .pct { color: var(--muted); font-size: 12px; }
.pick-row .value.dash { color: var(--muted); opacity: 0.6; }

/* -- how it works -- */
.steps { display: grid; gap: 18px; margin-top: 4px; }
.step { display: grid; grid-template-columns: 26px 1fr; gap: 12px; align-items: start; }
.step .num {
  font-family: var(--font-mono); font-size: 12.5px; color: var(--accent);
  width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--rule); border-radius: 2px;
}
.step p { margin: 0; font-size: 14px; color: var(--ink); }
.step b { font-weight: 600; }

/* -- forms (login, admin) -- */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
input, select, textarea {
  font-family: var(--font-sans);
  font-size: 15px;
  padding: 9px 10px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--surface);
  color: var(--ink);
}
textarea { resize: vertical; font-family: inherit; }
input:focus-visible, select:focus-visible, textarea:focus-visible { border-color: var(--accent); }

/* -- select chevron (render.js's selectField) -- one minimal affordance for every dropdown in the
 * app, replacing whatever box/arrow the browser would draw on its own: appearance:none strips the
 * native control, this ::after chevron stands in for it. `em` units throughout (not px) so the
 * chevron and its clearance scale WITH whatever font is in play — a `className` passed to
 * selectField (e.g. `week-dial-select`/`acting-as-select`) goes on the wrapper, not the `<select>`
 * itself, specifically so the chevron (a child of the wrapper) inherits that font too: same
 * pixel-face size as the value it's attached to, not a fixed small mark next to big text. The
 * `<select>` in turn gets `font: inherit` so it keeps rendering at that same size — wrapping never
 * changes what the value itself looks like, only what surrounds it. `.underline` is for an
 * ordinary form select with nothing else marking its edges — a hairline rule takes the place of
 * the box this app never draws around a select; the week dial and admin-picks' acting-as select
 * skip it, since the block they sit in already carries its own thick rule under the whole row (a
 * second line right under the value would double up).
 *
 * The chevron itself is drawn as five hard-edged squares (a `box-shadow` stack off one 0.16em
 * base pixel, no glyph, no image) — a plain '▾' character renders as a smooth vector triangle in
 * whatever font actually carries that glyph (not Silkscreen, which doesn't), which reads as
 * off-family next to this app's blocky pixel type. Anchored from its own top-right pixel (`right`)
 * so the shape always sits flush to the same margin regardless of how many squares it's built
 * from. */
/* align-self: a .field (views/render.js's field()) is a column flex container, which stretches its
 * children full-width — that left this wrapper spanning the whole form while the <select> inside
 * kept its own narrow width, stranding the chevron at the far right edge, nowhere near the value
 * it belongs to. Shrink-to-content keeps the two together in every container. */
.select-wrap { position: relative; display: inline-flex; align-items: center; align-self: flex-start; }
.select-wrap::after {
  content: ''; position: absolute; right: 0.1em; top: 50%;
  width: 0.16em; height: 0.16em; background: var(--muted);
  transform: translateY(-0.24em);
  box-shadow:
    -0.64em 0 var(--muted),
    -0.48em 0.16em var(--muted),
    -0.16em 0.16em var(--muted),
    -0.32em 0.32em var(--muted);
  pointer-events: none;
}
.select-wrap select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background: none; border: none; border-radius: 0; font: inherit; color: inherit;
  padding-right: 1.15em; cursor: pointer;
}
.select-wrap.underline select { border-bottom: 1px solid var(--rule); padding-bottom: 5px; }
.select-wrap.underline select:hover { border-bottom-color: var(--ink); }
.select-wrap.underline select:focus-visible { border-bottom-color: var(--accent); outline: none; }

/* Mobile browsers paint a translucent grey rectangle over whatever you tap; on a control whose
 * whole visible area IS its own text, that reads as the value flashing grey on every tap. (The
 * separate one-frame text flash as a picker opens is WebKit's own, on any select, styled or not —
 * see render.js's selectField. Nothing below affects it.) */
.select-wrap select {
  -webkit-tap-highlight-color: transparent;
  user-select: none; -webkit-user-select: none;
}

/* Display mode (render.js's selectField `display: true`) — the two big pixel-face dials. The
 * visible value is this `<span>`, sized to the CURRENT value; the real `<select>` is stretched
 * transparently over the whole wrapper, so it still handles every tap with the platform's own
 * picker while contributing nothing to layout. Without it the control is as wide as its longest
 * option, stranding the chevron away from the value it belongs to. Long values ellipsize rather
 * than overflow their row. */
.select-wrap.display { max-width: 100%; }
.select-wrap.display .select-value {
  padding-right: 1.3em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  user-select: none; -webkit-user-select: none;
}
.select-wrap.display select {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; padding: 0; margin: 0; border: none; background: none;
  color: transparent; outline: none;
}
/* The select carries focus but is invisible, so the ring has to come from the wrapper. Scoped to
 * :focus-visible (via :has) rather than :focus-within: the latter fires on tap too, so the accent
 * ring flickered in and out around the dial every time a picker opened on a phone. */
.select-wrap:has(select:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; }

.btn {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 14px;
  padding: 9px 18px;
  background: var(--ink);
  color: #fff;
  border: 1px solid var(--ink);
  border-radius: 2px;
  cursor: pointer;
}
.btn:hover { background: #000; }
.btn:active { opacity: 0.85; }
.btn:disabled { opacity: 0.5; cursor: default; }
.btn.secondary { background: var(--surface); color: var(--ink); border-color: var(--rule); }
.btn.secondary:hover { border-color: var(--ink); }
/* .sm is the admin console's whole other button size — a row-level action (Rename, Link, Clear)
 * next to what it acts on, never a page-level one. .danger flags the one destructive role
 * (Delete) without adding a third fill color — same outline shape as .secondary, in --loss. */
.btn.sm { padding: 5px 12px; font-size: 12.5px; }
.btn.danger { background: var(--surface); color: var(--loss); border-color: var(--loss); }
.btn.danger:hover { background: var(--loss); color: #fff; }
/* The currently-set choice among a row of otherwise-equal .secondary buttons (admin's pick-result
 * override, views/admin-picks.js) — same outline shape, just re-inked in --accent so the active
 * one reads at a glance without a fourth button color. */
.btn.secondary.active { border-color: var(--accent); color: var(--accent); font-weight: 700; }
.link-btn { background: none; border: none; color: var(--accent); text-decoration: underline; cursor: pointer; font: inherit; padding: 0; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 12px; }

.password-field { position: relative; }
.password-field input { width: 100%; padding-right: 60px; }
.password-toggle {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  background: none; border: none; color: var(--muted); font-size: 11px;
  font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.04em;
  cursor: pointer; padding: 4px 6px;
}
.password-toggle:hover { color: var(--ink); }
.field-hint { font-size: 12px; color: var(--muted); margin: 6px 0 0; }

.login-screen { max-width: 400px; margin: 64px auto; padding: 0 16px; }
.login-card { background: var(--surface); border: 1px solid var(--rule); border-radius: 2px; padding: 28px 24px; }
.login-title { font-family: var(--font-mono); font-size: 12.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; }
.login-subtitle { font-size: 14px; color: var(--ink); margin: 0 0 20px; }
.login-form .btn { width: 100%; margin-top: 4px; }
.login-error { color: var(--loss); font-size: 13.5px; margin: 0 0 10px; }
.login-links { margin-top: 16px; text-align: center; font-size: 13.5px; }

.admin-form { max-width: 520px; }
.admin-form textarea { width: 100%; }

/* -- the week dial — shared by the admin console (views/admin-shell.js) and the pick screen's own
 * stepper (views/picks.js's weekStepper) --
 * The dial is the one place "which week am I looking at" gets said, set large in the pixel face
 * (its only other use besides the wordmark) so it reads before anything else on the screen — see
 * the plan's design direction. `.week-dial-block` carries the thick rule that also marks
 * .day-header (the app's most confident existing element), so the dial reads as native to the
 * system rather than bolted on. Admin's dial is a real <select> (every week of the season, one
 * click away); the pick screen has no route to that season-wide list, so its dial is the same
 * classes on a plain, non-interactive <span> instead — same look, prev/next only. */
.admin-header { margin-bottom: 28px; }
.week-dial-block { border-bottom: 2px solid var(--ink); padding-bottom: 14px; margin-bottom: 4px; }
.week-dial { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.week-dial-nav { display: flex; align-items: center; gap: 8px; }
.week-dial-select {
  font-family: var(--font-pixel); font-size: 28px; letter-spacing: 0.02em; color: var(--ink);
  padding: 0 2px;
}
.week-dial-meta {
  font-family: var(--font-mono); font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums; margin-top: 8px;
}
.admin-header .segmented { margin-top: 16px; margin-bottom: 0; }

/* -- "acting as" bar (views/admin-picks.js) -- deliberately the same recipe as .week-dial-block
 * just above (thick rule, pixel-face value) so "which player am I editing" reads with the same
 * confidence as "which week" does, one register down (22px vs. the week dial's 28px) since it
 * always appears below that dial, never in place of it. */
.acting-as-block { border-bottom: 2px solid var(--ink); padding-bottom: 14px; margin-bottom: 20px; }
.acting-as-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.acting-as-label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
.acting-as-select {
  font-family: var(--font-pixel); font-size: 22px; letter-spacing: 0.02em; color: var(--ink);
  text-transform: uppercase; padding: 0 2px; cursor: pointer;
}

/* The one status line every admin action reports through, instead of alert()/statusBox drifting
 * between screens. */
.admin-status { font-size: 13.5px; color: var(--muted); margin-top: 10px; }
.admin-status:empty { display: none; }
.admin-status.err { color: var(--loss); }

/* Claude's run list and the player roster (views/admin-claude.js, views/admin-players.js) —
 * stacked blocks, not a table. A phone is this app's primary device (see memory: mobile-first),
 * and a table wide enough for either one's real content doesn't fit a phone width without
 * clipping a column off-screen; .run-row/.run-list (below, already used by the pick screen's own
 * game rows in spirit) never need horizontal scroll at any width. */
.run-row-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.run-use { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.run-use input[type="radio"] { margin: 0; flex: none; }
.run-dash { display: inline-flex; width: 16px; justify-content: center; color: var(--muted); }
.run-row-meta { margin-top: 4px; color: var(--muted); }
.run-toggle { font-size: 14px; }
.run-row > .btn-row:first-child { margin-top: 0; }

.admin-footnote { font-family: var(--font-mono); font-size: 12px; color: var(--muted); margin: -12px 0 40px; }

/* -- pick screen -- */
.week-header {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 0 0 16px;
}
.game-list { border-top: 1px solid var(--rule); }
.game-row { padding: 14px 0; border-bottom: 1px solid var(--rule); }
.game-row:last-child { border-bottom: none; }
.game-row.disabled { opacity: 0.5; }
/* A claimed game stays at full opacity — teams and line are still fully legible, see
 * game-row.js's header — even though it's also .disabled (no buttons left to use). */
.game-row.claimed { opacity: 1; }
.game-teams { font-weight: 600; margin-bottom: 2px; }
.game-meta { font-family: var(--font-mono); font-size: 12px; color: var(--muted); margin-bottom: 10px; }
.pick-btn-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* The two pick buttons — always a stacked column of full-width bars, never side by side. Side by
 * side (the old .pick-btn-row layout, still used above for admin's own inline forms) resized the
 * two buttons inconsistently against each other whenever one team's name ran longer than the
 * other's, sometimes wrapping one onto two lines while its sibling stayed on one. Team name and
 * line are separate elements within the bar (game-row.js's pickButton), not one concatenated
 * string, so the line always sits at the right edge and only the team name truncates if it's
 * long — the bar's height never depends on how long the name is. */
.pick-choices { display: flex; flex-direction: column; gap: 8px; }
.pick-btn {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; font-family: var(--font-mono); text-align: left;
}
.pick-team { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-line { flex: none; font-weight: 700; }

/* The claim stamp — replaces a claimed game's two pick buttons. Flat, filled, on --claimed:
 * the one deliberate bit of visual weight on the picks screen (see the plan's design direction)
 * — everything else here stays quiet. */
.claim-stamp {
  display: inline-block;
  padding: 6px 10px;
  background: var(--claimed);
  border-radius: 2px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.claim-stamp.mine { background: var(--accent); color: #fff; }
/* The Dan Driscoll Lock of the Week wins over .mine's blue when both apply (your own locked
 * pick) — declared after it on purpose, same specificity, later rule takes it. */
.claim-stamp.locked { background: var(--lock-bg); color: var(--lock); }
.claim-stamp.locked .lock-label { color: inherit; }

/* Your own locked-in pick, shown above the filter bar once you've made one this league. */
.your-pick-card { border: 1px solid var(--rule); border-radius: 2px; background: var(--bg); padding: 14px 16px; margin-bottom: 20px; }
.your-pick-card.locked { border-color: var(--lock); background: var(--lock-bg); }
.your-pick-card .panel-label { border-bottom: none; padding-bottom: 4px; margin-bottom: 6px; }
.your-pick-card .value { font-family: var(--font-mono); font-size: 16px; font-weight: 600; }

/* The dial's prev/next arrows — shared by the admin console and the pick screen's own stepper
 * (see the week dial block above; the dial's center content differs between them, these don't). */
.week-stepper-btn {
  font-family: var(--font-mono); font-size: 16px; line-height: 1; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--ink); border: 1px solid var(--rule); border-radius: 2px; cursor: pointer;
}
.week-stepper-btn:hover:not(:disabled) { border-color: var(--ink); }
.week-stepper-btn:disabled { opacity: 0.35; cursor: default; }

/* Read-only-week banner — a past or future week's slate, browsed via the stepper. */
.week-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  font-size: 13px; color: var(--muted);
  padding: 10px 14px; margin-bottom: 20px;
  background: var(--bg); border: 1px solid var(--rule); border-radius: 2px;
}

/* Segmented control — league tabs on the picks screen, slot chips in its filter bar. */
.segmented { display: flex; gap: 4px; border-bottom: 1px solid var(--rule); margin-bottom: 18px; }
.segmented-tab {
  background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer;
  padding: 8px 4px 10px; margin-bottom: -1px;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 12.5px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
.segmented-tab + .segmented-tab { margin-left: 14px; }
.segmented-tab.active { color: var(--ink); border-bottom-color: var(--accent); font-weight: 600; }
.segmented-meta { font-size: 10px; color: var(--muted); border: 1px solid var(--rule); border-radius: 2px; padding: 1px 5px; }
.segmented-tab.active .segmented-meta { border-color: var(--accent); color: var(--accent); }

/* Filter bar — team search, slot chips, two toggles, clear-all. */
.filter-bar { margin-bottom: 20px; }
.filter-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 10px; }
.filter-row:last-child { margin-bottom: 0; }
.filter-search { flex: 1; min-width: 160px; }
.filter-toggle { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); cursor: pointer; }
.filter-toggle input { margin: 0; }

/* Time rail — one big header per ET calendar day, one smaller sub-header per broadcast slot
 * within it (schedule.js's groupBySlot). The day header is deliberately oversized against
 * everything below it (Ryan's reference point was ESPN's own schedule view, sized up further) —
 * "what day is this" should read at a glance while scrolling a long slate, full width, no gutter
 * column competing with it. */
.day-header {
  font-family: var(--font-mono);
  font-size: 27px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--ink);
  padding-bottom: 10px;
  margin-bottom: 16px;
  border-bottom: 2px solid var(--ink);
}
.time-day + .time-day .day-header { padding-top: 22px; border-top: 1px solid var(--rule); }
.time-slot + .time-slot { margin-top: 20px; }
.slot-header {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.slot-header .slot-name { color: var(--accent); font-weight: 600; }
.slot-header .slot-anchor { color: var(--muted); }
.slot-games .game-list { border-top: none; }
.slot-games .game-row:first-child { padding-top: 0; }
.slot-games .game-row:last-child { border-bottom: none; padding-bottom: 0; }

/* -- home page: chronological week-card list, week 1 first -- */
.week-list { display: flex; flex-direction: column; gap: 10px; }
/* A plain box, not a link — see home.js's weekCard header on why. */
.week-card {
  display: block;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 12px 14px;
}
.week-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.week-card-title { font-weight: 600; }
.week-card-meta { font-family: var(--font-mono); font-size: 12px; color: var(--muted); white-space: nowrap; }
.week-card .picks-board { border-top: none; }
.week-card .player-card { padding: 8px 0; }
.week-card .player-card:first-child { padding-top: 0; }

/* Claude's reasoning (home.js's reasoningRow) — always printed under its pick row, no click
 * required. Boxed and set apart from the plain pick line above it, since it's a different kind
 * of content (a paragraph, not a stat), but otherwise as visible as everything else on the card. */
.reasoning-text {
  margin: 6px 0 2px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink);
}

/* home.js's "How Claude Picks" card — the agent's real system prompt and a real slate line,
 * verbatim. Same boxed-paragraph geometry as .reasoning-text above (this is the same kind of
 * content, a block of text set apart from prose), but mono and pre-wrapped since it's source
 * text, not a sentence. */
.prompt-block {
  margin: 6px 0 14px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink);
  white-space: pre-wrap;
}
.prompt-block.slate { color: var(--muted); }

/* -- admin run list -- */
.run-list { border-top: 1px solid var(--rule); }
.run-row { padding: 10px 0; border-bottom: 1px solid var(--rule); font-family: var(--font-mono); font-size: 13px; }
.run-row:last-child { border-bottom: none; }

/* -- misc states -- */
.spinner { display: flex; justify-content: center; padding: 48px 0; }
.spinner-ball {
  width: 20px; height: 20px; border: 2px solid var(--rule); border-top-color: var(--accent);
  border-radius: 50%; animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner-ball { animation: none; } }

.error-state { max-width: 480px; margin: 64px auto; text-align: center; padding: 0 16px; }
.error-state h2 { font-family: var(--font-mono); font-size: 14px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--loss); margin: 0 0 8px; }
.error-state p { color: var(--muted); font-size: 14px; margin: 0; }

.not-found { max-width: 480px; margin: 80px auto; text-align: center; padding: 0 16px; color: var(--muted); font-size: 14px; }

/* The odds-staleness notice — deliberately placed above the Picks panel (see picks.js's
 * oddsNote), not buried in a footer, so it's read before anyone trusts a line. Accent-tinted
 * rather than the plain muted tone .week-banner uses, so it doesn't read as just more chrome. */
.odds-note {
  font-size: 13px; color: var(--ink);
  padding: 10px 14px; margin-bottom: 20px;
  background: #eef3fc; border: 1px solid var(--accent); border-radius: 2px;
}

@media (max-width: 480px) {
  #outlet { padding: 24px 16px 48px; }
  .panel { padding: 18px 18px 20px; }
  .hero { padding: 36px 16px 4px; }
  .wordmark { font-size: 26px; }
  .tagline { font-size: 14px; }

  /* A player-card's fixed-width name column (needed on desktop to line up the NFL/CFB/TOTAL
   * columns across cards) was squeezing the pick value into too little width on a narrow phone —
   * a picked team name plus line plus result tag was getting cut off. Stack name above picks
   * instead, so the value gets the card's full width; drop the size a notch too. */
  .player-card { flex-direction: column; align-items: stretch; gap: 4px; }
  .player-card .who { min-width: 0; }
  .pick-row { font-size: 13px; }

  .day-header { font-size: 22px; }

  .week-dial-select { font-size: 22px; }
}
