/* The two web fonts, served from this site (2026-10-11) rather than Google Fonts: no render-blocking
   stylesheet from a third-party host before first paint, no second font host, no visitor addresses
   sent to Google. Files and character ranges are Google Fonts' own (Inter v20, a variable font covering
   400-700; Barlow Condensed v13), in static/fonts/ with their SIL Open Font License texts. The ?v=
   query makes the static handler cache them as immutable; base.html preloads the two the first screen
   uses, and its preload URLs must match these exactly. A browser fetches the latin-ext files only for
   a page that uses those characters. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/inter-latin.woff2?v=20") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/inter-latin-ext.woff2?v=20") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/barlow-condensed-600-latin.woff2?v=13") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/barlow-condensed-600-latin-ext.woff2?v=13") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/barlow-condensed-700-latin.woff2?v=13") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/barlow-condensed-700-latin-ext.woff2?v=13") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Inter's metrics on a local font, used until the web font arrives. Text set in an unadjusted
   fallback re-wrapped when Inter swapped in: the home page shifted 22px at ~8s on a throttled
   phone, CLS 0.150 against the 0.1 "good" line (site audit 2026-10-02). Overrides are Inter's
   measured ascent/descent/advance relative to Arial (the values fontaine/Next.js publish). */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("ArialMT"), local("Helvetica"), local("Liberation Sans");
  ascent-override: 90.49%;
  descent-override: 22.56%;
  line-gap-override: 0%;
  size-adjust: 107.06%;
}

:root {
  /* Warm near-black + burnished gold -- replaces a generic safe blue with
     something that actually reads as premium/elite for a sports-prediction
     product (gold-on-black is a deliberate, well-worn "high-tier" signal --
     watches, VIP tiers, casino/sportsbook branding -- and it's distinct
     from every competitor's blue/green). Neutrals get a slight warm tilt
     to match rather than staying a cool blue-grey next to a warm accent. */
  /* Neutral surface ramp, darkest to lightest: page ground, header/chrome,
     panels, nested panels, hover/raised. */
  --surface-0: #0a0a0c;
  --surface-1: #131316;
  --surface-2: #18181b;
  --surface-3: #202023;
  --surface-4: #26262a;
  --border: #2b2b2f;
  --border-strong: #3e3e44;
  --text: #f5f3ee;
  --text-dim: #9c988d;
  /* 4.6:1 on surface-0 -- this token carries real data (win %, labels)
     at 11px, so it must clear AA 4.5:1 (design review measured the old
     #67635a at 3.3:1) */
  --text-faint: #8e897d;  /* 4.9:1 on the card surface (was #7e7a6f, 4.1:1) */
  --accent: #d9a94a;
  --accent-2: #f0c979;
  --favorite: #f5f3ee;
  /* 8:1 -- the underdog is the OTHER TEAM, not a disabled control; the
     favorite's pill + weight carry the hierarchy (old #6e6b62 = 3.7:1) */
  --underdog: #a8a39a;
  /* Per-sport hues (--sport-mlb, --sport-nfl, ...) are NOT defined here --
     base.html generates them from the sport registry (app/sports.py),
     along with the per-sport nav-active and badge rules, so the CSS can
     never drift from the sport list again.

     THE SPORT THEMING CONTRACT: components never name a sport. They
     consume --sport-accent, which defaults to the brand gold here and is
     re-pointed at the sport's own hue by the generated
     `[data-sport="X"]` rules -- set on <body> for sport pages and on
     each .sport-section on the home page. Soft washes are derived at
     the point of use via color-mix (deriving them here wouldn't work:
     a custom property's var() substitutes where the property is DEFINED,
     so a :root-level mix would bake in the gold and ignore the
     data-sport override). */
  --sport-accent: var(--accent);
  /* The sport hue as TEXT. The raw hues are tuned for fills and bars; as small
     text most fail 4.5:1 in one theme (CFB 2.4 in dark, tennis 2.0 in light).
     base.html re-points this per sport (sports.sport_ink: the hue, lightened or
     darkened just enough to clear 4.6:1 on every surface) -- so use it for any text
     that wears the sport colour, and --sport-accent for fills. */
  --sport-ink: var(--accent);
  --shadow-card-hover: 0 2px 4px rgba(0, 0, 0, 0.45), 0 14px 32px -14px rgba(0, 0, 0, 0.6);
  --scroll-shadow: rgba(0, 0, 0, 0.45);
  --win: #4fd18b;
  --lose: #d66a6a;

  --radius-card: 14px;
  --radius-pill: 999px;
  /* Faint inner top highlight -- the "beveled glass" edge premium dark
     dashboards (Linear, Stripe, Vercel) use to make a flat panel read as
     a lifted physical surface rather than a plain rectangle. */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.5), 0 12px 32px -16px rgba(0, 0, 0, 0.65), inset 0 1px 0 rgba(255, 255, 255, 0.04);

  --font-body: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Barlow Condensed", "Inter", sans-serif;

  --fs-hero: 2.25rem;
  --fs-hero-sm: 1.5rem;
  --fs-title: 1.0625rem;
  --fs-body: 0.9375rem;
  --fs-label: 0.75rem;
  --fs-meta: 0.6875rem;

  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;

  /* Motion: one ease (expo-out -- fast start, soft landing) and three
     durations. Everything animated in this file uses these, so the whole
     site decelerates the same way; prefers-reduced-motion (end of file)
     kills all of it at once. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-1: 150ms;
  --dur-2: 350ms;
  --dur-3: 650ms;

  font-variant-numeric: tabular-nums;
}

* { box-sizing: border-box; }

/* backstop: no single overflowing element may ever drag the whole page
   wider than the viewport again (clip, not hidden -- hidden would create
   a scroll container and break the sticky header) */
html, body { overflow-x: clip; }
/* The sticky header is ~63px tall. Without this, a keyboard focus scroll, the skip link and a
   linked game (#g-<id>) all landed UNDER it: on /nfl a focused "Player props" button sat fully
   behind the header on 7 of 25 tab stops (WCAG 2.4.11; site audit 2026-10-02). */
html { scroll-padding-top: 5rem; }

body {
  margin: 0;
  /* One very soft sport-tinted wash bleeding down from the top of the
     page over the solid ground -- every sport's page warms toward its
     own hue while the home page glows brand gold. Deliberately faint
     (5%) and nothing else: an earlier pass layered an SVG film-grain
     noise tile on top and ran the wash at 8%, and at real slate density
     the combination read as a dirty, mottled canvas rather than texture
     (user-reported 2026-09-02). Flat-and-clean beats textured-and-grubby. */
  background-color: var(--surface-0);
  background-image:
    radial-gradient(900px 420px at 50% -120px, color-mix(in srgb, var(--sport-accent) 5%, transparent), transparent 68%);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

/* off-screen until keyboard-focused */
.skip-link {
  position: absolute;
  left: var(--sp-3);
  top: -100px;
  z-index: 100;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-weight: 600;
  font-size: 0.85rem;
}
.skip-link:focus { top: var(--sp-3); }

/* Keyboard focus: one visible, sport-tinted ring everywhere. Mouse
   clicks (plain :focus) stay ringless. */
:focus-visible {
  outline: 2px solid var(--sport-ink);  /* the raw hue was under 3:1 on three themes (1.4.11) */
  outline-offset: 2px;
  border-radius: 4px;
}

a { color: inherit; text-decoration: none; }

.stat-hero {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  line-height: 1;
}

/* ---------- header / nav ---------- */

header.site-header {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  /* Sticky frosted-glass chrome: the header stays put while the page
     scrolls under it, semi-translucent so the sport mesh reads through. */
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--surface-1) 82%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

header.site-header .brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

.brand-mark {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
}

.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  /* The wordmark in plain ink beside the gold mark: gold on both read as one heavy gold smear
     next to the nav (site review 2026-10-11); the mark alone carries the brand colour now. */
  color: var(--text);
}

nav.sport-nav {
  scroll-padding-inline: 28px;  /* the edge fade's width: a scrolled-to tab clears it */
  display: flex;
  gap: var(--sp-1);
  flex-wrap: wrap;
}

nav.sport-nav a {
  position: relative;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-pill);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dim);
  border: 1px solid transparent;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}

nav.sport-nav a:hover {
  color: var(--text);
  border-color: var(--border);
  background: var(--surface-2);
}

nav.sport-nav a.active {
  color: var(--text);
  /* sport-tinted pill -- replaces the old straight underline, which cut
     across the pill's curve and collided with the header border */
  border-color: color-mix(in srgb, var(--tab-accent, var(--accent)) 45%, var(--border));
  background: color-mix(in srgb, var(--tab-accent, var(--accent)) 14%, var(--surface-2));
}

.utility-nav { display: contents; }
.nav-utility {
  margin-left: auto;
  padding: var(--sp-2) var(--sp-3);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dim);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  transition: color 150ms ease, border-color 150ms ease;
  white-space: nowrap;
}
.nav-utility:hover { color: var(--text); border-color: var(--border); }
/* a phone header has room for the sport tabs and ONE utility link (Record); Lines lives in the
   footer there -- with both, the sport tabs shrank to one and a half visible */
.nav-utility--wide ~ .nav-utility { margin-left: 0; }  /* Lines takes the auto margin, Record sits beside it */
@media (max-width: 640px) {
  .nav-utility--wide { display: none; }
  .nav-utility--wide ~ .nav-utility { margin-left: auto; }
}
/* when the record link is present it takes the auto margin */
.nav-utility ~ .sign-out { margin-left: 0; }

/* the light/dark switch: shows the sun in dark mode (go light), the moon in light mode (go dark) */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  margin-left: calc(var(--sp-2) * -1);   /* sits with the utility links, not a header gap away */
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-dim);
  cursor: pointer;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease, background-color var(--dur-1) ease;
  -webkit-tap-highlight-color: transparent;
}
.theme-toggle:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-3); }
.theme-toggle:active { scale: 0.92; transition: scale 60ms var(--ease-out); }
.theme-icon { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.theme-icon--moon { display: none; fill: currentColor; stroke: none; }
:where(html[data-theme="light"]) .theme-icon--sun { display: none; }
:where(html[data-theme="light"]) .theme-icon--moon { display: block; }
:root { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }

.sign-out {
  margin-left: auto;
  padding: var(--sp-2) var(--sp-3);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-faint);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  transition: color 150ms ease, border-color 150ms ease;
}
.sign-out:hover { color: var(--text-dim); border-color: var(--border); }


/* Per-sport `nav.sport-nav a.{sport}.active` accent rules are generated
   from the sport registry in base.html -- see app/sports.py. */

/* ---------- layout ---------- */

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--sp-5);
}

h1 {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 700;
  margin: 0 0 var(--sp-1);
}

/* ---------- page hero ---------- */

.page-hero {
  margin: var(--sp-3) 0 var(--sp-5);
}
.page-hero-eyebrow {
  margin: 0;
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sport-ink);
}
.page-hero-slate {
  font-size: 2.6rem;
  letter-spacing: 0.01em;
  margin: 0 0 var(--sp-1);
}
/* The sport/home h1 wraps the eyebrow + slate lines (SEO: the heading names
   the sport, not only the date); each span keeps its standalone look. */
.page-hero-h1 { margin: 0 0 var(--sp-1); font: inherit; letter-spacing: normal; }
.page-hero-h1 > span { display: block; }
.page-hero-h1 > .page-hero-slate {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  margin: 0;
}
.page-hero-intro {
  margin: var(--sp-1) 0 var(--sp-3);
  max-width: 68ch;
  color: var(--text-dim);
  font-size: 0.9rem;
}
.page-hero-intro a { white-space: nowrap; }
.page-hero-sub {
  margin: 0 0 var(--sp-3);
  font-size: 0.85rem;
  color: var(--text-dim);
}
.page-hero-sub strong { color: var(--text); font-variant-numeric: tabular-nums; }

