/* ── 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: #4ade80; }

/* ── 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 ───────────────────────────────────────────
   Finish-position semantics; these encode DATA meaning, so they sit outside
   the shared block. The five fill tokens back the .fb-* classes below
   (wave-3 E45); the two ink tokens are the box LABEL colours — pure
   black/white on purpose, NOT --bg/--text: --cut-red measures 4.83:1 under
   white but only 4.01 under --text (an AA fail), and --wd-purple takes
   BLACK per the legend precedent (4.96:1; white fails at 4.23). */
:root {
    --win-blue: #38bdf8;
    --top10-green: #4ade80;
    --top20-green: #166534;
    --cut-red: #dc2626;
    --wd-purple: #8b5cf6;
    --fb-ink-dark: #000000;   /* label on the bright fills (win/top10/wd)  */
    --fb-ink-light: #ffffff;  /* label on the dark fills (top20/cut)      */
}

/* ── Reset ─────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
    background: var(--bg);
    color: var(--text-dim);
    font-family: var(--font-family);
    font-size: 14px;
    line-height: 1.5;
    min-height: 100vh;
}

/* ── Sign-in gate (static/js/app.js :: _renderSignInGate) ──────────────── */
.admin-locked {
    background: var(--bg-row); padding: 24px; border-radius: 6px;
    border: 1px dashed var(--border); text-align: center;
}
button.primary {
    background: var(--accent); border: 1px solid var(--accent); color: var(--bg);
    border-radius: 4px; font-family: var(--font-family); font-size: 14px;
    font-weight: 600;
}
button.primary:hover { filter: brightness(1.12); }

/* ── 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;
    /* E17 (wave 3): the L1 row sticks to the viewport top. z-index 30 clears
       every in-page sticky cell (the data grids use 3/4); the account
       dropdown (z 100) lives INSIDE this stacking context so it still
       paints above. */
    position: sticky;
    top: 0;
    z-index: 30;
}
.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; }

/* ── Skip link (E18, wave 3) — first element of <body>, visually parked
   off-screen until keyboard focus lands on it. Filled control → dark label
   (white on --accent fails AA; DESIGN_SYSTEM.md §1). ─────────────────────── */
.skip-link {
    position: absolute;
    top: -9999px;
    left: 16px;
    z-index: 40;               /* above the sticky .site-nav (30) */
    background: var(--accent);
    color: var(--bg);
    padding: 8px 14px;
    border-radius: 0 0 8px 8px;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
}
.skip-link:focus { top: 0; }

/* E18: keyboard focus ring on the chrome links (mouse clicks stay clean —
   :focus-visible). */
.site-nav-link:focus-visible,
.subsite-nav-link:focus-visible,
.nav-right-link:focus-visible,
.site-footer-links a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* ── Page Nav ──────────────────────────────────────────────────────────── */
.page-nav {
    display: flex;
    gap: 8px;
    padding: 12px 20px;
    flex-wrap: wrap;
}
.page-nav-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-dim);
    padding: 8px 16px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    font-family: var(--font-family);
    transition: all 0.15s;
}
.page-nav-btn:hover { border-color: var(--text-muted); }
.page-nav-btn.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--bg);
}
.refresh-btn { min-width: auto; padding: 8px 12px; }

.divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0 20px;
}

/* ── Page Containers ───────────────────────────────────────────────────── */
.page-container {
    display: none;
    padding: 16px 20px;
    max-width: 1400px;
}
.page-container.active { display: block; }

