/* NFL ELO Dashboard — minimal, dark theme */

/* ── SSA design system — shared token block (docs/DESIGN_SYSTEM.md §1).
   Copied verbatim; do not edit values here. ──────────────────────────────── */
:root {
  /* ── Surfaces ────────────────────────────────────────────────────────── */
  --bg:            #0b0f16;  /* page background                            */
  --bg-elev:       #161b22;  /* cards, nav bar, table headers              */
  --bg-row:        #1d2129;  /* table rows, inset panels                   */
  --border:        #2a2f3a;
  --border-subtle: #22262f;

  /* ── Text ────────────────────────────────────────────────────────────── */
  --text:          #e8eaef;  /* headings + body                            */
  --text-dim:      #9aa0aa;  /* secondary                                  */
  --text-muted:    #8b929d;  /* captions, timestamps, footnotes            */

  /* ── Brand / chrome ──────────────────────────────────────────────────── */
  --accent:        #58a6ff;  /* THE brand accent. All chrome.              */
  --accent-dim:    #2f6fd6;  /* hover + pressed FILLS ONLY — never text    */
  --text-disabled: #6b727d;  /* recessive but legible; NOT --text-muted */
  --accent-wash:   rgba(88, 166, 255, 0.14);  /* translucent accent fills */
  --accent-2:      #a78bfa;  /* secondary highlight (All-Access, badges)   */

  /* ── Data semantics — NEVER used for chrome ──────────────────────────── */
  --good:          #3fb950;  /* win, cover, +EV, positive delta            */
  --bad:           #f85149;  /* loss, no-cover, -EV, negative delta        */
  --warn:          #e6c26b;  /* push, stale data, caution                  */

  /* ── Type ────────────────────────────────────────────────────────────── */
  --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
                 Roboto, sans-serif;
}

/* League identity — legal in charts, team badges and hero art only; never in
   nav, buttons or focus rules (DESIGN_SYSTEM.md §1 "Per-league tint"). */
:root { --league-tint: #2f6fd6; }   /* NFL blue */

/* ── SSA grade ramp — shared token block (DESIGN_SYSTEM.md §1 "The grade
   ramp"). Byte-identical across golf-tournament-predictor, cfl-elo-dashboard,
   ncaaf-dashboard and nfl-elo-dashboard; copied verbatim, do not edit values
   here. Band 1 = best. Nine bands, ONE ramp, TWO ladders — football ends
   `D+ · D` and golf ends `D · F`, which is exactly why the bands are numbered
   and not lettered (`--grade-d` would mean band 8 in one repo and band 9 in
   the other). Every band is a TEXT colour on an UNFILLED badge and clears
   WCAG AA against --bg, --bg-elev and --bg-row; worst band is 4.81. ─────── */
:root {
    --grade-1: #c084fc;  /* purple     */
    --grade-2: #60a5fa;  /* blue       */
    --grade-3: #38bdf8;  /* sky        */
    --grade-4: #4ade80;  /* green      */
    --grade-5: #86efac;  /* pale green */
    --grade-6: #eab308;  /* yellow     */
    --grade-7: #f97316;  /* orange     */
    --grade-8: #f85149;  /* red        */
    --grade-9: #fca5a5;  /* pale red   */
}

/* ── Repo-specific data colours ───────────────────────────────────────────
   These encode DATA meaning, so they sit outside the shared block. */
:root {
    --bar-projw: #6b7fd6;     /* Forecast projected-wins fill bar (slate-purple) */
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text);
    font-family: var(--font-family);
    font-size: 14px; line-height: 1.5;
}
a { color: var(--text); text-decoration: none; }

/* ── Site nav (standardized across SSA portfolio) ────────────────────── */
.site-nav {
    background: var(--bg-elev);
    border-bottom: 1px solid var(--border);
    padding: 8px 20px;
    display: flex;
    align-items: center;
    gap: 24px;
    flex-wrap: wrap;
}
.site-nav-brand {
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
    letter-spacing: -0.3px;
}
.site-nav-divider {
    width: 1px;
    height: 18px;
    background: var(--border);
}
.site-nav-link {
    text-decoration: none;
    font-size: 13px;
    color: var(--text-dim);
    font-weight: 500;
}
.site-nav-link:hover { color: var(--text); }
.site-nav-link.active {
    color: var(--accent);
    font-size: 15px;
    font-weight: 600;
}
.site-nav-link.disabled { color: var(--text-muted); cursor: not-allowed; }
/* ── Sub-header (Level 2: pages within this domain) ──────────
   Sits directly under the top sports row (.site-nav). Uses the page
   base background so it reads as a distinct, recessed second tier. */
.subsite-nav {
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: 8px 20px;
    display: flex;
    gap: 20px;
    flex-wrap: wrap;
    align-items: center;
}
.subsite-nav-link {
    text-decoration: none;
    font-size: 13px;
    color: var(--text-dim);
    font-weight: 500;
}
.subsite-nav-link:hover { color: var(--text); }
.subsite-nav-link.active { color: var(--accent); font-weight: 600; }
/* ── Header / season picker ──────────────────────────────── */
.hdr {
    display: flex; align-items: center; justify-content: space-between;
    padding: 16px 24px; border-bottom: 1px solid var(--border);
}
.hdr-title { font-size: 18px; font-weight: 600; }
.hdr-meta { display: flex; align-items: center; gap: 12px; }
.hdr-meta label { font-size: 13px; color: var(--text-dim); }
/* Static season value on the public shell — replaces the season <select> when
   the manifest lists a single season (public season lockdown, 2026-08-08). */