.back-link { margin: 0 0 var(--sp-2); font-size: 0.8rem; font-weight: 600; }
.back-link a { color: var(--text-faint); }
.back-link a:hover { color: var(--text-dim); text-decoration: underline; }

.slate-meta--measure { max-width: 68ch; }

/* Home hero: the intro on the left, the day's numbers and the record on the right (stacked on a
   phone). The record stats link to /results: the board and the record it answers to, connected. */
.page-hero--home { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--sp-4) var(--sp-6); }
.page-hero--home .page-hero-text { flex: 1 1 340px; min-width: 0; }
.page-hero--home .page-hero-intro { margin-bottom: 0; }
.hero-stats { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.hero-stat {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 5.5rem;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}
.hero-stat-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hero-stat-label { font-size: var(--fs-meta); font-weight: 600; color: var(--text-faint); white-space: nowrap; }
.hero-stat--link { transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease; }
.hero-stat--link:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--surface-3); }
.hero-stat--link .hero-stat-value { color: var(--accent-2); }
@media (max-width: 640px) {
  .hero-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); width: 100%; gap: var(--sp-1); }
  .hero-stat { min-width: 0; padding: var(--sp-2); }
  .hero-stat-value { font-size: 1.2rem; }
  .hero-stat-label { white-space: normal; line-height: 1.25; }
}

.summary-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-5);
  color: var(--text-dim);
  font-size: 0.85rem;
}
.summary-stat strong {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--text);
}
.summary-note {
  color: var(--text-faint);
  font-size: var(--fs-meta);
}

.view-slate-link {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--sport-ink);
  white-space: nowrap;
}
.view-slate-link:hover { text-decoration: underline; }

/* ---------- home mini cards ---------- */

.mini-grid {
  display: grid;
  /* 250px floor keeps long names ("Diamondbacks") from truncating at
     4-across density; gap matches the main game grid's rhythm */
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: var(--sp-4);
}

/* Four previews fill a row only from ~1096px (4 x 250px tracks + gaps in the 1052px main). Between
   ~830 and 1095px the grid is three across, and the fourth card sat alone on a second row. */
@media (min-width: 830px) and (max-width: 1095px) {
  .mini-grid > .mini-card:nth-child(4) { display: none; }
}
.mini-card {
  display: block;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--sp-3) var(--sp-4);
  transition: transform var(--dur-1) ease, border-color var(--dur-1) ease;
  animation: card-in var(--dur-3) var(--ease-out) backwards;
}
.mini-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--sport-accent) 45%, var(--border));
}

.mini-team {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) 0;
  color: var(--text-dim);
}
.mini-team.is-favorite { color: var(--text); }

.mini-logo-wrap {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}
.mini-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.mini-logo.player-headshot { object-fit: cover; border-radius: 50%; }
.mini-logo-wrap .team-logo-fallback { font-size: 0.6rem; }  /* was 8px, the smallest text on the site */

.mini-name {
  font-size: 0.85rem;
  font-weight: 600;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mini-team.is-favorite .mini-name { font-weight: 700; }

.mini-score {
  margin-left: auto;
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 1rem;
  color: var(--text-dim);
}
.mini-team.is-favorite .mini-score { color: var(--favorite); font-size: 1.1rem; }
/* score leads; win% is quiet supporting type beside it. When a game has
   no score, margin-left:auto below keeps the % right-aligned alone. */
.mini-winprob {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: var(--fs-meta);
  color: var(--text-faint);
  min-width: 3.2rem;
  text-align: right;
}
.mini-score + .mini-winprob { margin-left: var(--sp-2); }

.win-prob-bar--thin { height: 3px; margin: var(--sp-1) 0; }
.win-prob-bar--thin::after { display: none; }

/* the full card's bar shares the team rows' full-bleed axis */
.game-card > .win-prob-bar { margin-inline: calc(var(--sp-3) * -1); }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
h2 {
  font-size: var(--fs-title);
  margin: var(--sp-6) 0 var(--sp-3);
  color: var(--text-dim);
  font-weight: 600;
}

.slate-meta {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-bottom: var(--sp-2);
}

.run-status {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0.2rem var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-faint);
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: var(--sp-5);
  cursor: default;
}
.run-status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--win);
  flex-shrink: 0;
}
.run-status--stale {
  color: var(--lose);
  border-color: var(--lose);
}
.run-status--stale::before {
  background: var(--lose);
}

.date-nav {
  display: flex;
  align-items: center;
  /* wrap on narrow screens -- an unwrappable control row was dragging the
     whole page wider than a phone viewport (design review, mobile) */
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

/* one control height across the row (36px); phones bump to 44px below */
.date-nav-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 1.15rem;
  line-height: 1;
  flex-shrink: 0;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}
.date-nav-arrow:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-3); }
.date-nav-arrow.disabled { color: var(--text-faint); opacity: 0.35; pointer-events: none; }

.slate-picker {
  height: 2.25rem;
  padding: 0 var(--sp-6) 0 var(--sp-3);
  border-radius: var(--radius-pill);
  background-color: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  font-family: var(--font-body);
  cursor: pointer;
  /* replace the OS-native select chevron -- the one raw form control on
     the page -- with a drawn one */
  appearance: none;
  -webkit-appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 16px center, right 11px center;
  background-repeat: no-repeat;
}

@media (max-width: 640px) {
  .date-nav-arrow { width: 2.75rem; height: 2.75rem; }
  .slate-picker { height: 2.75rem; }
}

.past-slate-banner {
  display: inline-block;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  color: var(--accent-2);
  font-size: var(--fs-meta);
  font-weight: 600;
  margin-bottom: var(--sp-3);
}
.past-slate-banner a { text-decoration: underline; }

/* section = a row in a table of sports: divider below, one header line */
.sport-section {
  margin-bottom: var(--sp-6);
  padding-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.sport-section:last-child { border-bottom: none; }
.sport-section-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3);
}
.sport-section-header .run-status { margin-bottom: 0; }
/* hairline ties the label cluster to the right-aligned links, so wide
   screens don't leave 700px of dead space mid-header */
.sport-section-header::before {
  content: "";
  flex: 1 1 40px;
  height: 1px;
  background: var(--border);
  order: 8;
}
.sport-section-header .view-slate-link { order: 9; margin-left: 0; }
.sport-section-header .section-links { order: 10; display: inline-flex; align-items: center; gap: var(--sp-2); }
.section-links .record-link + .record-link::before { content: "·"; margin-right: var(--sp-2); color: var(--text-faint); }
.record-link {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-faint);
  white-space: nowrap;
}
.record-link:hover { color: var(--text-dim); text-decoration: underline; }
.sport-section-date {
  color: var(--text-dim);
  font-size: 0.9rem;
  font-weight: 600;
}
.sport-badge {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 4px;
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* Per-sport `.sport-badge.{sport}` color rules are generated from the
   sport registry in base.html -- see app/sports.py. */

.game-grid {
  display: grid;
  /* min(100%, ...) so a phone narrower than a track still gets one full-width
     column rather than a track that overflows it */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  gap: var(--sp-4);
  align-items: start;
}
/* Every card on a slate is the same size wherever cards sit side by side (owner decision
   2026-10-11, superseding the earlier "cards hug their own content" review): every grid row takes
   the tallest card's height, and the card is a column whose action row (props / factors) sits on
   the bottom edge, so spare space falls above the buttons instead of leaving them mid-card.
   Not in a phone's single column: nothing sits beside a card there, and padding every card to the
   slate's tallest left up to 159px of empty card (CFB week 6, measured). */
@media (min-width: 600px) {
  .game-grid, .mini-grid { grid-auto-rows: 1fr; align-items: stretch; }
}
/* sparse slates: one or two cards own a real measure instead of sitting
   in a wide grid's leftover tracks (progressive -- :has() supported in
   every current engine; older browsers keep the plain grid) */
.game-grid:has(> .game-card:only-child) { grid-template-columns: minmax(0, 520px); }
/* Two-up only where two cards fit: on a phone this made two 163px columns
   with the names gone (site audit 2026-09-30). */
@media (min-width: 700px) {
  .game-grid:has(> .game-card:nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(0, 420px)); }
}

/* Only rendered when a slate spans 2+ tournaments (e.g. two Masters or
   majors overlapping) -- an ordinary single-tournament day never shows
   this, so it reads as a bonus label, not a permanent extra layer. */
.tournament-header {
  font-size: var(--fs-title);
  font-weight: 700;
  margin: var(--sp-6) 0 var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border-strong);
}
.tournament-header:first-of-type { margin-top: var(--sp-3); }
/* [hidden] alone loses this fight -- the UA stylesheet's `[hidden] {
   display: none}` and this class rule tie on specificity, and the author
   rule (this one) wins the cascade by coming later, so the Games/Players
   toggle's `panel.hidden = true` was silently doing nothing without this
   override. */
[data-view-panel][hidden] { display: none; }

/* ---------- countdown ---------- */

.opener-countdown {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--sp-6);
  text-align: center;
}

.opener-timer {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: var(--fs-hero);
  color: var(--sport-ink);
  margin: 0 0 var(--sp-2);
}

.opener-label {
  color: var(--text-dim);
  font-size: var(--fs-body);
  margin: 0;
}

.opener-unconfirmed {
  font-style: italic;
  color: var(--text-faint);
}

/* Home-page variant: a slim banner row, not a hero block. */
.opener-countdown--compact {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
}
.opener-countdown--compact .opener-timer { font-size: var(--fs-hero-sm); margin: 0; }
.opener-countdown--compact .opener-label { font-size: 0.85rem; color: var(--text-faint); }

/* ---------- game card ---------- */

@keyframes card-in {
  from { opacity: 0; transform: translateY(8px); }
}

/* the card a home-page link pointed at: a brief, quiet marker of "this one" */
.game-card:target { border-color: var(--sport-accent); box-shadow: 0 0 0 1px var(--sport-accent); }
/* the action row and the legacy disclosures sit on the card's bottom edge */
/* (the gap they had as a margin is kept as padding, so even the tallest card keeps it) */
.game-card > .props-open-row, .game-card > .card-disclosures { margin-top: auto; padding-top: var(--sp-3); }
.game-card {
  /* a grid item's automatic minimum is its min-content width: one long
     unbreakable line in ONE card widened the shared track, and every card
     on the slate ran off a phone screen */
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--sp-4) var(--sp-5);
  transition: transform var(--dur-1) ease, box-shadow var(--dur-1) ease, border-color var(--dur-1) ease;
  animation: card-in var(--dur-3) var(--ease-out) backwards;
}
/* Entrance stagger for the first grid row or two; later cards (below the
   fold anyway) come in together. `backwards` above keeps a delayed card
   invisible until its own animation starts. */
.game-grid .game-card:nth-child(2) { animation-delay: 45ms; }
.game-grid .game-card:nth-child(3) { animation-delay: 90ms; }
.game-grid .game-card:nth-child(4) { animation-delay: 135ms; }
.game-grid .game-card:nth-child(5) { animation-delay: 180ms; }
.game-grid .game-card:nth-child(6) { animation-delay: 225ms; }
.game-grid .game-card:nth-child(7) { animation-delay: 270ms; }
.game-grid .game-card:nth-child(8) { animation-delay: 315ms; }
.game-card:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-card-hover);
}

/* kickoff line: filled client-side in the reader's timezone (countdown.js) */
.card-kickoff {
  text-align: right;
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-dim);
  min-height: 1em;
  margin-bottom: var(--sp-1);
  font-variant-numeric: tabular-nums;
}
.card-kickoff--started { color: var(--text-faint); }
/* the card's top row: head-slot chips left, kickoff right. A FIXED height,
   the chip's own, on every card -- a flagged card must not be taller than
   its row-mates. The kickoff keeps its right alignment via margin-left. */