/* ── Typography ────────────────────────────────────────────────────────── */
h1 { font-size: 28px; font-weight: 700; color: var(--text); margin-bottom: 12px; }
h2 { font-size: 22px; font-weight: 700; color: var(--text); margin-bottom: 8px; }
h3 { font-size: 18px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
.caption { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }

/* ── Metric Cards ──────────────────────────────────────────────────────── */
.metric-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}
.metric-card {
    background: linear-gradient(135deg, var(--bg), var(--bg-elev));
    padding: 12px 16px;
    border-radius: 12px;
    border: 1px solid var(--border);
}
.metric-card-label { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.metric-card-value {
    font-size: 24px;
    font-weight: 700;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Stale-data caution on a metric card (wave-3 E25/F215) — the Tournament
   page's "Odds As Of" card takes this when the stamp lags the 3-hourly
   pipeline cadence. --warn is the DATA colour for staleness (never chrome);
   the card carries an explanatory title, so meaning is never colour-only. */
.metric-card.warn { border-color: var(--warn); }
.metric-card.warn .metric-card-value { color: var(--warn); }

/* ── Select / Input ────────────────────────────────────────────────────── */
.select-wrapper { margin-bottom: 12px; }
.select-wrapper label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; font-weight: 600; }
select, .text-input {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 13px;
    font-family: var(--font-family);
    width: 100%;
    max-width: 500px;
}
select:focus, .text-input:focus { outline: none; border-color: var(--accent); }
.text-input::placeholder { color: var(--text-muted); }

/* ── Sortable Table ────────────────────────────────────────────────────── */
.table-container {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 16px;
}
.data-table {
    width: 100%;
    min-width: 900px;
    border-collapse: collapse;
    font-size: 11px;
}
.data-table th {
    position: sticky;
    top: 0;
    background: var(--bg);
    color: var(--text-muted);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 6px 4px;
    text-align: center;
    cursor: pointer;
    user-select: none;
    border-bottom: 2px solid var(--border);
    white-space: nowrap;
    z-index: 2;
}
.data-table th:hover { color: var(--accent); }
/* The sort control is a real <button> (table.js) for keyboard/AT users; it
   must be visually indistinguishable from the bare th text it replaced. */
.data-table th .th-sort {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    text-align: inherit;
    white-space: inherit;
    cursor: pointer;
}
.data-table th .th-sort:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.data-table tbody tr[role="button"]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.data-table th .sort-arrow { font-size: 8px; margin-left: 2px; }
.data-table td {
    padding: 4px;
    text-align: center;
    color: var(--text-dim);
    border-bottom: 1px solid var(--border-subtle);
    white-space: nowrap;
}
.data-table tr:nth-child(even) { background: var(--bg-elev); }
.data-table tr:nth-child(odd) { background: var(--bg); }
.data-table tr:hover { background: var(--bg-row); }
.data-table tr.selected { background: var(--bg-row); outline: 1px solid var(--accent); }

/* Sticky columns */
.data-table .col-rank,
.data-table .col-player {
    position: sticky;
    z-index: 1;
}
.data-table .col-rank { left: 0; width: 30px; min-width: 30px; }
.data-table .col-player {
    left: 30px;
    min-width: 70px;
    text-align: left;
    font-weight: 500;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100px;
    border-right: 1px solid var(--border);
}
.data-table tr:nth-child(even) .col-rank,
.data-table tr:nth-child(even) .col-player { background: var(--bg-elev); }
.data-table tr:nth-child(odd) .col-rank,
.data-table tr:nth-child(odd) .col-player { background: var(--bg); }
.data-table tr:hover .col-rank,
.data-table tr:hover .col-player { background: var(--bg-row); }
.data-table th.col-rank, .data-table th.col-player { background: var(--bg); z-index: 3; }

/* Score coloring */
.score-high { color: var(--good); }
.score-mid { color: var(--accent); }

/* ── Finish Boxes ──────────────────────────────────────────────────────── */
.finish-boxes {
    display: flex;
    gap: 2px;
    flex-wrap: nowrap;
}
.finish-box {
    width: 36px;
    min-width: 36px;
    height: 26px;
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-weight: 600;
    cursor: default;
}
.finish-box-empty {
    width: 36px;
    min-width: 36px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    font-size: 11px;
}
.finish-box-sm { width: 28px; min-width: 28px; height: 24px; font-size: 9px; }

/* THE FINISH-BOX RAMP (wave-3 E45) — one semantic class per box, defined
   from the finish tokens above. `src/precompute.py::_finish_box_class`
   stamps the class on every box dict and `finish-box.js` PREFERS it; the
   legacy baked `bg`/`text_color` fields stay on the payload solely so
   blobs precomputed before this change (and the whole archive) keep
   rendering through the old inline path. The JS legend renders from these
   same classes, so badge and legend cannot disagree again. Ink pairing per
   the token-block comment: bright fills take --fb-ink-dark (WD included,
   per the legend precedent), dark fills take --fb-ink-light. */
.fb-win   { background: var(--win-blue);    color: var(--fb-ink-dark); }
.fb-top10 { background: var(--top10-green); color: var(--fb-ink-dark); }
.fb-top20 { background: var(--top20-green); color: var(--fb-ink-light); }
.fb-cut   { background: var(--cut-red);     color: var(--fb-ink-light); }
.fb-wd    { background: var(--wd-purple);   color: var(--fb-ink-dark); }
.fb-field { background: var(--bg-row);      color: var(--text-dim); }

/* ── Matchup Cards ─────────────────────────────────────────────────────── */
.matchup-header {
    display: flex;
    align-items: center;
    padding: 6px 14px;
    margin-bottom: 4px;
    gap: 6px;
}
.matchup-header-cell {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.matchup-card {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 12px;
    overflow-x: auto;
}
.matchup-card-inner { min-width: 700px; }
.matchup-row {
    display: flex;
    align-items: center;
    padding: 10px 14px;
    gap: 6px;
}
.matchup-row + .matchup-row { border-top: 1px solid var(--border-subtle); }
.matchup-badge {
    flex: 0 0 30px;
    margin: 0 4px;
    text-align: center;
    font-size: 13px;
    font-weight: 700;
}
/* THE D-S RAMP — the grade letter's colour, one band per class.
   `static/js/components/matchup-card.js::gradeClass` maps the letter to the
   band; band 1 is best. This REPLACED an inline `badge.style.color` fed by a
   hex on the payload (`_ds_grade` in src/precompute.py) — see that file's
   comment for why. Golf's ladder ends D · F, so its D is band 8 and its F is
   band 9; the football repos' D+ · D take the same two bands.
   The badge is unfilled: the LETTER carries the colour.

   DELIBERATELY UNSCOPED. Both the matchup-card badge and the page's D-S
   legend wear these classes, and the legend used to carry its own hand-copied
   hex ramp that had already drifted from the badges'. One rule set, so a key
   cannot start lying about the thing it explains. */
.g-band-1 { color: var(--grade-1); }
.g-band-2 { color: var(--grade-2); }
.g-band-3 { color: var(--grade-3); }
.g-band-4 { color: var(--grade-4); }
.g-band-5 { color: var(--grade-5); }
.g-band-6 { color: var(--grade-6); }
.g-band-7 { color: var(--grade-7); }
.g-band-8 { color: var(--grade-8); }
.g-band-9 { color: var(--grade-9); }
.matchup-badge-empty { flex: 0 0 30px; margin: 0 4px; }
.matchup-name {
    flex: 0 0 160px;
}
.matchup-name-text { font-size: 13px; font-weight: 600; color: var(--text); }
.matchup-name-sub { font-size: 11px; color: var(--text-muted); }
.matchup-odds {
    flex: 0 0 80px;
    text-align: center;
    font-size: 14px;
    font-weight: 700;
    color: var(--text);
}
.matchup-med {
    flex: 0 0 60px;
    text-align: center;
    font-size: 13px;
    color: var(--text-dim);
}
.matchup-boxes {
    flex: 1;
    display: flex;
    gap: 2px;
    flex-wrap: nowrap;
}

.matchup-course-boxes {
    flex: 0 0 150px;
    display: flex;
    gap: 2px;
    flex-wrap: nowrap;
    margin-left: 12px;
}

.matchup-year-strip {
    display: flex;
    gap: 2px;
    margin-top: 2px;
}

.matchup-year-strip div {
    width: 28px;
    min-width: 28px;
    text-align: center;
    font-size: 9px;
    font-weight: 400;
    color: var(--text-muted);
    text-transform: none;
    letter-spacing: 0;
}

/* E21 (wave 3): stacked per-player matchup blocks at phone widths. Desktop
   keeps the 700px min-width strip above untouched; below 600px each
   .matchup-row becomes a grid block — the grade badge leads the name line
   with the odds right-aligned, then the two labelled round-median cells,
   then the two box strips as full-width labelled rows. The positional
   .matchup-header cells cannot label a wrapped layout, so the header hides
   and the labels come from the renderer's data-label attributes via
   ::before — the football schedule card-per-game pattern. The box strips
   flex-wrap under their full-width label (10×28px fits a 375px viewport;
   narrower phones wrap to a second line instead of scrolling). */
@media (max-width: 600px) {
    .matchup-header { display: none; }
    .matchup-card { overflow-x: visible; }
    .matchup-card-inner { min-width: 0; }
    .matchup-row {
        display: grid;
        grid-template-columns: 30px minmax(0, 1fr) minmax(0, 1fr) auto;
        gap: 8px 10px;
        align-items: center;
        padding: 12px;
    }
    .matchup-badge, .matchup-badge-empty { grid-row: 1; grid-column: 1; margin: 0; }
    .matchup-badge { font-size: 16px; }
    .matchup-name { grid-row: 1; grid-column: 2 / 4; }
    .matchup-odds { grid-row: 1; grid-column: 4; text-align: right; }
    /* The two round-median cells are always children 4 and 5 when the
       market has a round (matchup-card.js appends them between the odds
       and the box strips); absent, the box strips auto-place upward. */
    /* .matchup-card prefix outranks the <=768px .matchup-med rule below,
       which otherwise wins on order at equal specificity. */
    .matchup-card .matchup-med { text-align: left; font-size: 12px; }
    .matchup-med:nth-child(4) { grid-row: 2; grid-column: 2 / 3; }
    .matchup-med:nth-child(5) { grid-row: 2; grid-column: 3 / 5; }
    .matchup-boxes, .matchup-course-boxes {
        grid-column: 1 / -1;
        flex-wrap: wrap;
        margin-left: 0;
    }
    /* 10 boxes must fit one line on a 375px viewport: the strips get
       375 − 28 (public-main) − 16 (page-container) − 2 (card border)
       − 24 (row padding) = 281px, and 10×28 + 9×2 = 298 overflows by one
       orphan box. 26px boxes need 278px. Narrower phones still wrap. */
    .matchup-boxes .finish-box-sm,
    .matchup-course-boxes .finish-box-sm {
        width: 26px;
        min-width: 26px;
    }
    .matchup-med[data-label]::before,
    .matchup-boxes[data-label]::before,
    .matchup-course-boxes[data-label]::before {
        content: attr(data-label);
        display: block;
        flex: 1 1 100%;
        font-size: 10px;
        font-weight: 600;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: var(--text-muted);
        margin-bottom: 2px;
    }
}

/* ── Legend ─────────────────────────────────────────────────────────────── */
.legend {
    display: flex;
    gap: 12px;
    margin-bottom: 8px;
    flex-wrap: wrap;
    align-items: center;
}
.legend-label { font-size: 12px; color: var(--text-muted); }
.legend-item {
    padding: 2px 8px;
    border-radius: 3px;
    font-size: 11px;
    font-weight: 600;
}

/* ── Player Detail Panel ───────────────────────────────────────────────── */
.player-detail {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}
.player-detail h3 { margin-bottom: 12px; }
.detail-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 8px;
    margin-bottom: 16px;
}
.detail-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    margin-bottom: 16px;
}
.detail-table th {
    background: var(--bg-elev);
    color: var(--text-muted);
    padding: 6px 8px;
    text-align: left;
    font-size: 11px;
    border-bottom: 1px solid var(--border);
}
.detail-table td {
    padding: 5px 8px;
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-dim);
}
.chart-container {
    max-width: 600px;
    height: 250px;
    margin-bottom: 16px;
}