.season-static { color: var(--text); font-weight: 600; }
select, input[type=number], input[type=text], button {
    background: var(--bg-elev); color: var(--text); border: 1px solid var(--border);
    padding: 6px 10px; border-radius: 4px; font-size: 13px; font-family: inherit;
}
select:focus, input:focus, button:focus { outline: 1px solid var(--accent); }
button { cursor: pointer; }
button:hover { background: var(--bg-row); }
button.primary { background: var(--accent); border-color: var(--accent); color: var(--bg); }
button.primary:hover { filter: brightness(1.12); }
.pill {
    font-size: 11px; padding: 2px 8px; border-radius: 10px;
    background: var(--bg-row); color: var(--text-dim);
}

/* ── Page nav ────────────────────────────────────────────── */
.page-nav {
    display: flex; gap: 4px; padding: 12px 24px;
    border-bottom: 1px solid var(--border);
}
.page-nav-btn {
    background: transparent; border: 1px solid transparent;
    padding: 6px 14px; color: var(--text-dim);
}
.page-nav-btn:hover { color: var(--text); }
.page-nav-btn.active {
    background: var(--bg-elev); border-color: var(--border); color: var(--text);
}

/* ── Pages ───────────────────────────────────────────────── */
main { padding: 24px; max-width: 1280px; margin: 0 auto; }
.page { display: none; }
.page.active { display: block; }
.section-head {
    margin: 24px 0 12px; font-size: 13px; font-weight: 600;
    color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em;
}
.section-head:first-child { margin-top: 0; }

/* ── Tables ──────────────────────────────────────────────── */
table {
    width: 100%; border-collapse: collapse; font-size: 12px;
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 6px;
    overflow: hidden;
}
thead th {
    background: var(--bg-row); color: var(--text-dim);
    text-align: left; padding: 10px 12px; font-weight: 600;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
tbody td {
    padding: 8px 12px; border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--bg-row); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.center, th.center { text-align: center; }
td.dim { color: var(--text-dim); }
td.good { color: var(--good); }
td.bad { color: var(--bad); }
td.tag {
    font-size: 11px; padding: 2px 6px; border-radius: 3px;
    background: var(--bg-row); display: inline-block;
}

/* ── Team logo (self-hosted ESPN dark-variant PNGs, static/logos/) ── */
.team-logo {
    display: inline-block; width: 22px; height: 22px;
    object-fit: contain; vertical-align: middle;
    flex-shrink: 0;
}
.team-logo.sm { width: 18px; height: 18px; }
.team-logo.lg { width: 32px; height: 32px; }
td .team-logo { margin-right: 7px; }

/* ── Team badge (abbreviation chip; fallback when a logo is missing) ── */
.team-badge {
    display: inline-block; min-width: 34px; text-align: center;
    background: var(--bg-row); border: 1px solid var(--border); border-radius: 4px;
    padding: 2px 6px; font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
    color: var(--text); vertical-align: middle;
}

/* ── Division standings ──────────────────────────────────── */
.standings-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px; margin-bottom: 24px;
}
.standings-div h3 {
    margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--text-dim);
    text-transform: uppercase; letter-spacing: 0.04em;
}
.standings-table { font-size: 12px; }

/* ── Schedule (sportsbook-style table) ───────────────────── */
/* ONE control bar: week filter + hide-completed with the odds/model freshness
   readout on the right. All chrome — surface tokens + --accent only. Replaced
   the old standalone freshness bar 2026-08-08. (The NCAAF sibling's bar also
   hosts its FCS toggle; the NFL has no FCS concept, so no third control.) */
.sched-blurb {
    color: var(--text-muted);
    font-size: 12.5px;
    line-height: 1.5;
    margin: 0 0 10px;
    max-width: 720px;
}