.card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: 1.7rem;   /* the pill's own height (was 1.4rem for the old, shorter chip) */
  margin-bottom: var(--sp-1);
}
.card-head .card-kickoff { margin: 0 0 0 auto; min-height: 0; white-space: nowrap; flex-shrink: 0; }
.card-head .meta-chip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* A head chip with a short form swaps to it when the row cannot hold the full
   text beside a dated kickoff: widest date "Sat, Nov 28 · 12:00 PM" is 123px,
   "FCS · low confidence" 163px at the pill padding (2026-09-27; 155 before), gap
   8 -- 294px, so below 298 the chip shortens ("FCS · low conf.", 123px) instead
   of the row growing or the text clipping. The long form stays the accessible
   name. */
.card-head { container-type: inline-size; }
.card-head .chip-short { display: none; }
@container (max-width: 297px) {
  .card-head .chip-long { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .card-head .chip-short { display: inline; }
}
/* mini-card head: verdict pill left, kickoff or "Final" right, one fixed height on every card */
.mini-head { display: flex; align-items: center; gap: var(--sp-2); height: 1.35rem; margin-bottom: var(--sp-1); }
.mini-head .pill { font-size: 0.66rem; padding: 0.05rem 0.45rem; }
.card-kickoff--mini { display: block; min-height: 0; margin: 0 0 0 auto; white-space: nowrap; }

.team-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: 10px;
  margin-bottom: var(--sp-2);
  /* full-bleed inset row: the pill bleeds into the card padding so its
     TEXT and numbers share one left/right axis with the chips, total
     line, and market strip below (design review: "one axis per card") */
  margin-inline: calc(var(--sp-3) * -1);
  border: 1px solid transparent;
  /* NOTE: --team-color is set inline by the team_row macro and available
     here, but the two attempts at using it decoratively (an inset
     left-edge bar, a radial halo behind the logo) both read as visual
     glitches at real slate density -- edge slivers fighting the rounded
     corners, color smears behind 28px logos (user-reported 2026-09-02).
     The logo itself already carries the team's identity; leave it alone. */
}
.team-row.is-favorite {
  background: color-mix(in srgb, var(--favorite) 8%, transparent);
  border-color: var(--border-strong);
}
.team-row.is-underdog {
  color: var(--text-dim);
}

.team-id {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
}

.team-logo-wrap {
  position: relative;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
}
.team-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.team-logo.player-headshot {
  /* A photographic headshot should fill and crop to a circle like a
     profile-picture avatar; a team logo mark should show in full
     (`contain`, above) since cropping it could cut off part of the mark. */
  object-fit: cover;
  border-radius: 50%;
}
.team-logo-fallback {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.92);
  letter-spacing: 0.02em;
}

.team-name {
  font-size: var(--fs-title);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.team-row.is-favorite .team-name { color: var(--favorite); font-weight: 700; }
.team-row.is-underdog .team-name { color: var(--underdog); font-weight: 500; }

/* The projected SCORE is each row's hero number; win chance sits under
   it in quiet supporting type (user request 2026-09-02 -- the score is
   the thing people came for). */
.team-nums {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.05rem;
  flex-shrink: 0;
}
.team-score {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  /* one size for both rows -- a 0.1rem delta read as a rendering bug,
     not hierarchy; the favorite's pill/name-weight/brightness do the work */
  font-size: var(--fs-hero-sm);
  line-height: 1;
  color: var(--text-dim);
}
.team-row.is-favorite .team-score { color: var(--favorite); }

.team-winprob {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: var(--fs-meta);
  color: var(--text-faint);
  flex-shrink: 0;
  white-space: nowrap;
}
/* the favorite's win% is the one number compared ACROSS cards -- keep it
   small but give it full ink so a slate can be scanned for the biggest
   favorite (design review) */
.team-row.is-favorite .team-winprob { color: var(--text); font-weight: 700; }
/* win% in the hero slot (2026-09-15): same treatment the scoreless fallback
   has always used, now the default -- see _game_card.html for why. */
.team-winprob--hero {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-hero-sm);
  line-height: 1;
  color: var(--text-dim);
}
.team-row.is-favorite .team-winprob--hero { color: var(--favorite); }
/* the projected score is supporting detail under the win chance, never compared
   across cards (sports with show_team_scores; MLB shows the combined total only) */
.team-score--sub {
  font-family: inherit;
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-faint);
  white-space: nowrap;
}
.team-row.is-favorite .team-score--sub { color: var(--text-dim); }

/* scoreless fallback: win% takes the hero slot, as it did pre-2026-09-02 */
@keyframes bar-grow {
  from { transform: scaleX(0); }
}

.win-prob-bar {
  position: relative;
  height: 5px;
  border-radius: 3px;
  background: var(--border);
  overflow: hidden;
  margin: var(--sp-3) 0;
}
/* 50% coinflip tick -- the fill is the TOP row's win chance, and the
   tick gives the eye a reference for "how far from even" */
.win-prob-bar::after {
  content: "";
  position: absolute;
  left: calc(50% - 1px);
  top: 0;
  bottom: 0;
  width: 2px;
  background: color-mix(in srgb, var(--text) 30%, transparent);
}
.win-prob-bar > span {
  display: block;
  height: 100%;
  /* Solid sport-tinted fill (brand gold on the home page's un-themed
     states), sweeping in from the left on load. Solid, not a gradient --
     the old fade-to-55%-white tip read washed-out at 5px tall. The
     span's inline width is the home side's probability; scaleX keeps
     the animation off layout. */
  background: var(--sport-accent);
  border-radius: 3px;
  transform-origin: left;
  animation: bar-grow 900ms var(--ease-out) 100ms backwards;
}

/* card footer: label left, value right, hairline above -- matches the
   Model/Market strip's grammar instead of a centered orphan.

   Every footer row is exactly one 1.35rem line box tall, so two cards
   side by side line their footers (and their bottom edges) up whatever
   they carry. That needs three things together: neither span may wrap;
   both carry the same fixed line-height; and the row centres them rather
   than aligning baselines -- a baseline-aligned row's height is
   maxAscent + maxDescent, which still moves when `.score-total--fit`
   scales the value's font down. Before this, one long spread ("Florida
   International -22.1") wrapped both spans onto two lines and made its
   card ~26px taller than its neighbours. */
.score-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border);
  container-type: inline-size;
}
/* the label speaks the Model / Market strip's grammar (sentence case, the same size and ink), so a
   card's footer rows read as one list; the value keeps the display face, which the spread's
   shrink-to-fit below is measured on */
.score-label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-dim);
  line-height: 1.35rem;
  white-space: nowrap;
  /* the label is the redundant half of the pair -- if a row ever does run
     out of room, truncate it rather than the number it labels */
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.score-total {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.35rem;
  white-space: nowrap;
  flex: 0 0 auto;
}

/* Shrink-to-fit, for the one footer value that is text rather than a
   number: the spread, which carries a team's display name ("Florida
   International -22.1" is 27 characters where "TCU -3.5" is 8).

   The template sets `--fit-len` to the rendered string's length and the
   row is a `container-type: inline-size` query container, so the value
   can size itself from the space it actually has:

     available = 100cqw (the row) - 134px (widest label + the flex gap)
     needed    = len * 0.415em      (0.403em is the widest per-character
                                     cost measured across a full 71-game
                                     CFB slate; 0.415 leaves ~3% margin)

   `min()` with the base size means a short value is never scaled UP. On
   a browser with no container-query support the whole declaration is
   invalid and dropped, leaving the 1.05rem above -- the old behaviour,
   not a broken one. Verified against the live Week 4 slate: 0 of 71
   values overflow, at both the 3-column desktop card (260px row, worst
   case 11.2px) and the phone card (363px row, no shrink needed). */
.score-total--fit {
  font-size: min(1.05rem, calc((100cqw - 134px) / (var(--fit-len) * 0.415)));
}

/* Player-entity sports (tennis): the photo is the identity, so the
   avatar gets more presence than a team logo mark. */
.game-card--player .team-logo-wrap { width: 36px; height: 36px; }
.game-card--player .team-logo-fallback { font-size: 0.72rem; }

.team-tag {
  flex-shrink: 0;
  padding: 0 0.35rem;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  text-transform: uppercase;
}

/* Tennis court surfaces -- the one place a fixed palette beats the sport
   accent: clay/grass/hard ARE colors to anyone who watches tennis. */
.meta-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin-top: var(--sp-2);
}
/* ONE PILL on the card (2026-09-27, owner request): badges share the
   player-projections / factors toggle's exact geometry -- pill radius, the
   same padding, a 1px border -- so every pill on a card is the same shape
   and height. Tone stays in the fill and the border tint, never the shape. */
.meta-chip {
  display: inline-flex;
  align-items: center;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface-3);
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  text-transform: uppercase;
}
/* attention (weather warnings, regime) and highlight (postseason,
   playoffs) tones for registry-computed badges (sports.game_badges) */