/* ── Two Column Layout ─────────────────────────────────────────────────── */
.two-col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
}

/* ── Slider ────────────────────────────────────────────────────────────── */
.slider-wrapper { margin-bottom: 12px; }
.slider-wrapper label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; font-weight: 600; }
input[type="range"] {
    width: 100%;
    max-width: 300px;
    accent-color: var(--accent);
}
.slider-value { font-size: 12px; color: var(--text-dim); margin-left: 8px; }

/* ── Filter Row ────────────────────────────────────────────────────────── */
.filter-row {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    align-items: flex-end;
    margin-bottom: 16px;
}
.filter-row > div { flex: 1; min-width: 150px; }

/* ── Loading ───────────────────────────────────────────────────────────── */
.loading-overlay {
    position: fixed;
    inset: 0;
    background: rgba(11, 15, 22, 0.85);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 100;
}
.loading-spinner {
    width: 36px;
    height: 36px;
    border: 3px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { margin-top: 12px; color: var(--text-muted); font-size: 13px; }

/* ── Loading skeletons (wave-3 E28) ────────────────────────────────────────
   Token-styled shimmer rows (components/skeleton.js) shown in a page's
   table/content area during its first fetch — the free public tabs used to
   render their headers and then sit blank while the events manifest + first
   blob loaded. Purely decorative (aria-hidden on the wrapper); every
   consumer replaces the container wholesale when real data lands. Surfaces
   only — the shimmer is --bg-elev↔--bg-row, no data or chrome colours. */
.skeleton-rows {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}
.skeleton-row {
    height: 28px;
    border-radius: 6px;
    background: linear-gradient(90deg,
        var(--bg-elev) 25%, var(--bg-row) 45%, var(--bg-elev) 65%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
    .skeleton-row { animation: none; background: var(--bg-elev); }
}

/* ── Info / Empty States ───────────────────────────────────────────────── */
.info-msg {
    padding: 12px 16px;
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-muted);
    font-size: 13px;
}

/* ── Responsive ────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
    .page-container { padding: 12px 8px; }
    .page-nav { padding: 8px 8px; gap: 4px; }
    .page-nav-btn { padding: 6px 10px; font-size: 11px; }
    .two-col { grid-template-columns: 1fr; }
    .metric-cards { grid-template-columns: 1fr 1fr; }
    .data-table { font-size: 10px; }
    .finish-box { width: 28px; min-width: 28px; height: 22px; font-size: 8px; }
    .matchup-name { flex: 0 0 120px; }
    .matchup-name-text { font-size: 11px; }
    .matchup-odds { flex: 0 0 60px; font-size: 12px; }
    .matchup-med { flex: 0 0 48px; font-size: 11px; }
    h1 { font-size: 22px; }
    h2 { font-size: 18px; }
    .dfs-lineup-grid { grid-template-columns: repeat(2, 1fr); }
    .dfs-optimal-totals { flex-direction: column; gap: 4px; }
    .dfs-grade { flex-direction: column; text-align: center; }
}

/* ── DFS Builder ──────────────────────────────────────────────────────── */

.dfs-section {
    margin-bottom: 24px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}

/* Budget bar */
.dfs-budget-bar { margin-bottom: 16px; }
.dfs-budget-labels {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    margin-bottom: 6px;
}
.dfs-budget-used { color: var(--text-dim); font-weight: 600; }
.dfs-budget-remaining { color: var(--text-muted); }
.dfs-budget-remaining.over-budget { color: var(--bad); font-weight: 600; }
.dfs-budget-track {
    width: 100%;
    height: 10px;
    background: var(--border);
    border-radius: 5px;
    overflow: hidden;
}
.dfs-budget-fill {
    height: 100%;
    border-radius: 5px;
    transition: width 0.3s ease, background-color 0.3s ease;
}

/* Lineup slot grid */
.dfs-lineup-grid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 10px;
    margin-bottom: 12px;
}
.dfs-lineup-slot {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px;
    text-align: center;
    min-height: 90px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
}
.dfs-lineup-slot.empty {
    border-style: dashed;
    color: var(--text-muted);
}
.dfs-lineup-slot .slot-player {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 4px;
}
.dfs-lineup-slot .slot-salary {
    font-size: 12px;
    color: var(--text-muted);
}
.dfs-lineup-slot .slot-score {
    font-size: 12px;
    color: var(--accent);
    font-weight: 600;
}
.dfs-lineup-slot .slot-remove {
    position: absolute;
    top: 4px;
    right: 6px;
    background: none;
    border: none;
    color: var(--bad);
    cursor: pointer;
    font-size: 16px;
    font-weight: 700;
    line-height: 1;
    padding: 2px 4px;
}
.dfs-lineup-slot .slot-remove:hover {
    color: var(--bg);
    background: var(--bad);
    border-radius: 3px;
}