.sched-controls {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px;
    margin: 0 0 18px; padding: 8px 12px;
    max-width: 1100px;
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 4px;
    font-size: 13px; color: var(--text-dim);
}
.sched-controls label { display: inline-flex; align-items: center; gap: 6px; margin: 0; cursor: pointer; }
.sched-controls select {
    background: var(--bg-row); color: var(--text);
    border: 1px solid var(--border); border-radius: 4px;
    padding: 4px 8px; font-size: 13px;
}
.sched-controls input[type="checkbox"] { accent-color: var(--accent); }
.sched-controls .sched-fresh { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.sched-controls .label {
    text-transform: uppercase; letter-spacing: 0.04em;
    font-weight: 600; font-size: 11px; margin-right: 4px;
}
.sched-controls .freshness-sep { opacity: 0.5; }

.week-section { margin-bottom: 28px; }
.week-section h3 {
    margin: 0 0 10px; font-size: 13px; font-weight: 600;
    color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em;
}
table.schedule-table {
    width: 100%; max-width: 1100px; border-collapse: separate; border-spacing: 0;
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 8px;
    overflow: hidden; font-size: 13px;
}
.schedule-table thead th {
    background: var(--bg-row); color: var(--text-dim);
    text-align: center; padding: 10px 8px; font-weight: 600; font-size: 11px;
    text-transform: uppercase; letter-spacing: 0.04em;
    border-bottom: 1px solid var(--border);
}
.schedule-table thead th.matchup-col { text-align: left; padding-left: 14px; }
.schedule-table tbody td { padding: 10px 8px; border-bottom: 1px solid var(--border); text-align: center; }
.schedule-table tbody tr:last-child td { border-bottom: none; }
.schedule-table tbody tr.future { opacity: 0.92; }
.schedule-table .matchup-cell { display: flex; align-items: center; gap: 10px; padding-left: 6px; min-width: 200px; }
.schedule-table .matchup-cell .team-block { display: flex; flex-direction: column; line-height: 1.2; }
.schedule-table .matchup-cell .team-name { font-weight: 600; white-space: nowrap; }
.schedule-table .matchup-cell .record { font-size: 11px; color: var(--text-dim); }
.schedule-table .matchup-row { display: flex; flex-direction: column; gap: 8px; }
.schedule-table .score-cell { font-weight: 700; font-size: 16px; min-width: 26px; text-align: center; }
.schedule-table .score-cell.dim { color: var(--text-dim); font-weight: 400; font-size: 13px; }
.schedule-table .odds-cell { background: var(--bg-row); border-radius: 6px; padding: 6px 10px; display: inline-block; min-width: 64px; }
.schedule-table .odds-cell .price { display: block; font-size: 10px; color: var(--text-dim); margin-top: 2px; }
.schedule-table .pred-cell { font-weight: 600; }
.schedule-table .pred-cell.fav { color: var(--good); }
.schedule-table .edge-cell { font-size: 12px; color: var(--text-dim); }
.schedule-table .edge-cell.big { color: var(--accent); font-weight: 600; }
/* Final-game market marks: which side of each book number the score landed
   on, computed client-side from the final score. GREEN/AMBER ARE DATA here —
   an outcome, not chrome — and each mark's title-text states the meaning in
   words so it is never colour-only. */
.schedule-table .mark-win  { color: var(--good); font-weight: 700; margin-left: 5px; }
.schedule-table .mark-push { color: var(--warn); font-weight: 700; font-size: 11px; margin-left: 5px; }
/* The Grade column: SYSTEM 2's letter (see the .sg-tier ramp), on the edge
   team's row beside SYSTEM 1's edge number — separate columns, never summed. */
.schedule-table .grade-cell { text-align: center; }
/* Completed public games: Pred/Edge/Grade collapse into one link out to the
   box score. Chrome — accent, like every other link. */
.schedule-table .boxscore-cell { text-align: center; }
.schedule-table .boxscore-link { color: var(--accent); font-weight: 600; font-size: 12px; white-space: nowrap; }
.schedule-table .result-cell.win  { color: var(--good); font-weight: 700; font-size: 16px; }
.schedule-table .result-cell.loss { color: var(--bad);  font-weight: 700; font-size: 16px; }
.schedule-table .result-cell.push { color: var(--warn); font-weight: 700; font-size: 13px; }
.schedule-table .game-date {
    font-size: 11px; color: var(--text-dim);
    padding: 6px 14px; background: rgba(0,0,0,0.15);
}
.schedule-table .game-date td { padding: 6px 14px; text-align: left; border-bottom: 1px solid var(--border); }

/* ── The situational grade badge (SYSTEM 2's letter) ──────────────────────────
   THE LETTER IS DATA, so it takes the full grade ramp — DESIGN_SYSTEM.md §1
   "Letter grades ARE data" and "The grade ramp". Nine bands, band 1 best,
   shared byte-for-byte with golf's D-S badge so one letter means one colour
   across the whole portfolio.

   THE BADGE IS NEVER FILLED. `background: transparent` and the LETTER carries
   the colour — this is golf's treatment, adopted portfolio-wide 2026-08-16
   after John: the football columns "are all gray, no matter what the grade".
   The old ramp gave six of nine bands a literal `var(--bg-row)` box, so the
   grey box dominated and the letter's colour read as incidental. A fill would
   also drag `color: var(--bg)` back in per the filled-control rule and undo
   exactly that.

   Every band is measured against --bg, --bg-elev AND --bg-row (the badge sits
   on --bg-elev in a table body and on --bg-row when the row is hovered);
   worst band is 4.81:1. No hex literals — the tokens live in the shared block
   at the top of this file, so a retune moves every grade at once.

   THE RAMP REACHES RED AT THE BOTTOM, AND THAT IS DELIBERATE. This ladder's
   floor means the scheduling and situational factors cut AGAINST the side the
   model chose — a genuine negative on a signed scale, not a warning. The model
   has still chosen that side and none of the bottom labels says "avoid". The
   plain-English label rides in the cell's `title`, so colour is never the only
   carrier of meaning. ─────────────────────────────────────────────────────── */
/* min-width + padding rather than a fixed width: four of the nine letters are
   two glyphs ("A+", "B+", "C+", "D+") and a hard 26px clipped them. The single
   letters still render as squares because min-width wins on a one-glyph
   label. */
.sg-tier {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    min-width: 26px; height: 26px; padding: 0 5px; border-radius: 6px;
    font-size: 14px; font-weight: 700; line-height: 1;
}
/* "A+" is not a legal class fragment, so the letter is slugged `+` -> `plus`
   by static/js/sharpgrade.js::tierClass. One slug function on the client, one
   set of rules here; tests/test_sharp_grade_ui.py asserts a rule exists for
   every tier the config declares, using that same slug. */
/* The Grade COLUMN cell (schedule table). Bigger than the old 18px date-row
   chip so the letter reads at a row glance; the ramp classes below apply
   unchanged. */
.sg-tier-row {
    min-width: 26px; height: 26px; padding: 0 6px;
    border-radius: 6px; font-size: 15px;
}
/* THE RAMP — nine bands, best first. Band N == var(--grade-N); the football
   ladder's letters map onto the bands in declaration order (see the table in
   DESIGN_SYSTEM.md, which also carries golf's differing bottom two). */
.sg-tier.t-s     { background: transparent; color: var(--grade-1); }  /* purple     */
.sg-tier.t-aplus { background: transparent; color: var(--grade-2); }  /* blue       */
.sg-tier.t-a     { background: transparent; color: var(--grade-3); }  /* sky        */
.sg-tier.t-bplus { background: transparent; color: var(--grade-4); }  /* green      */
.sg-tier.t-b     { background: transparent; color: var(--grade-5); }  /* pale green */
.sg-tier.t-cplus { background: transparent; color: var(--grade-6); }  /* yellow     */
.sg-tier.t-c     { background: transparent; color: var(--grade-7); }  /* orange     */
.sg-tier.t-dplus { background: transparent; color: var(--grade-8); }  /* red        */
.sg-tier.t-d     { background: transparent; color: var(--grade-9); }  /* pale red   */

/* ── ATS Performance ─────────────────────────────────────── */
.ats-headlines {
    display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px;
}
.ats-headline {
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 8px;
    padding: 18px 20px;
}
.ats-headline-label {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-dim); margin-bottom: 4px;
}
.ats-headline-record {
    font-size: 28px; font-weight: 700; line-height: 1.1;
}
.ats-headline-meta {
    font-size: 13px; color: var(--text-dim); margin-top: 6px;
}