.meta-chip--warn { background: color-mix(in srgb, var(--lose) 18%, transparent); border-color: color-mix(in srgb, var(--lose) 35%, transparent); color: color-mix(in srgb, var(--lose) 75%, white); }
.meta-chip--accent { background: color-mix(in srgb, var(--sport-accent) 20%, transparent); border-color: color-mix(in srgb, var(--sport-accent) 38%, transparent); color: color-mix(in srgb, var(--sport-accent) 70%, white); }
.meta-chip.surface-clay { background: color-mix(in srgb, #b0592e 24%, transparent); border-color: color-mix(in srgb, #b0592e 40%, transparent); color: color-mix(in srgb, #b0592e 65%, white); }
.meta-chip.surface-grass { background: color-mix(in srgb, #3e8e52 24%, transparent); border-color: color-mix(in srgb, #3e8e52 40%, transparent); color: color-mix(in srgb, #3e8e52 65%, white); }
.meta-chip.surface-hard { background: color-mix(in srgb, #2f6ea8 24%, transparent); border-color: color-mix(in srgb, #2f6ea8 40%, transparent); color: color-mix(in srgb, #2f6ea8 65%, white); }

/* ---------- graded outcome (site review 2026-10-11) ----------
   A finished card keeps the upcoming card's shape: the real score takes the hero slot (winner in
   full ink), the verdict is a pill in the head row, and the result against the first line is one
   more row of the Model / Market strip. The old separate "Final" box and result line made every
   final card 55-76px taller, and the equal-height grid passed that height to every upcoming card. */
.team-actual {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: var(--fs-hero-sm);
  line-height: 1;
  color: var(--text-dim);
}
.team-row.is-winner .team-actual { color: var(--favorite); }
.team-row.is-loser .team-actual { color: var(--text-faint); }
.card-head .pill { flex-shrink: 0; }
.mkt-row--result { align-items: center; color: inherit; text-decoration: none; margin-top: 0.15rem; }
.mkt-row--result .mkt-label { color: var(--text-faint); }
.mkt-row--result:hover .mkt-label { color: var(--text-dim); text-decoration: underline; }
.mkt-row--result .pill { font-size: 0.66rem; padding: 0.05rem 0.45rem; }

/* ---------- record / results ---------- */

/* the slate's 30-day record, inline in the hero's summary line */
.hero-sub-link { color: var(--sport-ink); font-weight: 600; white-space: nowrap; }
.hero-sub-link:hover { text-decoration: underline; }
/* the view switch and the freshness pill: one row, switch first, status at the right */
.view-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-4); }
.view-bar .view-toggle { margin-bottom: 0; }
.view-bar .run-status { margin: 0; }
.view-bar .view-toggle + .run-status { margin-left: auto; }

/* "Hide FCS games" -- same pill as the record chip, so the slate controls
   read as one row. The checkbox stays a real, focusable checkbox. */
.filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 2.25rem;
  padding: 0 var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  user-select: none;
}
.filter-toggle:hover { color: var(--text); }
.filter-toggle input { accent-color: var(--sport-accent); margin: 0; width: 0.95rem; height: 0.95rem; }
.filter-toggle:focus-within { outline: 2px solid var(--sport-ink); outline-offset: 2px; }
/* "Hide FCS games": the card keeps its place in the grid (so rows don't
   reflow and the reader can see a game was withheld, not missing), its
   content blurs, and a label sits over it. */
.hide-fcs .game-card[data-fcs] { position: relative; overflow: hidden; }
.hide-fcs .game-card[data-fcs] > * {
  filter: blur(7px);
  opacity: 0.55;
  pointer-events: none;
  user-select: none;
}
.hide-fcs .game-card[data-fcs]::after {
  content: "FCS game hidden";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  box-shadow: var(--shadow-card);
  white-space: nowrap;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}
@media (max-width: 640px) { .filter-toggle { height: 2.75rem; } }

.record-cards {
  display: grid;
  /* auto-FIT: empty tracks collapse, so four cards fill the measure and
     share a right edge with the table below (auto-fill left a phantom
     fifth track and a 200px overhang -- design review) */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.record-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}
/* Every record figure in plain ink. They wore the sport's hue, which put MLB's winning record in
   red, the site's colour for a loss (site review 2026-10-11); the labels carry the meaning. */
.record-card-value { font-size: var(--fs-hero); color: var(--text); }
.record-card-label { font-size: var(--fs-meta); color: var(--text-dim); }

/* carded like every other module on the page */
.calibration {
  max-width: 720px;
  margin-bottom: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}
.calibration-row {
  display: grid;
  grid-template-columns: 5rem 1fr 6rem;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-1) 0;
}
.calibration-label { font-size: var(--fs-meta); font-weight: 600; color: var(--text-dim); }
.calibration-track {
  position: relative;
  height: 10px;
  border-radius: 5px;
  background: var(--surface-3);
  overflow: hidden;
}
.calibration-bar {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 5px;
  background: var(--sport-accent);
}
.calibration-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 2px;
  background: var(--text);
  /* halo separates the notch from the fill it may sit on */
  box-shadow: 0 0 0 1px var(--surface-2);
}
.calibration-value { font-size: 0.85rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.calibration-n { font-weight: 500; color: var(--text-faint); font-size: var(--fs-meta); }
.calibration-legend { font-size: var(--fs-meta); color: var(--text-faint); margin: var(--sp-2) 0 0; }

/* Record page: the favorite's hits and misses slate by slate (site review 2026-10-11). Each column
   is a link to its slate; its height is that slate's graded games against the busiest slate's,
   hits stacked under misses. */
.slate-chart {
  display: flex;
  width: fit-content;
  align-items: stretch;
  gap: var(--sp-2);
  max-width: min(720px, 100%);
  padding: var(--sp-4) var(--sp-4) var(--sp-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow-x: auto;
}
.slate-col {
  flex: 0 0 2.75rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  color: inherit;
  text-decoration: none;
  border-radius: 8px;
  padding: 0.2rem 0.1rem;
  transition: background-color var(--dur-1) ease;
}
.slate-col:hover { background: var(--surface-3); }
.slate-col-track { height: 120px; width: 100%; max-width: 2.25rem; display: flex; align-items: flex-end; }
.slate-col-stack { width: 100%; display: flex; flex-direction: column; border-radius: 5px; overflow: hidden; min-height: 4px; }
.slate-col-hit { background: color-mix(in srgb, var(--win) 78%, var(--surface-2)); min-height: 0; }
.slate-col-miss { background: color-mix(in srgb, var(--lose) 55%, var(--surface-2)); min-height: 0; }
.slate-col-hit, .slate-col-miss { flex-basis: 0; }
.slate-col-value { font-size: var(--fs-meta); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.slate-col-label { font-size: 0.66rem; font-weight: 600; color: var(--text-faint); white-space: nowrap; }
.key { display: inline-block; width: 0.6rem; height: 0.6rem; border-radius: 2px; vertical-align: -0.05rem; margin-right: 0.2rem; }
.key--hit { background: color-mix(in srgb, var(--win) 78%, var(--surface-2)); }
.key--miss { background: color-mix(in srgb, var(--lose) 55%, var(--surface-2)); }
@media (max-width: 640px) {
  .slate-chart { gap: var(--sp-1); padding: var(--sp-3) var(--sp-2) var(--sp-2); }
  /* a phone shows the latest seven slates rather than scrolling the newest off-screen */
  .slate-col:nth-last-child(n+8) { display: none; }
  .slate-col-track { height: 96px; }
  .slate-col-value { font-size: 0.62rem; }
}

/* overview: each sport's win rate as a small bar in its own hue, with the coin-flip line marked */
.rate-cell { display: inline-flex; align-items: center; gap: var(--sp-2); }
.rate-meter { position: relative; width: 3.5rem; height: 5px; border-radius: 3px; background: var(--surface-4); overflow: hidden; }
.rate-meter > span { position: absolute; inset: 0 auto 0 0; background: var(--sport-accent); border-radius: 3px; }
.rate-meter::after { content: ""; position: absolute; left: calc(50% - 1px); top: 0; bottom: 0; width: 2px; background: var(--surface-1); }
@media (max-width: 640px) { .rate-meter { display: none; } }
/* each row wears its sport's hue in the bar only; the row links stay quiet */
.results-table .view-slate-link { color: var(--text-dim); }
.results-table .view-slate-link:hover { color: var(--text); }

/* the graded list past its two newest slates */
.results-more { margin-top: var(--sp-3); }
.results-more > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.results-more > summary::-webkit-details-marker { display: none; }
.results-more > summary::after { content: "▾"; font-size: 0.75rem; transition: rotate var(--dur-1) var(--ease-out); }
.results-more[open] > summary::after { rotate: 180deg; }
.results-more > summary:hover { color: var(--text); border-color: var(--border-strong); }
.results-more[open] > summary { margin-bottom: var(--sp-3); }

.result-pill {
  display: inline-block;
  padding: 0.1rem var(--sp-2);
  border-radius: var(--radius-pill);
  font-size: var(--fs-meta);
  font-weight: 700;
}
.result-pill--hit { background: color-mix(in srgb, var(--win) 18%, transparent); color: var(--win); }
.result-pill--miss { background: color-mix(in srgb, var(--lose) 18%, transparent); color: color-mix(in srgb, var(--lose) 80%, white); }
/* 11px bold on its own 18% tint: dark "miss" was 4.1:1 and light "hit" 4.2:1 */
/* light theme, chosen with the header switch (dark is the default) */
  :where(html[data-theme="light"]) .result-pill--hit { color: color-mix(in srgb, var(--win) 80%, black); }
  :where(html[data-theme="light"]) .result-pill--miss { color: var(--lose); }

.results-table td a:hover { text-decoration: underline; }
.results-table .mkt-side { white-space: normal; }
.mkt-side-final { display: block; font-size: var(--fs-meta); font-weight: 500; color: var(--text-faint); }

/* ---------- model vs market strip ---------- */

.mkt-strip {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border);
  font-size: 0.8rem;
}
.mkt-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--sp-3);
  padding: 0.15rem 0;
  align-items: baseline;
}
.mkt-row--head {
  font-size: var(--fs-meta);  /* was 9.6px across 158 NFL/tennis card headers */
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.mkt-label {
  color: var(--text-dim);
  font-weight: 600;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mkt-value {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--text);
  min-width: 4.5rem;
  text-align: right;
}
.mkt-row--head span { min-width: 4.5rem; text-align: right; }
/* ---------- model vs market page (/lines): one comparison table per sport ---------- */

.mk-kind { text-decoration: none; font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.04em; color: var(--text-faint); }
.market-switch { display: flex; gap: var(--sp-1); margin: 0 0 var(--sp-3); }
.record-tabs { display: flex; gap: var(--sp-1); margin-top: var(--sp-4); border-bottom: 1px solid var(--border); }
.record-tab {
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: -1px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.record-tab:hover { color: var(--text); }
.record-tab.is-active { color: var(--text); border-bottom-color: var(--accent); }
.record-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.lines-controls { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-3); margin: 0 0 var(--sp-3); }
.lines-filter, .lines-sort { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.lines-chip {
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  white-space: nowrap;
}
.lines-chip:hover { color: var(--text); border-color: var(--border-strong); }
.lines-chip.is-active { color: var(--text); background: var(--surface-3, var(--surface-2)); border-color: var(--border-strong); }
.lines-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lines-coverage { color: var(--text-dim); margin: 0 0 var(--sp-4); font-size: 0.85rem; }
.lines-section { margin-bottom: var(--sp-6); }
.lines-heading { display: flex; align-items: baseline; gap: var(--sp-3); font-size: 1rem; margin: 0 0 var(--sp-2); }
.lines-count { font-size: var(--fs-meta); font-weight: 600; color: var(--text-faint); }
.lines-table-wrap {
  position: relative;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.lines-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
/* numbers in even columns beside the game, not stretched across the width */
@media (min-width: 561px) {
  .lines-table .lt-game { width: 34%; }
  .lines-table .lt-num { width: 16%; }
  .lines-table .lt-gap-col { width: auto; }
}
.lines-table thead th {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.lines-table tbody tr + tr { border-top: 1px solid var(--border); }
.lines-table tbody tr:hover { background: color-mix(in srgb, var(--text) 3%, transparent); }
.lines-table td, .lines-table tbody th { padding: var(--sp-2) var(--sp-3); vertical-align: middle; }
.lines-table .lt-num { text-align: right; }
.lt-game { text-align: left; font-weight: 400; min-width: 0; }
.lt-matchup { display: block; font-weight: 700; color: var(--text); text-decoration: none; white-space: nowrap; }
.lt-matchup:hover { text-decoration: underline; }
.lt-when { display: block; font-size: var(--fs-meta); color: var(--text-faint); white-space: nowrap; }
.lt-model, .lt-mkt { display: block; font-variant-numeric: tabular-nums; white-space: nowrap; }
.lt-model { font-weight: 700; color: var(--text); }
.lt-mkt { font-size: var(--fs-meta); color: var(--text-faint); }
.lt-mkt::before { content: "mkt "; letter-spacing: 0.04em; }
.lt-gap { font-size: var(--fs-meta); color: var(--text-dim); font-weight: 600; }
.lt-nil { color: var(--text-faint); }
.lt-none { color: var(--text-faint); font-size: var(--fs-meta); text-align: right; }
.lines-source { margin: var(--sp-2) 0 0; font-size: var(--fs-meta); color: var(--text-faint); }
/* phones: each game is a block -- its name across the top, then its markets side by side, each
   labelled (a 4-5 column table ran off a 375px screen); the gap column folds away (the sort still
   uses it) */
@media (max-width: 560px) {
  .lines-table thead { display: none; }
  .lines-table, .lines-table tbody { display: block; }
  .lines-table tbody tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
  .lines-table tbody th.lt-game { grid-column: 1 / -1; padding: 0 0 var(--sp-1); display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
  .lt-matchup, .lt-when { display: inline; }
  .lines-table td { padding: 0; text-align: left; }
  .lines-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: var(--fs-meta);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
  }
  .lt-gap-col { display: none; }
  .lt-none { grid-column: 1 / -1; text-align: left; }
  .lines-controls { flex-direction: column; }
}

/* the game sheet's Market view: cards in the Factors view's panel grammar (site review 2026-10-11) */
.ps-mgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--sp-3); }
.ps-mcard {
  min-width: 0;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius-card) - 4px);
}
.ps-mcard-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.ps-mcard-title { font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); }
.ps-mlines { margin: 0; display: grid; }
.ps-mlines > div { display: flex; justify-content: space-between; gap: var(--sp-2); padding: 0.35rem 0; border-top: 1px solid var(--border); font-size: 0.85rem; }
.ps-mlines dt { color: var(--text-dim); }
.ps-mlines dd { margin: 0; font-weight: 600; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.ps-mlines .is-model { border-top: 0; }
.ps-mlines .is-model dt, .ps-mlines .is-model dd { color: var(--text); font-weight: 700; }
.ps-mprice { margin: var(--sp-1) 0 0; font-size: var(--fs-meta); color: var(--text-faint); font-variant-numeric: tabular-nums; }
.ps-mbet { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.ps-mbet-side { font-size: 1.15rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; margin-right: auto; }
.ps-mbet-units { font-variant-numeric: tabular-nums; }
.ps-mfacts { margin: 0; display: grid; }
.ps-mfacts > div { display: grid; grid-template-columns: 3.75rem 1fr; gap: var(--sp-2); padding: 0.35rem 0; border-top: 1px solid var(--border); font-size: 0.8rem; }
.ps-mfacts dt { color: var(--text-faint); font-weight: 600; }
.ps-mfacts dd { margin: 0; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.ps-mfacts > div:first-child dd { color: var(--text); font-weight: 600; }
.ps-msources { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: 0; font-size: var(--fs-meta); }
.ps-msources > div { display: flex; gap: var(--sp-1); }
.ps-msources dt { color: var(--text-faint); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.ps-msources dd { margin: 0; color: var(--text-dim); }
/* status pills, shared by the Market view (and anywhere a settled result needs one) */
.pill {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem var(--sp-2);
  border-radius: var(--radius-pill);
  font-size: var(--fs-meta);
  font-weight: 700;
  white-space: nowrap;
  border: 1px solid transparent;
}
.pill--win { background: color-mix(in srgb, var(--win) 16%, transparent); color: var(--win); }
.pill--loss { background: color-mix(in srgb, var(--lose) 16%, transparent); color: var(--lose); }
.pill--push, .pill--void { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text-dim); }
.pill--quiet { border-color: var(--border); color: var(--text-dim); font-weight: 600; font-variant-numeric: tabular-nums; }
.ps-side-score--final { display: inline-flex; flex-direction: column; align-items: center; line-height: 1.1; }
.ps-side-proj { font-size: var(--fs-meta); font-weight: 600; color: var(--text-faint); letter-spacing: 0; }
.ps-mresult-main { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); margin: 0 0 var(--sp-1); font-size: 1rem; }
.ps-mresult-side { font-weight: 700; font-variant-numeric: tabular-nums; }
.ps-mresult--win .ps-mresult-verdict { color: var(--win); }
.ps-mresult--loss .ps-mresult-verdict { color: var(--lose); }
.mkt-details { margin-top: var(--sp-1); font-size: var(--fs-meta); color: var(--text-faint); }
.mkt-details summary { cursor: pointer; text-align: right; list-style-position: inside; }
.mkt-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.mkt-details ul { margin: var(--sp-1) 0 0; padding: 0; list-style: none; color: var(--text-dim); }
.mkt-details li { padding: 0.1rem 0; font-variant-numeric: tabular-nums; }
.mkt-details li span { font-weight: 600; color: var(--text); }
.mkt-note {
  margin-top: 0.15rem;
  font-size: var(--fs-meta);
  color: var(--text-faint);
  text-align: right;
}

/* ---------- props ---------- */

/* disclosure chips sit side by side; an opened panel takes the full row */
.card-disclosures {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.card-disclosures > details[open] { flex-basis: 100%; }

.props-toggle{
  margin-top: 0;
}
.props-toggle summary{
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  transition: color 150ms ease, border-color 150ms ease;
}
.props-toggle summary::-webkit-details-marker{ display: none; }
.props-toggle summary::after{
  content: "▾";
  font-size: 0.65rem;
  transition: transform 150ms ease;
}
.props-toggle[open] summary::after{ transform: rotate(180deg); }
.props-toggle summary:hover{ color: var(--text); border-color: var(--border-strong); }

/* Smooth expand/collapse -- pure progressive enhancement: browsers with
   ::details-content + interpolate-size (Chrome 129+) animate the height;
   everything else keeps the instant toggle it has today. */
.props-toggle{ interpolate-size: allow-keywords; }
.props-toggle::details-content{
  block-size: 0;
  overflow-y: clip;
  transition: content-visibility var(--dur-2) allow-discrete, block-size var(--dur-2) var(--ease-out);
}
.props-toggle[open]::details-content{ block-size: auto; }

.stat-lines-panel {
  margin-top: var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.stat-lines-section {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.stat-lines-title {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2);
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}

.stat-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface-3);
}

.stat-line-name {
  min-width: 0;
  flex-shrink: 0;
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat-line-team {
  font-weight: 500;
  color: var(--text-faint);
  font-size: var(--fs-meta);
}

.stat-line-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
}
.stat-line-item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  font-variant-numeric: tabular-nums;
}
.stat-line-abbrev {
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
}
.stat-line-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--text);
}