/* Lineup summary */
.dfs-lineup-summary {
    display: flex;
    gap: 16px;
    font-size: 13px;
    color: var(--text-dim);
    margin-bottom: 12px;
}
.dfs-lineup-summary strong { color: var(--text); }

/* Grade display */
.dfs-grade {
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 16px;
    margin-bottom: 16px;
    display: flex;
    align-items: center;
    gap: 16px;
}
.dfs-grade-letter {
    font-size: 32px;
    font-weight: 700;
}
.dfs-grade-detail {
    font-size: 13px;
    color: var(--text-muted);
}
.dfs-grade-detail strong { color: var(--text-dim); }

/* Lineup mode toggle (segmented control above optimal section) */
.dfs-mode-toggle {
    display: inline-flex;
    flex-wrap: wrap;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    margin: 8px 0 6px;
    background: var(--bg-elev);
    max-width: 100%;
}
.dfs-mode-btn {
    background: transparent;
    color: var(--text-dim);
    border: none;
    padding: 6px 12px;
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
    white-space: nowrap;
}
.dfs-mode-btn + .dfs-mode-btn {
    border-left: 1px solid var(--border);
}
.dfs-mode-btn:hover {
    background: var(--bg-row);
    color: var(--text);
}
.dfs-mode-btn.active {
    background: var(--accent);
    color: var(--bg);
}
.dfs-pool-note {
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-muted);
}