/* ── Team detail ─────────────────────────────────────────── */
.team-summary {
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 6px;
    padding: 16px; margin-bottom: 16px;
    display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 16px;
}
.team-summary .stat-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; }
.team-summary .stat-val { font-size: 22px; font-weight: 700; margin-top: 2px; }

/* ── Team Detail — one-page cheat sheet (NCAAF ts-* port, 2026-08-05) ── */
.ts-header {
    display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    padding: 14px 16px; margin-bottom: 14px;
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 6px;
}
.ts-title h2 { margin: 0; font-size: 22px; }
/* The stats own the full right side of the header row (flex: 1) with even
   spacing, and every block centers VERTICALLY on the team name — without
   align-items the taller Proj Record block made its neighbors look
   top-pinned (John, 2026-08-06). */
.ts-stats { display: flex; flex: 1 1 260px; justify-content: space-evenly;
    align-items: center; gap: 14px; flex-wrap: wrap; }
.ts-stat { text-align: center; }
.ts-stat-val { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
.ts-stat-val .ts-big { font-size: 22px; }
.ts-stat-label { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .05em; }
.ts-panel {
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 6px;
    padding: 12px 14px; margin-bottom: 14px;
}
.ts-panel h4 { margin: 0 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; }
.ts-season-tag { font-weight: 400; font-size: 11px; color: var(--text-dim); text-transform: none; letter-spacing: 0; }
/* Metrics: one sub-table per section (offense / defense / special), side by
   side on wide screens. border:0 — the global table style would otherwise
   draw a card border inside the panel. */
.ts-metrics-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 8px 22px; align-items: start;
}
.ts-metrics { width: 100%; border: 0; font-size: 12px; }
.ts-metrics th, .ts-metrics td { padding: 4px 6px; border-bottom: 1px solid var(--border); }
.ts-metrics thead th { color: var(--text-dim); font-size: 10px; text-transform: uppercase; }
/* league-rank chip beside each metric — tiered by thirds of the 32-team
   league (1–11 green / 12–21 amber / 22–32 red) */
.ts-rank {
    display: inline-block; min-width: 26px; text-align: center;
    font-size: 10px; font-weight: 700; padding: 0 4px; margin-left: 6px;
    border-radius: 3px; font-variant-numeric: tabular-nums;
}
.ts-rank.r-good { background: rgba(63, 185, 80, 0.18); color: var(--good); }
.ts-rank.r-mid { background: rgba(230, 194, 107, 0.15); color: var(--warn); }
.ts-rank.r-bad { background: rgba(248, 81, 73, 0.06); color: var(--bad); }
.ts-jersey {
    display: inline-block; width: 20px; color: var(--text-dim);
    font-variant-numeric: tabular-nums; font-size: 10px; font-weight: 400;
}
/* injury flags (must be unmissable): OUT red, Q amber */
.ts-flag {
    font-size: 9px; font-weight: 700; color: var(--bg); background: var(--bad);
    border-radius: 3px; padding: 0 4px; margin-left: 5px; vertical-align: 1px;
}
.ts-flag.ts-flag-q { background: var(--warn); color: #3a2f10; }
.ts-log-title { margin: 20px 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; }
/* .good/.bad are td-scoped globally; the sheet uses them on spans too */
.ts-panel .good, .ts-header .good { color: var(--good); }
.ts-panel .bad, .ts-header .bad { color: var(--bad); }

/* ── Formation depth chart (ESPN formations, CFL-sheet style) ── */
.dc-panel { margin-bottom: 22px; }
.dc-band h5 {
    margin: 8px 0 4px; font-size: 10px; text-transform: uppercase;
    letter-spacing: .08em; color: var(--text-dim);
}
.dc-row { display: flex; justify-content: space-evenly; align-items: flex-start; gap: 10px; margin: 8px 0; flex-wrap: wrap; }
.dc-box { min-width: 150px; max-width: 190px; }
.dc-poslabel {
    display: inline-block; background: #000; color: #fff; font-weight: 700;
    font-size: 11px; letter-spacing: .05em; padding: 2px 10px; margin-bottom: 3px;
    border: 2px solid var(--accent); border-radius: 3px;
}
.dc-player {
    font-size: 11px; padding: 1px 0; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; color: var(--text-dim);   /* backups dim */
}
.dc-player.dc-starter { font-weight: 700; font-size: 12px; color: var(--text); }
/* out players read red, jersey included (keep after the base rules) */
.dc-player.ts-out, .dc-player.ts-out .ts-jersey { color: var(--bad); }
.dc-divider { border-top: 2px solid var(--accent); margin: 14px 0 6px; }

/* ── Cheat-sheet 2-col grid: coaches+metrics left, schedule right (2026-08-06,
   NCAAF ts-grid port). Collapses to one column on narrow screens; the
   formation depth chart sits full-width below the grid. ── */
.ts-grid {
    display: grid; grid-template-columns: 1fr 1.2fr; gap: 14px;
    margin-bottom: 14px; align-items: start;
}
@media (max-width: 1000px) { .ts-grid { grid-template-columns: 1fr; } }
.ts-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.ts-col .ts-panel { margin-bottom: 0; }   /* flex gap owns the spacing */

/* coaching + scheme rows (NCAAF ts-coach port + scheme extension) */
.ts-coach { padding: 3px 0; font-size: 13px; }
.ts-coach-role {
    display: inline-block; width: 34px; font-size: 10px; font-weight: 700;
    color: var(--text-dim);
}
.ts-scheme-role {
    width: auto; min-width: 90px; text-transform: uppercase;
    letter-spacing: .04em; padding-right: 8px;
}
.ts-scheme-sep { border-top: 1px solid var(--border); margin: 6px 0; }

/* schedule column (NCAAF ts-sched port) */
.ts-sched { width: 100%; border: 0; font-size: 12px; }
.ts-sched td { padding: 4px 6px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.ts-sched-line { color: var(--text-dim); margin-left: 4px; }

/* ── Formation-shaped depth-chart rows (2026-08-06): each band is the field
   seen from above — rows centered so e.g. a 3-man front sits centered under
   2 LBs; the receivers row spreads wide. Unknown-slug fallback rows carry
   .dc-extra + a data-extra-slugs attribute. ── */
.dc-frow {
    display: flex; justify-content: center; align-items: flex-start;
    gap: 14px; margin: 8px 0; flex-wrap: wrap;
}
.dc-frow-wide { justify-content: space-evenly; }
.dc-extra { border-top: 1px dashed var(--border); padding-top: 8px; }
.dc-row.dc-st { justify-content: flex-start; gap: 26px; }
.dc-scheme {
    margin-left: 8px; color: var(--accent); font-weight: 700;
    letter-spacing: .05em;
}

/* ── Admin ───────────────────────────────────────────────── */
.admin-section {
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 6px;
    padding: 16px; margin-bottom: 16px;
}
.admin-section h3 { margin: 0 0 12px; font-size: 14px; color: var(--text); }
.admin-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
}
.admin-field { display: flex; flex-direction: column; gap: 4px; }
.admin-field label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; }
.admin-field input { width: 100%; }
.admin-actions { display: flex; gap: 8px; margin-top: 16px; flex-wrap: wrap; }
.admin-locked {
    background: var(--bg-row); padding: 24px; border-radius: 6px;
    border: 1px dashed var(--border); text-align: center;
}