.empty-state {
  color: var(--text-dim);
  font-style: italic;
  padding: var(--sp-6) 0;
}

/* ---------- view toggle + leaderboard ---------- */

.view-toggle {
  display: inline-flex;
  gap: var(--sp-1);
  margin-bottom: var(--sp-4);
  padding: 0.2rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.view-toggle-btn {
  font-family: var(--font-body);
  padding: 0.5rem var(--sp-4);
  border-radius: var(--radius-pill);
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: color 150ms ease, background 150ms ease;
}
.view-toggle-btn:hover { color: var(--text); }
.view-toggle-btn.active {
  /* neutral raised segment (iOS/Linear style) -- a saturated accent fill
     here outshouted the page title and spent the sport hue on a tab
     (design review); the accent stays reserved for data */
  color: var(--text);
  background: var(--surface-4);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* ---------- marquee prop board ---------- */

/* Home rails: a compact one-line teaser that links into the sport. */
.board-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  font-size: 0.8rem;
  color: var(--text-dim);
  transition: border-color var(--dur-1) ease;
}
.board-strip:hover { border-color: color-mix(in srgb, var(--sport-accent) 45%, var(--border)); }
.board-strip-label {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sport-ink);
}
.board-strip-item { white-space: nowrap; }
.board-strip-item + .board-strip-item { border-left: 1px solid var(--border); padding-left: var(--sp-3); }
.board-strip-item strong { color: var(--text); font-variant-numeric: tabular-nums; }
.board-strip-more { margin-left: auto; color: var(--sport-ink); font-weight: 700; }
/* a phone: the label on its own line, the names in a tidy two-column grid (four name/% pairs
   wrapped raggedly across three lines, each line led by a stray divider) */
@media (max-width: 640px) {
  .board-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-1) var(--sp-4); position: relative; padding: var(--sp-3) var(--sp-4); }
  .board-strip-label { grid-column: 1 / -1; margin-bottom: 0.1rem; }
  .board-strip-item { display: flex; justify-content: space-between; gap: var(--sp-2); min-width: 0; }
  .board-strip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .board-strip-item + .board-strip-item { border-left: 0; padding-left: 0; }
  .board-strip-more { position: absolute; top: var(--sp-3); right: var(--sp-4); }
}

/* Players tab: a real ranked card, not a wrapping text strip. */
.board-card {
  max-width: 420px;
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}
.board-card-title {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sport-ink);
}
/* `display:flex` on an <li> suppresses the list marker, so the rank comes
   from a counter instead (the numbers were simply missing otherwise). */
.board-card-list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: board-rank;
}
.board-card-list li {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: 0.15rem 0;
  font-size: 0.85rem;
  color: var(--text-dim);
  counter-increment: board-rank;
}
.board-card-list li::before {
  content: counter(board-rank);
  min-width: 1rem;
  font-size: var(--fs-meta);
  font-weight: 700;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.board-card-list .bcl-name { flex: 1 1 auto; }
.bcl-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bcl-value { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

/* ---------- leaderboard toolbar + market picker ---------- */

.lb-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.lb-field { display: inline-flex; flex-direction: column; gap: 0.2rem; }
.lb-field-label {
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.lb-section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-2);
}
.lb-section-head .leaderboard-title { margin: 0; }

.market-picker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
}
.market-picker-label {
  margin-right: var(--sp-1);
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.market-chip {
  font-family: var(--font-body);
  padding: 0.25rem var(--sp-2);
  border-radius: var(--radius-pill);
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: var(--fs-meta);
  font-weight: 600;
  cursor: pointer;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.market-chip:hover { color: var(--text); border-color: var(--border-strong); }
.market-chip.active {
  /* the neutral raised segment the view toggle and props tabs use: black on the
     sport hue was 3.3-4.1:1 (MLB, CFB, NHL) */
  color: var(--text);
  background: var(--surface-4);
  border-color: var(--border-strong);
}

/* ---------- leaderboard extras ---------- */

.lb-headshot {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-3);
  vertical-align: middle;
  margin-right: var(--sp-2);
}
.lb-headshot--missing { display: none; }

/* player names open their projection history */
.player-history-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.player-history-btn:hover, .player-history-btn.open { color: var(--sport-ink); }

tr.history-tr td {
  background: var(--surface-3);
  font-weight: 400;
  padding: var(--sp-3);
}
.player-history-empty { margin: 0; color: var(--text-faint); font-size: 0.85rem; }

.player-search {
  display: block;
  width: 100%;
  max-width: 320px;
  height: 2.25rem;
  margin-bottom: var(--sp-3);
  padding: 0 var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  /* the border is the only thing that says "type here": --border was 1.4:1 dark, 1.2-1.4:1 light,
     under the 3:1 a control boundary needs (WCAG 1.4.11) */
  border: 1px solid var(--text-faint);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.85rem;
}
.player-search::placeholder { color: var(--text-faint); }
.player-search:focus { border-color: var(--sport-ink); }

.leaderboard-section { margin-bottom: var(--sp-6); }
.leaderboard-section:last-child { margin-bottom: 0; }
.leaderboard-title {
  margin: 0 0 var(--sp-2);
  padding-bottom: var(--sp-1);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}

.leaderboard-table-wrap {
  overflow-x: auto;
  /* the containing block for the table's screen-reader-only labels (position: absolute): without it
     they sat at their place in the wide table relative to the PAGE, and /results on a phone could be
     panned 300px sideways past the edge (site review 2026-10-11) */
  position: relative;
  border: 1px solid var(--border);
  border-radius: 10px;
  /* Scroll-shadow affordance: soft inner shadows appear at whichever
     horizontal edge still has content, fading as you reach it
     (background-attachment: local trick -- the white "cover" layers
     scroll with the content and mask the fixed shadow layers). */
  background:
    linear-gradient(90deg, var(--surface-2) 30%, transparent) local,
    linear-gradient(270deg, var(--surface-2) 30%, transparent) local,
    linear-gradient(90deg, var(--scroll-shadow), transparent 14px) scroll,
    linear-gradient(270deg, var(--scroll-shadow), transparent 14px) scroll,
    var(--surface-2);
  background-repeat: no-repeat;
  background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%, auto;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
.leaderboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  white-space: nowrap;
}
.leaderboard-table th, .leaderboard-table td {
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
}
.leaderboard-table th.num, .leaderboard-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.leaderboard-table thead th {
  background: var(--surface-2);
  color: var(--text-faint);
  font-size: var(--fs-meta);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
}
.leaderboard-table th.sortable {
  cursor: pointer;
  user-select: none;
  transition: color 150ms ease;
}
.leaderboard-table th.sortable:hover { color: var(--text); }
/* the header's label is a real button so keyboard users can sort too */
.th-sort {
  all: unset;
  cursor: pointer;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.th-sort:focus-visible { outline: 2px solid var(--sport-ink); outline-offset: 2px; border-radius: 4px; }
.leaderboard-table th.sortable::after { content: ""; margin-left: 0.3rem; opacity: 0.4; }
.leaderboard-table th.sort-desc::after { content: "▾"; opacity: 1; color: var(--accent); }
.leaderboard-table th.sort-asc::after { content: "▴"; opacity: 1; color: var(--accent); }
.leaderboard-table tbody tr { border-bottom: 1px solid var(--border); }
.leaderboard-table tbody tr:last-child { border-bottom: none; }
/* Borders, not zebra stripes -- and a row highlight on hover. (Zebra
   also fought the sticky first column, which needs its own opaque
   background per row state.) */
.leaderboard-table tbody tr:hover td { background: var(--surface-3); }
.leaderboard-table td { color: var(--text); font-weight: 600; }
/* Sticky identity column so horizontal scroll (mobile especially) never
   loses who a row belongs to. Opaque backgrounds required -- sticky
   cells slide over their siblings. */
.leaderboard-table th:first-child,
.leaderboard-table td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface-2);
}
.leaderboard-table thead th:first-child { background: var(--surface-2); z-index: 2; }
.leaderboard-table tbody tr:hover td:first-child { background: var(--surface-3); }
.leaderboard-matchup { color: var(--text-faint); font-weight: 500; }
.leaderboard-table td.num { font-weight: 700; }