/* Optimal lineup totals */
.dfs-optimal-table { margin-bottom: 12px; }
.dfs-optimal-totals {
    display: flex;
    gap: 24px;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-top: 8px;
}

/* Player pool action buttons */
.dfs-add-btn, .dfs-remove-btn {
    padding: 2px 8px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-family: var(--font-family);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}
.dfs-add-btn { background: var(--good); color: var(--bg); }
.dfs-add-btn:hover { opacity: 0.85; }
.dfs-remove-btn { background: var(--bad); color: var(--bg); }
.dfs-remove-btn:hover { opacity: 0.85; }
.dfs-btn-disabled {
    padding: 2px 8px;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
}

/* Selected row highlight — matches .data-table tr.selected; selection is
   chrome, so it resolves through --accent, never a data colour. */
.data-table tr.dfs-selected { background: var(--bg-row); outline: 1px solid var(--accent); }

@media (max-width: 1024px) {
    .dfs-lineup-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ── Public paywall module view (2026-07-31, port of the CFL pilot) ────── */
/* The tabbed customer surface on golf.SSA reuses the internal .page-nav /
   .page-container pattern plus the internal table styles; these are the
   paywall-specific bits (nav-right account widget, hero, lock cards,
   free/locked chips, unlock banner). Uses the shared SSA tokens
   (--bg-elev / --text-muted / --accent …). */

.nav-right { display: flex; align-items: center; gap: 16px; margin-left: auto; }
.nav-right-link { color: var(--text-muted); 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-dim);
    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-muted);
    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;
}
.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; }