/* QB weekly adjustment matrix — wide, horizontally scrollable grid with a
   sticky team column so the week headers stay readable while scrolling. */
.qb-matrix { border-collapse: separate; border-spacing: 0; font-size: 12px; }
.qb-matrix thead th {
    position: sticky; top: 0; z-index: 2; background: var(--bg-row);
    text-align: center; font-size: 10px;
}
.qb-matrix th.qb-team-col, .qb-matrix td.qb-team-col {
    position: sticky; left: 0; z-index: 1; background: var(--bg-elev);
    text-align: left; min-width: 150px; font-weight: 600;
}
.qb-matrix thead th.qb-team-col { z-index: 3; }
.qb-matrix td { padding: 4px 6px; text-align: center; }
.qb-matrix input { padding: 4px 6px; }

/* ── Toast / status ──────────────────────────────────────── */
.toast {
    position: fixed; bottom: 24px; right: 24px;
    background: var(--bg-elev); border: 1px solid var(--border);
    padding: 10px 14px; border-radius: 6px; font-size: 13px;
    transform: translateY(8px); opacity: 0; pointer-events: none;
    transition: transform 0.18s, opacity 0.18s;
    z-index: 10;
}
.toast.visible { transform: translateY(0); opacity: 1; }
.toast.err { border-color: var(--bad); color: var(--bad); }
.toast.ok { border-color: var(--good); color: var(--good); }

/* ── Misc ────────────────────────────────────────────────── */
.empty { padding: 48px; text-align: center; color: var(--text-dim); }
.spinner {
    display: inline-block; width: 14px; height: 14px;
    border: 2px solid var(--border); border-top-color: var(--accent);
    border-radius: 50%; animation: spin 0.8s linear infinite; vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }
.rank-1 { color: var(--accent); font-weight: 700; }

/* ── Forecast — "Dynamic Futures" grid ──────────────────── */
/* Full-bleed: the forecast page escapes main's max-width cap so the (wide) grids
   use the whole viewport instead of overflowing a centred 1280px column. */
#page-forecast.active {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    padding: 0 24px;
    box-sizing: border-box;
}
.fc-head h3 { margin: 0 0 4px; }
.fc-head .dim { line-height: 1.5; max-width: 760px; margin: 0 0 14px; }
/* One grid per conference (AFC / NFC), stacked. */
.fc-conf-list { display: flex; flex-direction: column; gap: 22px; }
.fc-conf { min-width: 0; }
.fc-conf-title {
    margin: 0 0 8px; font-size: 13px; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--accent);
}
/* Each conference scrolls independently on BOTH axes. A bounded height makes the
   container itself the vertical scrollport, so sticky column headers (top:0) work
   on vertical scroll — on a tall conference/short screen you keep the headers —
   while overflow keeps the horizontal scrollbar per-grid (not pinned to the page
   bottom) with the sticky Team column (left:0). */
.fc-scroll {
    overflow: auto; max-height: calc(100vh - 90px);
    border: 1px solid var(--border); border-radius: 8px;
}
.fc-grid {
    border-collapse: separate; border-spacing: 0; font-size: 12px; min-width: 100%;
    /* Override the global `table { overflow: hidden }` (used to clip other tables'
       border-radius). On a table it makes the TABLE the sticky cells' scroll
       ancestor, which silently defeats the sticky Team column + column headers —
       they must stick to .fc-scroll instead. Corners are clipped by .fc-scroll. */
    overflow: visible; border: 0; border-radius: 0;
}
.fc-grid th, .fc-grid td {
    border-bottom: 1px solid var(--border); padding: 5px 6px;
    text-align: center; white-space: nowrap;
}
.fc-grid thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--bg-elev); color: var(--text-dim);
    font-weight: 600; text-transform: uppercase; font-size: 10px;
}
/* sticky Team column (header + body + division band rows). The conference is
   now a per-grid section heading (.fc-conf-title), not an in-table band, so it's
   dropped from this selector. */