/* ---------- fantasy tab ---------- */
.fantasy-omitted-note {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-meta);
  color: var(--text-faint);
}
.leaderboard-table td.fantasy-points { color: var(--accent); font-weight: 800; }
.ph-wrap { overflow-x: auto; }
.player-history .ph-table { border-collapse: collapse; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.player-history .ph-table thead th {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: right;
  vertical-align: bottom;
  padding: 0 var(--sp-1) var(--sp-1) 0.4rem;
  line-height: 1.2;
}
.player-history .ph-table td { text-align: right; padding: 0.2rem var(--sp-1) 0.2rem 0.4rem; vertical-align: top; white-space: nowrap; }
.player-history .ph-table tbody tr { border-bottom: 1px solid var(--border); }
.player-history .ph-table tbody tr:last-child { border-bottom: 0; }
.player-history .ph-table tbody th.ph-slate { text-align: left; font-weight: 400; padding: 0.2rem var(--sp-2) 0.2rem 0; white-space: nowrap; }
.ph-proj { display: block; font-weight: 700; color: var(--text); }
.ph-act { display: block; font-size: var(--fs-meta); color: var(--text-faint); }
.ph-nil { color: var(--text-faint); }
.ph-legend { margin: var(--sp-1) 0 0; font-size: var(--fs-meta); color: var(--text-faint); }
.ph-date {
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-dim);
}
.ph-date:hover { color: var(--sport-ink); text-decoration: underline; }
/* the history panel sits inside a leaderboard cell: undo the leaderboard's own cell styles
   (sticky first column, opaque backgrounds, bottom rules) for its table */
.player-history .ph-table tbody tr > th,
.player-history .ph-table tbody tr > td,
.player-history .ph-table thead tr > th {
  position: static;
  background: transparent;
  border-bottom: 0;
  font-weight: 400;
}
.player-history .ph-table thead tr > th { font-weight: 700; white-space: normal; border-bottom: 1px solid var(--border); }

/* ---------- login ---------- */

.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-5);
}
/* the header's gold lockup, centered -- the first screen a visitor sees
   should carry the brand, not a muted text fallback */
.login-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
}
.login-brand .brand-mark { width: 26px; height: 26px; }
.login-box {
  width: 100%;
  max-width: 320px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--sp-6) var(--sp-5);
}
.login-box h1 {
  margin: 0 0 var(--sp-4);
  font-size: 1.5rem;
}
.login-box input {
  width: 100%;
  padding: var(--sp-3);
  margin: var(--sp-2) 0 var(--sp-4);
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-0);
  color: var(--text);
  font-size: var(--fs-body);
  transition: border-color 150ms ease;
}
.login-box input:focus {
  border-color: var(--accent);
}
.login-box button {
  width: 100%;
  padding: 0.65rem;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: #000;
  font-weight: 700;
  font-size: var(--fs-body);
  cursor: pointer;
  transition: background 150ms ease, transform 150ms ease;
}
.login-box button:hover { background: var(--accent-2); }
.login-box button:active { transform: scale(0.98); }
.login-error { margin: 0 0 var(--sp-3); color: var(--lose); font-size: 0.85rem; }