.public-main { max-width: 920px; margin: 0 auto; padding: 32px 20px 64px; }
.public-main.wide { max-width: 1280px; }
.public-hero { margin-bottom: 20px; }
.public-hero .kicker {
    font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--text-muted); margin: 0 0 10px;
}
.public-hero h1 { font-size: 30px; line-height: 1.15; margin: 0 0 12px; }
.public-lede { color: var(--text-muted); line-height: 1.6; font-size: 15px; max-width: 660px; margin: 0; }
.public-footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--border); }
.public-footer p { color: var(--text-muted); font-size: 12px; line-height: 1.6; margin: 0; }

/* Free / Subscribers chips on the page-nav tabs */
.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); }
/* Inside an accent-filled active tab the translucent wash composites to
   accent-on-accent (1.00:1) and the pill disappears — force it opaque. */
.page-nav-btn.active .collapsible-tag.locked { background: var(--bg); }
.page-nav .collapsible-tag { margin-left: 6px; font-size: 10px; padding: 2px 6px; }

/* Lock cards (gated tabs) */
.lock-card {
    text-align: center; padding: 36px 24px;
    /* var(--bg-row), matching the portfolio lock-card contract (wave 3). */
    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-muted); font-size: 14px; line-height: 1.55; }
.lock-card .lock-price { color: var(--text); font-weight: 600; }
.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; text-decoration: none;
}
.lock-card .cta:hover { opacity: 0.85; }
.lock-card .cta-secondary {
    display: inline-block; margin-left: 14px; color: var(--text-muted);
    font-size: 13px; text-decoration: none;
    border-bottom: 1px dotted var(--text-muted); padding-bottom: 1px;
}
.lock-card .cta-secondary:hover { color: var(--text); }