.fc-team-head, .fc-team, .fc-div-cell {
    position: sticky; left: 0; z-index: 2;
    text-align: left !important; padding-left: 10px !important;
    background: var(--bg-elev);
}
.fc-team-head { z-index: 4; min-width: 160px; }
.fc-team { min-width: 160px; background: var(--bg); }
.fc-grid tbody tr:hover .fc-team { background: var(--bg-elev); }
.fc-team .team-badge, .fc-team .team-logo { margin-right: 7px; vertical-align: middle; }
.fc-team .team-name { font-weight: 600; vertical-align: middle; }
.fc-grid tbody tr:hover td { background: var(--bg-elev); }


/* Conference/division roll-up row: season-odds sums (Σ wins vs market, expected
   playoff teams, group title/champ probability). The label reuses .fc-team so it
   stays sticky; the week region is one empty spanning cell. */
.fc-sum-row td { border-top: 2px solid var(--border); background: var(--bg-elev); }
.fc-sum-row td.fc-team { background: var(--bg-elev); }
.fc-sum-row .fc-sum-label { font-weight: 600; font-size: 11px; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--text-dim); }
.fc-sum-row td.fc-num { font-weight: 600; }

/* Division sub-header: a lighter, indented band nested under the conference. */
.fc-div-row td.fc-div-cell {
    font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-dim); background: var(--bg);
    padding: 4px 10px 4px 22px;
}
.fc-wk { min-width: 46px; }
.fc-cell { min-width: 46px; line-height: 1.15; }
.fc-cell.fc-byewk { background: var(--bg); }
.fc-opp { font-size: 9px; color: var(--text-dim); letter-spacing: .02em; }
.fc-wp { font-weight: 700; font-size: 12px; }
.fc-cell.fc-w .fc-wp { color: var(--good); }
.fc-cell.fc-l .fc-wp { color: var(--bad); }
/* division-game marker: a white inner ring (inset box-shadow doesn't shift
   layout the way a border would, so the heatmap grid stays aligned). */
.fc-cell.fc-confgame { box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.6); }
.fc-confgame-legend {
    color: var(--text); padding: 0 4px; border-radius: 3px;
    box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.6);
}
/* numeric/outcome columns */
.fc-num { text-align: right; font-variant-numeric: tabular-nums; min-width: 50px; }
.fc-natty { font-weight: 700; }
.fc-sep { border-left: 2px solid var(--border); }
/* implied-odds column: dim, board-style, sits right of its probability */
.fc-odds {
    text-align: right; font-variant-numeric: tabular-nums; min-width: 52px;
    color: var(--text-dim); font-size: 11px;
}
.fc-odds-head { color: var(--text-dim); }
/* section buffer: empty channel between the three forecast zones. */
.fc-grid th.fc-gap, .fc-grid td.fc-gap {
    min-width: 18px; width: 18px; padding: 0;
    background: var(--bg); border-bottom: none;
}
.fc-grid tbody tr:hover td.fc-gap { background: var(--bg); }
/* Projected-record cell: horizontal fill bar to the win fraction. */
.fc-rec {
    min-width: 64px; font-weight: 700;
    background: linear-gradient(
        90deg,
        var(--bar-projw) 0%,
        var(--bar-projw) var(--rec-fill, 0%),
        var(--bg-elev)   var(--rec-fill, 0%),
        var(--bg-elev)   100%
    );
}
.fc-grid tbody tr:hover .fc-rec { background: linear-gradient(
    90deg, var(--bar-projw) 0%, var(--bar-projw) var(--rec-fill, 0%),
    var(--bg-elev) var(--rec-fill, 0%), var(--bg-elev) 100%); }
.fc-prob { background: rgba(63, 185, 80, calc(var(--p, 0) * 0.5)); padding: 1px 5px; border-radius: 4px; }

/* per-game win-probability heatmap. The dog alphas are lower than the fav ones
   on purpose: a played loss renders its % in var(--bad) ON this wash, and --bad
   has far less headroom than --good, so .22 measured 3.91:1. */
.wp-strong-fav { background: rgba(63, 185, 80, 0.22); }
.wp-light-fav  { background: rgba(63, 185, 80, 0.10); }
.wp-coinflip   { background: transparent; }
.wp-light-dog  { background: rgba(248, 81, 73, 0.05); }
.wp-strong-dog { background: rgba(248, 81, 73, 0.10); }

/* ATS "All seasons" total row (tfoot). */
.ats-total td {
    padding: 8px 12px;
    background: var(--bg-elev);
    font-weight: 700;
    border-top: 2px solid var(--border);
    white-space: nowrap;
}

/* ── ATS conference breakdowns (dual-scope: season | all-time) ──────────── */
.collapsible .collapsible-body > .ats-scope-table { margin-top: 0; }
.ats-scope-table { width: 100%; }
.ats-scope-table th.ats-scope-head {
    font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em;
    border-bottom: 1px solid var(--border);
}
.ats-scope-table .ats-scope-sep { border-left: 2px solid var(--border); }
.ats-scope-table tbody tr:hover { background: var(--bg-row); }
.ats-scope-table tbody tr:last-child td {
    border-top: 1px solid var(--border); font-weight: 700;   /* Total / All row */
}

/* ── Nav-right (Help + Account stub) — used by public.html. */
.nav-right { display: flex; align-items: center; gap: 16px; margin-left: auto; }
.nav-right-link { color: var(--text-dim); text-decoration: none; font-size: 13px; font-weight: 500; }
.nav-right-link:hover { color: var(--text); }
.account-stub { position: relative; }
/* Signed-out state: account.js replaces the stub button with a bare <a>,
   which otherwise inherits UA link colors on pages without a global a rule. */