/* light theme, chosen with the header switch (dark is the default) */
  :root[data-theme="light"] {
    /* deeper warm ground so white cards actually lift off it -- pure
       #f7f6f3 vs #fff gave ~2% figure/ground separation and the grid
       read as one flat field (design review) */
    --surface-0: #efece5;
    --surface-1: #ffffff;
    --surface-2: #ffffff;
    --surface-3: #f1efe9;
    --surface-4: #e2ded4;
    --border: #e0dcd2;
    --border-strong: #c9c3b4;
    --text: #211f1a;
    /* AA-checked light ramp: dim 6.8:1, faint 4.6:1 (the old faint
       measured 2.95:1 while carrying the win% labels) */
    --text-dim: #5c554a;
    --text-faint: #6c6558;  /* 4.9:1 on the page ground (was #736c5e, 4.4:1) */
    --favorite: #14130f;
    --underdog: #6b6357;
    /* Darker/deeper than the dark-theme gold pair -- the same hue tuned
       for a near-black panel reads as too light/washed-out on a white one
       (measured ~1.8:1 contrast, well under WCAG AA's 4.5:1 for text). A
       deep bronze holds the same "premium" read while staying legible.
       accent-2 is DARKER than accent here (the inverse of dark mode,
       where it's lighter) -- .past-slate-banner uses accent-2 as text on
       a light accent-tinted wash, and the lighter dark-mode-style value
       fails contrast there (measured ~3.1:1) while this passes (~7.6:1). */
    --accent: #96701f;
    --accent-2: #6b4f16;
    --sport-ink: var(--accent-2);  /* the light --accent is 3.4:1 as small text */
    --shadow-card-hover: 0 2px 4px rgba(20, 18, 14, 0.10), 0 12px 28px -12px rgba(20, 18, 14, 0.22);
    --scroll-shadow: rgba(20, 18, 14, 0.14);
    /* the dark theme's neon pair measured 1.9:1 (win) and 3.4:1 (lose) as
       text on white -- the results page's hit/miss column was unreadable */
    --win: #1c7a47;
    --lose: #b3363c;
    --shadow-card: 0 1px 2px rgba(20, 18, 14, 0.08), 0 8px 24px -12px rgba(20, 18, 14, 0.16);
  }

  :where(html[data-theme="light"]) body {
    /* same single soft wash as dark, one notch fainter on the light ground */
    background-image:
      radial-gradient(900px 420px at 50% -120px, color-mix(in srgb, var(--sport-accent) 4%, transparent), transparent 68%);
  }

  /* the favorite's wash nearly vanished on white cards -- give it a real
     tinted ground in light mode */
  :where(html[data-theme="light"]) .team-row.is-favorite {
    background: #f2efe7;
    border-color: var(--border-strong);
  }

  /* Tinted chips mix their text toward WHITE for the dark theme; on a
     light wash that fails contrast, so re-mix toward black here. (The
     generated .sport-badge rules get the same treatment in base.html.) */
  :where(html[data-theme="light"]) .meta-chip--warn { color: color-mix(in srgb, var(--lose) 55%, black); }
  :where(html[data-theme="light"]) .meta-chip--accent { color: color-mix(in srgb, var(--sport-accent) 55%, black); }
  :where(html[data-theme="light"]) .meta-chip.surface-clay { color: color-mix(in srgb, #b0592e 70%, black); }
  :where(html[data-theme="light"]) .meta-chip.surface-grass { color: color-mix(in srgb, #3e8e52 70%, black); }
  :where(html[data-theme="light"]) .meta-chip.surface-hard { color: color-mix(in srgb, #2f6ea8 70%, black); }


/* Up to 1000px the header stays ONE row: the sport tabs scroll sideways (the phone rail) instead
   of wrapping. Between 641 and 1000px they wrapped the header to two or three rows (126-180px,
   measured 2026-10-11) -- deeper than scroll-padding-top, so a #g- game link landed underneath. */
@media (max-width: 1000px) {
  header.site-header { flex-wrap: nowrap; }
  nav.sport-nav {
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    padding: 2px;
    margin: -2px;
    mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  }
  nav.sport-nav::-webkit-scrollbar { display: none; }
  nav.sport-nav a { scroll-snap-align: center; flex-shrink: 0; }
  .nav-utility { flex-shrink: 0; }
}

/* Phones and small tablets: the sport nav becomes a horizontally-
   scrolling pill rail, the header stops wrapping, and the desktop-scale
   type steps down. (These used to live at 480px, leaving the 481-640px
   band with desktop values in a single-column layout -- design review.) */
@media (max-width: 640px) {
  header.site-header {
    flex-wrap: nowrap;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
  }
  nav.sport-nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    /* let the pills' focus rings breathe inside the scroller */
    padding: 2px;
    margin: -2px;
    /* fade the cut edge so it's obvious more sports live off-screen */
    mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  }
  nav.sport-nav::-webkit-scrollbar { display: none; }
  nav.sport-nav a {
    scroll-snap-align: center;
    flex-shrink: 0;
  }
  main { padding: var(--sp-4); }
  /* the mark alone identifies the site; the wordmark doesn't fit next
     to a scrollable 8-tab rail */
  .brand-name { display: none; }
  .brand-mark { width: 26px; height: 26px; }
  .page-hero-slate { font-size: 2rem; }
  .view-slate-link { margin-left: 0; }
}

@media (max-width: 480px) {
  .game-grid { grid-template-columns: 1fr; }
}
/* Home previews on a phone: one swipeable row per sport, the next card peeking in, instead of four
   full-width cards stacked per sport (24 cards, a dozen screens, on a busy Sunday; site review
   2026-10-11). The row bleeds to the screen edges so a card can slide all the way across. */
@media (max-width: 640px) {
  .mini-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    gap: var(--sp-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    margin-inline: calc(var(--sp-4) * -1);
    padding: 2px var(--sp-4) var(--sp-1);
    scroll-padding-inline: var(--sp-4);
    scrollbar-width: none;
  }
  .mini-grid::-webkit-scrollbar { display: none; }
  .mini-grid > .mini-card { scroll-snap-align: start; }
  /* a single preview takes the whole width rather than leaving 18% of empty track */
  .mini-grid:has(> .mini-card:only-child) { grid-auto-columns: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    /* the staggered card entrances hid each card for up to 315ms, then popped it in */
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Signed prop bias on the results page. The emphasis deliberately encodes
   "is this bias real or noise", NOT "good or bad": a bias inside +/-0.05 is
   dimmed as noise, anything larger is shown at full text weight. No red or
   green -- direction is already carried by the +/- sign, and this site
   shows projections, not bets (REDESIGN_PLAN decision #2), so colour-coding
   a direction as good or bad would be editorialising a number. */
.prop-bias-real { color: var(--text); font-variant-numeric: tabular-nums; }
/* qualified by the table: `.leaderboard-table td` (0,1,1) set every cell's colour, so this rule
   never won and the noise/real emphasis never rendered (site audit 2026-10-02) */
.leaderboard-table td.prop-bias-noise { color: var(--text-faint); font-weight: 500; font-variant-numeric: tabular-nums; }

/* ---------- player props sheet (2026-09-30) ----------
   A game card opens one dialog of that game's props (templates/_props_sheet.html,
   static/props_sheet.js) in place of the card's long stat-line disclosure. Flat surfaces
   and the site's own tokens only -- both themes follow from them. The sport accent is spent
   in exactly one place: the column the table is sorted by. */

html.ps-lock { overflow: hidden; }
/* the sheet's filters toggle the `hidden` attribute; a component's own display (flex rows, the
   inline-flex "Show more") would otherwise override it and leave filtered rows on screen */
.props-sheet [hidden] { display: none !important; }

.game-card--sheet { cursor: pointer; }
.props-open {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  margin-top: var(--sp-3);
  padding: 0.6rem var(--sp-3);
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface-3);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  transition: border-color var(--dur-1) ease, background var(--dur-1) ease;
}
/* Player props and Factors stack as two identical full-width rows at the foot of the card --
   side by side, a 3-column card squeezed "50 players" and the arrows past the button edge */
/* the card's entry points into its sheet: one row of equal compact buttons (they were stacked,
   full-width, two to a card) */
.props-open-row { display: flex; flex-direction: row; gap: var(--sp-2); margin-top: var(--sp-3); }
.props-open-row .props-open { margin-top: 0; white-space: nowrap; flex: 1 1 0; justify-content: center; min-width: 0; }
.props-open-row .props-open-count { margin-left: var(--sp-1); }
.props-open-label { font-weight: 600; }
.props-open-count { margin-left: auto; color: var(--text-dim); font-size: var(--fs-label); }
.props-open-icon { width: 14px; height: 14px; color: var(--text-dim); flex-shrink: 0; transition: transform var(--dur-1) var(--ease-out); }
.props-open:hover, .game-card--sheet:hover .props-open { border-color: var(--border-strong); background: var(--surface-4); }
.props-open:hover .props-open-icon, .game-card--sheet:hover .props-open-icon { transform: translateX(2px); color: var(--text); }

@keyframes ps-in { from { opacity: 0; transform: translateY(10px) scale(0.985); } }
@keyframes ps-up { from { transform: translateY(100%); } }
@keyframes ps-fade { from { opacity: 0; } }

.props-sheet {
  width: min(920px, calc(100vw - 2 * var(--sp-5)));
  height: min(860px, calc(100dvh - 2 * var(--sp-6)));
  max-width: none;
  max-height: none;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  background: var(--surface-1);
  color: var(--text);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.75);
  overflow: hidden;
}
.props-sheet[open] { display: flex; flex-direction: column; animation: ps-in var(--dur-2) var(--ease-out); }
.props-sheet::backdrop { background: rgba(6, 6, 8, 0.66); animation: ps-fade var(--dur-2) ease; }
.ps-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* head: title row, the matchup, then the two controls -- all fixed; only the body scrolls */
.ps-head {
  flex: none;
  padding: var(--sp-4) var(--sp-5) var(--sp-3);
  border-bottom: 1px solid var(--border);
  background: var(--surface-1);
}
.ps-head-top { display: flex; align-items: center; gap: var(--sp-3); }
.ps-title {
  margin: 0;
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.ps-kickoff { font-size: var(--fs-label); font-weight: 600; color: var(--text-dim); }
/* with two views the switch below stands in for the heading, which stays for screen readers */
.ps-title--hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* the view switch reads as the eyebrow it replaces: same type, the current view in full text
   colour over a neutral rule -- the accent stays on the sorted column alone */
.ps-views { display: inline-flex; gap: var(--sp-4); }
.ps-view {
  white-space: nowrap;
  padding: 0.35rem 0;
  border: none;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--text-faint);
  font: inherit;
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.ps-view:hover { color: var(--text-dim); }
.ps-view[aria-pressed="true"] { color: var(--text); border-bottom-color: var(--text); }
.ps-kickoff:empty { display: none; }
.ps-close {
  margin-left: auto;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-dim);
  cursor: pointer;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease, background var(--dur-1) ease;
}
.ps-close svg { width: 16px; height: 16px; }
.ps-close:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-3); }

.ps-matchup {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-4);
  margin: var(--sp-3) 0 var(--sp-4);
}
.ps-side { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.ps-side:last-child { flex-direction: row-reverse; text-align: right; }
.ps-side-logo { width: 40px; height: 40px; object-fit: contain; flex-shrink: 0; }
.ps-side-text { display: flex; flex-direction: column; min-width: 0; }
.ps-side-name {
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-weight: 700;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ps-side-meta { font-size: var(--fs-label); color: var(--text-dim); }
.ps-side-score {
  margin-left: auto;
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
  color: var(--underdog);
}
.ps-side:last-child .ps-side-score { margin-left: 0; margin-right: auto; }
.ps-side.is-favorite .ps-side-score { color: var(--favorite); }
.ps-at { color: var(--text-faint); font-weight: 600; }

.ps-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.ps-tabs, .ps-teams {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.ps-tab, .ps-team {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 34px;
  padding: 0 0.85rem;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: color var(--dur-1) ease, background var(--dur-1) ease;
}
.ps-tab:hover, .ps-team:hover { color: var(--text); }
.ps-tab[aria-selected="true"], .ps-team[aria-pressed="true"] {
  color: var(--text);
  background: var(--surface-4);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.ps-count { font-size: var(--fs-meta); font-weight: 600; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.ps-tab[aria-selected="true"] .ps-count { color: var(--text-dim); }
.ps-team-logo { width: 18px; height: 18px; object-fit: contain; }

/* body: the only scrolling region */
.ps-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
  /* gap, not sibling margins: a filtered-out (hidden) section takes no gap, where a
     ".ps-section + .ps-section" margin left an empty band above the first visible table */
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.ps-section-title {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ps-single .ps-section .ps-section-title { display: none; }

.ps-table-wrap {
  position: relative;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}
.ps-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.875rem; white-space: nowrap; }
.ps-table th, .ps-table td { padding: 0.55rem var(--sp-3); }
.ps-table thead th, .ps-table thead td {
  background: var(--surface-2);
  color: var(--text-faint);
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.ps-group-row th { text-align: center; padding-bottom: 0.2rem; padding-top: 0.6rem; }
.ps-group { position: relative; }
/* a hairline under each group label, inset, reads as a bracket over its columns */
.ps-group::after {
  content: "";
  position: absolute;
  left: var(--sp-3);
  right: var(--sp-3);
  bottom: 0;
  height: 1px;
  background: var(--border-strong);
}
.ps-col-row th { border-bottom: 1px solid var(--border); padding-top: 0.35rem; }
.ps-group--first, .ps-group-start { border-left: 1px solid var(--border); }
.ps-num { text-align: right; font-variant-numeric: tabular-nums; }
.ps-sort {
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0.15rem 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
}
.ps-sort:hover { color: var(--text); }
.ps-sort::after { content: "▾"; font-size: 0.7rem; opacity: 0; }
th[aria-sort] .ps-sort { color: var(--sport-ink); }
th[aria-sort] .ps-sort::after { opacity: 1; }
th[aria-sort="ascending"] .ps-sort::after { content: "▴"; }

.ps-table tbody th, .ps-table tbody td { border-bottom: 1px solid var(--border); }
.ps-table tbody tr:last-child th, .ps-table tbody tr:last-child td { border-bottom: none; }
.ps-table tbody td { color: var(--text-dim); font-weight: 500; }
.ps-table tbody td.is-sorted {
  color: var(--text);
  font-weight: 700;
  background: color-mix(in srgb, var(--sport-accent) 7%, var(--surface-2));
}
.ps-table tbody tr:hover th, .ps-table tbody tr:hover td { background: var(--surface-3); }
.ps-table tbody tr:hover td.is-sorted { background: color-mix(in srgb, var(--sport-accent) 10%, var(--surface-3)); }
.ps-nil { color: var(--text-faint); font-weight: 400; }

/* sticky identity column: horizontal scroll on a phone never loses whose row it is */
.ps-player-head, .ps-player {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface-2);
  text-align: left;
  min-width: 11.5rem;
  box-shadow: 1px 0 0 var(--border);
}
.ps-player { font-weight: 600; color: var(--text); }
.ps-player-inner { display: flex; align-items: center; gap: 0.55rem; }
.ps-row-logo { width: 18px; height: 18px; object-fit: contain; flex-shrink: 0; }
.ps-name { overflow: hidden; text-overflow: ellipsis; }
.ps-name-short { display: none; }
.ps-note {
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 0 0.4rem;
}

.ps-section:not(.ps-show-depth) tr.ps-depth { display: none; }
.ps-more {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
  padding: 0.3rem 0.1rem;
  border: none;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-label);
  font-weight: 600;
  cursor: pointer;
}
.ps-more:hover { color: var(--text); }
.ps-more-hint { font-weight: 400; color: var(--text-faint); }
.ps-title:focus { outline: none; }

.ps-empty { color: var(--text-dim); text-align: center; padding: var(--sp-6) 0; margin: 0; }

.ps-out { padding-top: var(--sp-4); border-top: 1px solid var(--border); }
.ps-out-note { margin: 0 0 var(--sp-3); font-size: var(--fs-label); color: var(--text-faint); }
.ps-out-team { display: flex; gap: var(--sp-3); margin: 0 0 var(--sp-2); font-size: 0.85rem; }
.ps-out-label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
  width: 4.25rem;
  font-weight: 700;
  color: var(--text-dim);
}
.ps-out-names { display: flex; flex-wrap: wrap; gap: 0.25rem var(--sp-3); color: var(--text-dim); }
.ps-out-pos { margin-left: 0.3rem; font-size: var(--fs-meta); color: var(--text-faint); }
.ps-foot { margin: 0; font-size: var(--fs-meta); color: var(--text-faint); }

/* a view's pane is a grouping only: its sections still take the body's gap */
.ps-pane { display: contents; }
/* factors view: a label column and a few value columns, no sorting */
.ps-ftable { width: 100%; }
.ps-ftable .ps-player { font-weight: 500; color: var(--text-dim); white-space: nowrap; }
.ps-ftable tbody td { color: var(--text); font-weight: 600; white-space: pre-line; }
.ps-ftable thead th { width: 5.5rem; }
.ps-fcol { display: inline-flex; align-items: center; gap: 0.4rem; }
.ps-frow-section th {
  text-align: left;
  padding-top: 0.8rem;
  padding-bottom: 0.35rem;
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-faint);
  background: var(--surface-2);
}
.ps-ftable tbody tr.ps-frow-section:hover th { background: var(--surface-2); }
/* the sum of the rows above it: a total row, ruled off and in full ink */
.ps-ftable tbody tr.ps-frow-total > * { border-top: 2px solid var(--border-strong); font-weight: 800; color: var(--text); }
/* NFL: the margin and the total, each built up part by part */
.ps-build { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.ps-bpanel {
  border: 1px solid var(--border);
  border-radius: calc(var(--radius-card) - 4px);
  background: var(--surface-2);
  padding: var(--sp-3) var(--sp-4);
  min-width: 0;
}
.ps-bhead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); }
.ps-btitle {
  font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-faint);
}
.ps-bfinal { font-size: 1.15rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.ps-bcap { margin-top: 0.1rem; font-size: var(--fs-meta); color: var(--text-faint); text-align: right; }
.ps-blist { list-style: none; margin: var(--sp-2) 0 0; padding: 0; }
.ps-brow {
  display: grid; grid-template-columns: minmax(0, 1fr) 4.5rem 2.75rem; align-items: center; gap: var(--sp-2);
  padding: 0.45rem 0; border-top: 1px solid var(--border);
  font-size: 0.85rem;
}
.ps-blabel { display: flex; align-items: center; flex-wrap: wrap; gap: 0.2rem 0.45rem; color: var(--text-dim); }
.ps-blabel .pill { font-size: 0.68rem; padding: 0.05rem 0.45rem; }
.ps-bval { text-align: right; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.ps-brow.is-zero .ps-bval { color: var(--text-faint); font-weight: 500; }
/* a diverging bar: right of the centre line adds, left subtracts */
.ps-bbar { position: relative; height: 6px; border-radius: 3px; background: var(--surface-3); }
.ps-bbar::before { content: ""; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 1px; background: var(--border-strong); }
.ps-bbar i { position: absolute; top: 0; bottom: 0; width: calc(var(--w) * 0.5%); background: var(--sport-ink); border-radius: 3px; }
.ps-bbar i.is-pos { left: 50%; }
.ps-bbar i.is-neg { right: 50%; opacity: 0.6; }
.ps-brow.is-start .ps-bbar { visibility: hidden; }

/* NHL goaltending: the two goalies side by side, equal halves */
.ps-gmatch { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.ps-goalie {
  border: 1px solid var(--border);
  border-radius: calc(var(--radius-card) - 4px);
  background: var(--surface-2);
  padding: var(--sp-3) var(--sp-4);
  min-width: 0;
}
.ps-goalie-team {
  display: flex; align-items: center; gap: 0.45rem;
  font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-faint);
}
.ps-goalie-name { margin-top: var(--sp-1); font-size: 1.05rem; font-weight: 700; color: var(--text); }
.ps-goalie-facts { margin: var(--sp-2) 0 0; display: grid; gap: 0.35rem; }
.ps-goalie-facts > div { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.ps-goalie-facts dt { font-size: var(--fs-meta); color: var(--text-dim); }
.ps-goalie-facts dd { margin: 0; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; justify-content: flex-end; }
.ps-goalie-df { font-size: var(--fs-meta); color: var(--text-dim); }
/* desktop: a fixed label column and evenly shared value columns, so numbers don't pile at the right */
@media (min-width: 641px) {
  .ps-ftable { table-layout: fixed; }
  .ps-ftable .ps-player-head { width: 13rem; }
  .ps-ftable thead th { width: auto; }
}
.ps-fnote { margin: var(--sp-2) 0 0; font-size: var(--fs-meta); color: var(--text-faint); max-width: 65ch; }

/* light theme, chosen with the header switch (dark is the default) */
  :where(html[data-theme="light"]) .props-sheet { box-shadow: 0 30px 80px -30px rgba(20, 18, 14, 0.35); }
  :where(html[data-theme="light"]) .props-sheet::backdrop { background: rgba(28, 25, 19, 0.4); }


/* phones: a bottom sheet, the controls full-width, the numbers a notch smaller */
@media (max-width: 640px) {
  .props-sheet {
    width: 100vw;
    height: 92dvh;
    margin: auto 0 0;
    border-radius: 18px 18px 0 0;
    border-width: 1px 0 0;
  }
  .props-sheet[open] { animation: ps-up var(--dur-2) var(--ease-out); }
  .ps-head { padding: var(--sp-3) var(--sp-4) var(--sp-3); }
  .ps-body { padding: var(--sp-3) var(--sp-4) var(--sp-5); }
  .ps-matchup { gap: var(--sp-2); margin: var(--sp-2) 0 var(--sp-3); }
  .ps-side { gap: var(--sp-2); }
  .ps-side-logo { width: 28px; height: 28px; }
  .ps-side-name { font-size: 1.15rem; }
  .ps-side-score { font-size: 1.5rem; }
  .ps-controls { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .ps-tabs, .ps-teams { display: flex; }
  .ps-tab, .ps-team { flex: 1; padding: 0 0.4rem; }
  .ps-table { font-size: 0.8125rem; }
  .ps-table th, .ps-table td { padding: 0.5rem 0.55rem; }
  /* number columns a little tighter: a 4-market NHL table ran 8-16px past a 375px phone */
  .ps-table th.ps-num, .ps-table td.ps-num { padding-left: 0.35rem; padding-right: 0.35rem; }
  .ps-player-head, .ps-player { min-width: 0; max-width: 9.5rem; }
  /* "M. Stafford": first initial + surname, the phone convention; the full name stays as the tooltip */
  .ps-name-full { display: none; }
  .ps-name-short { display: inline; }
  .ps-out-team { flex-direction: column; gap: var(--sp-1); }
  /* with the view switch in the title row there's no room for the start time; the card has it */
  .ps-views ~ .ps-kickoff { display: none; }
  /* factor tables fit the width: labels wrap, value columns take only what they need */
  .ps-ftable .ps-player { max-width: none; white-space: normal; }
  .ps-ftable thead th { width: auto; }
  .ps-ftable th, .ps-ftable td { padding: 0.5rem 0.4rem; }
  .ps-ftable th:first-child, .ps-ftable td:first-child { padding-left: 0.6rem; }
  /* the two build-ups stack: each needs the full width for label, bar and value */
  .ps-build { grid-template-columns: 1fr; }
  .ps-bpanel { padding: var(--sp-3); }
  /* goalie halves are ~165px wide: each fact's label sits above its value */
  .ps-gmatch { gap: var(--sp-2); }
  .ps-goalie { padding: var(--sp-3); }
  .ps-goalie-name { font-size: 1rem; }
  .ps-goalie-facts { gap: var(--sp-2); }
  .ps-goalie-facts > div { flex-direction: column; align-items: flex-start; gap: 0.2rem; }
  .ps-goalie-facts dd { justify-content: flex-start; }
}

/* Site footer: crawlable links to the methodology and record pages. */
.site-footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  justify-content: center;
  margin: var(--sp-7) auto var(--sp-5);
  padding: var(--sp-4) 16px 0;
  border-top: 1px solid var(--border);
  max-width: 1100px;
  font-size: 0.8rem;
  color: var(--text-dim);
}
.site-footer a { color: var(--text-dim); }
.site-footer a:hover { color: var(--text); }

/* /about: plain reading column. */
.error-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.about-nav { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-3); }
.about-body { max-width: 760px; display: grid; gap: var(--sp-4); }
/* each sport a card of its own, scroll-margined so a jump link lands clear of the header */
.about-sport {
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}
.about-sport h2 { margin: 0 0 var(--sp-2); font-size: 1.25rem; }
.about-grading { margin: 0 0 var(--sp-3); display: grid; gap: var(--sp-3); }
.about-grading dt { font-size: var(--fs-label); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 0.15rem; }
.about-grading dd { margin: 0; line-height: 1.6; max-width: 68ch; }
@media (max-width: 640px) { .about-sport { padding: var(--sp-4); } }
.about-sport h2 a { color: var(--sport-ink, var(--text)); text-decoration: none; }
.about-sport h2 a:hover { text-decoration: underline; }
.about-sport p { margin: 0 0 var(--sp-2); line-height: 1.6; }

.opener-timer { font-variant-numeric: tabular-nums; }
.nav-utility.active { color: var(--text); border-color: var(--border-strong); }

/* Phones: the player (or first) column stays put while stats scroll under
   it -- the MLB Players table opened on Player + Game with every number
   off-screen. The Game column is redundant there (the game filter above it
   says the same), and the results table's Slate column gives way to Final. */
@media (max-width: 640px) {
  .leaderboard-table th:first-child, .leaderboard-table td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--surface-2);
  }
  .leaderboard-table .leaderboard-matchup { display: none; }
  .record-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Phone tap targets (REDESIGN_PLAN: 44px). Measured at 375px before this:
   market chips 24px tall, view toggle 32, sheet tabs 34, sheet close 36,
   search 36, "Show more" 28. Heights only -- widths already fit a thumb. */
@media (max-width: 640px) {
  .market-chip,
  .view-toggle-btn,
  .ps-tab, .ps-team,
  .ps-more,
  .player-search { min-height: 2.75rem; }
  .market-chip { display: inline-flex; align-items: center; }
  .ps-close { width: 2.75rem; height: 2.75rem; }
}


/* Links inside running text look like links. `a { color: inherit; text-decoration: none }` left
   "How it works", "See the track record", the 404's "the model record" and the MoneyPuck credit
   identical to the words around them -- 1.00:1, same weight (WCAG 1.4.1 / F73; site audit
   2026-10-02). Underlines only where a link sits in prose; nav, pills and cards stay as they are. */
.page-hero-intro a, .site-footer span a, .error-message a, .empty-state a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

/* Readable line length (WCAG 1.4.8 suggests <= 80 characters; Baymard 50-75): the hero intro ran
   86 characters, /about paragraphs 81-83, the calibration legend 134. */
.page-hero-intro, .about-sport p, .calibration-legend { max-width: 68ch; }

.results-group th { text-align: left; padding-top: var(--sp-3); font-size: var(--fs-meta); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-dim); background: var(--surface-1); }
.results-group a { color: inherit; }
.results-table td:first-child { white-space: nowrap; }
.record-card-value { white-space: nowrap; }  /* "564–267" broke across lines at 375px */


/* Tims tab (NHL, 2026-10-05): the day's three Tims Hockey Challenge groups, ranked by Goal %. */
.tims-intro { margin: 0 0 var(--sp-4); color: var(--text-dim); font-size: var(--fs-meta); max-width: 70ch; }
.tims-summary { display: block; margin-top: var(--sp-1); color: var(--text); }
.tims-groups { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); }
@media (max-width: 900px) { .tims-groups { grid-template-columns: 1fr; } }
.tims-group { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-card); padding: var(--sp-3); }
.tims-group-title { margin: 0 0 var(--sp-2); font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--sport-ink); }
.tims-list { margin: 0; padding: 0; list-style: none; }
/* rows are separated by hairlines; only the best pick is a rounded, filled row (a pill-shaped row with
   a top border drew curved slivers along every row; site review 2026-10-11) */