/* Synthetic sample behind the Matchups lock card (public.js E6 teaser).
   The content is fabricated and decorative: blurred, watermarked, inert
   (pointer-events/user-select none) and aria-hidden in the markup. The lock
   card overlaps it slightly and stays fully readable — never blur the CTA.
   Wrapper class is .lock-wrap — the portfolio-wide name the vendored
   lockcard.js emits (wave 3). */
.lock-wrap { position: relative; }
.lock-teaser {
    position: relative;
    overflow: hidden;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
}
.lock-teaser-content {
    filter: blur(2.5px);
    opacity: 0.65;
}
.lock-teaser-watermark {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    font-weight: 700;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    color: var(--text-muted);
    opacity: 0.55;
}
.lock-wrap .lock-card {
    position: relative;
    z-index: 1;
    margin-top: -24px;
}

/* Slim unlock banner (Tournament tab, stripped-rankings fallback) */
.unlock-banner {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin: 0 0 12px; padding: 10px 14px;
    background: var(--bg-elev); border: 1px solid var(--border);
    border-left: 3px solid var(--accent); border-radius: 6px;
    color: var(--text-muted); font-size: 13px;
}
.unlock-banner .cta {
    background: var(--accent); color: var(--bg); border-radius: 6px;
    padding: 5px 12px; font-size: 12px; font-weight: 600; text-decoration: none;
    margin-left: auto; white-space: nowrap;
}
.unlock-banner .cta:hover { opacity: 0.85; }

/* Loading / empty placeholders used by the public tab controller */
.empty { padding: 48px; text-align: center; color: var(--text-muted); }
.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;
}

@media (max-width: 600px) {
    .public-main { padding: 22px 14px 48px; }
    .public-hero h1 { font-size: 24px; }
    .public-lede { font-size: 14px; }
    /* E23 (wave 3): single-row horizontal scroll instead of wrapping. The
       edge fade is a sticky final flex item pinned to the scrollport's
       right edge (it returns to its flow position — the end of the row —
       at full scroll, so the affordance disappears exactly when there is
       nothing more to reach). Fade colour = --bg-elev, the nav's own
       surface. */
    .site-nav {
        gap: 14px;
        padding: 8px 14px;
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .site-nav::-webkit-scrollbar { display: none; }
    .site-nav::after {
        content: "";
        position: sticky;
        right: 0;
        flex: none;
        align-self: stretch;
        width: 28px;
        margin-left: -28px;
        z-index: 1;
        pointer-events: none;
        background: linear-gradient(to left, var(--bg-elev), transparent);
    }
    .site-nav-brand, .site-nav-link, .nav-right-link { white-space: nowrap; }
    .site-nav-divider, .nav-right { flex: none; }
    /* The scroll container clips absolutely-positioned descendants, so the
       account dropdown escapes it as position: fixed, just below the
       sticky L1 row. */
    .account-stub-menu { position: fixed; top: 54px; right: 10px; }
}

/* ── 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; }