.account-stub > a { color: var(--text-dim); text-decoration: none; font-size: 13px; font-weight: 500; }
.account-stub > a:hover { color: var(--text); }
.account-stub button {
    background: transparent; border: 1px solid var(--border); color: var(--text);
    font: inherit; font-size: 13px; padding: 6px 12px; border-radius: 6px;
    cursor: pointer; display: flex; align-items: center; gap: 6px;
}
.account-stub button:hover { border-color: var(--accent); }
.account-stub-menu {
    display: none; position: absolute; right: 0; top: calc(100% + 6px);
    background: var(--bg-elev); border: 1px solid var(--border); border-radius: 8px;
    min-width: 240px; padding: 8px 0; box-shadow: 0 8px 30px rgba(0,0,0,0.4); z-index: 100;
}
.account-stub-menu.open { display: block; }
.account-stub-menu .item {
    padding: 8px 16px; font-size: 13px; color: var(--text-dim);
    display: flex; justify-content: space-between; gap: 12px;
}
.account-stub-menu .item.header {
    color: var(--text); font-weight: 600;
    border-bottom: 1px solid var(--border); padding-bottom: 12px; margin-bottom: 4px;
}

/* ── Public surface (nfl.SSA/elomodel) ────────────────────────────────── */
.public-main { max-width: 920px; margin: 0 auto; padding: 32px 20px 64px; }
.public-hero { margin-bottom: 28px; }
.public-hero .kicker {
    font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--text-dim); margin: 0 0 10px;
}
.public-hero h1 { font-size: 30px; line-height: 1.15; margin: 0 0 12px; }
.public-lede { color: var(--text-dim); line-height: 1.6; font-size: 15px; max-width: 660px; margin: 0; }

/* Collapsible sections (native <details>) */
.collapsible {
    background: var(--bg-elev); border: 1px solid var(--border);
    border-radius: 10px; margin-bottom: 16px; overflow: hidden;
}
.collapsible > summary {
    list-style: none; cursor: pointer; user-select: none;
    display: flex; align-items: center; gap: 12px;
    padding: 16px 18px; font-size: 16px; font-weight: 600;
}
.collapsible > summary::-webkit-details-marker { display: none; }
.collapsible > summary::after {
    content: "▾"; margin-left: auto; color: var(--text-dim);
    font-size: 13px; transition: transform 0.15s ease;
}
.collapsible[open] > summary::after { transform: rotate(180deg); }
.collapsible > summary:hover { background: var(--bg-row); }
.collapsible-tag {
    font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
    padding: 3px 8px; border-radius: 10px;
}
.collapsible-tag.free { color: var(--text-muted); background: var(--bg-row); }
.collapsible-tag.locked { color: var(--accent); background: var(--accent-wash); }
.collapsible-body { padding: 4px 18px 20px; }
.section-note { color: var(--text-dim); font-size: 13px; line-height: 1.55; margin: 4px 0 16px; }

/* Lock cards (gated content placeholders) */
.lock-card {
    text-align: center; padding: 36px 24px;
    border: 1px dashed var(--border); border-radius: 10px; background: var(--bg-row);
}
.lock-card.wide { padding: 48px 24px; }
.lock-card .lock-icon { color: var(--accent); margin-bottom: 12px; }
.lock-card h3 { margin: 0 0 8px; font-size: 16px; color: var(--text); }
.lock-card p { margin: 0 auto 18px; max-width: 460px; color: var(--text-dim); font-size: 14px; line-height: 1.55; }
.lock-card .cta {
    display: inline-block; background: var(--accent); color: var(--bg);
    border: 1px solid var(--accent); border-radius: 6px;
    padding: 9px 20px; font-size: 14px; font-weight: 600;
}
.lock-card .cta:hover { filter: brightness(1.12); }

.schedule-public { margin-top: 24px; }
.public-section-h {
    font-size: 13px; font-weight: 600; color: var(--text-dim);
    text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 12px;
}

/* Single headline card (public ATS shows All-time only) */
.ats-headlines.single { grid-template-columns: 1fr; max-width: 360px; }
/* Wide ATS table scrolls horizontally on narrow screens. */
.ats-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.public-footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--border); }
.public-footer p { color: var(--text-dim); font-size: 12px; line-height: 1.6; margin: 0; }

/* ── Season Overview ─────────────────────────────────────────────────────
   Reuses the forecast sticky-grid mechanics: a bounded, scrollable container
   is the sticky ancestor (so `.so-grid` must NOT be the global overflow:hidden
   table), the Team column sticks left, the header row sticks top. */
.so-head h3 { margin: 0 0 4px; }
.so-head p { margin: 0 0 14px; max-width: 900px; }
.so-note {
    margin: 0 0 14px; padding: 8px 12px; max-width: 900px;
    background: var(--bg-elev); border: 1px solid var(--border);
    border-left: 3px solid var(--warn); border-radius: 4px;
    font-size: 12px; color: var(--text-dim);
}
.so-controls { display: flex; align-items: center; gap: 16px; margin: 0 0 12px; }
.so-controls label { font-size: 12px; color: var(--text-dim); }
.so-controls select { margin-left: 6px; }
.so-hint { font-size: 11px; color: var(--text-dim); }