.tims-row { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2); border-top: 1px solid var(--border); }
.tims-row:first-child { border-top: 0; }
.tims-row.is-best { background: var(--surface-3); border-radius: 10px; border-top-color: transparent; }
.tims-row.is-best + .tims-row { border-top-color: transparent; }
.tims-row.is-unpriced { opacity: 0.6; }
.tims-logo { width: 22px; height: 22px; flex: none; }
.tims-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.tims-name { font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tims-meta { font-size: 0.75rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.tims-p { display: flex; flex-direction: column; align-items: flex-end; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; line-height: 1.15; }
.tims-best { font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sport-ink); }
.tims-note { font-size: 0.75rem; color: var(--text-faint); white-space: nowrap; }
.tims-source { margin-top: var(--sp-4); font-size: 0.75rem; color: var(--text-faint); }


/* ---------- press feedback (site review 2026-10-11) ----------
   Only the login button answered a press; every other control gave no sign it had been hit.
   Now every control does the same thing: a quick squeeze on press, released at once. It uses the
   `scale` property, not `transform`, so it composes with the cards' hover lift instead of
   cancelling it. The browser's own grey tap flash is turned off where this replaces it. */
:where(.props-open, .view-toggle-btn, .market-chip, .lines-chip, .filter-toggle,
       .date-nav-arrow, .slate-picker, .ps-close, .ps-tab, .ps-team, .ps-view, .player-history-btn,
       nav.sport-nav a, .nav-utility, .record-tab, .th-sort, .mini-card, .board-strip--link,
       .game-card--sheet, .brand, .login-box button) {
  -webkit-tap-highlight-color: transparent;
}
:where(.props-open, .view-toggle-btn, .market-chip, .lines-chip, .filter-toggle,
       .date-nav-arrow, .ps-close, .ps-tab, .ps-team, .ps-view, .player-history-btn,
       nav.sport-nav a, .nav-utility, .record-tab):active {
  scale: 0.96;
  transition: scale 60ms var(--ease-out);
}
/* big surfaces press less, or the whole card would visibly lurch */
:where(.mini-card, .board-strip--link, .game-card--sheet):active {
  scale: 0.99;
  transition: scale 80ms var(--ease-out);
}
/* a press inside a card belongs to that control, not the card */
.game-card--sheet:has(.props-open:active) { scale: none; }