.so-scroll {
    overflow: auto; max-height: calc(100vh - 190px);
    border: 1px solid var(--border); border-radius: 8px;
}
.so-grid {
    border-collapse: separate; border-spacing: 0; font-size: 12px; min-width: 100%;
    overflow: visible; border: 0; border-radius: 0;
}
.so-grid th, .so-grid td {
    border-bottom: 1px solid var(--border); padding: 6px 10px;
    text-align: right; white-space: nowrap;
}
.so-grid thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--bg-elev); color: var(--text-dim);
    font-weight: 600; text-transform: uppercase; font-size: 10px;
    cursor: pointer; user-select: none;
}
.so-grid thead th:hover { color: var(--text); }
.so-col.so-sort-desc::after { content: " ▾"; opacity: 0.8; }
.so-col.so-sort-asc::after  { content: " ▴"; opacity: 0.8; }
/* Zone separators (rest block, quirks block). */
.so-sep { border-left: 1px solid var(--border); }
/* Sticky Team column (header + body + group band). */
.so-team-head, .so-team, .so-group-cell {
    position: sticky; left: 0; z-index: 2;
    text-align: left !important; padding-left: 10px !important;
    background: var(--bg-elev);
}
.so-team-head { z-index: 4; min-width: 170px; }
.so-team { min-width: 170px; background: var(--bg); }
.so-grid tbody tr:hover .so-team { background: var(--bg-elev); }
.so-grid tbody tr:hover td { background: var(--bg-elev); }
.so-team .team-badge, .so-team .team-logo { margin-right: 7px; vertical-align: middle; }
.so-team .team-name { font-weight: 600; vertical-align: middle; }
.so-group-row td.so-group-cell {
    font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-dim); background: var(--bg); padding: 5px 10px 5px 12px;
}
.so-num { font-variant-numeric: tabular-nums; }

/* ── Public paywall module view (2026-07-30 pilot; NFL port 2026-07-31) ── */
/* The tabbed customer surface reuses the internal .page-nav/.page pattern
   plus the internal table styles; these are only the paywall-specific bits. */
.public-main.wide { max-width: 1280px; }
.page-nav .collapsible-tag { margin-left: 6px; font-size: 10px; padding: 2px 6px; }
.lock-card .cta-secondary {
    display: inline-block; margin-left: 14px; color: var(--text-dim);
    font-size: 13px; text-decoration: none;
    border-bottom: 1px dotted var(--text-dim); padding-bottom: 1px;
}
.lock-card .cta-secondary:hover { color: var(--text); }
.account-stub-menu a.item { text-decoration: none; }
.account-stub-menu a.item:hover,
.account-stub-menu .item.action:hover { background: var(--bg-row); color: var(--text); }
.account-stub-menu .item.action { cursor: pointer; color: var(--text); }
.acct-btn-avatar {
    width: 18px; height: 18px; border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: var(--bg);
    font-size: 10px; font-weight: 700; display: inline-flex;
    align-items: center; justify-content: center; overflow: hidden;
}
.acct-btn-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ── Mobile ───────────────────────────────────────────────────────────── */
@media (max-width: 600px) {
    .public-main { padding: 22px 14px 48px; }
    .public-hero h1 { font-size: 24px; }
    .public-lede { font-size: 14px; }
    .collapsible > summary { padding: 14px; font-size: 15px; }
    .collapsible-body { padding: 4px 14px 16px; }
    .ats-headlines.single { max-width: none; }
    .ats-headline-record { font-size: 24px; }
    .site-nav { gap: 14px; padding: 8px 14px; }
    .standings-grid { grid-template-columns: 1fr; }
}

/* Right-column gap fillers (injury report + division race, 2026-08-06). */
.ts-injury td, .ts-divrace td { padding: 5px 8px; font-size: 12px; border-bottom: 1px solid var(--border); }
.ts-injury tr:last-child td, .ts-divrace tr:last-child td { border-bottom: none; }
.ts-divrace .ts-div-me td { background: var(--bg-row); font-weight: 600; }
.ts-divrace td .team-logo.sm { margin-right: 5px; }

/* ── Site footer — the legal footer block (DESIGN_SYSTEM.md §3, 2026-08-13).
   Byte-identical markup on every SSA page; CSS copied verbatim from apex
   tokens.css. Chrome tokens only — no data colours here. */
.site-footer {
  text-align: center;
  padding: 40px 20px 28px;
  margin-top: auto;
  border-top: 1px solid var(--border-subtle);
}
.site-footer p { margin: 0 auto 8px; max-width: 760px; }
.site-footer-legal {
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.6;
}
.site-footer-links {
  color: var(--text-muted);
  font-size: 12.5px;
  padding-top: 4px;
}
.site-footer a { color: var(--accent); text-decoration: none; }
.site-footer a:hover { color: var(--text); }
.site-footer-copy { color: var(--text-muted); font-size: 12px; padding-top: 2px; }

/* SSA player-grade chips on the Team Detail depth chart (internal only).
   Rides the existing rank-chip ramp; NR uses the neutral token because an
   unreliable board is not "bad", it is unmeasured. */
.pg-chip { margin-left: 4px; min-width: 22px; }

/* Forecast team name -> Team Detail. Chrome, so the hover/focus affordance
   resolves through var(--accent) and carries no hex literal. Needed because
   the global `a { color: var(--text); text-decoration: none }` would otherwise
   render the link pixel-identically to the plain text it replaced. The anchor
   stays INLINE: .fc-team is `position: sticky; left: 0` with !important
   alignment and a min-width, and a block/flex anchor would fight both. The
   roll-up label cells (.fc-sum-label) carry no anchor, so they are untouched. */
.fc-team-link { color: inherit; text-decoration: none; cursor: pointer; }
.fc-team-link:hover .team-name,
.fc-team-link:focus-visible .team-name { color: var(--accent); text-decoration: underline; }

/* ── Rankings ELO teaser (free public view; John 2026-08-26) ──────────────
   Blurs SYNTHETIC digits — the server never sends the real rating to an
   unentitled session, so this is a preview of the column's shape, not a
   style over real data. */
.elo-teaser {
    filter: blur(5px);
    user-select: none;
    pointer-events: none;
    opacity: 0.85;
}
.elo-teaser-unlock {
    color: var(--accent);
    text-decoration: none;
    white-space: nowrap;
}
.elo-teaser-unlock:hover { text-decoration: underline; }
