    :root {
      --bg-base: #09090b; --bg-surface: #111113; --bg-elevated: #18181b;
      --bg-hover: #27272a; --bg-active: #3f3f46;
      --bg-card: #111113; --bg-detail: #18181b; --bg-page: #09090b;
      --bg-raised: #27272a; --bg-sidebar: #0e0e10;
      --border: rgba(255,255,255,0.1); --border-subtle: rgba(255,255,255,0.06);
      --border-default: rgba(255,255,255,0.1); --border-strong: rgba(255,255,255,0.16); --border-card: rgba(255,255,255,0.22);
      --accent: #10b981; --accent-hover: #059669;
      --accent-muted: rgba(16,185,129,0.15); --accent-glow: #10b981;
      /* FIX 2026-07-23: the secondary/muted ramp cleared the 4.5:1 AA floor but
         still read as dim gray at a glance. Raised the whole muted end so the
         lowest rung sits ~7:1 and the top of the ramp ~11:1 on --bg-surface,
         keeping the ordering primary > secondary > muted > faint > dim. */
      --text-primary: #f4f4f5; --text-secondary: #d4d4d8; --text-muted: #c6c6cd; /* was #b4b4bc */
      --text-faint: #b0b0ba; /* was #8a8a94, 5.6:1 → 9.9:1 */
      --text-dim: #a3a3ad;   /* was #86868f, 5.2:1 → 7.5:1 (#71717a before that was a 3.9:1 AA fail) */
      --accent-blue: #10b981; --accent-blue-light: #34d399;
      --accent-green: #22c55e; --accent-green-bg: rgba(34,197,94,0.12);
      --accent-red: #f87171; --accent-red-bg: rgba(239,68,68,0.12); /* #ef4444 text was 4.49:1 on light rows — WCAG 1.4.3 nudge to red-400 (FIX 2026-07-18, ~6.8:1); bg tint unchanged */
      --accent-amber: #f59e0b; --accent-amber-bg: rgba(245,158,11,0.12);
      --ev-pos: #22c55e; --ev-neg: #f87171; /* lightened from #ef4444 for WCAG 1.4.3 AA (FIX 2026-07-18) */
      --shadow-card: 0 2px 8px rgba(0,0,0,0.4); --shadow-card-sm: 0 1px 3px rgba(0,0,0,0.4); --shadow-hover: 0 4px 16px rgba(0,0,0,0.5);
      --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-pill: 999px;
      --transition-fast: 120ms ease-in-out; --transition-normal: 200ms ease;
      --font-mono: 'IBM Plex Mono','JetBrains Mono','SF Mono','Fira Code',monospace;
      --space-xs: 4px; --space-sm: 8px; --space-md: 12px; --space-lg: 16px; --space-xl: 24px;
      /* FIX 2026-07-23: type ramp was 15/13/11/9 — body copy at 13px and micro
         labels at 9px are well under any comfortable reading floor. Raised across
         the board, and every hardcoded px/rem font-size in the app was swept onto
         the same curve. 12px is now the smallest text any surface can render; a
         live /ui pass is the check (209 text nodes sat at 11px after the first
         pass — game times, book names, grade chips — so the floor moved again). */
      --text-lg: 18px; --text-base: 15px; --text-sm: 13px; --text-xs: 12px;
    }
    /* Light theme removed — dark mode only */
    html { scroll-behavior: smooth; }
    body { font-family: 'IBM Plex Sans',system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; margin:0; background:var(--bg-page); color:var(--text-secondary); font-size:var(--text-base); line-height:1.55; }
    :focus-visible { outline:2px solid var(--accent-blue); outline-offset:2px; }
    /* Top Nav */
    /* gap 2026-09-05: xl->md. With Home removed the bar has two gaps, not
       three; --space-md (12px) x2 = 24 -- one piece of the real 235px
       deficit measured at 1440px, not the whole of it (2026-09-05 fix round
       1, finding 4: this alone does not "complete" anything; the tagline and
       .top-nav-tabs's own margin-left, removed elsewhere, are needed too).
       Already the gap the 640-1399px block below uses, so desktop is now
       consistent -- that block's own gap declaration is redundant but
       harmless; left in place. */
    .top-nav { position:fixed; top:0; left:0; right:0; height:56px; padding-top:env(safe-area-inset-top); background:var(--bg-surface); border-bottom:1px solid var(--border-subtle); display:flex; align-items:center; padding-left:var(--space-xl); padding-right:var(--space-xl); padding-bottom:0; z-index:100; gap:var(--space-md); }
    .top-nav-logo-wrap { display:flex; flex-direction:column; line-height:1; min-width:160px; flex-shrink:0; }
    .top-nav-logo { font-size:1.4rem; font-weight:300; color:var(--text-primary); white-space:nowrap; font-family:'IBM Plex Sans',sans-serif; letter-spacing:-0.02em; }
    .top-nav-logo .logo-pipe { font-weight:800; color:var(--accent); text-shadow:0 0 8px rgba(16,185,129,0.4); }
    .top-nav-logo .logo-bold { font-weight:700; }
    .top-nav-logo .logo-sigma { font-weight:700; font-size:0.75em; vertical-align:0.1em; color:var(--accent); text-shadow:0 0 14px rgba(16,185,129,0.5); }
    /* Prediction Markets tables */
    .pm-table { width:100%; border-collapse:collapse; font-size:var(--text-sm); }
    .pm-table th { font-size:var(--text-xs); color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.06em; font-weight:700; padding:8px 12px; text-align:left; border-bottom:1px solid var(--border-default); background:var(--bg-surface); }
    .pm-table td { padding:8px 12px; border-bottom:1px solid var(--border-subtle); color:var(--text-secondary); vertical-align:top; }
    .pm-table tr:hover td { background:var(--bg-hover); }
    .pm-table td:nth-child(n+3) { font-family:var(--font-mono); }
    .top-nav-tabs { display:flex; gap:var(--space-xs);
      /* No margin-left here (removed 2026-09-05, BASE/DESKTOP SCOPE ONLY):
         .top-nav's own flex gap already separates the strip from the logo,
         so this was redundant double-spacing that predated the gap -- worth
         24 of the real 235px deficit (the bar wanted 1675px at 1440, not the
         1651/211 an earlier draft of this arithmetic missed). The
         (min-width:640px) and (max-width:1399px) block below deliberately
         keeps its own smaller margin-left: var(--space-sm) (8px) on this
         same selector -- that band was not measured for this fix and is
         left untouched (2026-09-05 fix round 1, finding 3). */
      /* min-width:0 is the load-bearing bit: a flex item's default min-width is
         auto, i.e. its content, so this strip refused to shrink and shoved
         .top-nav-right (Sign In included) past the viewport edge with no way to
         scroll to it. Now the strip is the only thing that gives. */
      min-width:0; flex-shrink:1; overflow-x:auto; overscroll-behavior-x:contain;
      scrollbar-width:none; -ms-overflow-style:none; }
    /* A visible scrollbar inside a 56px nav would eat the tabs' underline. */
    .top-nav-tabs::-webkit-scrollbar { display:none; }

    /* Scroll affordance for the tab strip (2026-09-05).

       The strip is the only thing in this bar that scrolls, and its scrollbar
       is deliberately hidden above -- so between 1024px and ~1217px a clipped
       tab had NO signal at all that it existed. Measured on prod at 94c5c8cf:
       at 1280px the UFC tab was cut 7px, at 1024px 257px, with no scrollbar,
       no fade and no mask.

       A mask, not a scrollbar: a visible scrollbar inside a 56px nav eats the
       tabs' underline (the reason it was hidden in the first place), and a
       background gradient sits BEHIND the tab text so it cannot soften the
       cut edge. mask-image is the only one of the three that fades content.

       Applied via classes, never unconditionally: an always-on mask would dim
       the last tab at 1440px where nothing overflows. app.js toggles these
       from the strip's own scroll metrics, so a signed-out user (5 tabs) and
       a Pro user (7) each get the fade exactly when their own strip clips. */
    .top-nav-tabs.nav-fade-end {
      -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
              mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); }
    .top-nav-tabs.nav-fade-start {
      -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent 100%);
              mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent 100%); }
    .top-nav-tabs.nav-fade-start.nav-fade-end {
      -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
              mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%); }

    /* Sidebar chip strip fade (2026-09-05). Same problem as the nav strip and
       the same fix: at <=1024px #sport-tabs-container lays the league rows out
       horizontally with its scrollbar suppressed. Measured on prod at 52ace68f
       at a 375px viewport it was 1948px of content in a 375px box -- 1573px
       hidden, 12 of 15 chips entirely off-screen -- with no scrollbar, no mask
       and no fade. A partially cut chip at the right edge was the only hint.

       MEDIA-SCOPED, and that is load-bearing. app.js sets these classes from
       the element's own scroll metrics, and above 1024px the SAME element is
       the vertical sidebar -- where it still reports ~6px of incidental
       horizontal overflow (measured 146 scrollWidth against 140 clientWidth at
       1440px). That is enough to trip a >1px test and paint a 28px fade down
       the desktop league list. Caught in a browser before shipping; the class
       may still be set up there, but no mask is painted.

       Inside the query the classes still decide, so a narrow viewport whose
       chips happen to fit gets no fade either. */
    @media (max-width: 1024px) {
      #sport-tabs-container.chip-fade-end {
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
                mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); }
      #sport-tabs-container.chip-fade-start {
        -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent 100%);
                mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent 100%); }
      #sport-tabs-container.chip-fade-start.chip-fade-end {
        -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
                mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%); }
    }
    /* Icon-only pill (Settings). Keeps the pill's height and hit area while
       dropping the ~44px the word cost; the accessible name moves to
       aria-label + title on the button in index.html. */
    .top-nav-icon-pill { padding-left:9px; padding-right:9px; }
    .top-nav-icon-pill svg { display:block; }
    .top-nav-tab { padding:var(--space-sm) var(--space-md); font-size:15px; font-weight:500; color:var(--text-muted); cursor:pointer; border-bottom:2px solid transparent; transition:color var(--transition-fast),border-color var(--transition-fast); line-height:52px; white-space:nowrap; flex-shrink:0; }
    .top-nav-tab:hover { color:var(--text-secondary); }
    .top-nav-tab.active { color:var(--accent); border-bottom-color:var(--accent); font-weight:600; }
    .top-nav-right { margin-left:auto; display:flex; align-items:center; gap:var(--space-sm); flex-shrink:0; }
    .top-nav-settings { position:relative; }
    .top-nav-pill { background:none; border:1px solid var(--border-strong); border-radius:var(--radius-sm); padding:6px 14px; color:var(--text-secondary); cursor:pointer; font-size:15px; font-weight:500; transition:all var(--transition-fast); font-family:'IBM Plex Sans',sans-serif; display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
    .top-nav-pill:hover { border-color:var(--accent); color:var(--text-primary); }
    .bankroll-pill { gap:2px; }
    /* margin-left removed 2026-09-05: it separated "$" from the "Bankroll"
       label removed the same day; "$" is now the pill's first child, and the
       margin was leaving it visibly off-centre against the right side. */
    .bankroll-pill .bankroll-sep { color:var(--text-muted); font-family:var(--font-mono); font-weight:600; }
    .bankroll-pill .bankroll-input { background:none; border:none; color:var(--text-primary); font-family:var(--font-mono); font-size:15px; font-weight:600; width:60px; padding:0; outline:none; -moz-appearance:textfield; }
    .bankroll-pill .bankroll-input::-webkit-inner-spin-button,
    .bankroll-pill .bankroll-input::-webkit-outer-spin-button { -webkit-appearance:none; margin:0; }
    .bankroll-pill .bankroll-input:focus { color:var(--accent); }
    .bankroll-pill .bankroll-units { color:var(--text-muted); font-family:var(--font-mono); font-size:15px; font-weight:600; border-left:1px solid var(--border-default); padding-left:6px; margin-left:4px; }
    .top-nav-dropdown { position:absolute; top:calc(100% + 4px); right:0; width:260px; background:var(--bg-surface); border:1px solid var(--border-default); border-radius:var(--radius-md); padding:var(--space-md); display:none; box-shadow:0 8px 24px rgba(0,0,0,0.4); z-index:200; }
    .top-nav-dropdown.open { display:block; }
    .top-nav-dropdown label { font-size:var(--text-sm); color:var(--text-secondary); display:block; margin-bottom:var(--space-xs); }
    .top-nav-dropdown input[type="number"] { width:100%; box-sizing:border-box; background:var(--bg-elevated); border:1px solid var(--border-default); border-radius:var(--radius-sm); padding:var(--space-xs) var(--space-sm); color:var(--text-primary); font-size:var(--text-base); margin-bottom:var(--space-sm); }
    .top-nav-dropdown input:focus { border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-muted); outline:none; }
    /* Hamburger (mobile only) + mobile menu sheet */
    .top-nav-hamburger { display:none; background:none; border:1px solid var(--border-strong); border-radius:var(--radius-sm); width:44px; height:44px; padding:11px 9px; cursor:pointer; flex-direction:column; justify-content:space-between; align-items:stretch; flex-shrink:0; }
    .top-nav-hamburger:hover { border-color:var(--accent); }
    .top-nav-hamburger:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
    .top-nav-hamburger span { display:block; height:2px; background:var(--text-secondary); border-radius:1px; }
    .top-nav-mobile-menu { display:none; left:auto; right:var(--space-md); top:calc(56px + env(safe-area-inset-top) + 4px); width:240px; }
    .top-nav-mobile-menu.open { display:flex; flex-direction:column; gap:6px; }
    .top-nav-mobile-menu .top-nav-pill { width:100%; justify-content:flex-start; min-height:44px; }
    /* Bottom nav (mobile chrome) */
    .bottom-nav { display:none; position:fixed; left:0; right:0; bottom:0; padding-bottom:env(safe-area-inset-bottom); background:var(--bg-surface); border-top:1px solid var(--border-subtle); z-index:100; }
    .bottom-nav-inner { display:flex; align-items:stretch; justify-content:space-around; height:64px; }
    .bottom-nav-item { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; flex:1 1 0; min-width:44px; min-height:44px; background:none; border:none; color:var(--text-muted); font-size:12px; font-weight:500; cursor:pointer; transition:color var(--transition-fast); padding:6px 4px; font-family:'IBM Plex Sans',sans-serif; }
    .bottom-nav-item:hover { color:var(--text-secondary); }
    .bottom-nav-item.active { color:var(--accent); }
    .bottom-nav-item:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; }
    .bottom-nav-item .bn-icon { font-size:20px; line-height:1; letter-spacing:-0.05em; display:inline-flex; align-items:center; justify-content:center; }
    .bottom-nav-item .bn-icon svg { width:22px; height:22px; display:block; }
    .bottom-nav-item .bn-label { letter-spacing:0.02em; }
    /* Medium-desktop (640-1399px): full desktop chrome but tightened so it fits
       narrower laptop widths (1280, 1366, 1440) without overflowing. Without this,
       7 tabs + bankroll-pill + Settings + Parlay + Sign-In overflows the viewport
       at ~1363px minimum natural width.

       FIX 2026-08-22: tightening alone never closed the gap. Measured on prod at
       a 1100px viewport the nav still wanted 1445px, so ~345px ran off the right
       edge -- and because nothing here scrolls horizontally, everything past the
       edge was simply unreachable. Sign In is the last child of .top-nav-right,
       so it was the first thing lost: at 1280px it sat at x=1375. The whole band
       from 640px (where the hamburger takes over) to ~1400px was a dead zone.
       The real fix is the min-width:0 on .top-nav-tabs above; this block now only
       does what its name says, buying space rather than pretending to fix
       overflow. */
    @media (min-width: 640px) and (max-width: 1399px) {
      .top-nav { gap: var(--space-md); padding-left: var(--space-md); padding-right: var(--space-md); }
      .top-nav-tabs { margin-left: var(--space-sm); gap: 0; }
      .top-nav-pill { padding: 6px 10px; }
      .bankroll-pill .bankroll-units { display: none; }
    }
    /* 640-1204px: below ~860px even a zero-width tab strip cannot absorb enough --
       the fixed chrome alone (logo 160 + right-hand controls ~448 + gaps)
       needs ~688px now that the Home button and the Bankroll label are gone
       (2026-09-05); the ~860 figure above predates those removals and has
       not been re-measured since.
       CUTOFF 1204 (was 1039, widened 2026-09-05). The old comment justified
       1039 by a one-pixel cliff -- "a 900px viewport gave the tabs 27px while
       899px gave them 257px". That cliff is gone: re-measured on prod at
       0e8ac831, 899px gives the strip 351px and 900px gives it 352px. It was
       an artifact of the strip refusing to shrink, and the min-width:0 fix
       (2026-08-22) removed it. The stated reason for 1039 no longer holds.

       1039 was also 160px too early, which made the nav NON-MONOTONIC: at
       1039 the strip is 506px with 120px of overflow; at 1040 the full-size
       logo returns, the strip drops to 466px and overflow RISES to 160px.
       Widening the window by one pixel hid another tab.

       1204 is where the full-size logo stops costing anything. Measured on
       prod at 0e8ac831, anonymous (7 tabs, the worst case): overflow is 40px
       at 1160, 10px at 1190 and 0 at 1205. So the small logo now persists
       exactly as long as the strip needs the 40px, and the one remaining
       size change lands where there is no overflow on either side.

       Tier note: a user whose tabs do not include cricket/ufc fits sooner, so
       they keep the smaller logo a little past the point they needed it. That
       is cosmetic and one-directional -- it never causes clipping -- whereas a
       breakpoint set for the fewest-tabs case would clip the most-tabs case.

       Recover the space from the lowest-value pixels, never from Sign In:
         - the oversized logo, per the <=639px rules.
         - the Home button and the "Bankroll" word are both already gone
           (2026-09-05), so there is nothing left to hide there at any width.
       */
    @media (min-width: 640px) and (max-width: 1204px) {
      .top-nav-logo-wrap { min-width: auto; }
      .top-nav-logo { font-size: 1.05rem; }
    }
    /* Mobile breakpoint (<640px): collapse tabs + Settings + Parlay into hamburger; show bottom nav */
    @media (max-width: 639px) {
      .top-nav { padding-left:var(--space-md); padding-right:var(--space-md); gap:var(--space-sm); }
      .top-nav-hamburger { display:flex; }
      .top-nav-tabs { display:none; }
      .top-nav-logo-wrap { min-width:auto; }
      .top-nav-logo { font-size:1.05rem; }
      .top-nav-right { gap:var(--space-xs); margin-left:auto; }
      /* FIX 2026-07-12: collapse everything except the bankroll pill into the
         hamburger/bottom-nav on phones. The old selector targeted
         onclick*="openParlaySlip" — a handler that no longer exists (the
         Parlay Cart button now calls Slate.toggleCart) — so Parlay Cart,
         Builder and Sign In never hid and overflowed/collided with the
         hamburger at <=430px. Login stays reachable via the bottom-nav
         Account item. */
      .top-nav-right > *:not(.bankroll-pill) { display:none; }
      /* 2026-05-17 audit S2: keep '$' on mobile so "1000" doesn't read as a phone number. */
      .bankroll-pill .bankroll-units { display:none; }
      .top-nav-pill { min-height:44px; padding:8px 12px; }
      .bottom-nav { display:block; }
      .feedback-fab { display:none; }
      /* Touch targets (WCAG 2.5.5). A live 375px audit found these well under
         the 44px floor this very block already applies to .top-nav-pill — the
         2026-07-18 sweep set the standard but nothing enforced it, so controls
         outside its scope never got it. Measured: bankroll input 19px, tutorial
         skip 17px, role="link" logo/home wrapper 16px.
         .bankroll-input drives unit sizing + Kelly math, so a mis-tap there
         mis-sizes every stake the product recommends.
         NOT included: the inline "See how it works" link inside the sign-in
         sentence — WCAG exempts inline links in a text block.
         Pinned by tests/test_slate_touch_targets.py. */
      .bankroll-pill .bankroll-input { min-height:44px; }
      .tutorial-skip-link { min-height:44px; display:flex; align-items:center; justify-content:center; }
      .top-nav-logo-wrap { min-height:44px; justify-content:center; }
    }
    /* 640-799px: the desktop bar does not fit here (2026-09-06).
       Measured on prod at 03b6bec5, anonymous, 7 tabs: the strip gets 107px at
       640 and 252px at 800 against a 355px fixed right rail, so 1 of 7 tabs is
       visible at 640 and 2 at 800. One pixel below 640 the same user gets a
       hamburger listing all seven at 44px each. This band takes that layout.

       NOT a copy of the <=639px block above: this keeps the account control in
       the bar. At 375px the rail has no room for it (the pill overflowed 61px
       on 2026-09-02, unreachable) and login lives in the bottom-nav Account
       item; here there is room, and Sign In is the primary CTA for anonymous
       users. Exactly one of #login-btn / #user-pill is ever visible, so both
       are exempted -- naming only #login-btn would drop a signed-in user's
       account menu across this whole band.

       The <=639px block is deliberately NOT edited: three test files pin rules
       inside it and the 375px rendering is already correct.

       Spec: docs/superpowers/specs/2026-09-05-hamburger-breakpoint-design.md
       Plan: docs/superpowers/plans/2026-09-06-hamburger-breakpoint.md */
    @media (min-width: 640px) and (max-width: 799px) {
      .top-nav-hamburger { display:flex; }
      .top-nav-tabs { display:none; }
      .bottom-nav { display:block; }
      .top-nav-right > *:not(.bankroll-pill):not(#login-btn):not(#user-pill) { display:none; }
    }
    /* Layout */
    .app-layout { display:grid; grid-template-columns:180px 1fr; min-height:100vh; padding-top:56px; }
    /* Tutorial centered card */
    .tutorial-overlay { position:fixed; inset:0; z-index:10000; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,0.6); backdrop-filter:blur(4px); transition:opacity 0.3s ease; }
    .tutorial-overlay.hidden { display:none; }
    .tutorial-card { background:var(--bg-card); border:1px solid var(--border-default); border-top:3px solid var(--accent); border-radius:var(--radius-lg); width:min(560px, 90vw); box-shadow:0 24px 80px rgba(0,0,0,0.5); animation:modalFadeIn 0.3s ease; overflow:hidden; }
    .tutorial-card-header { display:flex; align-items:center; justify-content:space-between; padding:16px 20px; }
    .tutorial-card-logo { font-size:1.2rem; font-weight:300; color:var(--text-primary); }
    .tutorial-card-logo .pipe { font-weight:800; color:var(--accent); text-shadow:0 0 8px rgba(16,185,129,0.3); }
    .tutorial-card-logo .bold { font-weight:700; }
    .tutorial-card-logo .sigma { font-weight:700; font-size:0.7em; vertical-align:0.08em; color:var(--accent); text-shadow:0 0 8px rgba(16,185,129,0.3); }
    .tutorial-card-close { background:none; border:1px solid var(--border-default); color:var(--text-muted); width:44px; height:44px; border-radius:var(--radius-sm); cursor:pointer; font-size:16px; display:flex; align-items:center; justify-content:center; transition:all 0.15s; }
    .tutorial-card-close:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-muted); }
    .tutorial-card-body { padding:0 20px 20px; text-align:center; }
    .tutorial-card-tagline { font-family:var(--font-mono); font-size:15px; letter-spacing:0.18em; text-transform:uppercase; color:var(--accent); margin-bottom:16px; text-align:center; }
    .tutorial-card-headline { font-size:1.3rem; font-weight:700; color:var(--text-primary); letter-spacing:-0.02em; margin-bottom:8px; }
    .tutorial-card-desc { font-size:var(--text-base); color:var(--text-muted); line-height:1.6; max-width:420px; margin:0 auto 24px; }
    /* gap 20px -> 12px (2026-08-01): the row lists all ten steps now, not the
       seven it used to show while the copy beside it claimed ten. */
    .tutorial-card-steps { display:flex; justify-content:center; gap:12px; margin-bottom:24px; flex-wrap:wrap; }
    .tutorial-card-step { display:flex; flex-direction:column; align-items:center; gap:4px; }
    .tutorial-card-step-num { width:28px; height:28px; border-radius:50%; background:var(--accent-muted); border:1px solid rgba(16,185,129,0.25); display:flex; align-items:center; justify-content:center; font-family:var(--font-mono); font-size:12px; font-weight:700; color:var(--accent); }
    .tutorial-card-step-label { font-size:12px; font-weight:600; color:var(--text-faint); text-transform:uppercase; letter-spacing:0.06em; }
    .tutorial-play-btn { display:inline-flex; align-items:center; gap:8px; padding:12px 32px; border-radius:var(--radius-sm); border:none; background:var(--accent); color:#0d1117; font-family:'IBM Plex Sans',sans-serif; font-size:15px; font-weight:700; cursor:pointer; transition:all 0.15s; letter-spacing:0.02em; min-height:44px; } /* dark text: #fff was 2.54:1 on the green — WCAG 1.4.3 fail (FIX 2026-07-18, now 7.5:1) */
    .tutorial-play-btn:hover { background:#0ea572; transform:translateY(-1px); box-shadow:0 4px 16px rgba(16,185,129,0.3); }
    .tutorial-play-btn svg { flex-shrink:0; }
    .tutorial-skip-link { display:block; margin-top:12px; font-size:var(--text-sm); color:var(--text-faint); cursor:pointer; text-decoration:underline; text-underline-offset:2px; transition:color 0.15s; }
    .tutorial-skip-link:hover { color:var(--text-secondary); }
    /* Tutorial fullscreen mode */
    .tutorial-fullscreen { position:fixed; inset:0; z-index:10001; background:#06080c; display:none; flex-direction:column; }
    .tutorial-fullscreen.active { display:flex; }
    .tutorial-fs-header { display:flex; align-items:center; justify-content:flex-end; padding:8px 16px; background:rgba(9,9,11,0.95); border-bottom:1px solid rgba(255,255,255,0.08); flex-shrink:0; }
    .tutorial-fs-close { background:none; border:1px solid rgba(255,255,255,0.12); color:var(--text-muted); width:36px; height:36px; border-radius:var(--radius-sm); cursor:pointer; font-size:20px; display:flex; align-items:center; justify-content:center; transition:all 0.15s; }
    .tutorial-fs-close:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-muted); }
    .tutorial-fs-iframe { flex:1; width:100%; border:none; background:#06080c; }
    .sidebar { background:var(--bg-sidebar); border-right:1px solid var(--border-default); padding:12px 0; position:sticky; top:56px; height:calc(100vh - 56px); overflow-y:auto; display:flex; flex-direction:column; }
    .sidebar-section { padding:12px 12px 4px; }
    .sidebar-section-title { font-size:var(--text-sm); font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.1em; padding:0 8px; margin-bottom:4px; }
    .sidebar-nav-item { display:flex; align-items:center; padding:8px 12px; border-radius:var(--radius-sm); cursor:pointer; font-size:var(--text-base); font-weight:600; color:var(--text-primary); transition:background var(--transition-fast),color var(--transition-fast); border-left:3px solid transparent; margin:1px 0; gap:8px; }
    .sidebar-nav-icon { width:18px; text-align:center; font-size:1rem; line-height:1; flex-shrink:0; }
    .sidebar-count { margin-left:auto; }
    .sidebar-nav-item:hover { background:var(--accent-muted); color:var(--text-primary); }
    .sidebar-nav-item.active { background:var(--accent-muted); color:var(--accent-blue); font-weight:700; border-left-color:var(--accent-blue); }
    /* No flex-shrink meant this defaulted to 1: in a tight row the badge was
       compressed toward its 18px min-width while its digits needed 25px, and
       they spilled outside the pill. Measured on #ucl-count with a two-digit
       value. Data-dependent -- only shows with a long-named league AND a
       two-digit count -- so it is guarded by a test, not left to inspection. */
    .sidebar-count { font-size:var(--text-base); font-weight:600; background:var(--bg-hover); color:var(--text-muted); padding:2px 7px; border-radius:var(--radius-pill); min-width:18px; text-align:center; flex-shrink:0; }
    .sidebar-nav-item.active .sidebar-count { background:var(--accent-muted); color:var(--accent-blue); }
    /* ADDED 2026-09-05 (NCAAF): liquidity-tier chip on the game header. slate.css loads
       after this file but defines no .tier-chip rule (verified by grep), so this wins. */
    .tier-chip { display:inline-block; margin-left:6px; padding:1px 6px; border-radius:var(--radius-pill); font-size:0.75rem; font-weight:700; line-height:1.4; background:var(--bg-hover); color:var(--text-muted); vertical-align:middle; }
    .tier-chip.tier-1 { background:#1f6f43; color:#e8fff1; }
    .tier-chip.tier-3 { background:#6b4e12; color:#fff3d6; }
    .tier-chip.tier-4 { background:#6b1f1f; color:#ffe3e3; }
    /* Hierarchical category styles */
    .sidebar-category { border:none; padding:0; margin:0; }
    .sidebar-category > summary { display:flex; align-items:center; gap:6px; padding:6px 12px; font-size:var(--text-sm); font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.06em; cursor:pointer; list-style:none; user-select:none; }
    .sidebar-category > summary::-webkit-details-marker { display:none; }
    .sidebar-category > summary::before { content:'\25BE'; font-size:var(--text-sm); width:10px; text-align:center; transition:transform 0.15s ease; color:var(--text-faint); }
    .sidebar-category:not([open]) > summary::before { transform:rotate(-90deg); }
    .sidebar-category > summary:hover { color:var(--text-primary); }
    .sidebar-category > summary .cat-icon { font-size:var(--text-base); line-height:1; }
    .sidebar-league-list { padding:0 0 2px; }
    /* Row budget: the row is 152px, of which up to 107px was chrome (28
       padding + 32 logo + 12 gaps + ~23 count + 12 padding), leaving the name
       36-50px against names needing up to 133px. Reclaiming padding, gaps and
       logo size gives back ~30px. Rows become 39px rather than 42 because the
       logo shrinks -- accepted.

       The <=1024px chip strip (main.css:~1170, `.app-layout > .sidebar
       .sidebar-league-item`) overrides this rule's padding, margin and
       border, but NOT its gap, and nothing in that media block overrides
       .league-icon or .league-icon img below -- so both the gap change
       (6px->4px) and the logo change (32px->22px) reach the chip strip too.
       Inspected, not covered by an automated test.

       Coupling with the count badge (Task 1, .sidebar-count flex-shrink:0):
       now that the badge no longer shrinks, the name's available width
       depends on the count's digit count -- measured ~75px with a one-digit
       count, ~66px with two digits. Bundesliga is the tightest real name at
       78px, so it has ~3px of slack at one digit and overruns slightly at
       two, absorbed by the flex layout without wrapping or spilling. A
       league name longer than ~75px, or a three-digit count, should prompt a
       re-measure. */
    .sidebar-league-item { display:flex; align-items:center; padding:6px 12px 6px 12px; border-radius:var(--radius-sm); cursor:pointer; font-size:var(--text-base); font-weight:600; color:var(--text-primary); transition:background var(--transition-fast),color var(--transition-fast); border-left:3px solid transparent; margin:1px 0; gap:4px; }
    .sidebar-league-item:hover { background:var(--accent-muted); color:var(--text-primary); }
    .sidebar-league-item.active { background:var(--accent-muted); color:var(--accent-blue); font-weight:700; border-left-color:var(--accent-blue); }
    .sidebar-league-item .league-icon { flex-shrink:0; line-height:0; width:22px; display:flex; align-items:center; justify-content:center; }
    .sidebar-league-item .league-icon img { height:22px; width:auto; max-width:22px; object-fit:contain; display:block; }
    .sidebar-league-item .sidebar-count { margin-left:auto; }
    .sidebar-league-item.active .sidebar-count { background:var(--accent-muted); color:var(--accent-blue); }
    .sidebar-league-item.placeholder { opacity:0.5; }
    .sidebar-control { padding:4px 8px; }
    .sidebar-control label { display:block; font-size:var(--text-sm); font-weight:500; color:var(--text-muted); margin-bottom:2px; }
    .sidebar-control input[type="number"], .sidebar-control input[type="text"] { width:100%; box-sizing:border-box; background:var(--bg-page); border:1px solid var(--border-default); color:var(--text-primary); border-radius:var(--radius-sm); padding:5px 8px; font-size:var(--text-base); }
    .sidebar-control input:focus { border-color:var(--accent-blue); box-shadow:0 0 0 2px var(--accent-muted); outline:none; }
    .sidebar-control-row { display:flex; align-items:center; gap:6px; padding:4px 8px; }
    .sidebar-control-row label { font-size:var(--text-sm); color:var(--text-muted); font-weight:500; }
    .sidebar-control-row input[type="checkbox"] { accent-color:var(--accent-blue); }
    .sidebar-settings-collapse { border:none; padding:12px 12px 4px; margin:0; }
    .sidebar-settings-collapse > summary::-webkit-details-marker { display:none; }
    .sidebar-settings-collapse > summary { list-style:none; }
    .sidebar-settings-arrow { font-size:var(--text-sm); transition:transform 0.15s ease; display:inline-block; }
    .sidebar-settings-collapse:not([open]) .sidebar-settings-arrow { transform:rotate(-90deg); }
    .sidebar-advanced { padding:0 8px; margin-top:4px; }
    .sidebar-advanced summary { cursor:pointer; font-size:var(--text-sm); color:var(--text-muted); font-weight:500; padding:4px 0; }
    .sidebar-refresh { margin:12px 16px; background:var(--accent-blue); border:none; color:#0d1117; padding:8px 16px; border-radius:var(--radius-sm); cursor:pointer; font-size:var(--text-base); font-weight:600; text-transform:uppercase; letter-spacing:0.04em; transition:opacity var(--transition-normal); }
    .sidebar-refresh:hover { opacity:0.9; }
    .sidebar-footer { margin-top:auto; padding:0 16px 24px; display:flex; gap:8px; }
    .sidebar-footer a { font-size:var(--text-sm); color:#fff; text-decoration:none; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; padding:6px 10px; border:none; border-radius:var(--radius-sm); background:var(--accent-blue); cursor:pointer; transition:opacity var(--transition-normal); flex:1; text-align:center; }
    .sidebar-footer a:hover { opacity:0.9; color:#fff; }
    .main-content { padding:20px 24px; min-width:0; width:100%; box-sizing:border-box; display:flex; flex-direction:column; height:calc(100vh - 56px); overflow:hidden; }
    /* Mobile (<640px): subtract bottom-nav height + safe-area-inset from main content */
    @media (max-width: 639px) {
      .main-content { height:calc(100vh - 56px - 64px - env(safe-area-inset-bottom)); }
    }
    .main-header { margin-bottom:20px; display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex-shrink:0; }
    .main-header h1 { margin:0 0 8px; font-size:1.1rem; font-weight:700; color:var(--text-primary); letter-spacing:-0.02em; }
    .how-pill { display:inline-flex; align-items:center; gap:5px; padding:10px 16px; border-radius:999px; border:1px solid var(--border-default); background:var(--bg-surface); color:var(--text-primary); font-size:var(--text-base); font-weight:600; cursor:pointer; white-space:nowrap; transition:all 0.15s; letter-spacing:0.02em; min-height:44px; }
    .how-pill:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-muted); }
    .status-line { font-size:var(--text-base); color:var(--text-muted); margin-bottom:12px; padding:8px 14px; background:var(--bg-surface); border:1px solid var(--border-subtle); border-radius:var(--radius-sm); display:flex; align-items:center; gap:8px; min-height:20px; }
    .status-line::before { content:''; display:inline-block; width:8px; height:8px; border-radius:50%; background:var(--accent-green); flex-shrink:0; }
    .status-line:empty { display:none; }
    .status-line.loading::before { background:var(--accent-amber); animation:statusPulse 1.2s ease-in-out infinite; }
    @keyframes statusPulse { 0%,100%{opacity:1} 50%{opacity:0.3} }

    /* FIX 2026-08-07: the mobile header measured 228px and pushed Best Bets off
       the fold. Most of that was a SQUEEZE, not content. `.main-header` is a
       nowrap flex row; `.how-pill` is white-space:nowrap and so claims 220px of
       a 327px screen and refuses to shrink, leaving 117px for the whole text
       block. The one-sentence status line then wrapped to ~7 lines: measured
       181px at 117px wide vs 65px given full width.

       Stacking gives the status line the full width. Do NOT "fix" this by
       hiding or truncating the status text — it is unsqueezed, not removed.

       640px, not this file's usual 639px, deliberately: this trim exists to
       give the proof hero its fold, and must engage at exactly the same width
       as the hero reorder in slate.css. */
    @media (max-width: 640px) {
      .main-header { flex-direction: column; align-items: flex-start; }

      /* THE LAST 8px, measured 2026-09-06 on prod at 375x812.
         After the three cuts above and in slate.css, a full game card came to
         820px against an 812px viewport — eight pixels from the fold, having
         started the evening at 983px.
         Those eight are SPACING, not content. Above the board sat 52px of it
         around a single status sentence: .main-content's 20px padding-top,
         .main-header's 20px margin-bottom, and the status line's own 12px
         margin-bottom (which stays — it is the gap that keeps the sentence off
         the board, and main.css:424 forbids touching that line's legibility).
         Halving the outer two frees 16px and lands the card at 804px with 8px
         to spare. Measured live before it was written: 20/20 -> 820 (does not
         fit), 12/20 -> 812 (fits with ZERO headroom, one pixel of card growth
         from failing again), 12/12 -> 804.
         Mobile only. On desktop these paddings frame a 1260px column and have
         nothing to do with a fold.
         NOT a claim that every card now fits: card height varies with how many
         strips a game carries -- 583px here, 631px measured on another board an
         hour earlier. This makes the median card fit, not the tallest. */
      .main-header { margin-bottom: 12px; }
      .app-layout > .main-content { padding-top: 12px; }

      /* MEASURED 2026-09-06 at a real 375x812: 272px of chrome sits above the
         first game, and 112px of it is this header — 27px of which is an <h1>
         reading "Game Board", directly above the game board.

         Hidden ONLY because the wayfinding it provides already exists at this
         width: .bottom-nav is display:block from 0 to 799px and marks the
         active view (measured: items Games / All Bets / Trust / Account, with
         Games .active on the slate). The <h1> is the second answer to a
         question already answered on screen.

         That pairing is load-bearing, so tests/test_mobile_header_title.py
         asserts BOTH halves: hide this title and the bottom nav must still be
         shown here. If the bottom nav is ever hidden on phones, that test goes
         red and this rule must come out with it — otherwise the view loses its
         only label.

         NOT the status line. main.css:424 is explicit that its 181px was a
         SQUEEZE and must not be hidden or truncated; this removes a redundant
         heading, not information. */
      .main-header #main-title { display: none; }

      /* Bill 2026-08-07, with the consequence stated: this is the ONLY
         reachable openGlossary() trigger on a phone — the sidebar's "Reference"
         item has a display:none parent at this width and #mobile-menu is empty.
         Hiding it makes the glossary unreachable on mobile.

         Scoped to .main-header on purpose: a bare `.how-pill` rule would also
         kill the props view's separate "How Prop Predictions Work" pill. */
      .main-header .how-pill { display: none; }
    }
    /* Each view tab fills remaining height and scrolls internally */
    #view-premium, #view-pmdebug, #view-analytics { flex:1 1 0; min-height:0; overflow-y:auto; width:100%; box-sizing:border-box; }
    /* view-props uses an inner scroll container so the header row stays pinned */
    #view-props.view-props-flex { flex:1 1 0; min-height:0; overflow:hidden; width:100%; box-sizing:border-box; display:flex; }
    #view-analytics { padding:16px; }
    .game-cards-container { display:flex; flex-direction:column; gap:5px; max-width:none; }
    /* FIX 2026-08-06: the rule above outranks the UA `[hidden]{display:none}`,
       so Slate._toggleVisibility setting `.hidden = true` for the drilldown
       takeover changed the ATTRIBUTE and nothing else — measured live: hidden
       was true, computed display stayed `flex`, and all 15 cards were still
       4,625px tall on top of the takeover. A class selector beats a bare
       attribute selector; this one is 0-2-0 and wins without !important.
       Only mattered once the board moved ABOVE the slate. */
    .game-cards-container[hidden] { display:none; }
    /* 2026-08-05: the board is visible again. `#game-cards-container { display:
       none }` (2026-07-18) hid the legacy Game Board because it re-rendered the
       full slate a 2nd time (~35,500px) below Today's Slate. That loop is gone;
       renderGameCards() now emits the per-market odds box (.ob-card, styled at
       the foot of this file) for the visible sport only. */
    .date-header { display:block; font-size:var(--text-base); font-weight:700; color:var(--text-secondary); padding:16px 0 6px; border-bottom:1px solid var(--border-subtle); margin-bottom:4px; text-transform:uppercase; letter-spacing:0.06em; }
    .date-header:first-child { padding-top:0; }
    .game-card { background:var(--bg-card); border:1px solid var(--border-default); border-top:2px solid var(--accent-muted); border-radius:var(--radius-lg); box-shadow:var(--shadow-card); transition:box-shadow var(--transition-normal),border-color var(--transition-normal); overflow:hidden; }
    .game-card:hover { box-shadow:var(--shadow-hover); border-color:var(--border-strong); border-top-color:var(--accent); }
    .game-card-summary { cursor:pointer; user-select:none; }
    .game-header-bar { display:flex; align-items:center; justify-content:space-between; padding:6px 12px 1px; }
    .game-header-left { display:flex; align-items:center; gap:8px; font-size:var(--text-base); }
    .game-header-right { display:flex; align-items:center; gap:8px; }
    .game-time-label { color:var(--text-muted); font-weight:500; }
    .fallback-badge { display:inline-block; font-size:var(--text-base); padding:1px 5px; background:var(--accent-amber-bg); color:var(--accent-amber); border-radius:4px; font-weight:600; }
    .game-status-live { display:inline-flex; align-items:center; gap:5px; color:var(--ev-neg); font-weight:700; font-size:var(--text-base); text-transform:uppercase; letter-spacing:0.04em; }
    .game-status-live::before { content:''; display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--ev-neg); box-shadow:0 0 0 0 rgba(248,113,113,0.6); animation:livePulse 1.5s ease-in-out infinite; flex-shrink:0; }
    @keyframes livePulse { 0%{box-shadow:0 0 0 0 rgba(248,113,113,0.6)} 50%{box-shadow:0 0 0 6px rgba(248,113,113,0)} 100%{box-shadow:0 0 0 0 rgba(248,113,113,0)} }
    .game-status-final { display:inline-block; font-size:var(--text-base); font-weight:700; color:var(--text-faint); text-transform:uppercase; letter-spacing:0.04em; }
    .game-card-live { border-right:3px solid var(--ev-neg); }
    .game-card-final { opacity:0.7; }
    .game-card-bet { border-left:3px solid var(--accent-green); }
    .game-card-lock { border-left:3px solid #3b82f6; }
    .game-card-lean { border-left:3px solid var(--accent-amber); }
    .game-card.drawer-active { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent),var(--shadow-hover); }
    .game-rows { padding:0 12px 4px; }
    .game-row { display:grid; grid-template-columns:1fr repeat(3, 80px); column-gap:4px; align-items:center; }
    .team-cell { display:flex; align-items:center; gap:6px; padding:4px 4px; font-size:var(--text-base); color:var(--text-primary); }
    .odds-group { display:contents; }
    .team-cell .team-name { font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    .team-cell .team-score { font-weight:700; font-family:var(--font-mono); font-size:var(--text-lg); margin-left:8px; flex-shrink:0; }
    .game-card-live .team-score { color:var(--ev-neg); }
    .game-card-final .team-score { color:var(--text-faint); }
    .team-cell .team-at { font-size:var(--text-sm); color:var(--text-faint); margin:0 2px; }
    .odds-cell { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:3px 4px; border:1px solid var(--border-default); border-radius:var(--radius-sm); min-height:28px; transition:background var(--transition-fast),border-color var(--transition-fast); }
    .odds-cell:hover { background:var(--bg-surface); border-color:var(--accent-blue-light); }
    .odds-cell.cell-bet { background:var(--accent-green-bg); border-color:var(--ev-pos); }
    .odds-cell.cell-lean { background:var(--accent-amber-bg); border-color:var(--accent-amber); }
    .odds-cell.cell-empty { border-color:transparent; min-height:0; }
    .odds-cell.cell-empty:hover { background:transparent; border-color:transparent; }
    .odds-val { font-size:var(--text-base); font-weight:600; color:var(--text-primary); line-height:1.3; font-family:var(--font-mono); }
    .odds-juice { font-size:var(--text-sm); font-weight:700; color:var(--text-muted); line-height:1.3; font-family:var(--font-mono); }
    .sizing-badge { display:inline-flex; align-items:center; gap:3px; padding:2px 8px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:700; text-transform:uppercase; letter-spacing:0.04em; }
    .sizing-badge-bet { background:var(--accent-green-bg); color:var(--accent-green); }
    .sizing-badge-lock { background:rgba(59,130,246,0.12); color:#3b82f6; }
    .sizing-badge-lean { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .sizing-badge-pass { background:var(--bg-surface); color:var(--text-faint); }
    .analysis-btn { font-size:var(--text-sm); color:var(--accent-blue); font-weight:600; display:inline-flex; align-items:center; gap:4px; padding:2px 10px; border:1px solid var(--accent-blue-light); border-radius:var(--radius-pill); transition:all 0.2s; cursor:pointer; }
    .analysis-btn:hover { background:var(--accent-muted); border-color:var(--accent-blue); }
    .card-col-headers { display:grid; grid-template-columns:1fr repeat(3, 80px); column-gap:4px; padding:0 0 2px; }
    .card-col-headers span { font-size:var(--text-sm); font-weight:700; color:var(--text-secondary); text-align:center; text-transform:uppercase; letter-spacing:0.06em; }
    .game-card-detail { display:none; padding:0 16px 16px; background:var(--bg-detail); border-top:1px solid var(--border-subtle); }
    .game-card.expanded .game-card-detail { display:block; animation:detailSlide 0.2s ease; }
    @keyframes detailSlide { from{opacity:0} to{opacity:1} }
    /* Game Board Row styles (new row-based board) */
    .gb-header { display:grid; grid-template-columns:70px 1fr repeat(3,90px) 110px; padding:var(--space-xs) var(--space-md); gap:var(--space-sm); border-bottom:1px solid var(--border-default); }
    .gb-header span { font-size:var(--text-xs); color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.08em; font-weight:700; text-align:center; }
    .gb-header span:first-child { text-align:left; }
    .gb-header span:last-child { text-align:right; }
    .gb-row { display:grid; grid-template-columns:70px 1fr repeat(3,90px) 110px; align-items:center; padding:var(--space-sm) var(--space-md); border-bottom:1px solid var(--border-subtle); cursor:pointer; transition:background var(--transition-fast); gap:var(--space-sm); }
    .gb-row:hover { background:var(--bg-hover); }
    .gb-row.pass-row { opacity:0.55; }
    .gb-row.active { background:var(--bg-elevated); border-left:2px solid var(--accent); }
    .gb-time { font-size:var(--text-sm); color:var(--text-muted); white-space:nowrap; }
    .gb-time .live-dot { display:inline-block; width:6px; height:6px; border-radius:50%; background:var(--accent-red); margin-right:4px; animation:livePulse 1.5s ease-in-out infinite; }
    .gb-teams { font-size:var(--text-base); font-weight:600; color:var(--text-primary); line-height:1.4; }
    .gb-teams .away-line { color:var(--text-secondary); }
    .gb-teams .gb-score { color:var(--text-muted); font-family:var(--font-mono); margin-left:var(--space-xs); font-weight:700; }
    .gb-odds { text-align:center; padding:var(--space-xs) 2px; border-radius:var(--radius-sm); font-family:var(--font-mono); font-size:var(--text-base); font-weight:600; color:var(--text-secondary); background:var(--bg-elevated); cursor:pointer; transition:all var(--transition-fast); line-height:1.3; }
    .gb-odds:hover { background:var(--bg-hover); box-shadow:0 0 0 1px var(--accent); }
    .gb-odds.recommended { background:var(--accent-muted); color:var(--accent); }
    .gb-odds .odds-sub { font-size:var(--text-xs); color:var(--text-faint); font-weight:400; display:block; }
    .gb-sizing { display:flex; align-items:center; gap:var(--space-xs); justify-content:flex-end; flex-wrap:wrap; }
    .gb-sizing .sizing-pill { font-size:var(--text-xs); font-weight:700; padding:2px var(--space-sm); border-radius:var(--radius-sm); text-transform:uppercase; letter-spacing:0.04em; }
    .sizing-pill.bet { color:var(--ev-pos); background:var(--accent-green-bg); }
    .sizing-pill.lean { color:var(--accent-amber); background:var(--accent-amber-bg); }
    .sizing-pill.pass-pill { color:var(--text-faint); }
    .gb-ev { font-size:var(--text-sm); font-family:var(--font-mono); font-weight:600; }
    .detail-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:12px; }
    .detail-section { background:var(--bg-card); border:1px solid var(--border-default); border-radius:var(--radius-md); padding:14px 16px; }
    .detail-section-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; padding-bottom:8px; border-bottom:1px solid var(--border-subtle); }
    .detail-section-title { font-size:var(--text-sm); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; }
    .original-pick-row { display:flex; align-items:center; gap:8px; padding:4px 0; font-size:var(--text-base); }
    .original-pick-row .op-label { color:var(--text-faint); font-weight:600; min-width:28px; text-transform:uppercase; font-size:var(--text-sm); }
    .original-pick-row .op-side { color:var(--text-primary); font-weight:600; flex:1; }
    .original-pick-row .op-prob { color:var(--text-secondary); font-family:var(--font-mono); font-size:var(--text-sm); }
    .original-pick-row .op-badge { display:inline-block; padding:1px 7px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:700; text-transform:uppercase; letter-spacing:0.04em; }
    .original-pick-row .op-badge-bet { background:var(--accent-green-bg); color:var(--accent-green); }
    .original-pick-row .op-badge-lock { background:rgba(59,130,246,0.12); color:#3b82f6; }
    .original-pick-row .op-badge-lean { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .original-pick-row .op-badge-pass { background:var(--bg-surface); color:var(--text-faint); }
    .detail-row { display:flex; justify-content:space-between; align-items:baseline; padding:3px 0; font-size:var(--text-base); }
    .detail-row .label { color:var(--text-muted); font-weight:500; }
    .detail-row .value { color:var(--text-primary); font-weight:600; font-family:var(--font-mono); }
    .source-row { display:flex; justify-content:space-between; align-items:baseline; padding:2px 0; font-size:var(--text-sm); }
    .source-row .label { color:var(--text-muted); }
    .source-row .value { color:var(--text-secondary); font-family:var(--font-mono); }
    .gauge-row { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
    .gauge-label { font-size:var(--text-base); color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; min-width:80px; font-weight:500; }
    .gauge-track { flex:1; height:8px; background:var(--accent-muted); border-radius:4px; overflow:hidden; }
    .gauge-fill { height:100%; border-radius:4px; transition:width 0.3s ease; }
    .gauge-score { font-size:var(--text-base); font-weight:700; min-width:40px; text-align:right; font-family:var(--font-mono); }
    .gauge-grade { font-size:var(--text-sm); font-weight:600; min-width:70px; }
    .modal-backdrop { position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:9999; display:flex; align-items:center; justify-content:center; }
    .modal-backdrop.hidden { display:none; }
    .modal { background:var(--bg-card); color:var(--text-secondary); width:min(900px,92vw); max-height:85vh; overflow-y:auto; border-radius:var(--radius-lg); border:1px solid var(--border-default); border-top:3px solid var(--accent-blue); padding:14px 16px; box-shadow:0 20px 60px rgba(0,0,0,0.15); animation:modalFadeIn 0.25s ease; }
    @keyframes modalFadeIn { from{opacity:0;transform:translateY(12px) scale(0.98)} to{opacity:1;transform:translateY(0) scale(1)} }
    .modal h2 { margin:4px 0 10px; font-size:1.1rem; color:var(--text-primary); }
    .modal .row { display:grid; grid-template-columns:140px 1fr; gap:10px; padding:8px 0; border-bottom:1px solid var(--border-subtle); }
    .modal .k { color:var(--accent-blue); font-weight:600; font-size:var(--text-base); }
    .modal .v { color:var(--text-secondary); font-size:var(--text-base); }
    .modal-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:10px; }
    .modal-header { display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-bottom:1px solid var(--border-strong); }
    .modal-header h2 { margin:0; font-size:var(--text-lg); }
    .close-btn { background:none; border:1px solid transparent; color:var(--text-faint); font-size:22px; font-weight:bold; cursor:pointer; line-height:1; padding:4px 8px; border-radius:var(--radius-sm); transition:background var(--transition-normal); }
    .close-btn:hover { color:var(--text-primary); background:var(--bg-hover); border-color:var(--border-default); }
    .modal-body { padding:16px 18px; }
    .modal-body h4 { margin-top:14px; margin-bottom:4px; font-size:var(--text-base); }
    .modal-body p { font-size:var(--text-base); line-height:1.45; margin:0 0 6px; }
    /* Reference Center */
    .ref-modal { background:var(--bg-card); color:var(--text-secondary); width:min(720px,94vw); max-height:88vh; display:flex; flex-direction:column; border-radius:var(--radius-lg); border:1px solid var(--border-default); border-top:3px solid var(--accent-blue); box-shadow:0 20px 60px rgba(0,0,0,0.15); animation:modalFadeIn 0.25s ease; overflow:hidden; }
    .ref-header { display:flex; align-items:center; justify-content:space-between; padding:14px 18px 10px; flex-shrink:0; }
    .ref-header h2 { margin:0; font-size:1rem; color:var(--text-primary); font-weight:700; }
    .ref-search-wrap { padding:0 18px 10px; flex-shrink:0; position:relative; }
    .ref-search-input { width:100%; padding:9px 14px 9px 36px; border:1px solid var(--border-default); border-radius:var(--radius-md); font-size:var(--text-base); background:var(--bg-page); color:var(--text-primary); outline:none; transition:border-color 0.2s; box-sizing:border-box; }
    .ref-search-input:focus { border-color:var(--accent-blue); box-shadow:0 0 0 3px var(--accent-muted); }
    .ref-search-icon { position:absolute; left:30px; top:50%; transform:translateY(-50%); color:var(--text-faint); font-size:var(--text-base); pointer-events:none; }
    .ref-search-count { position:absolute; right:30px; top:50%; transform:translateY(-50%); font-size:var(--text-xs); color:var(--text-faint); }
    .ref-pills { display:flex; gap:6px; padding:0 18px 12px; flex-shrink:0; flex-wrap:wrap; }
    .ref-pill { padding:4px 12px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:600; border:1px solid var(--border-default); background:var(--bg-surface); color:var(--text-muted); cursor:pointer; transition:all 0.15s; white-space:nowrap; }
    .ref-pill:hover { border-color:var(--accent-blue); color:var(--accent-blue); }
    .ref-pill.active { background:var(--accent-blue); color:#0d1117; border-color:var(--accent-blue); }
    .ref-body { flex:1; overflow-y:auto; padding:0 18px 14px; }
    .ref-cat-header { font-size:var(--text-sm); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; margin:16px 0 8px; padding-bottom:4px; border-bottom:1px solid var(--border-subtle); }
    .ref-cat-header:first-child { margin-top:4px; }
    .ref-term { padding:8px 12px; margin-bottom:6px; border-radius:var(--radius-sm); border:1px solid var(--border-subtle); background:var(--bg-page); transition:border-color 0.15s; }
    .ref-term:hover { border-color:var(--border-default); }
    .ref-term-name { font-weight:700; font-size:var(--text-base); color:var(--text-primary); }
    .ref-term-aka { font-size:var(--text-sm); color:var(--text-faint); margin-left:6px; font-weight:500; }
    .ref-term-def { font-size:var(--text-sm); color:var(--text-secondary); line-height:1.55; margin-top:3px; }
    .ref-term-sport { display:inline-block; font-size:var(--text-xs); padding:1px 6px; border-radius:var(--radius-pill); background:var(--accent-muted); color:var(--accent-blue); font-weight:600; margin-left:6px; }
    .ref-highlight { background:var(--accent-amber-bg); color:var(--accent-amber); border-radius:2px; padding:0 1px; }
    .ref-empty { text-align:center; padding:40px 20px; color:var(--text-faint); font-size:var(--text-base); }
    .ref-empty span { display:block; font-size:1.5rem; margin-bottom:8px; }
    .pill { border-radius:var(--radius-pill); padding:2px 8px; font-size:var(--text-base); font-weight:600; }
    .pill-gold { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .pill-silver { background:var(--bg-hover); color:var(--text-secondary); }
    .pill-bronze { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .pill-none { background:var(--bg-surface); color:var(--text-muted); }
    .badge { display:inline-block; font-size:var(--text-base); padding:2px 5px; border-radius:var(--radius-pill); border:1px solid var(--border-default); margin-left:4px; color:var(--text-muted); white-space:nowrap; }
    .badge-ok { border-color:var(--accent-green); color:var(--accent-green); }
    .badge-warn { border-color:var(--accent-amber); color:var(--accent-amber); }
    .badge-bad { border-color:var(--accent-red); color:var(--accent-red); }
    .badges { display:inline-flex; gap:6px; flex-wrap:wrap; align-items:center; }
    .margin-badge { display:inline-block; padding:2px 8px; border-radius:4px; font-size:var(--text-base); font-weight:700; }
    .margin-badge.positive { background:var(--accent-green-bg); color:var(--accent-green); }
    .margin-badge.negative { background:var(--accent-red-bg); color:var(--accent-red); }
    .margin-badge.neutral { background:var(--accent-amber-bg); color:var(--accent-amber); }
    table { width:100%; border-collapse:collapse; font-size:var(--text-base); table-layout:auto; }
    th,td { padding:8px 10px; border-bottom:1px solid var(--border-subtle); vertical-align:top; }
    th { text-align:left; background:var(--bg-surface); font-size:var(--text-base); font-weight:600; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; white-space:nowrap; }
    td { color:var(--text-secondary); }
    tbody tr:hover { background:var(--bg-hover); }
    .sub { font-size:var(--text-base); color:var(--text-muted); }
    .book-best { font-size:var(--text-base); color:var(--text-primary); font-weight:600; }
    .book-best-odds { color:var(--accent-blue); font-weight:700; }
    .book-others { font-size:var(--text-sm); color:var(--text-muted); margin-top:2px; line-height:1.4; }
    .book-others-odds { color:var(--text-muted); }
    .ev-pos { color:var(--accent-green); font-weight:600; }
    .ev-neg { color:var(--accent-amber); font-weight:500; }
    details { margin-top:4px; }
    details summary { cursor:pointer; font-size:var(--text-base); color:var(--accent-blue); }
    pre { font-size:var(--text-sm); background:var(--bg-page); border-radius:var(--radius-sm); padding:6px; border:1px solid var(--border-default); overflow-x:auto; white-space:pre-wrap; word-break:break-word; }
    .context-block { margin-top:8px; padding:8px 10px; border-radius:var(--radius-sm); background:var(--bg-page); border:1px solid var(--border-subtle); }
    .context-toggle { color:var(--text-muted); display:flex; align-items:center; gap:6px; padding:2px 0; }
    .context-toggle:hover { color:var(--text-primary); }
    .context-label { color:var(--text-secondary); font-weight:500; }
    .context-headline { font-weight:600; }
    .context-chevron { display:inline-block; transition:transform 0.25s ease; font-size:0.8em; color:var(--text-faint); }
    .context-toggle[data-open="true"] .context-chevron { transform:rotate(90deg); }
    .context-drawer { margin-top:6px; padding:6px 0 6px 10px; border-left:2px solid var(--border-default); animation:drawerSlide 0.2s ease; }
    @keyframes drawerSlide { from{opacity:0;transform:translateY(-4px)} to{opacity:1;transform:translateY(0)} }
    .q { display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; border-radius:var(--radius-pill); border:1px solid var(--border-default); color:var(--text-muted); font-size:12px; margin-left:6px; cursor:help; }
    .help-link { display:inline-block; margin-left:8px; color:var(--accent-blue); font-size:13px; cursor:help; text-decoration:underline; text-underline-offset:2px; }
    .tip { position:relative; display:inline-block; vertical-align:middle; }
    .tip:hover::after { content:attr(data-tip); position:absolute; left:0; top:22px; min-width:220px; max-width:360px; background:var(--bg-surface); border:1px solid var(--border-default); padding:10px; border-radius:var(--radius-md); color:var(--text-secondary); font-size:var(--text-base); line-height:1.25; z-index:50; box-shadow:0 10px 30px rgba(0,0,0,0.12); white-space:normal; }
    .pm-stack { margin-top:6px; }
    .pm-stack .row { display:grid; grid-template-columns:62px 1fr; gap:8px; margin:2px 0; align-items:baseline; }
    .pm-stack .k { color:var(--text-muted); font-size:var(--text-base); }
    .pm-stack .v { color:var(--text-secondary); font-size:var(--text-base); }
    .pm-vol-liq { font-size:var(--text-base); color:var(--text-secondary); margin-top:4px; }
    .muted { color:var(--text-muted); }
    .card { border-radius:var(--radius-lg); border:1px solid var(--border-default); padding:12px 14px; background:var(--bg-card); margin-bottom:12px; }
    .card h3 { margin:0 0 4px; font-size:var(--text-lg); color:var(--text-primary); }
    .card p { margin:6px 0; font-size:var(--text-base); color:var(--text-muted); line-height:1.35; }
    .tab-btn { background:var(--bg-page); border:1px solid var(--border-default); color:var(--text-secondary); padding:6px 16px; border-radius:var(--radius-sm); cursor:pointer; font-size:var(--text-base); font-weight:600; }
    .tab-btn:hover { background:var(--bg-hover); color:var(--text-primary); }
    .pill-btn { background:var(--bg-page); border:1px solid var(--border-default); color:var(--text-secondary); padding:4px 12px; border-radius:var(--radius-sm); cursor:pointer; font-size:var(--text-sm); font-weight:600; transition:background 0.15s,color 0.15s; }
    .pill-btn:hover { background:var(--bg-hover); color:var(--text-primary); }
    .pill-btn.active { background:var(--accent-muted); color:var(--accent); border-color:var(--accent); }
    .props-sport-pill { background:none; border:none; border-bottom:2px solid transparent; color:var(--text-muted); padding:var(--space-xs) var(--space-md); cursor:pointer; font-size:var(--text-base); font-weight:600; transition:color var(--transition-fast),border-color var(--transition-fast); }
    .props-sport-pill:hover { color:var(--text-secondary); }
    .props-sport-pill.active { color:var(--accent); border-bottom-color:var(--accent); }
    /* .hidden keeps !important deliberately: it is the global hide utility
       toggled by 15+ JS sites (modals, backdrops, overlays, view switching)
       and must beat any later display rule. Competing view containers use
       :not(.hidden) on their display rule instead of counter-!importants
       (FIX 2026-07-12, Batch 2 — ended the cc-view-container arms race). */
    .hidden { display:none !important; }
    /* FIX 2026-04-06: scoped flex layout for #view-props that only applies
       when the element is visible (2026-07-12: now via :not(.hidden);
       flex-direction moved here from an inline style on the element). */
    .view-props-flex:not(.hidden) { display:flex; flex-direction:column; }

    /* Inline Line Shopping */
    .game-card-row { display:flex; gap:12px; align-items:flex-start; }
    .game-card-row .game-card { flex:0 0 500px; }
    .ls-box { display:grid; grid-template-columns:1fr 1fr; gap:10px; flex:1; min-width:0; }
    .ls-expand-toggle {
      padding:8px 16px; font-size:var(--text-sm); font-weight:600;
      color:var(--accent-blue); cursor:pointer; user-select:none;
      border-top:1px solid var(--border-subtle);
    }
    .ls-expand-toggle:hover { background:var(--bg-hover); }
    .ls-card {
      background:var(--bg-surface); border:1px solid var(--border-default);
      border-radius:var(--radius-lg); box-shadow:var(--shadow-card); overflow:hidden;
    }
    .ls-header {
      padding:12px 16px; border-bottom:1px solid var(--border-subtle);
      font-size:var(--text-lg); font-weight:700; color:var(--text-primary);
      display:flex; align-items:center; justify-content:space-between;
      cursor:pointer; user-select:none;
    }
    .ls-header:hover { background:var(--bg-hover); }
    .ls-header .ls-chevron { transition:transform 0.2s; font-size:var(--text-sm); color:var(--text-faint); margin-right:6px; }
    .ls-card.ls-collapsed .ls-header { border-bottom:none; }
    .ls-card.ls-collapsed .ls-body { display:none; }
    .ls-best-badge {
      display:inline-flex; align-items:center; gap:5px;
      background:var(--accent-green-bg); border:1px solid var(--ev-pos); border-radius:6px;
      padding:4px 10px; font-size:var(--text-sm); font-weight:700; color:var(--ev-pos);
    }
    .ls-best-badge svg { flex-shrink:0; }
    .ls-best-grid {
      display:grid; grid-template-columns:repeat(3, 1fr); gap:1px;
      background:var(--border-subtle); border-bottom:1px solid var(--border-subtle);
    }
    /* 2026-05-17 audit S3 mobile: 3-column ML/SPREAD/O/U grid clipped at
       <640px. Stack vertically on phones — each cell takes full width. */
    @media (max-width: 640px) {
      .ls-best-grid { grid-template-columns: 1fr; }
      .ls-table-wrap { scrollbar-width: thin; }
      .ls-table th, .ls-table td { padding: 6px 4px; font-size: var(--text-sm); }
      .ls-table th:first-child, .ls-table td:first-child { padding-left: 6px; }
    }
    .ls-best-cell {
      background:var(--accent-green-bg); padding:10px 12px; text-align:center;
    }
    .ls-best-cell-label {
      font-size:var(--text-sm); font-weight:700; color:var(--text-muted);
      text-transform:uppercase; letter-spacing:0.06em; margin-bottom:4px;
    }
    .ls-best-cell-book {
      font-size:var(--text-base); font-weight:700; color:var(--accent-green);
    }
    .ls-best-cell-odds {
      font-size:var(--text-base); font-weight:800; color:var(--accent-green);
      font-family:var(--font-mono);
    }
    .ls-table-wrap { padding:0; overflow-x:auto; }
    .ls-table {
      width:100%; border-collapse:collapse;
      font-size:var(--text-base);
    }
    .ls-table th {
      padding:8px 6px; text-align:center; font-size:var(--text-sm); font-weight:700;
      color:var(--text-muted); text-transform:uppercase; letter-spacing:0.06em;
      background:var(--bg-surface); border-bottom:1px solid var(--border-subtle);
      white-space:nowrap;
    }
    .ls-table th:first-child { text-align:left; padding-left:12px; }
    .ls-table td {
      padding:6px 6px; text-align:center; font-family:var(--font-mono);
      font-weight:500; color:var(--text-secondary);
      border-bottom:1px solid var(--border-subtle);
    }
    .ls-table td:first-child {
      text-align:left; padding-left:12px;
      font-family:system-ui,sans-serif; font-weight:600; color:var(--text-primary);
      font-size:var(--text-sm); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    }
    .ls-table tbody tr:hover { background:var(--bg-hover); }
    .ls-cell-best { background:var(--accent-green-bg); color:var(--ev-pos); font-weight:700; }
    .ls-cell-worst { color:var(--text-faint); opacity:0.6; }
    .ls-cell-missing { font-style:italic; color:var(--text-faint); opacity:0.5; }
    .ls-vig-low { color:var(--ev-pos); font-weight:600; }
    .ls-vig-high { color:var(--ev-neg); font-weight:600; }
    .ls-fair-prob { font-weight:700; color:var(--text-primary); }
    .ls-no-data {
      padding:32px 24px; text-align:center;
      color:var(--text-faint); font-size:var(--text-base);
    }
    .ls-legend {
      padding:8px 16px; border-top:1px solid var(--border-subtle);
      display:flex; flex-direction:column; gap:2px;
      font-size:var(--text-sm); color:var(--text-faint); line-height:1.4;
    }
    /* ============================================================
       GAME DASHBOARD — full-screen game view layout
       ============================================================ */
    .app-layout-gameview { display:grid; grid-template-columns:220px 280px 1fr; min-height:100vh; }
    .app-layout-gameview .main-content { display:none; }

    /* Game Strip — compact scrollable game list */
    .game-strip { background:var(--bg-base); border-right:1px solid var(--border-default); display:flex; flex-direction:column; height:100vh; position:sticky; top:0; overflow:hidden; }
    .game-strip-header { padding:2px 4px; border-bottom:1px solid var(--border-subtle); display:flex; align-items:center; justify-content:space-between; }
    .game-strip-header h2 { margin:0; font-size:1.1rem; font-weight:700; color:var(--text-primary); }
    .game-strip-header .strip-count { font-size:1.0rem; color:var(--text-muted); font-weight:600; }
    .league-logo { height:28px; }
    .game-strip-header .strip-league-logo .league-logo { height:80px; }
    .game-strip-list { flex:1; overflow-y:auto; padding:4px 0; }
    .game-strip-date { font-size:var(--text-sm); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.06em; padding:12px 14px 4px; }
    .game-strip-date:first-child { padding-top:8px; }
    .game-strip-item { padding:8px 14px; cursor:pointer; border-left:3px solid transparent; transition:background var(--transition-fast),border-color var(--transition-fast); border-bottom:1px solid var(--border-subtle); background:var(--bg-card); margin:0 6px 4px; border-radius:var(--radius-sm); border-bottom:none; }
    .game-strip-item:hover { background:var(--bg-hover); }
    .game-strip-item.active { background:var(--bg-raised); border-left-color:var(--accent-blue); box-shadow:inset 3px 0 8px var(--accent-muted); }
    .game-strip-item.strip-live { border-right:2px solid var(--ev-neg); }
    .game-strip-item.strip-final { opacity:0.55; }
    .strip-score-inline { font-weight:700; font-family:var(--font-mono); font-size:var(--text-base); margin-left:auto; padding-left:4px; min-width:16px; text-align:right; }
    .strip-live .strip-score-inline { color:var(--ev-neg); }
    .strip-final .strip-score-inline { color:var(--text-faint); }
    .strip-odds-abbr-scored { display:flex; align-items:center; }
    .strip-top-row { display:flex; align-items:center; justify-content:space-between; margin-bottom:2px; }
    .strip-top-time { font-size:var(--text-sm); font-weight:600; color:var(--text-secondary); }
    .strip-live .strip-top-time { color:var(--ev-neg); font-weight:700; display:inline-flex; align-items:center; gap:4px; }
    .strip-live .strip-top-time::before { content:''; display:inline-block; width:6px; height:6px; border-radius:50%; background:var(--ev-neg); animation:livePulse 1.5s ease-in-out infinite; flex-shrink:0; }
    .strip-final .strip-top-time { color:var(--text-faint); }
    .game-strip-live { font-size:var(--text-sm); font-weight:700; color:var(--ev-neg); text-transform:uppercase; }
    .game-strip-badge { display:inline-flex; padding:1px 6px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:700; text-transform:uppercase; letter-spacing:0.03em; }
    .game-strip-badge-bet { background:var(--accent-green-bg); color:var(--accent-green); }
    .game-strip-badge-lock { background:rgba(59,130,246,0.12); color:#3b82f6; }
    .game-strip-badge-lean { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .game-strip-badge-pass { background:var(--bg-surface); color:var(--text-faint); }

    /* Props game cards */
    .props-game-card { background:var(--bg-card); border:1px solid var(--border-default); border-radius:var(--radius-lg); overflow:hidden; }
    .props-game-header { display:flex; align-items:center; justify-content:space-between; padding:10px 16px; background:var(--bg-surface); border-bottom:1px solid var(--border-subtle); cursor:pointer; user-select:none; }
    .props-game-header:hover { background:var(--bg-hover); }
    .props-game-matchup { font-size:var(--text-base); font-weight:700; color:var(--text-primary); }
    .props-game-count { font-size:var(--text-sm); color:var(--text-muted); }
    .props-game-body { padding:0; }
    /* min-height 2026-08-12: every row carries an odds cell, and the odds cell
       is a control (app.js attaches toggleParlayLeg to it on unlocked rows).
       Its 44px target needs a 44px row or it spills ~3px into the neighbour
       above and below and two adjacent prices overlap — the same arithmetic
       recorded for the SGP checkbox below. Rows were 40.9px. */
    .props-player-row { display:grid; grid-template-columns:1fr 78px 76px 62px 76px 56px; gap:0; align-items:center; padding:6px 16px; min-height:44px; font-size:var(--text-base); border-bottom:1px solid var(--border-subtle); transition:background 0.12s; }
    .props-player-row:last-child { border-bottom:none; }
    .props-player-row:hover { background:rgba(255,255,255,0.02); }
    .props-player-name { font-weight:700; font-size:var(--text-base); color:var(--text-primary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    .props-player-factors { font-size:var(--text-base); color:var(--text-muted); margin-left:8px; }
    .props-line { font-family:var(--font-mono); font-weight:600; font-size:var(--text-base); color:var(--text-primary); text-align:right; }
    .props-odds-cell { text-align:right; font-family:var(--font-mono); font-size:var(--text-lg); color:var(--text-primary); position:relative; }
    /* FIX 2026-08-12 — WCAG 2.5.5. The price is a CONTROL: app.js:8214 gives
       every unlocked row's odds cell `onclick=... toggleParlayLeg(...)`, so a
       tap here commits a leg. Measured 76 x 27.9 at 390px in both normal and
       SGP mode, with adjacent odds cells 13px apart — a mis-tap adds the wrong
       player's leg, and nothing tells the user.

       ::after rather than sizing the cell, for the same reason as
       .props-sgp-checkbox: `.props-player-row` is `align-items:center`, so the
       cell does not stretch to the row, and growing the cell to 44px would
       push every row 40.9 -> 56px on a board that shows dozens at a time.
       A click on a pseudo-element is attributed to its originating element,
       so the inline onclick needs no change.

       Full cell width (76px, already over the minimum) and 44px tall, which
       exactly fills the row's new 44px min-height — so vertically adjacent
       targets touch without overlapping. Locked rows are unaffected: they set
       pointer-events:none below.
       Guarded by tests/test_props_row_mobile_scroll.py. */
    .props-odds-cell::after {
      content:''; position:absolute; left:0; right:0; top:50%;
      height:44px; transform:translateY(-50%);
    }
    /* Phase 9 (C2) 2026-04-08: line shopping badge */
    .props-shop-badge { display:inline-block; margin-left:6px; padding:1px 5px; border-radius:6px; background:rgba(59,130,246,0.10); color:var(--accent-blue, #3b82f6); font-family:var(--font-sans); font-size:var(--text-xs); font-weight:600; vertical-align:middle; cursor:help; white-space:nowrap; }
    .props-shop-badge:hover { background:rgba(59,130,246,0.18); }
    /* Phase 9 (B2) 2026-04-08: injury status badge */
    .props-injury-badge { display:inline-block; margin-right:6px; padding:1px 5px; border-radius:5px; font-family:var(--font-sans); font-size:var(--text-xs); font-weight:700; vertical-align:middle; cursor:help; }
    .props-injury-badge-quest { background:rgba(245,158,11,0.15); color:#f59e0b; border:1px solid rgba(245,158,11,0.30); }
    .props-injury-badge-doubt { background:rgba(239,68,68,0.15); color:#f87171; border:1px solid rgba(239,68,68,0.30); }
    /* Phase 9 (C3) 2026-04-08: live-lock pill and row state */
    .props-row-locked { opacity:0.68; }
    .props-row-locked .props-odds-cell { pointer-events:none; }
    .props-row-locked .props-shop-badge { display:none; }
    .props-lock-badge { display:inline-block; margin-left:6px; padding:1px 6px; border-radius:5px; font-family:var(--font-sans); font-size:var(--text-xs); font-weight:700; letter-spacing:0.03em; vertical-align:middle; }
    .props-lock-live { background:rgba(239,68,68,0.20); color:#f87171; border:1px solid rgba(239,68,68,0.35); }
    .props-lock-final { background:rgba(107,114,128,0.25); color:var(--text-faint); border:1px solid rgba(107,114,128,0.40); }
    .props-proj { text-align:right; font-family:var(--font-mono); color:var(--text-primary); font-size:var(--text-lg); }
    .props-ev { text-align:right; font-family:var(--font-mono); font-weight:700; font-size:var(--text-lg); }
    .props-sizing-cell { text-align:center; }
    .props-col-header { display:grid; grid-template-columns:1fr 78px 76px 62px 76px 56px; gap:0; padding:5px 16px; font-size:var(--text-sm); font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.04em; border-bottom:1px solid var(--border-default); }
    .props-col-header > span { text-align:right; }
    .props-col-header > span:first-child { text-align:left; }
    .props-type-accordion { border-bottom:1px solid var(--border-subtle); }
    .props-type-accordion:last-child { border-bottom:none; }
    .props-type-toggle { display:flex; align-items:center; justify-content:space-between; padding:10px 16px; cursor:pointer; user-select:none; background:var(--bg-surface); transition:background 0.15s; }
    .props-type-toggle:hover { background:var(--bg-hover); }
    .props-type-toggle-label { font-size:1rem; font-weight:700; color:var(--text-primary); letter-spacing:0.02em; }
    .props-type-toggle-chevron { font-size:var(--text-base); color:var(--text-muted); transition:transform 0.2s; }
    .props-type-toggle.open .props-type-toggle-chevron { transform:rotate(90deg); }
    .props-type-body { }
    .props-team-accordion { border-bottom:1px solid var(--border-subtle); }
    .props-team-accordion:last-child { border-bottom:none; }
    .props-team-toggle { display:flex; align-items:center; justify-content:space-between; padding:7px 16px 7px 28px; cursor:pointer; user-select:none; background:var(--bg-card); transition:background 0.15s; }
    .props-team-toggle:hover { background:var(--bg-hover); }
    .props-team-toggle-label { font-size:var(--text-base); font-weight:600; color:var(--text-secondary); }
    .props-team-toggle-chevron { font-size:var(--text-sm); color:var(--text-muted); transition:transform 0.2s; }
    .props-team-toggle.open .props-team-toggle-chevron { transform:rotate(90deg); }

    /* ============================================================
       Props category navigation — Phase 1 restructure 2026-04-07
       Left rail on desktop, horizontal pills on mobile, matches
       DraftKings / FanDuel props taxonomy (Scoring / Batter / etc).
       ============================================================ */
    .props-main-layout { display:flex; gap:var(--space-md); align-items:flex-start; }
    .props-content { flex:1; min-width:0; display:flex; flex-direction:column; gap:var(--space-sm); }

    /* Left rail (desktop) */
    .props-category-rail { flex:0 0 180px; position:sticky; top:var(--space-md); background:var(--bg-card); border:1px solid var(--border-default); border-radius:var(--radius-lg); overflow:hidden; }
    .props-category-rail-header { padding:10px 14px; font-size:var(--text-sm); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.06em; border-bottom:1px solid var(--border-subtle); background:var(--bg-surface); }
    .props-category-rail-list { display:flex; flex-direction:column; padding:4px 0; }
    .props-category-item { display:flex; align-items:center; justify-content:space-between; padding:9px 14px; cursor:pointer; border-left:3px solid transparent; color:var(--text-secondary); font-size:var(--text-base); font-weight:600; transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast); user-select:none; }
    .props-category-item:hover { background:var(--bg-hover); color:var(--text-primary); }
    .props-category-item.active { background:var(--bg-raised); color:var(--accent); border-left-color:var(--accent); }
    .props-category-item.is-empty { opacity:0.45; cursor:default; }
    .props-category-item.is-empty:hover { background:transparent; color:var(--text-secondary); }
    .props-category-item-count { font-size:var(--text-sm); color:var(--text-muted); font-weight:500; font-family:var(--font-mono); }
    .props-category-item.active .props-category-item-count { color:var(--accent); }

    /* Horizontal pills (mobile) */
    .props-category-pills { display:none; gap:6px; overflow-x:auto; padding:6px 0 8px; scrollbar-width:none; -ms-overflow-style:none; }
    .props-category-pills::-webkit-scrollbar { display:none; }
    .props-category-pill { flex:0 0 auto; padding:6px 14px; border:1px solid var(--border-default); border-radius:var(--radius-pill); background:var(--bg-surface); color:var(--text-secondary); font-size:var(--text-sm); font-weight:600; cursor:pointer; white-space:nowrap; transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast); }
    .props-category-pill:hover { background:var(--bg-hover); color:var(--text-primary); }
    .props-category-pill.active { background:var(--accent); color:var(--bg-base); border-color:var(--accent); }
    .props-category-pill.is-empty { opacity:0.45; cursor:default; }
    .props-category-pill.is-empty:hover { background:var(--bg-surface); color:var(--text-secondary); }
    .props-category-pill-count { font-size:var(--text-xs); opacity:0.8; margin-left:4px; font-family:var(--font-mono); }

    /* Responsive breakpoint — flip rail to pills */
    @media (max-width: 768px) {
      /* FIX 2026-07-25: align-items:flex-start (line ~643) SURVIVED this
         direction flip. In a column flex the cross axis is horizontal, so
         flex-start stopped .props-content stretching and sized it to
         MAX-CONTENT — the sum of every category pill. Measured on prod at
         390px: .props-main-layout 295px but .props-content 911px, giving
         576px of horizontal PAGE scroll, and .props-category-pills'
         overflow-x:auto (line ~659) was inert, because a scroll container
         cannot scroll while its parent is sized to its own max-content.
         With stretch: overflow 576 -> 0, content 911 -> 310, and the pill
         strip's scrollWidth > clientWidth flips to TRUE (it finally
         scrolls). Guarded by tests/test_props_mobile_layout.py. */
      .props-main-layout { flex-direction:column; align-items:stretch; }
      .props-category-rail { display:none; }
      .props-category-pills { display:flex; }
    }


    /* ============================================================
       Phase 9 (B1) 2026-04-08: Same-Game Parlay (SGP) builder
       ============================================================ */
    .props-sgp-toggle { margin-left:auto; display:inline-flex; align-items:center; padding:6px 14px; border:1px solid var(--accent); border-radius:var(--radius-pill); background:transparent; color:var(--accent); font-size:var(--text-sm); font-weight:600; cursor:pointer; transition:background var(--transition-fast), color var(--transition-fast); }
    .props-sgp-toggle:hover { background:var(--accent); color:var(--bg-base); }

    /* Selection mode: each prop row gets a checkbox in the leftmost cell */
    /* Header takes the same 7-track template as the rows in SGP mode — see the
       FIX note at the colHeader in app.js. */
    .props-player-row.props-sgp-mode,
    .props-col-header.props-sgp-mode { grid-template-columns:44px 1fr 78px 76px 62px 76px 56px; }
    .props-sgp-checkbox { display:inline-flex; align-items:center; justify-content:center; width:20px; height:20px; border:1.5px solid var(--border-default); border-radius:4px; cursor:pointer; transition:background var(--transition-fast), border-color var(--transition-fast); color:transparent; position:relative; }
    .props-sgp-checkbox:hover { border-color:var(--accent); }
    .props-sgp-checkbox.checked { background:var(--accent); border-color:var(--accent); color:var(--bg-base); }
    /* FIX 2026-07-25 — WCAG 2.5.5 Target Size (44x44). The visible box stays
       20x20 by design; ::after carries the real pointer target. A click on a
       pseudo-element is attributed to its originating element, so the inline
       onclick in app.js needs no change.

       The two sizes that keep this target from OVERLAPPING a neighbour (an
       overlap would trade one 2.5.x problem for another): the SGP track went
       28px -> 44px so the target stays inside its own column, and the row
       gets min-height:44px so vertically adjacent checkboxes can't collide —
       rows were ~40px, i.e. 4px short, which is exactly how much a 44px
       target would have spilled into the row above and below.

       Applies at every width, not just phones: 2.5.5 covers mouse pointers
       too, and precision pointing is the exact thing this product must not
       assume. Guarded by tests/test_props_row_mobile_scroll.py. */
    .props-sgp-checkbox::after {
      content:''; position:absolute; left:50%; top:50%;
      width:44px; height:44px; transform:translate(-50%,-50%);
    }
    .props-player-row.props-sgp-mode { min-height:44px; }

    /* FIX 2026-07-25: the three LATENT props grids. `.props-col-header` and
       `.props-player-row` share `1fr 78px 76px 62px 76px 56px`, and
       `.props-player-row.props-sgp-mode` prepends a 44px checkbox column.
       The fixed tracks alone are 348px (392px in SGP mode) plus 32px of
       padding — inside a ~295px .props-content at a 390px viewport. Same bug
       class as the analytics grids and .slate-prop-row, but it renders ZERO
       rows out of NBA season, so it was invisible to the live sweep and would
       have bitten in ~Oct.

       Scroll rather than collapse to a card (Bill's analytics call, and the
       only option that survives this markup): `.props-col-header` is a real
       header whose spans align to the row tracks, and a player's extra lines
       render with an EMPTY name cell (app.js ~8651) so they read as a group.
       A card stack destroys both. Making the team body the scroller keeps
       header and rows in one aligned, swipeable table.

       Scoped to 639px to match .slate-row / .slate-prop-row. At 640-768px
       .props-content is ~600px+, which clears the 380px the desktop grid
       needs, so a wider scope would add a scrollbar to rows that already fit.

       THIS BLOCK MUST STAY BELOW the .props-sgp-mode rules above. A media
       query adds NO specificity, and `.props-player-row.props-sgp-mode` here
       ties the desktop rule at two classes — so whichever comes LAST wins.
       It originally sat ~30 lines higher and the desktop `44px 1fr ...` quietly
       beat it, leaving the phone SGP name track as a 1fr that resolved to the
       player name's max-content (97px for "Jayson Tatum") instead of the fixed
       120px. Guarded by tests/test_props_row_mobile_scroll.py, which asserts
       source ORDER — a declaration-level test cannot see the cascade. */
    @media (max-width: 639px) {
      .props-team-body { overflow-x:auto; -webkit-overflow-scrolling:touch; }
      /* Fixed first track, not 1fr: inside a scroller a 1fr name column would
         collapse to 0 and the row's max-content would then swing with the
         longest player name. 120px ellipsizes predictably (the cell already
         sets overflow:hidden + text-overflow) and pins the row at a known
         width, so the swipe is bounded. */
      .props-col-header,
      .props-player-row { grid-template-columns:120px 78px 76px 62px 76px 56px; }
      .props-player-row.props-sgp-mode,
      .props-col-header.props-sgp-mode { grid-template-columns:44px 120px 78px 76px 62px 76px 56px; }
      /* Without this the row BOX is only as wide as the 295px container while
         its tracks overflow, so the border, hover fill and locked-row tint
         stop mid-row once you scroll. min-width:100% keeps short rows full
         width. */
      .props-col-header,
      .props-player-row { width:max-content; min-width:100%; }
    }

    /* Floating slip panel — bottom-right of the props view */
    .props-sgp-panel { position:fixed; bottom:20px; right:20px; width:340px; max-height:70vh; overflow-y:auto; background:var(--bg-card); border:1px solid var(--border-default); border-radius:var(--radius-lg); box-shadow:0 10px 30px rgba(0,0,0,0.4); z-index:50; display:flex; flex-direction:column; }
    .props-sgp-panel-header { display:flex; align-items:center; gap:8px; padding:12px 16px; background:var(--bg-surface); border-bottom:1px solid var(--border-subtle); }
    .props-sgp-panel-title { font-weight:700; color:var(--text-primary); font-size:var(--text-base); }
    .props-sgp-leg-count-wrap { font-size:var(--text-sm); color:var(--text-muted); font-family:var(--font-mono); }
    .props-sgp-panel-close { margin-left:auto; background:none; border:none; color:var(--text-muted); font-size:20px; cursor:pointer; line-height:1; padding:0 4px; }
    .props-sgp-panel-close:hover { color:var(--text-primary); }
    .props-sgp-legs { padding:8px 12px; flex:1; min-height:60px; }
    .props-sgp-empty { font-size:var(--text-sm); color:var(--text-muted); text-align:center; padding:16px 8px; line-height:1.4; }
    .props-sgp-leg { display:flex; align-items:center; gap:8px; padding:8px 10px; margin-bottom:6px; background:var(--bg-surface); border-radius:6px; font-size:var(--text-sm); }
    .props-sgp-leg-name { font-weight:700; color:var(--text-primary); flex-shrink:0; }
    .props-sgp-leg-detail { color:var(--text-muted); font-family:var(--font-mono); font-size:var(--text-xs); flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .props-sgp-leg-remove { background:none; border:none; color:var(--text-faint); font-size:18px; cursor:pointer; line-height:1; padding:0 4px; flex-shrink:0; }
    .props-sgp-leg-remove:hover { color:var(--ev-neg); }
    .props-sgp-actions { display:flex; gap:8px; padding:8px 12px; border-top:1px solid var(--border-subtle); }
    .props-sgp-btn { flex:1; padding:8px 14px; border:none; border-radius:6px; font-size:var(--text-sm); font-weight:600; cursor:pointer; transition:opacity var(--transition-fast); }
    .props-sgp-btn-primary { background:var(--accent); color:var(--bg-base); }
    .props-sgp-btn-primary:hover { opacity:0.9; }
    .props-sgp-btn-secondary { background:var(--bg-surface); color:var(--text-secondary); border:1px solid var(--border-default); }
    .props-sgp-btn-secondary:hover { color:var(--text-primary); }
    .props-sgp-status { padding:0 16px; font-size:var(--text-sm); color:var(--text-muted); min-height:18px; }
    .props-sgp-result { padding:0 16px 12px; }
    .props-sgp-result-row { display:flex; align-items:center; justify-content:space-between; padding:6px 0; font-size:var(--text-sm); border-top:1px solid var(--border-subtle); color:var(--text-secondary); }
    .props-sgp-result-row:first-child { border-top:none; padding-top:10px; }
    .props-sgp-result-val { font-family:var(--font-mono); font-weight:700; color:var(--text-primary); }
    .props-sgp-result-corr { color:var(--accent-green); font-style:italic; font-size:var(--text-xs); padding:2px 0; border-top:none; }
    /* Grade-gate note: shown when a leg is not model-recommended so the joint
       EV above is not read as a green light. */
    .props-sgp-result-note { display:block; color:var(--accent-amber,#e8a40f); font-size:var(--text-xs); line-height:1.35; padding:4px 0; border-top:none; }

    /* Game strip odds rows */
    .strip-odds { display:grid; grid-template-columns:58px 1fr 1fr 1fr; gap:2px; align-items:center; font-size:var(--text-sm); font-family:var(--font-mono); }
    .strip-odds-abbr { font-size:var(--text-base); font-weight:600; color:var(--text-primary); padding:1px 2px; font-family:system-ui,sans-serif; white-space:nowrap; overflow:hidden; line-height:1.3; }
    .strip-odds-cell { text-align:center; padding:1px 2px; border-radius:3px; color:var(--text-secondary); line-height:1.3; cursor:pointer; transition:box-shadow 0.15s; }
    .strip-odds-cell .strip-odds-val { font-weight:600; }
    .strip-odds-cell .strip-odds-juice { font-size:var(--text-xs); color:var(--text-muted); font-weight:400; }
    .strip-odds-cell.strip-cell-bet { background:rgba(52,211,153,0.12); }
    .strip-odds-cell.strip-cell-lean { background:rgba(251,191,36,0.12); }
    .strip-odds-cell.strip-cell-focused { box-shadow:0 0 0 1.5px var(--accent); border-radius:4px; }
    .strip-odds-cell:hover { box-shadow:0 0 0 1px var(--accent); border-radius:4px; }

    /* ── Parlay Slip ─────────────────────────────────── */
    .parlay-slip{position:fixed;bottom:20px;right:20px;z-index:9000;width:380px;max-height:80vh;border-radius:var(--radius-lg);border:1px solid var(--border-default);background:var(--bg-surface);box-shadow:0 8px 32px rgba(0,0,0,0.4);overflow:hidden;transition:all 0.3s ease;font-family:'IBM Plex Sans',system-ui;}
    .parlay-slip.collapsed .parlay-expanded{display:none;}
    .parlay-slip.expanded .parlay-collapsed-body{display:none;}
    .parlay-slip .parlay-header{background:linear-gradient(135deg,var(--accent),var(--accent-hover));padding:10px 14px;display:flex;justify-content:space-between;align-items:center;cursor:pointer;}
    .parlay-slip .parlay-header .parlay-title{display:flex;align-items:center;gap:8px;font-weight:700;font-size:var(--text-base);color:#fff;}
    .parlay-slip .parlay-header .parlay-badge{background:rgba(0,0,0,0.3);border-radius:var(--radius-pill);padding:2px 8px;font-size:var(--text-xs);color:#fff;}
    .parlay-slip .parlay-header .parlay-toggle{color:rgba(255,255,255,0.8);font-size:var(--text-xs);}
    .parlay-collapsed-body{padding:12px 14px;display:flex;justify-content:space-between;font-size:var(--text-base);}
    .parlay-collapsed-body .parlay-stat{text-align:center;}
    .parlay-collapsed-body .parlay-stat-label{color:var(--text-muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.04em;}
    .parlay-collapsed-body .parlay-stat-value{font-weight:700;font-size:var(--text-lg);font-family:var(--font-mono);color:var(--text-primary);}
    .parlay-collapsed-body .parlay-stat-value.pos{color:var(--ev-pos);}
    .parlay-collapsed-body .parlay-stat-value.neg{color:var(--ev-neg);}
    .parlay-expanded{max-height:60vh;overflow-y:auto;}
    /* FIX 2026-08-11: these are the SLIP's legs (app.js renderParlaySlip,
       always inside #parlay-slip.parlay-slip). They were unscoped, so they
       also hit the Slate CART's legs — a different component, in
       .parlay-rail, styled from slate.css:555 under the same class name.
       slate.css loads second and won every property both declared, but
       `display` was declared ONLY here, so this flex row silently laid out
       the cart and put .parlay-leg-meta under its corner button.
       Scoped to .parlay-slip, matching this file's own convention for the
       rest of the component (see .parlay-slip .parlay-header above).
       Guarded by tests/test_slate_touch_targets.py. */
    .parlay-slip .parlay-leg{padding:10px 14px;border-bottom:1px solid var(--border-subtle);display:flex;justify-content:space-between;align-items:start;}
    .parlay-slip .parlay-leg-info{flex:1;}
    .parlay-slip .parlay-leg-badges{display:flex;gap:4px;align-items:center;margin-bottom:4px;}
    .parlay-slip .parlay-leg-badges .sport-badge{background:var(--bg-hover);color:var(--text-muted);font-size:var(--text-xs);padding:2px 5px;border-radius:var(--radius-sm);text-transform:uppercase;}
    .parlay-slip .parlay-leg-badges .sizing-badge-mini{font-size:var(--text-xs);padding:2px 5px;border-radius:var(--radius-sm);font-weight:700;}
    .parlay-slip .parlay-leg-badges .sizing-badge-mini.bet{background:var(--accent-green-bg);color:var(--ev-pos);}
    .parlay-slip .parlay-leg-badges .sizing-badge-mini.lean{background:var(--accent-amber-bg);color:var(--accent-amber);}
    .parlay-slip .parlay-leg-badges .sizing-badge-mini.pass{background:var(--bg-hover);color:var(--text-faint);font-weight:600;}
    .parlay-slip .parlay-leg-badges .sizing-badge-mini.defer{background:rgba(255,80,80,0.1);color:var(--ev-neg);font-weight:600;}
    .parlay-slip .parlay-leg-desc{color:var(--text-primary);font-size:var(--text-base);font-weight:600;}
    .parlay-slip .parlay-leg-context{color:var(--text-muted);font-size:var(--text-sm);}
    .parlay-slip .parlay-leg-odds{text-align:right;margin-right:8px;}
    .parlay-slip .parlay-leg-odds-val{font-weight:700;font-size:var(--text-base);font-family:var(--font-mono);}
    .parlay-slip .parlay-leg-odds-prob{color:var(--text-muted);font-size:var(--text-xs);font-family:var(--font-mono);}
    .parlay-slip .parlay-leg-remove{color:var(--text-faint);font-size:var(--text-base);cursor:pointer;padding:2px 4px;transition:color var(--transition-fast);}
    .parlay-slip .parlay-leg-remove:hover{color:var(--accent-red);}
    .parlay-warning{padding:8px 14px;background:var(--accent-amber-bg);border-bottom:1px solid var(--border-subtle);display:flex;align-items:center;gap:6px;font-size:12px;color:var(--accent-amber);}
    .parlay-summary{padding:12px 14px;background:var(--bg-elevated);}
    .parlay-metrics{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:var(--space-sm);margin-bottom:12px;}
    .parlay-metric-label{color:var(--text-muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.04em;}
    .parlay-metric-value{font-weight:700;font-size:18px;font-family:var(--font-mono);color:var(--text-primary);}
    .parlay-stake-row{display:flex;gap:8px;align-items:center;}
    .parlay-stake-input{flex:1;background:var(--bg-elevated);border:1px solid var(--border-default);border-radius:var(--radius-sm);padding:8px 10px 8px 24px;color:var(--text-primary);font-size:var(--text-base);font-weight:600;font-family:var(--font-mono);}
    .parlay-stake-box{background:var(--bg-elevated);border:1px solid var(--border-default);border-radius:var(--radius-sm);padding:8px 12px;text-align:center;}
    .parlay-actions{display:flex;justify-content:flex-end;padding:8px 14px;gap:8px;border-top:1px solid var(--border-subtle);}
    .parlay-actions button{background:none;border:1px solid var(--border-default);border-radius:var(--radius-sm);padding:4px 12px;color:var(--text-muted);font-size:var(--text-sm);cursor:pointer;transition:all var(--transition-fast);}
    .parlay-actions button:hover{border-color:var(--accent);color:var(--text-primary);}
    .odds-cell.parlay-selected{border:2px solid var(--ev-pos) !important;background:var(--accent-green-bg) !important;position:relative;}
    .odds-cell.parlay-selected::after{content:'✓';position:absolute;top:-6px;right:-6px;background:var(--ev-pos);color:#0d1117;border-radius:50%;width:14px;height:14px;font-size:12px;display:flex;align-items:center;justify-content:center;}
    .strip-odds-cell.parlay-selected{border:2px solid var(--ev-pos) !important;background:var(--accent-green-bg) !important;position:relative;}
    .strip-odds-cell.parlay-selected::after{content:'✓';position:absolute;top:-6px;right:-6px;background:var(--ev-pos);color:#0d1117;border-radius:50%;width:14px;height:14px;font-size:12px;display:flex;align-items:center;justify-content:center;}

    .strip-odds-head .strip-head-focused { color:var(--accent); }
    .gd-bet-row.gd-bet-row-focused { grid-template-columns:1fr; }
    .gd-bet-tabs { display:flex; align-items:center; gap:6px; margin-bottom:10px; }
    .gd-bet-tabs-label { font-size:var(--text-base); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; margin-right:4px; }
    .gd-bet-tab { padding:5px 14px; border-radius:var(--radius-pill); font-size:var(--text-base); font-weight:600; cursor:pointer; border:1.5px solid var(--border-strong); background:var(--bg-card); color:var(--text-secondary); transition:all 0.15s; }
    .gd-bet-tab:hover { border-color:var(--accent); color:var(--accent); }
    .gd-bet-tab.active { background:var(--accent); color:#0d1117; border-color:var(--accent); }
    .ls-col-focused { background:var(--accent-muted); }
    .ls-th-focused { color:var(--accent) !important; font-weight:700 !important; }
    .strip-odds-head { display:grid; grid-template-columns:58px 1fr 1fr 1fr; gap:2px; font-size:var(--text-sm); color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.06em; font-weight:700; text-align:center; margin-top:2px; }
    .strip-fallback { font-size:var(--text-xs); padding:0 4px; background:var(--accent-amber-bg); color:var(--accent-amber); border-radius:3px; font-weight:600; margin-left:4px; }
    .strip-steam { font-size:var(--text-xs); padding:0 4px; background:rgba(248,113,113,0.15); color:var(--ev-neg); border-radius:3px; font-weight:700; margin-left:4px; cursor:pointer; animation:steamPulse 1.8s ease-in-out infinite; }
    @keyframes steamPulse { 0%,100%{box-shadow:0 0 4px rgba(248,113,113,0.4);} 50%{box-shadow:0 0 12px rgba(248,113,113,0.8), 0 0 20px rgba(248,113,113,0.3);} }
    .steam-books-tbl { width:100%; border-collapse:collapse; font-size:var(--text-base); margin:10px 0; }
    .steam-books-tbl th { text-align:left; padding:4px 8px; border-bottom:2px solid var(--border-default); color:var(--text-muted); font-weight:600; font-size:var(--text-sm); text-transform:uppercase; }
    .steam-books-tbl td { padding:4px 8px; border-bottom:1px solid var(--border-subtle); }
    .steam-books-tbl .shift-pos { color:var(--ev-neg); font-weight:700; }
    .steam-books-tbl .shift-neg { color:var(--ev-pos); font-weight:700; }
    .steam-interp { border:1.5px solid var(--accent-amber); background:var(--accent-amber-bg); border-radius:8px; padding:12px 16px; margin-top:12px; font-size:var(--text-base); line-height:1.5; color:var(--accent-amber); }
    .steam-interp-action { margin-top:8px; padding:8px 12px; border-radius:6px; background:var(--accent-muted); border:1px solid var(--border-default); color:var(--accent); font-size:var(--text-sm); font-weight:600; }

    /* Game Dashboard — main content area */
    .game-dashboard { display:flex; flex-direction:column; height:100vh; overflow:hidden; background:var(--bg-page); }

    /* Dashboard Header */
    .gd-header { display:flex; align-items:center; gap:16px; padding:12px 24px; background:var(--bg-card); border-bottom:1px solid var(--border-default); flex-shrink:0; min-height:56px; }
    .gd-header-teams { font-size:1rem; font-weight:700; color:var(--text-primary); white-space:nowrap; }
    .gd-header-time { font-size:var(--text-base); color:var(--text-muted); }
    .gd-header-live { font-size:var(--text-sm); font-weight:700; color:var(--ev-neg); text-transform:uppercase; display:inline-flex; align-items:center; gap:4px; }
    .gd-header-live::before { content:''; display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--ev-neg); animation:livePulse 1.5s ease-in-out infinite; }
    .gd-header-badges { display:flex; gap:8px; margin-left:auto; flex-shrink:0; flex-wrap:wrap; }
    .gd-conf-badge { display:inline-flex; align-items:center; gap:4px; padding:3px 10px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:700; text-transform:uppercase; letter-spacing:0.03em; }
    .gd-conf-high { background:var(--accent-green-bg); color:var(--ev-pos); }
    .gd-conf-mod { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .gd-conf-low { background:var(--accent-red-bg); color:var(--ev-neg); }
    .gd-sizing-pill { display:inline-flex; align-items:center; gap:4px; padding:4px 12px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:800; text-transform:uppercase; }
    .gd-sizing-bet { background:var(--accent-hover); color:#fff; }
    .gd-sizing-lock { background:#3b82f6; color:#fff; }
    .gd-sizing-lean { background:var(--accent-amber); color:#fff; }
    .gd-sizing-pass { background:var(--bg-hover); color:var(--text-muted); }

    /* Dashboard Body */
    .gd-body { flex:1; overflow-y:auto; overflow-x:hidden; padding:16px 20px; display:flex; flex-direction:column; gap:14px; }

    /* Top Row — 3 Bet Type Cards */
    .gd-bet-row { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; flex-shrink:0; }
    .gd-bet-card { background:linear-gradient(135deg, var(--bg-card), var(--bg-surface)); border:1px solid var(--border-strong); border-radius:var(--radius-lg); padding:14px 16px; display:flex; flex-direction:column; gap:8px; box-shadow:var(--shadow-card); overflow:hidden; min-width:0; }
    .gd-bet-card-header { display:flex; align-items:center; justify-content:space-between; padding-bottom:6px; border-bottom:1px solid var(--border-subtle); }
    .gd-bet-card-type { font-size:var(--text-base); font-weight:700; color:var(--text-primary); text-transform:uppercase; letter-spacing:0.04em; }
    .gd-bet-card .sizing-badge-inline { padding:2px 8px; border-radius:var(--radius-pill); font-size:var(--text-sm); font-weight:700; text-transform:uppercase; }
    .gd-pick-line { font-size:var(--text-base); font-weight:700; color:var(--text-primary); }
    .gd-numbers { display:grid; grid-template-columns:1fr 1fr; gap:4px; }
    .gd-num-item { font-size:var(--text-base); }
    .gd-num-item .gd-num-label { color:var(--text-muted); font-weight:500; }
    .gd-num-item .gd-num-value { color:var(--text-primary); font-weight:600; font-family:var(--font-mono); }
    .gd-sources { border-top:1px solid var(--border-subtle); padding-top:6px; }
    .gd-source-row { display:flex; align-items:center; gap:6px; padding:2px 0; font-size:var(--text-base); }
    .gd-source-label { width:52px; color:var(--text-muted); font-weight:500; flex-shrink:0; }
    .gd-source-bar { flex:1; height:7px; background:var(--accent-muted); border-radius:3px; overflow:hidden; }
    .gd-source-bar-fill { height:100%; border-radius:3px; transition:width 0.3s ease; }
    .gd-source-val { width:48px; text-align:right; color:var(--text-secondary); font-family:var(--font-mono); font-weight:500; font-size:var(--text-base); }
    .gd-sizing-reason { padding:6px 10px; border-radius:6px; font-size:var(--text-base); color:var(--text-secondary); line-height:1.4; border-left:3px solid; }
    .gd-sizing-reason-bet { background:var(--accent-green-bg); border-color:var(--ev-pos); }
    .gd-sizing-reason-lock { background:rgba(59,130,246,0.08); border-color:#3b82f6; }
    .gd-sizing-reason-lean { background:var(--accent-amber-bg); border-color:var(--accent-amber); }
    .gd-sizing-reason-pass { background:var(--bg-surface); border-color:var(--text-muted); }
    .gd-best-book { font-size:var(--text-base); color:var(--accent-blue); font-weight:600; }

    /* Detail Panels — full-width vertical stack */
    .gd-detail-row { display:flex; flex-direction:column; gap:14px; overflow:visible; }
    .gd-detail-panel { background:var(--bg-card); border:1px solid var(--border-strong); border-radius:var(--radius-lg); box-shadow:var(--shadow-card); }
    .gd-detail-panel-header { padding:10px 14px; border-bottom:1px solid var(--border-subtle); font-size:var(--text-base); font-weight:700; color:var(--text-primary); text-transform:uppercase; letter-spacing:0.04em; display:flex; align-items:center; justify-content:space-between; }
    .gd-detail-panel-body { padding:12px 14px; font-size:var(--text-base); color:var(--text-secondary); line-height:1.5; overflow-wrap:break-word; word-break:break-word; }

    /* Signals panel styling */
    .gd-signal-item { padding:6px 0; border-bottom:1px solid var(--border-subtle); }
    .gd-signal-item:last-child { border-bottom:none; }
    .gd-signal-header { display:flex; align-items:center; gap:6px; }
    .gd-signal-name { font-weight:600; color:var(--text-primary); font-size:var(--text-sm); }
    .gd-signal-weight { color:var(--text-muted); font-size:var(--text-xs); }
    .gd-signal-favor { font-weight:600; font-size:var(--text-sm); margin-left:auto; }
    .gd-signal-drilldown { margin:4px 0 0 12px; padding:6px 0 4px 10px; border-left:2px solid var(--border-default); font-size:var(--text-sm); color:var(--text-secondary); }

    /* Books panel */
    .gd-books-best { display:grid; grid-template-columns:repeat(5,1fr); gap:1px; background:var(--border-subtle); border-radius:var(--radius-sm); overflow:hidden; margin-bottom:10px; }
    .gd-books-best-cell { background:var(--accent-green-bg); padding:8px 10px; text-align:center; }
    .gd-books-best-label { font-size:var(--text-xs); font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; }
    .gd-books-best-book { font-size:var(--text-sm); font-weight:700; color:var(--accent-green); }
    .gd-books-best-odds { font-size:var(--text-sm); font-weight:800; color:var(--accent-green); font-family:var(--font-mono); }

    /* Context panel */
    .gd-ctx-section { margin-bottom:10px; }
    .gd-ctx-label { font-size:var(--text-xs); font-weight:700; color:var(--accent-blue); text-transform:uppercase; letter-spacing:0.04em; margin-bottom:4px; }
    .gd-ctx-content { font-size:var(--text-sm); color:var(--text-secondary); line-height:1.5; }
    .gd-pm-row { display:flex; justify-content:space-between; padding:2px 0; font-size:var(--text-sm); }
    .gd-pm-label { color:var(--text-muted); }
    .gd-pm-value { color:var(--text-primary); font-weight:600; font-family:var(--font-mono); }

    /* AI panel extras */
    .gd-chalk { margin-top:8px; padding:8px 12px; background:var(--bg-surface); border-left:3px solid var(--text-dim); border-radius:6px; }
    .gd-chalk-label { font-size:var(--text-xs); font-weight:700; color:var(--text-dim); text-transform:uppercase; letter-spacing:0.04em; margin-bottom:6px; }
    .gd-chalk-row { padding:4px 0; }
    .gd-chalk-row + .gd-chalk-row { border-top:1px solid var(--border); margin-top:4px; padding-top:6px; }
    .gd-chalk-team { font-size:var(--text-lg); font-weight:700; color:var(--text-primary); }
    .gd-chalk-sources { font-size:var(--text-xs); color:var(--text-dim); margin-top:2px; }
    .gd-chalk-odds { font-size:var(--text-base); color:var(--text-secondary); margin-top:3px; }
    .gd-chalk-tag { display:inline-block; font-size:var(--text-xs); font-weight:600; padding:1px 6px; border-radius:9999px; margin-left:6px; vertical-align:middle; }
    .gd-chalk-tag--edge { background:var(--accent-green-bg); color:var(--ev-pos); }
    .gd-chalk-tag--low { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .gd-line-shop { margin-top:8px; padding:8px 12px; background:var(--accent-muted); border-left:3px solid var(--accent); border-radius:6px; }
    .gd-line-shop-label { font-size:var(--text-xs); font-weight:700; color:var(--accent); text-transform:uppercase; letter-spacing:0.04em; margin-bottom:2px; }
    .gd-line-shop-action { font-size:var(--text-base); color:var(--text-primary); line-height:1.4; }
    .gd-line-shop-fair { font-size:var(--text-sm); color:var(--text-muted); margin-top:3px; }
    .gd-clv-inline { display:inline-flex; gap:6px; align-items:center; font-size:var(--text-sm); color:var(--text-muted); }
    .gd-kelly-label { font-size:var(--text-sm); color:var(--text-muted); margin-top:3px; }
    .gd-ctx-delta { font-size:var(--text-sm); font-weight:600; margin-top:2px; }

    /* Empty state */
    .gd-empty { display:flex; align-items:center; justify-content:center; height:100%; color:var(--text-faint); font-size:var(--text-lg); }

    /* Fade transition for game switching */
    .gd-fade-enter { animation:gdFadeIn 0.15s ease; }
    @keyframes gdFadeIn { from{opacity:0;} to{opacity:1;} }

    /* Responsive: <1600px narrow strip */
    @media (max-width:1200px) {
      .app-layout-gameview { grid-template-columns:220px 240px 1fr; }
    }
    /* Tablet: collapse sidebar */
    @media (max-width:1024px) {
      .app-layout-gameview { grid-template-columns:0px 240px 1fr; }
      /* FIX 2026-07-12 (Batch 2): the sidebar used to display:none here with
         NO replacement — sport switching on phones/tablets depended entirely
         on the hamburger. It now becomes a horizontal sport-chip strip under
         the top nav (gameview keeps it hidden — that layout has its own
         strip). initSportTabs() force-opens the <details> categories at this
         width because closed-details content cannot be revealed by CSS. */
      .app-layout-gameview .sidebar { display:none; }
      .app-layout > .sidebar {
        position:static; width:auto; height:auto;
        /* min-width:0 is load-bearing: as a grid item the sidebar's default
           min-width:auto would size the track to the chip strip's min-content
           (~1300px of non-wrapping chips), blowing the mobile layout viewport
           out to ~1338px. Found live 2026-07-12. */
        min-width:0;
        padding:0; overflow:visible;
        border-right:none; border-bottom:1px solid var(--border-subtle);
      }
      .app-layout > .sidebar .sidebar-section { padding:0; }
      .app-layout > .sidebar .sidebar-section-title { display:none; }
      /* Footer utilities (Tutorial / Reference / Terms) stay hamburger-only */
      .app-layout > .sidebar > div:not(.sidebar-section) { display:none; }
      .app-layout > .sidebar #sport-tabs-container {
        display:flex; align-items:center; gap:6px;
        overflow-x:auto; -webkit-overflow-scrolling:touch;
        padding:8px 12px;
        scrollbar-width:none;
      }
      .app-layout > .sidebar #sport-tabs-container::-webkit-scrollbar { display:none; }
      .app-layout > .sidebar .sidebar-category { display:contents; }
      .app-layout > .sidebar .sidebar-category > summary { display:none; }
      /* FIX 2026-07-25: was display:contents, intending (with the same on
         .sidebar-category above) to flatten every league chip into ONE flex
         row. `<details>` has a UA shadow DOM and does NOT honour that
         flattening, so each CATEGORY stayed a single flex item with its
         leagues stacked VERTICALLY — making the strip as tall as the tallest
         category. Measured on prod at 390px, chips shared an x-slot per
         category: soccer dx=569 at dy 8/56/103/151/198/246 (6 leagues) =
         6 x 48px = 288 + padding = the 302px strip, i.e. 36% of the phone
         screen and almost all of it empty (align-items:center centred each
         shorter stack, hence the staggered look).
         An explicit horizontal row doesn't depend on that flattening:
         302px -> 66px, all 17 chips on one row, horizontal scroll intact.
         Do NOT revert to display:contents — it computes as "contents" with a
         0x0 rect, so it LOOKS applied while laying out as a block.
         Guarded by tests/test_mobile_chip_strip.py. */
      .app-layout > .sidebar .sidebar-league-list {
        display:flex; flex-direction:row; align-items:center; gap:6px;
      }
      .app-layout > .sidebar .sidebar-league-item {
        flex:0 0 auto; white-space:nowrap;
        padding:8px 14px; margin:0;
        min-height:44px;
        border:1px solid var(--border-default); border-left:1px solid var(--border-default);
        border-radius:var(--radius-pill);
      }
      /* Drill-down chips (2026-09-06). The strip has two levels: the top
         level shows single-league sports plus one chip per multi-league
         category, and tapping a category replaces the row with its leagues
         behind a back chip. app.js drives it; these rules only dress it.

         ONE ROW AT EVERY LEVEL, deliberately. The first attempt (c34cf377,
         reverted in f3032e53) disclosed the leagues in an absolutely
         positioned SECOND row. The sidebar and main content overlap in this
         layout, so z-index only picks which one is hidden: the leagues
         rendered under the game board and could not be tapped, and lifting
         the sidebar instead painted chips over the 'Game Board' heading.
         Nothing here is positioned, so neither can happen.

         .drill-chip reuses .sidebar-league-item for its box so a category
         chip is exactly a league chip's size -- styling it independently gave
         44px against the league chips' 45.25px, and align-items:center turned
         that into a visible 1.63px stagger.
         Guarded by tests/test_sidebar_drilldown.py. */
      .app-layout > .sidebar .sidebar-league-item[data-drill-hidden] { display:none; }
      .app-layout > .sidebar .drill-chip {
        background:none; font-family:inherit; cursor:pointer;
        justify-content:center; }
      .app-layout > .sidebar .drill-chip .league-icon { font-size:18px; line-height:1; }
      /* Dashed so the back chip reads as navigation, not as a sport. The
         accent fill stays reserved for .active -- the reverted attempt used
         the same accent treatment for 'open' and 'selected', so two chips
         looked chosen at once. */
      .app-layout > .sidebar .drill-back { border-style:dashed; color:var(--text-secondary); }
      .app-layout > .sidebar .sidebar-league-item.active {
        border-color:var(--accent); color:var(--accent);
        background:var(--accent-muted);
      }
      /* FIX 2026-07-12: sidebar hides at <=1024px but .app-layout kept its
         180px first track until <=639px, so across 640-1023px (iPad portrait,
         large phones landscape) main-content auto-placed into the dead 180px
         column and the whole Slate rendered in a ~180px ribbon. Collapse to a
         single column wherever the sidebar rail is gone (the chip strip spans
         the single column as a grid row). minmax(0,1fr), not bare 1fr — bare
         1fr's auto minimum lets wide row content (chip strip, tables) expand
         the track past the viewport. */
      /* FIX 2026-09-06: the sidebar row was allocated 133px for 63px of
         content — 70px of empty space sitting in the chrome above the board,
         measured on prod at a real 375x812.

         Cause: `.app-layout` has `min-height:100vh` and declares NO
         grid-template-rows (main.css:297), so rows are implicit and auto-sized
         — and a grid's default `align-content` is `normal`, which behaves as
         STRETCH. Whenever the content is shorter than 100vh, the leftover is
         split EQUALLY between the auto rows. Above 1024px this is invisible
         because the sidebar and main content share ONE row; the single column
         below is what turns the sidebar into a row of its own, so it starts
         collecting half the slack. The arithmetic: 63 + 70 = 133, and the main
         row showed the same +70.

         `auto` sizes the strip to its content; the second track takes the rest,
         so the layout still fills the viewport. `align-content:start` also
         removes the 70px but leaves it dead at the BOTTOM of the page — the
         content row loses it (measured: main 692px vs 554px).

         minmax(0,1fr) rather than bare 1fr for the same reason the column above
         gives: bare 1fr's auto minimum lets wide row content expand the track.
         Measured identical here today, and kept for consistency and safety.

         Verified 375/540/768/1024: sidebar 64px at every width, no horizontal
         overflow; and at 1200px the layout is still a single 756px row across
         two columns, which this rule cannot reach. */
      .app-layout { grid-template-columns:minmax(0,1fr); grid-template-rows:auto minmax(0,1fr); }
    }
    /* Mobile: single column — strip on top, dashboard below */
    @media (max-width:768px) {
      .app-layout-gameview { display:flex; flex-direction:column; }
      .app-layout-gameview .sidebar { display:none; }
      .game-strip { height:auto; max-height:35vh; position:relative; border-right:none; border-bottom:1px solid var(--border-default); }
      .game-dashboard { height:auto; min-height:65vh; }
      .game-strip-item { padding:8px 10px; }
      .strip-odds-head span { font-size:var(--text-xs); }
      .gd-header { flex-direction:column; align-items:flex-start; gap:6px; }
      .gd-source-grid { grid-template-columns:1fr 1fr; }
    }

    /* ============================================================
       TUTORIAL MODAL
       ============================================================ */
    .tutorial-modal { width:min(720px,92vw); max-height:85vh; overflow-y:auto; border-top:3px solid var(--accent-blue); }
    .tutorial-header { display:flex; align-items:center; justify-content:space-between; padding:16px 20px 12px; border-bottom:1px solid var(--border-subtle); }
    .tutorial-header h2 { margin:0; font-size:1rem; color:var(--text-primary); font-weight:700; }
    .tutorial-counter { font-size:var(--text-sm); color:var(--text-muted); font-weight:500; }
    .tutorial-progress { display:flex; gap:6px; padding:12px 20px 0; justify-content:center; }
    .tutorial-pip { width:10px; height:10px; border-radius:50%; background:var(--border-default); transition:background 0.2s, transform 0.2s; cursor:pointer; }
    .tutorial-pip:hover { transform:scale(1.2); }
    .tutorial-pip.active { background:var(--accent-blue); transform:scale(1.15); }
    .tutorial-pip.completed { background:var(--accent-green); }
    .tutorial-step-content { padding:20px 24px 16px; animation:tutorialFade 0.25s ease; }
    @keyframes tutorialFade { from{opacity:0;transform:translateX(8px)} to{opacity:1;transform:translateX(0)} }
    @keyframes analyticsSpinner { to{transform:rotate(360deg)} }
    .analytics-loading{display:flex;align-items:center;gap:8px;color:var(--text-dim);font-size:var(--text-base);}
    .analytics-loading::before{content:'';width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:analyticsSpinner 0.6s linear infinite;}
    .tutorial-step-title { font-size:var(--text-base); font-weight:700; color:var(--text-primary); margin:0 0 4px; }
    .tutorial-step-phase { font-size:var(--text-sm); font-weight:600; text-transform:uppercase; letter-spacing:0.06em; color:var(--accent-blue); margin-bottom:12px; }
    .tutorial-visual { background:var(--bg-page); border:1px solid var(--border-subtle); border-radius:var(--radius-md); padding:16px; margin-bottom:14px; }
    .tutorial-text { font-size:var(--text-base); color:var(--text-secondary); line-height:1.6; }
    .tutorial-text p { margin:0 0 8px; }
    .tutorial-text strong { color:var(--text-primary); }
    .tutorial-audio { display:flex; align-items:center; gap:10px; }
    .tutorial-audio-btn { display:inline-flex; align-items:center; gap:6px; padding:6px 14px; border-radius:var(--radius-pill); border:1px solid var(--border-default); background:var(--bg-surface); color:var(--text-secondary); font-size:var(--text-sm); font-weight:600; cursor:pointer; transition:all 0.15s; }
    .tutorial-audio-btn:hover { background:var(--bg-hover); border-color:var(--accent-blue); color:var(--accent-blue); }
    .tutorial-audio-btn.playing { background:var(--accent-muted); border-color:var(--accent-blue); color:var(--accent-blue); }
    .tutorial-nav { display:flex; align-items:center; justify-content:space-between; padding:12px 20px; border-bottom:1px solid var(--border-subtle); }
    .tutorial-nav-btn { padding:8px 20px; border-radius:var(--radius-sm); font-size:var(--text-base); font-weight:600; cursor:pointer; transition:all 0.15s; border:1px solid var(--border-default); }
    .tutorial-nav-btn.primary { background:var(--accent-blue); color:#0d1117; border-color:var(--accent-blue); }
    .tutorial-nav-btn.primary:hover { opacity:0.9; }
    .tutorial-nav-btn.secondary { background:var(--bg-surface); color:var(--text-secondary); }
    .tutorial-nav-btn.secondary:hover { background:var(--bg-hover); }
    .tutorial-nav-btn:disabled { opacity:0.4; cursor:not-allowed; }
    .tutorial-skip { font-size:var(--text-sm); color:var(--text-muted); cursor:pointer; text-decoration:underline; text-underline-offset:2px; }
    .tutorial-skip:hover { color:var(--text-primary); }
    /* Tutorial visual diagram annotations */
    .tv-card { background:var(--bg-card); border:1px solid var(--border-default); border-radius:var(--radius-md); overflow:hidden; font-size:var(--text-sm); }
    .tv-row { display:grid; grid-template-columns:1fr 60px 60px 60px; gap:4px; padding:4px 10px; align-items:center; }
    .tv-team { font-weight:600; color:var(--text-primary); }
    .tv-odds { text-align:center; padding:3px 4px; border:1px solid var(--border-default); border-radius:4px; font-family:var(--font-mono); font-size:var(--text-sm); }
    .tv-odds.tv-bet { background:var(--accent-green-bg); border-color:var(--ev-pos); }
    .tv-odds.tv-lean { background:var(--accent-amber-bg); border-color:var(--accent-amber); }
    .tv-header { display:grid; grid-template-columns:1fr 60px 60px 60px; gap:4px; padding:4px 10px; font-size:var(--text-xs); font-weight:700; color:var(--text-muted); text-transform:uppercase; text-align:center; }
    .tv-header span:first-child { text-align:left; }
    .tv-badge { display:inline-block; padding:2px 8px; border-radius:var(--radius-pill); font-size:var(--text-xs); font-weight:700; text-transform:uppercase; }
    .tv-badge-bet { background:var(--accent-green-bg); color:var(--accent-green); }
    .tv-badge-lock { background:rgba(59,130,246,0.12); color:#3b82f6; }
    .tv-badge-lean { background:var(--accent-amber-bg); color:var(--accent-amber); }
    .tv-badge-pass { background:var(--bg-surface); color:var(--text-faint); }
    .tv-annotation { display:flex; align-items:center; gap:6px; margin-top:8px; font-size:var(--text-xs); color:var(--text-muted); }
    .tv-annotation::before { content:''; display:inline-block; width:8px; height:8px; border-radius:50%; flex-shrink:0; }
    .tv-annotation.green::before { background:var(--accent-green-bg); border:1px solid var(--accent-green); }
    .tv-annotation.amber::before { background:var(--accent-amber-bg); border:1px solid var(--accent-amber); }
    .tv-annotation.blue::before { background:var(--accent-muted); border:1px solid var(--accent-blue); }
    .tv-source-bar { display:flex; gap:2px; height:8px; border-radius:4px; overflow:hidden; margin:4px 0; }
    .tv-source-bar > div { height:100%; border-radius:2px; }
    .tv-gauge-row { display:flex; align-items:center; gap:6px; margin:6px 0; }
    .tv-gauge-label { font-size:var(--text-sm); color:var(--text-muted); min-width:70px; font-weight:500; }
    .tv-gauge-track { flex:1; height:6px; background:var(--accent-muted); border-radius:3px; overflow:hidden; }
    .tv-gauge-fill { height:100%; border-radius:3px; }
    /* Analytics Dashboard Grid */
    .analytics-grid { display:flex; flex-direction:column; gap:var(--space-md); }
    .analytics-metrics { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--space-sm); margin-bottom:var(--space-md); }
    .analytics-metric { background:var(--bg-surface); border:1px solid var(--border-subtle); border-radius:var(--radius-md); padding:var(--space-md); }
    .analytics-metric-value { font-size:24px; font-weight:700; font-family:var(--font-mono); color:var(--text-primary); }
    .analytics-metric-label { font-size:var(--text-xs); color:var(--text-muted); text-transform:uppercase; letter-spacing:0.04em; margin-top:var(--space-xs); }
    .analytics-metric-trend { font-size:var(--text-sm); font-family:var(--font-mono); margin-top:var(--space-xs); }
    .analytics-metric-trend.up { color:var(--ev-pos); }
    .analytics-metric-trend.down { color:var(--ev-neg); }
    .analytics-charts { display:grid; grid-template-columns:1fr 1fr; gap:var(--space-md); }
    .analytics-chart-card { background:var(--bg-surface); border:1px solid var(--border-subtle); border-radius:var(--radius-md); padding:var(--space-md); }
    /* card-info: shared style for the "?" info blurb on analytics/PM cards.
       Extracted 2026-07-12 from 20 identical inline style="" blocks in
       index.html. The inline style="display:none;" is intentionally kept on
       each element because toggleInfo() reads/writes info.style.display. */
    .card-info { color:var(--text-muted); font-size:var(--text-base); line-height:1.6; margin-bottom:12px; padding:10px 12px; background:var(--bg-elevated); border-radius:6px; }
    .analytics-chart-title { font-size:var(--text-sm); font-weight:600; color:var(--text-secondary); margin-bottom:var(--space-sm); display:flex; align-items:center; gap:8px; }
    .analytics-full-width { grid-column:1/-1; }
    .analytics-section-header { font-size:var(--text-base); font-weight:800; color:var(--accent); margin:var(--space-xl) 0 var(--space-md) 0; text-transform:uppercase; letter-spacing:0.04em; }
    .analytics-filter-bar { display:flex; gap:var(--space-sm); align-items:center; padding:var(--space-sm) 0; flex-wrap:wrap; }
    .analytics-filter-pill { font-size:var(--text-sm); padding:var(--space-xs) var(--space-md); border-radius:var(--radius-pill); border:1px solid var(--border-default); color:var(--text-muted); cursor:pointer; background:none; transition:all var(--transition-fast); }
    .analytics-filter-pill.active { background:var(--accent-muted); color:var(--accent); border-color:var(--accent); }
    .analytics-filter-pill:hover { border-color:var(--text-muted); color:var(--text-secondary); }

    /* FIX 2026-07-25: the Analytics tab pushed the PAGE sideways 467px at a
       390px viewport (scrollWidth 857 vs clientWidth 390).

       Root cause is grid blowout, not the tables on their own: a `1fr` track's
       automatic minimum is min-content, so every analytics grid sized itself to
       its widest descendant and spilled out of its parent —

         .analytics-metrics  repeat(4,1fr)  -> tracks 98/98/98/88 in a 310px row,
                                               last KPI card reached x=446
         .analytics-charts   1fr 1fr        -> e.g. 326px + 397px in a 310px row
         #analytics-daily-perf's inline split (app.js) 1fr 1fr
                                            -> 354px + 434px in a 284px card;
                                               the Signal Accuracy table started
                                               at x=423 and ran to x=857

       Stacking the grids is necessary but NOT sufficient: at one column the
       Signal Accuracy table's own min-content (434px) still exceeds the 284px
       card interior. So each wide table also gets its own scroll box —
       wide content scrolls in its own container, never the page body
       (same contract as .slate-ss-scroll and #analytics-props-history). */
    .analytics-tbl-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
    /* Two-up split inside an analytics card (was an inline style in app.js;
       promoted to a class so the phone breakpoint below can reach it). */
    .analytics-split-2 { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
    /* Six-up stat strip inside the Daily Performance card (also de-inlined). */
    .analytics-tiles-6 { display:grid; grid-template-columns:repeat(6,1fr); gap:8px; margin-bottom:16px; }

    @media (max-width: 639px) {
      .analytics-metrics { grid-template-columns:repeat(2,1fr); }
      .analytics-tiles-6 { grid-template-columns:repeat(2,1fr); }
      .analytics-charts { grid-template-columns:1fr; }
      .analytics-split-2 { grid-template-columns:1fr; }
      /* min-width:0 is the declaration that actually defeats the blowout — a
         single-column track still refuses to shrink below its item's
         min-content without it, so dropping this re-breaks the page. */
      .analytics-metrics > *,
      .analytics-tiles-6 > *,
      .analytics-charts > *,
      .analytics-split-2 > * { min-width:0; }
    }

    /* Detail Drawer — Slide-out panel */
    .detail-drawer-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.3); z-index:50; opacity:0; pointer-events:none; transition:opacity var(--transition-normal); }
    .detail-drawer-overlay.open { opacity:1; pointer-events:auto; }
    /* FIX 2026-04-06: flex column with the scroll moved INSIDE #drawer-content
       so position:sticky on .drawer-bet-tabs-section actually works. Sticky was
       unreliable when the scroll container was the fixed+transformed
       .detail-drawer itself. */
    .detail-drawer { position:fixed; top:56px; right:0; bottom:0; width:420px; background:var(--bg-surface); border-left:1px solid var(--border-subtle); box-shadow:-8px 0 40px rgba(0,0,0,0.4); z-index:51; transform:translateX(100%); transition:transform 200ms ease-out; display:flex; flex-direction:column; overflow:hidden; }
    #drawer-content { flex:1 1 auto; overflow-y:auto; min-height:0; }
    .detail-drawer.open { transform:translateX(0); }
    .detail-drawer-close { position:absolute; top:var(--space-md); right:var(--space-md); background:none; border:none; color:var(--text-muted); font-size:18px; cursor:pointer; padding:var(--space-xs); border-radius:var(--radius-sm); z-index:1; min-width:44px; min-height:44px; display:inline-flex; align-items:center; justify-content:center; }
    .detail-drawer-close:hover { color:var(--text-primary); background:var(--bg-hover); }
    .drawer-header { padding:var(--space-lg); border-bottom:1px solid var(--border-subtle); }
    .drawer-header-teams { font-size:var(--text-lg); font-weight:700; color:var(--text-primary); }
    .drawer-header-meta { display:flex; align-items:center; gap:var(--space-sm); margin-top:var(--space-xs); flex-wrap:wrap; }
    .drawer-section { padding:var(--space-md) var(--space-lg); border-bottom:1px solid var(--border-subtle); }
    /* FIX 2026-04-06: pin the bet-type tab row (All / ML / Spread / O/U)
       at the top of #drawer-content so the user can switch bet types from
       anywhere while scrolling through Picks & Odds, AI Confidence, Books,
       Context, and Line Movement sections below. Background must be opaque
       so scrolling content passes underneath cleanly. The scroll container
       is #drawer-content (flex:1; overflow-y:auto) — sticky finds it as the
       nearest scrolling ancestor. */
    .drawer-bet-tabs-section { display:block; position:-webkit-sticky; position:sticky; top:0; background:var(--bg-surface); z-index:5; box-shadow:0 2px 8px rgba(0,0,0,0.4); }
    .drawer-section-label { font-size:var(--text-xs); color:var(--text-muted); text-transform:uppercase; letter-spacing:0.06em; margin-bottom:var(--space-sm); font-weight:700; }
    .drawer-odds-grid { display:grid; grid-template-columns:1fr 1fr 1fr; gap:var(--space-sm); }
    .drawer-odds-cell { background:var(--bg-elevated); border-radius:var(--radius-sm); padding:var(--space-sm); text-align:center; border:1px solid var(--border-subtle); }
    .drawer-odds-cell.best { border-color:var(--accent); background:var(--accent-muted); }
    .drawer-odds-val { font-size:var(--text-lg); font-weight:700; font-family:var(--font-mono); color:var(--text-primary); }
    .drawer-odds-label { font-size:var(--text-xs); color:var(--text-muted); margin-top:2px; }
    .drawer-odds-sub { font-size:var(--text-xs); color:var(--text-faint); font-family:var(--font-mono); }
    .drawer-signal-row { display:flex; justify-content:space-between; align-items:center; padding:var(--space-xs) 0; }
    .drawer-signal-name { font-size:var(--text-sm); color:var(--text-secondary); }
    .drawer-signal-value { font-size:var(--text-sm); font-family:var(--font-mono); font-weight:600; }
    .drawer-context-tags { display:flex; flex-wrap:wrap; gap:var(--space-xs); }
    .drawer-context-tag { font-size:var(--text-xs); padding:2px var(--space-sm); border-radius:var(--radius-pill); background:var(--bg-elevated); color:var(--text-secondary); border:1px solid var(--border-subtle); }
    .drawer-bet-tabs { display:flex; gap:2px; margin-bottom:var(--space-sm); }
    .drawer-bet-tab { padding:var(--space-xs) var(--space-md); font-size:var(--text-sm); font-weight:600; color:var(--text-muted); cursor:pointer; border-bottom:2px solid transparent; transition:all var(--transition-fast); }
    .drawer-bet-tab:hover { color:var(--text-secondary); }
    .drawer-bet-tab.active { color:var(--accent); border-bottom-color:var(--accent); }
    .drawer-gauge { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
    .drawer-gauge-label { font-size:var(--text-sm); color:var(--text-muted); min-width:70px; font-weight:500; }
    .drawer-gauge-track { flex:1; height:6px; background:var(--accent-muted); border-radius:3px; overflow:hidden; }
    .drawer-gauge-fill { height:100%; border-radius:3px; }
    .drawer-gauge-score { font-size:var(--text-sm); font-weight:700; min-width:32px; text-align:right; font-family:var(--font-mono); }
    .drawer-gauge-grade { font-size:var(--text-xs); font-weight:600; min-width:55px; }
    .drawer-books-row { display:grid; grid-template-columns:1fr auto auto auto; gap:var(--space-sm); align-items:center; padding:3px 0; font-size:var(--text-sm); border-bottom:1px solid var(--border-subtle); }
    .drawer-books-row:last-child { border-bottom:none; }
    .drawer-books-name { color:var(--text-secondary); font-weight:500; }
    .drawer-books-val { font-family:var(--font-mono); color:var(--text-primary); font-weight:600; text-align:center; min-width:45px; }
    .drawer-books-val.is-best { color:var(--accent); }
    .info-btn {
      width:17px; height:17px; border-radius:50%;
      border:1.5px solid var(--accent); background:var(--accent-muted);
      color:var(--accent); font-size:var(--text-sm); font-weight:700;
      cursor:pointer; display:inline-flex; align-items:center;
      justify-content:center; padding:0; transition:all 0.15s;
    }
    .info-btn:hover { background:var(--accent-muted); color:#fff; }
    /* Pick-history pager. Was inline padding+font-size in app.js, which no
       media query could override; promoted to a class so the phone
       breakpoint can reach it (same reason as .analytics-split-2).
       Colour and cursor stay inline — they depend on hasPrev/hasNext. */
    .pick-history-page-btn {
      padding:3px 10px; font-size:var(--text-sm);
      border-radius:4px; border:1px solid var(--border);
      background:var(--bg-card);
    }

    /* ----------------------------------------------------------------
       Bet Routing Teaser Pill (2026-04-09)
       ---------------------------------------------------------------- */
    .bet-routing-pill {
      display:inline-flex; align-items:center; gap:6px;
      margin-top:4px; padding:3px 10px;
      font-size:var(--text-xs); font-weight:600;
      color:var(--accent); background:var(--accent-muted);
      border:1px dashed var(--accent);
      border-radius:var(--radius-pill);
      cursor:pointer; transition:all 0.15s;
      user-select:none;
    }
    .bet-routing-pill:hover {
      background:var(--accent); color:#0d1117; border-style:solid;
    }
    .bet-routing-pill .pill-soon {
      font-size:12px; font-weight:700;
      padding:1px 5px; border-radius:var(--radius-pill);
      background:rgba(255,255,255,0.2);
      text-transform:uppercase; letter-spacing:0.05em;
    }
    .bet-routing-pill:hover .pill-soon {
      background:rgba(255,255,255,0.3);
    }

/* ── Auth Modal ── */
.auth-input {
  width:100%; padding:10px 12px;
  background:var(--bg-elevated); border:1px solid var(--border-default);
  border-radius:var(--radius-sm); color:var(--text-primary);
  font-size:15px; font-family:var(--font-sans);
}
.auth-input:focus { border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-muted); outline:none; }
.auth-btn {
  margin-top:12px; padding:10px 16px;
  background:var(--accent); color:#0d1117; border:none;
  border-radius:var(--radius-sm); font-size:15px; font-weight:600;
  cursor:pointer; transition:background var(--transition-fast);
}
.auth-btn:hover { background:var(--accent-hover); }
.auth-btn:disabled { opacity:0.5; cursor:not-allowed; }
/* ── User Pill + Menu ── */
.user-pill { position:relative; cursor:pointer; display:flex; align-items:center; gap:6px; }
.tier-badge {
  font-size:12px; font-weight:700; text-transform:uppercase; letter-spacing:0.05em;
  padding:2px 6px; border-radius:var(--radius-pill);
}
.tier-badge.free { background:var(--bg-hover); color:var(--text-muted); }
.tier-badge.pro { background:rgba(16,185,129,0.15); color:var(--accent); }
.tier-badge.enterprise { background:rgba(234,179,8,0.15); color:#eab308; }
.user-menu {
  display:none; position:absolute; top:calc(100% + 4px); right:0;
  min-width:140px; background:var(--bg-surface); border:1px solid var(--border-default);
  border-radius:var(--radius-md); box-shadow:0 8px 24px rgba(0,0,0,0.4); z-index:200;
}
.user-menu.open { display:block; }
.user-menu-item {
  padding:8px 14px; font-size:14px; color:var(--text-secondary); cursor:pointer;
  /* display/text-decoration added 2026-08-22: the menu gained <a> items
     (My Account, Alerts, Watchlist). Anchors are inline and underlined by
     default, which broke the row rhythm. No-ops for the <div> items. */
  display:block; text-decoration:none;
}
.user-menu-item:hover { background:var(--bg-hover); color:var(--text-primary); }

/* ── Feedback FAB ── */
.feedback-fab {
  position:fixed; bottom:20px; left:20px; z-index:8000;
  width:44px; height:44px; border-radius:50%;
  /* FIX 2026-07-23: #fff on --accent was 2.54:1 — WCAG 1.4.3 AA fail. Same
     treatment the tutorial play button already uses: dark ink on the green (7.5:1). */
  background:var(--accent); color:#0d1117; border:none;
  font-size:20px; cursor:pointer; box-shadow:0 4px 16px rgba(0,0,0,0.4);
  transition:background var(--transition-fast), transform var(--transition-fast);
}
.feedback-fab:hover { background:var(--accent-hover); transform:scale(1.1); }

/* =========================================
   COMMAND CENTER (2026-04-15)
   ========================================= */

#view-commandcenter {
  color: var(--text-primary);
  font-family: 'IBM Plex Sans', sans-serif;
}
.cc-view-container {
  padding: 0;
  width: 100%;
  box-sizing: border-box;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
}
/* FIX 2026-07-12 (Batch 2): display lives in a :not(.hidden) rule so the
   .hidden utility hides this container without an !important arms race
   (was display:flex !important + .cc-view-container.hidden counter-rule). */
.cc-view-container:not(.hidden) {
  display: flex;
}

.cc-summary-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: 6px;
  margin-bottom: 8px;
  flex-shrink: 0;
}

.cc-summary-count {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
}

.cc-summary-metrics {
  font-size: 14px;
  color: var(--text-muted);
  font-family: 'IBM Plex Mono', monospace;
}

.cc-refresh-btn {
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--text-muted);
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
}
.cc-refresh-btn:hover { background: var(--bg-hover); color: var(--text-primary); }

.cc-filter-bar {
  background: var(--bg-base);
  padding: 6px 12px;
  border: 1px solid var(--border-default);
  border-radius: 6px;
  margin-bottom: 8px;
  z-index: 10;
  flex-shrink: 0;
}

.cc-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  padding: 2px 0;
}

.cc-filter-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.cc-filter-label select,
.cc-filter-label input[type="number"],
.cc-filter-label input[type="text"] {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 14px;
  font-family: inherit;
}

.cc-checkgroup {
  display: inline-flex;
  gap: 8px;
}
.cc-checkgroup label {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 13px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-primary);
  font-weight: 500;
  cursor: pointer;
}

.cc-table-wrap {
  overflow: auto;
  flex: 1 1 0;
  min-height: 0;
  border: 1px solid var(--border-default);
  border-radius: 6px;
  background: var(--bg-surface);
}

.cc-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  font-family: 'IBM Plex Mono', monospace;
}

.cc-th {
  text-align: left;
  padding: 10px 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.05em;
  border-bottom: 1px solid var(--border-default);
  cursor: pointer;
  user-select: none;
  background: var(--bg-base);
  position: sticky;
  top: 0;
  z-index: 5;
}
.cc-th:hover { color: var(--text-primary); }
.cc-th-active { color: var(--text-primary); }

.cc-row {
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--border-default);
  cursor: pointer;
  transition: background 0.1s ease;
}
.cc-row:hover { background: var(--bg-hover); }
.cc-row td { padding: 8px 12px; vertical-align: middle; }

.cc-row-bet  { border-left-color: var(--accent-green, #10b981); }
.cc-row-lock { border-left-color: #3b82f6; }
.cc-row-lean { border-left-color: var(--accent-amber, #f59e0b); }
.cc-row-noedge { border-left-color: var(--text-faint, #b0b0ba); }

.cc-tier-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 3px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
}
.cc-tier-bet  { background: rgba(16,185,129,0.15); color: #10b981; }
.cc-tier-lock { background: rgba(59,130,246,0.15); color: #3b82f6; }
.cc-tier-lean { background: rgba(245,158,11,0.15); color: #f59e0b; }
.cc-tier-noedge { background: var(--bg-base); color: var(--text-faint); }

.cc-ev-positive { color: var(--accent-green, #10b981); font-weight: 600; }
.cc-ev-negative { color: var(--text-muted); }
/* FIX 2026-07-16 same-hole check: phantom (>30pp) EV never renders green */
.cc-ev-suspect { color: var(--accent-amber, #f59e0b); font-weight: 600; }
.cc-row-started { opacity: 0.5; }
.cc-started-chip {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em;
  color: var(--text-muted); border: 1px solid var(--border-color, #374151);
  border-radius: 3px; padding: 0 3px; margin-left: 4px; vertical-align: 1px;
}

/* Prob colored relative to breakeven — green = edge exists */
.cc-prob-strong { color: var(--accent-green, #10b981); font-weight: 600; }
.cc-prob-thin { color: var(--accent-amber, #f59e0b); }
.cc-prob-negative { color: var(--text-muted); }

.cc-grade-high   { color: var(--accent-green, #10b981); font-weight: 600; }
.cc-grade-mod    { color: var(--accent-amber, #f59e0b); }
.cc-grade-low    { color: var(--text-muted); }

.cc-empty {
  text-align: center;
  padding: 40px 12px;
  color: var(--text-faint);
  font-style: italic;
  font-family: 'IBM Plex Sans', sans-serif;
}

/* FIX 2026-07-22: the empty state now explains WHY the table is empty
   (see static/js/cc_empty_state.js). The headline is the verdict, the detail
   carries the counts, and the action is the escape hatch. Not italic — this
   is a statement of fact about the board, not a placeholder. */
.cc-empty-headline {
  font-style: normal;
  font-size: var(--text-base, 15px);
  color: var(--text-secondary);
  margin-bottom: 6px;
}

.cc-empty-detail {
  font-style: normal;
  font-size: var(--text-sm, 13px);
  color: var(--text-muted);
  max-width: 62ch;
  margin: 0 auto;
  line-height: 1.5;
}

.cc-empty-action {
  font-style: normal;
  font-family: inherit;
  font-size: var(--text-sm, 13px);
  margin-top: 14px;
  /* 44px min touch target — enforced by tests/test_mobile_touch_targets.py.
     Assert the DECLARATION, not the rendered rect. */
  min-height: 44px;
  padding: 10px 18px;
  cursor: pointer;
  color: var(--text-primary);
  background: transparent;
  border: 1px solid var(--border-default, #333);
  border-radius: 6px;
}

.cc-empty-action:hover {
  border-color: var(--accent-green, #10b981);
  color: var(--accent-green, #10b981);
}

/* The escape hatch is the one control on an empty All Bets table, so a
   fat-finger miss there leaves the user with nothing to do but leave.
   Enumerated in tests/test_slate_touch_targets.py::GUARDED_CONTROLS — the
   sentinel only reads declarations inside a mobile media query. */
@media (max-width: 768px) {
  .cc-empty-action {
    min-height: 44px;
  }
}

.cc-detail-row td {
  padding: 16px 24px;
  background: var(--bg-base);
  border-left: 3px solid var(--accent-muted, #b0b0ba);
}

/* Sports custom dropdown */
.cc-sports-wrap { position: relative; }
.cc-sports-dropdown { position: relative; display: inline-block; }
.cc-sports-btn {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: 4px;
  padding: 4px 12px 4px 8px;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
  min-width: 120px;
  text-align: left;
}
.cc-sports-btn:hover { border-color: var(--text-muted); }
.cc-sports-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 100;
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: 6px;
  padding: 6px 0;
  margin-top: 4px;
  min-width: 160px;
  max-height: 300px;
  overflow-y: auto;
  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.cc-sports-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  font-size: 14px;
  color: var(--text-primary);
  cursor: pointer;
  font-weight: 500;
}
.cc-sports-item:hover { background: var(--bg-hover); }

@media (max-width: 768px) {
  .cc-filter-row { gap: 8px; }
  .cc-checkgroup { flex-wrap: wrap; }
  .cc-th, .cc-row td { padding: 6px 8px; font-size: 13px; }
}

/* FIX 2026-07-12 (Batch 2): phone collapse for the 12-col All Bets table.
   At 390px the full grid needed ~700-800px of two-finger horizontal scroll.
   Keep the 5 decision-driving columns — Matchup(3), Side/Line(5), Odds(7),
   EV(8), Tier(12) — and hide Time(1), Sport(2), Bet(4), Book(6), Prob(9),
   B/E(10), Grade(11). Everything hidden is still shown in the tap-to-expand
   detail row (renderCCRow order pins the indices; detail/empty rows use
   colspan and are excluded via :not([colspan])). */
@media (max-width: 639px) {
  .cc-table thead th:nth-child(1),
  .cc-table thead th:nth-child(2),
  .cc-table thead th:nth-child(4),
  .cc-table thead th:nth-child(6),
  .cc-table thead th:nth-child(9),
  .cc-table thead th:nth-child(10),
  .cc-table thead th:nth-child(11),
  .cc-table tbody td:nth-child(1):not([colspan]),
  .cc-table tbody td:nth-child(2):not([colspan]),
  .cc-table tbody td:nth-child(4):not([colspan]),
  .cc-table tbody td:nth-child(6):not([colspan]),
  .cc-table tbody td:nth-child(9):not([colspan]),
  .cc-table tbody td:nth-child(10):not([colspan]),
  .cc-table tbody td:nth-child(11):not([colspan]) {
    display: none;
  }
}

/* ============================================================
   Cricket (IPL) card elements — Phase 1 Task 16
   ============================================================ */

/* Pitch type badge */
.gd-cricket-pitch-badge {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  text-transform: capitalize;
  letter-spacing: 0.02em;
}
.gd-cricket-pitch-green   { background: rgba(16,185,129,0.18); color: #10b981; }
.gd-cricket-pitch-dusty   { background: rgba(217,119,6,0.18);  color: #d97706; }
.gd-cricket-pitch-flat    { background: rgba(59,130,246,0.18); color: #3b82f6; }
.gd-cricket-pitch-balanced{ background: rgba(107,114,128,0.18);color: var(--text-faint); }
.gd-cricket-pitch-unknown { background: rgba(107,114,128,0.12);color: var(--text-dim); }

/* DLS risk amber chip */
.gd-cricket-dls-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 12px;
  font-weight: 600;
  color: #f59e0b;
  background: rgba(245,158,11,0.15);
  border: 1px solid rgba(245,158,11,0.3);
  border-radius: 4px;
  padding: 2px 6px;
  margin-left: 6px;
  vertical-align: middle;
}

/* ============================================================
   Tennis card elements — Phase 1 Task 13
   ============================================================ */

/* Surface badge */
.gd-tennis-surface-badge {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  text-transform: capitalize;
  letter-spacing: 0.02em;
}
/* Clay — terracotta */
.gd-tennis-surface-clay    { background: rgba(180,83,9,0.18);    color: #b45309; }
/* Hard court — blue */
.gd-tennis-surface-hard    { background: rgba(59,130,246,0.18);  color: #3b82f6; }
/* Grass — green */
.gd-tennis-surface-grass   { background: rgba(16,185,129,0.18);  color: #10b981; }
/* Indoor / carpet — purple */
.gd-tennis-surface-indoor  { background: rgba(139,92,246,0.18);  color: #8b5cf6; }
/* Unknown fallback */
.gd-tennis-surface-unknown { background: rgba(107,114,128,0.12); color: var(--text-dim); }

/* ==========================================================================
   UFC / MMA card badges — Phase 1 Task 13
   ========================================================================== */

/* Weight class badge — replaces "SPREAD" column in UFC cards */
.gd-ufc-weight-class {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  background: rgba(212, 175, 55, 0.15);
  color: #d4af37;
  border: 1px solid rgba(212, 175, 55, 0.30);
}

/* Title fight highlights the weight class in a brighter accent */
.gd-ufc-weight-class.title-fight {
  background: rgba(212, 175, 55, 0.28);
  border-color: rgba(212, 175, 55, 0.55);
}

/* Rounds O/U badge — replaces "O/U" column in UFC cards */
.gd-ufc-rounds {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  background: rgba(99, 102, 241, 0.12);
  color: #818cf8;
  border: 1px solid rgba(99, 102, 241, 0.25);
  white-space: nowrap;
}

/* Cross-region book dispersion panel — Phase 2 Task 9 */
.gd-dispersion-panel {
  font-size: var(--text-sm);
}
.gd-dispersion-book-row {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 3px 0;
  border-bottom: 1px solid var(--border-default);
  font-size: var(--text-sm);
}
.gd-dispersion-book-row:last-child { border-bottom: none; }
.gd-dispersion-book-name {
  flex: 1;
  color: var(--text-secondary);
  font-weight: 500;
  min-width: 100px;
}
.gd-dispersion-book-prob {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}
.gd-dispersion-outlier .gd-dispersion-book-name {
  color: #f59e0b;
}
.gd-dispersion-outlier {
  background: rgba(245,158,11,0.07);
  border-radius: 4px;
  padding-left: 4px;
  padding-right: 4px;
}

/* Synthetic totals — Phase 2 Task 8 */
.gd-synthetic-badge {
  display: inline-block;
  background: #7c3aed;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 4px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin-left: 8px;
  vertical-align: middle;
  cursor: help;
}
.gd-synthetic-row {
  background: rgba(124,58,237,0.06);
  border: 1px solid rgba(124,58,237,0.18);
  border-radius: 6px;
  padding: 10px 14px;
}

/* ── Mobile input zoom-prevention (iOS Safari zooms on focus when input <16px) ──
   NOTE (2026-09-02): despite the name, this block is NOT reliably last in
   the file -- .br-input's own plain rule (grep this file for the bare
   `.br-input {` declaration, not the media-guarded one) was added
   2026-07-12, two months after this block was written, and sits AFTER it in
   source order, so it silently beat this block's entry for .br-input (both
   are specificity (0,1,0); a media query adds none). Before adding a
   selector here, grep the file for a later plain re-declaration of it -- if
   one exists, this block cannot win and the override must instead go in the
   "Mobile input zoom-prevention: .br-input (intra-file collision)" block
   further down this file: genuinely last in the file, not just in this
   named block. Targets each app input class explicitly to keep specificity
   equal-or-greater. */
@media (max-width: 639px) {
  .bankroll-pill .bankroll-input,
  .top-nav-dropdown input[type="number"],
  .sidebar-control input[type="number"],
  .sidebar-control input[type="text"],
  .ref-search-input,
  .auth-input,
  .cc-filter-label select,
  .cc-filter-label input[type="number"],
  .cc-filter-label input[type="text"],
  /* FIX 2026-08-11: the Analytics page-size select renders at --text-sm
     (13px) via .analytics-filter-pill and zoomed the viewport on focus.
     Qualified with `select` so it beats the .analytics-filter-pill rule
     (0,1,1 vs 0,1,0) — an unqualified .analytics-filter-pill here would tie
     and lose nothing, but would also restyle the pill BUTTONS, which are not
     focusable text controls and must keep their compact size. */
  select.analytics-filter-pill {
    font-size: 16px;
  }
}

/* WCAG 2.5.5 touch targets — 2026-08-11 picks-page sweep (All Bets,
   Analytics, Props). Measured at 390x844: every failure was HEIGHT, never
   width, because these controls were sized for a mouse. End of file so they
   win on source order. Guarded by tests/test_slate_touch_targets.py. */
@media (max-width: 639px) {
  /* Buttons and label rows: centre the existing content in a 44px box. */
  .cc-checkgroup label,
  .cc-sports-btn,
  .cc-refresh-btn,
  .analytics-filter-pill,
  .pick-history-page-btn,
  .props-sport-pill,
  .props-category-pill,
  .props-sgp-toggle {
    min-height: 44px;
    /* WCAG 2.5.5 is 44 in BOTH axes. Height was the common failure, but the
       short labels are narrow too: measured after the height fix,
       .cc-refresh-btn was 35 wide, the "ML" bet-type label 40.3, and the
       "All" clv/cal tabs 40.5. */
    min-width: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Form controls get height only. `display:inline-flex` on a <select>
     fights the native control's own rendering, so it is not set here. */
  .cc-filter-label select,
  .cc-filter-label input[type="number"],
  .cc-filter-label input[type="text"] {
    min-height: 44px;
  }

  /* .info-btn is a 17x17 dot INLINE inside chart headings — 22 of them. A
     44px box would reflow every heading, so grow the TARGET, not the
     control: an absolutely-positioned ::after contributes no layout.
     17 + 2*14 = 45px. (accessibility-audit skill: expand the hit area with
     padding or a pseudo-element when the visual design needs to stay small.) */
  .info-btn { position: relative; }
  .info-btn::after {
    content: "";
    position: absolute;
    inset: -14px;
  }
}

/* ── Mobile game-card layout (<640px) ──
   Desktop: .game-card-row is a flex row with .game-card fixed at 500px + .ls-box (line-shopping)
   side-by-side. At mobile width the 500px overflows the 390px viewport; .main-content has
   overflow:hidden so the overflow is silently CLIPPED (right half of every card is cut off
   without horizontal scroll appearing). Fix: stack vertically and let .game-card fill width.
   Also collapse .ls-box's 2-col grid to single-column so the line-shopping cards are readable.
   Also: .sidebar is already display:none at <1024px (line ~837), but .app-layout's grid template
   still allocates 180px to the empty column — collapse the sidebar column at mobile so .main-content
   actually gets the full viewport width. */
@media (max-width: 639px) {
  .app-layout { grid-template-columns: 1fr; }
  .game-card-row { flex-direction: column; }
  .game-card-row .game-card { flex: 1 1 auto; width: 100%; max-width: 100%; }
  .ls-box { grid-template-columns: 1fr; }
}

/* ── 100vh → 100dvh upgrade (iOS Safari URL-bar-collapse fix) ──
   100vh on iOS includes the URL bar's space. When the bar collapses on scroll, content
   jumps. 100dvh (dynamic viewport height) tracks the actual visible area. Wrapped in
   @supports so older browsers fall through to the original 100vh rules unchanged. */
@supports (height: 100dvh) {
  .app-layout { min-height: 100dvh; }
  .sidebar { height: calc(100dvh - 56px); }
  .main-content { height: calc(100dvh - 56px); }
  .app-layout-gameview { min-height: 100dvh; }
  .game-strip { height: 100dvh; }
  .game-dashboard { height: 100dvh; }
  @media (max-width: 639px) {
    .main-content { height: calc(100dvh - 56px - 64px - env(safe-area-inset-bottom)); }
  }
}

/* ── Mobile detail drawer (<640px) ──
   Desktop: 420px-wide right-side drawer. At 390px viewport that overflows by 30px,
   AND the drawer's bottom:0 sits BEHIND the new bottom-nav (z-index 51 vs 100), so
   the bottom 64px of drawer content is hidden. Fix: full-width drawer at mobile,
   stop above the bottom-nav. Slide-from-right animation preserved. */
@media (max-width: 639px) {
  /* 65px = 64 (bottom-nav-inner) + 1 (bottom-nav border-top) */
  .detail-drawer { width: 100%; left: 0; right: 0; bottom: calc(65px + env(safe-area-inset-bottom)); border-left: none; }
}

/* ============================================================
   UI cleanup Batch 2 (2026-07-12) — component classes extracted
   from inline style="" scaffolding in index.html. Values are
   verbatim from the inline originals unless commented.
   ============================================================ */

/* Modal size modifiers (were per-modal inline max-width overrides) */
.modal--sm { max-width: 420px; }
.modal--md { max-width: 520px; }
.modal--lg { max-width: 720px; }
.modal--xl { max-width: 900px; }
/* Modal header row: title left, close button right */
.modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.modal-head h2 { margin: 0; font-size: 1.05rem; }

/* Prediction-markets (Polymarket / Kalshi) panels */
.pm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 1023px) { .pm-grid { grid-template-columns: 1fr; } }
.pm-panel { background: var(--bg-card); border: 1px solid var(--border-default); border-radius: var(--radius-lg); overflow: hidden; }
.pm-panel-head { padding: 16px; border-bottom: 1px solid var(--border-subtle); }
.pm-panel-head .status-line { margin-top: 8px; }
.pm-panel-title-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.pm-panel-title-row h3 { margin: 0; font-size: var(--text-lg); color: var(--text-primary); font-weight: 700; }
.pm-panel-badge { font-size: var(--text-xs); padding: 2px 8px; border-radius: var(--radius-pill); font-weight: 600; }
.pm-panel-badge--crypto { background: var(--accent-muted); color: var(--accent); }
.pm-panel-badge--usd { background: var(--accent-green-bg); color: var(--ev-pos); }
.pm-panel-meta { font-size: var(--text-sm); color: var(--text-muted); }
.pm-panel-count { font-size: var(--text-sm); color: var(--text-faint); }
.pm-panel-push { margin-left: auto; }
.pm-panel-desc { margin: 0; font-size: var(--text-sm); color: var(--text-muted); line-height: 1.5; }
.pm-panel-scroll { overflow-x: auto; }

/* Bet-routing teaser modal internals.
   NOTE: --accent-blue-bg is not a defined token — the original inline style
   referenced it too, so the badge background has always resolved transparent.
   Kept verbatim to preserve behavior; candidate for a real token later. */
.feature-badge { display: inline-block; font-size: var(--text-xs); font-weight: 700; color: var(--accent-blue); background: var(--accent-blue-bg); border: 1px solid var(--accent-blue); padding: 2px 8px; border-radius: var(--radius-pill); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 14px; }
.feature-copy { font-size: var(--text-base); color: var(--text-secondary); line-height: 1.5; margin: 0 0 14px; }
.feature-list { list-style: none; padding: 0; margin: 0 0 18px; font-size: var(--text-base); color: var(--text-secondary); }
.feature-list li { margin: 6px 0; display: flex; align-items: flex-start; gap: 8px; }
.feature-check { color: var(--accent); font-weight: 700; }
.br-label { display: block; font-size: var(--text-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 4px; }
.br-input { width: 100%; padding: 9px 12px; font-size: var(--text-base); border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--bg-input, var(--bg-card)); color: var(--text-primary); box-sizing: border-box; }
.br-submit { margin-top: 10px; width: 100%; padding: 10px 14px; font-size: var(--text-base); font-weight: 600; background: var(--accent-blue); color: white; border: none; border-radius: var(--radius-md); cursor: pointer; }
.br-status { margin-top: 8px; font-size: var(--text-sm); min-height: 18px; }
.br-footnote { font-size: var(--text-xs); color: var(--text-muted); margin: 14px 0 0; line-height: 1.4; }

/* Props Scanner header + scroll container (were inline on #view-props kids) */
.props-header { z-index: 20; background: var(--bg-base); padding: var(--space-sm) var(--space-lg); display: flex; align-items: center; gap: var(--space-lg); flex-wrap: wrap; border-bottom: 1px solid var(--border-subtle); flex-shrink: 0; }
.props-title { margin: 0; font-size: var(--text-lg); font-weight: 700; color: var(--text-primary); }
.props-sport-pills { display: flex; gap: 2px; }
.props-scroll { padding: var(--space-sm) var(--space-lg) var(--space-lg); flex: 1 1 0; min-height: 0; overflow-y: auto; }
.props-status-line { margin-bottom: var(--space-sm); color: var(--text-muted); font-size: var(--text-sm); }
.props-games { display: flex; flex-direction: column; gap: var(--space-sm); }

/* Steam modal chart box */
.steam-chart-box { height: 240px; position: relative; }

/* ===== Reduced motion — WCAG 2.3.3 / motion sensitivity (FIX 2026-07-18) =====
   Nothing respected the OS "reduce motion" setting, so the infinite loops
   (taglineGlow, livePulse on live games, steamPulse, spinners) ran non-stop for
   everyone. Neutralize CSS animations + transitions for users who opt out;
   unchanged for everyone else. Canonical reset (accessibility-audit skill). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Slate drilldown Context panel (2026-07-22) — injuries, weather, officials,
   fatigue, prediction markets. This data shipped to the browser for months
   with no screen rendering it: the display lived on the legacy Game Board,
   hidden 2026-07-18, and the parity check never listed context.
   See static/js/slate_context.js. */
/* 2026-09-07: .slate-ctx and .slate-sigstack are no longer emitted by the
   /slate drilldown (every section renders through CESectionCard, see
   static/js/section_card.js and slate.css .slate-sec). Kept for any other
   consumer. Note --accent-blue resolves to #10b981, so this "blue" rule
   was always green on screen. */
.slate-ctx {
  margin-top: 8px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border-left: 3px solid var(--accent-blue, #3b82f6);
  border-radius: 6px;
}

/* Signal Stack — the complete weighted stack behind the consensus number.
   Sits above Edge math; mirrors the Context panel chrome but keeps its own
   green accent so "our signals" reads apart from "external context". */
.slate-sigstack {
  margin-top: 8px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border-left: 3px solid var(--slate-green, #22c55e);
  border-radius: 6px;
}

.slate-ss-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
/* FIX 2026-07-24: a global th nowrap kept team-name headers on one line, so
   the table's min-content width (442px) overflowed the 312px takeover body
   at 390px viewport and clipped the Weight column off-screen. Headers wrap;
   the scroll wrapper below is the safety net for extreme names. */
.slate-ss-scroll { overflow-x: auto; }
.slate-ss-table th {
  text-align: right;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-muted);
  padding: 3px 6px;
  border-bottom: 1px solid var(--border-subtle);
  white-space: normal;
  overflow-wrap: break-word;
}
.slate-ss-table th:first-child { text-align: left; }
.slate-ss-table td {
  padding: 3px 6px;
  text-align: right;
  color: var(--text-secondary);
  font-family: var(--font-mono);
  border-bottom: 1px solid var(--border-subtle);
}
.slate-ss-table td:first-child {
  text-align: left;
  font-family: inherit;
}
.slate-ss-pick { font-weight: 700; color: var(--text-primary); }
.slate-ss-wt { color: var(--text-faint); }
.slate-ss-consensus td {
  font-weight: 700;
  color: var(--text-primary);
  border-top: 2px solid var(--border-default);
}

/* FIX 2026-07-24 (desktop review): unconstrained, the panel stretched to the
   full drilldown width — 1149px at a 1280 viewport — leaving ~280px of dead
   space between a signal's name and its number, and shoving the advanced
   rows' team/prob pair against the far right edge. Sharps scan DOWN a number
   column; a stretched data table is the classic mobile-styles-let-loose tell
   (OddsJam and Unabated both cap theirs). 720px also holds the explanation
   prose to a readable measure. No effect on mobile — the container is
   already narrower. */
.slate-ss-scroll,
.slate-ss-footnote,
.slate-ss-advanced,
.slate-ss-ai { max-width: 720px; }

.slate-ss-footnote {
  margin-top: 4px;
  font-size: var(--text-xs);
  color: var(--text-faint);
  line-height: 1.4;
}

.slate-ss-advanced, .slate-ss-ai { margin-top: 10px; }
.slate-ss-count {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
}

.slate-ss-sig {
  display: grid;
  grid-template-columns: 1fr auto 52px;
  gap: 4px 8px;
  padding: 3px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
  align-items: baseline;
}
.slate-ss-sig--unavail { opacity: 0.55; }
.slate-ss-sig-name { color: var(--text-secondary); }
.slate-ss-sig-wt { color: var(--text-faint); font-size: var(--text-xs); }
.slate-ss-sig-favor { color: var(--text-primary); font-weight: 600; text-align: right; }
.slate-ss-sig-prob {
  color: var(--text-secondary);
  font-family: var(--font-mono);
  text-align: right;
}
.slate-ss-sig-explain {
  grid-column: 1 / -1;
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
}
/* Redistribution note for a signal that picked up weight from a dark one —
   Task 8: "+24.2pp absorbed from starting_pitcher". Same row shape as
   slate-ss-sig-explain (full width, own line) but faint: it is provenance
   for the weight badge above it, not the headline explanation. */
.slate-ss-sig-absorbed {
  grid-column: 1 / -1;
  font-size: var(--text-xs);
  color: var(--text-faint);
  line-height: 1.4;
}

/* Confidence conviction list -- Task B4: panel.confidence.conviction
   (game.ai_side_conviction_signals) rendered as its own card. Reuses the
   .slate-ss-sig row shape (name / qualifier / value) unchanged -- a
   conviction source and a signal-stack row are the same visual shape.
   Agree/disagree color the qualifier the same way the rest of the app
   colors edge: var(--ev-pos)/var(--ev-neg), never a new palette. */
.slate-ss-conviction { max-width: 720px; }
.slate-ss-sig-favor--agree { color: var(--ev-pos); }
.slate-ss-sig-favor--disagree { color: var(--ev-neg); }

.slate-ctx-head {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 8px;
}

.slate-ctx-section { margin-bottom: 10px; }
.slate-ctx-section:last-child { margin-bottom: 0; }

.slate-ctx-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--accent-blue, #3b82f6);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}

/* The priced impact. Amber by default, red past 2pp — a 4pp injury swing is
   the single biggest thing on this panel and must not read as a footnote. */
.slate-ctx-impact {
  margin-left: 8px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--accent-amber, #f59e0b);
}
.slate-ctx-impact--severe { color: var(--ev-neg, #ef4444); }

.slate-ctx-line,
.slate-ctx-note {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.slate-ctx-note { color: var(--text-muted); font-style: italic; }

.slate-ctx-player {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: baseline;
  font-size: var(--text-sm);
  padding: 2px 0;
  color: var(--text-secondary);
}
.slate-ctx-player-name { font-weight: 600; color: var(--text-primary); }
.slate-ctx-player-team { color: var(--text-faint); font-size: var(--text-xs); }
.slate-ctx-status { font-weight: 600; color: var(--accent-amber, #f59e0b); }
.slate-ctx-status--out { color: var(--ev-neg, #ef4444); }

/* 50 rows is real evidence, but it cannot bury the edge math above it. */
.slate-ctx-details { margin-top: 6px; }
.slate-ctx-details summary {
  font-size: var(--text-sm);
  color: var(--text-muted);
  cursor: pointer;
  /* 44px touch target — enforced by tests/test_slate_touch_targets.py.
     Assert the CSS DECLARATION, not the rendered rect. */
  min-height: 44px;
  display: flex;
  align-items: center;
}
.slate-ctx-report { max-height: 220px; overflow-y: auto; margin-top: 4px; }

@media (max-width: 768px) {
  .slate-ctx-details summary { min-height: 44px; }
}

/* ── Mobile/tablet shell: ONE scroll region, not two (2026-07-25) ──
   `.app-layout` is a grid. At <=1024px it collapses to a single column
   (main.css:956), so `.sidebar` stops being a column BESIDE `.main-content`
   and becomes a ~302px sport-chip-strip ROW ABOVE it. But `.main-content`
   kept `height: calc(100dvh - 56px [- 64px])` — a formula written for the
   desktop case where it starts directly under the 56px top nav.

   Measured on prod at 390x844 before this fix:
     .main-content top = 358 (56 nav + 302 stacked strip), height = 724
     358 + 724 = 1082 = document.scrollHeight EXACTLY
     1082 - 844 = 238 = exactly what the window scrolled
   That outer scrollbar plus `#view-premium`'s own overflow-y:auto gave two
   nested scroll regions, and letterboxed the Slate drilldown into a 421px
   window on an 844px phone.

   Fix: below the grid-collapse breakpoint, stop deriving the pane height
   from the viewport and let the PAGE scroll. Range is <=1024px to match the
   grid collapse — scoping it to 639px would leave 640-1024px broken
   (verified 768x1024 pre-fix: window scrolled 302px AND pane scrolled
   2379px), the same trap main.css:948 documents them hitting before.

   Verified at 390px that no view container collapses once the pane height
   is released: premium 3894px, pmdebug 4373, analytics 6545, commandcenter
   398, props 383, cricket 217 — and zero nested scrollers remain in the
   shell (component-level table scrollers are intentional and untouched). */
@media (max-width: 1024px) {
  .main-content { height: auto; overflow: visible; }
  #view-premium, #view-pmdebug, #view-analytics,
  #view-commandcenter, #cricket-view, #ufc-view,
  #view-props.view-props-flex { flex: 0 1 auto; min-height: 0; overflow: visible; }
  .props-scroll { flex: 0 1 auto; min-height: 0; overflow-y: visible; }
  /* The top nav is position:fixed at 56px. With the page scrolling, a
     sticky child would otherwise pin UNDER it instead of below it. */
  #proof-sticky { top: 56px; }
}

/* `.bottom-nav` is position:fixed and display:block ONLY at <=639px
   (main.css:124). The page now scrolls beneath it, so reserve its height
   (64px inner + 1px border) plus the safe-area inset. */
@media (max-width: 639px) {
  .main-content { padding-bottom: calc(65px + env(safe-area-inset-bottom)); }
}

/* ============================================================
   GAMES BOARD — PER-MARKET ODDS BOX (2026-08-05)
   Markup: renderOddsBoxCard() in static/js/app.js.
   Nothing here may drop below 12px (tests/test_type_scale_floor.py), and
   anything carrying meaning uses --text-primary/--text-secondary rather than
   --text-faint — grey text was called out as hard to read.
   ============================================================ */
/* 2026-09-03: the card edge was invisible. --border (0.1) on --bg-surface
   (#111113) against a --bg-page of #09090b put border AND surface within a few
   percent of the page, so 19 games read as one column. Separation now comes
   from depth as well as a line: the elevated surface plus a soft shadow, with
   --border-card carrying more contrast than --border-strong. Guarded by
   tests/test_board_card_separation.py. */
.ob-card { background:var(--bg-elevated); border:1px solid var(--border-card); border-radius:var(--radius-lg); margin-bottom:10px; overflow:hidden; box-shadow:var(--shadow-card-sm); }
.ob-card-live { border-color:rgba(248,113,113,0.35); }
.ob-head { padding:10px 14px; border-bottom:1px solid var(--border); font-size:var(--text-base); font-weight:600; color:var(--text-primary); }
.ob-head .ob-at { color:var(--text-muted); font-weight:400; margin:0 2px; }
.ob-live-chip { font-family:var(--font-mono); font-size:var(--text-xs); font-weight:700; color:var(--ev-neg); text-transform:uppercase; letter-spacing:0.04em; margin-left:8px; }
.ob-live-score { font-family:var(--font-mono); font-size:var(--text-sm); font-weight:600;
                 margin-left:8px; color:var(--text-primary); }
.ob-score-sep  { margin:0 4px; color:var(--text-secondary); font-weight:400; }
.ob-locked { padding:14px; font-size:var(--text-sm); color:var(--text-primary); text-align:center; }

/* Header actions (2026-08-29): watch / log / share, migrated from the slate's
   per-bet list — see renderCardActions() in app.js. Right-aligned so they read
   as controls on the header rather than more matchup text. */
.ob-actions { display:flex; justify-content:flex-end; align-items:center; gap:8px;
  padding:0 14px 10px; }
.ob-action-watch, .ob-action-log, .ob-action-share {
  font-size:12px; font-weight:600; line-height:1; padding:5px 9px;
  border-radius:var(--radius-sm); border:1px solid var(--border);
  background:var(--bg-surface); color:var(--text-secondary); text-decoration:none;
  cursor:pointer;
}
.ob-action-watch { padding:5px 8px; }
.ob-action-watch.on { color:var(--accent-amber); border-color:var(--accent-amber); }
.ob-action-log:hover, .ob-action-share:hover, .ob-action-watch:hover {
  color:var(--text-primary); border-color:var(--text-muted);
}

/* 2026-09-02 board-card sweep. .ob-actions is emitted on EVERY card, so a
   15-game board carries 45 of these; measured 28x24 / 39x24 / 52x24 at 375px
   against the 44px floor. This block must stay directly AFTER the shared
   .ob-action-watch, .ob-action-log, .ob-action-share rule above (and its
   :hover rule) -- that desktop rule declares no min-height, min-width, or
   display, so this override wins the cascade only by coming later. Moving
   it earlier, or adding one of those properties to the desktop rule without
   moving this block again, silently drops mobile back below the 44px floor.
   Share already clears 44 wide by its label, and is recorded in
   WIDTH_BY_LAYOUT rather than given a no-op min-width.
   Guarded by tests/test_slate_touch_targets.py. */
@media (max-width: 639px) {
  .ob-action-watch,
  .ob-action-log,
  .ob-action-share {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .ob-action-watch,
  .ob-action-log {
    min-width: 44px;
  }
}

/* Staleness stamp (2026-08-29), also migrated from the slate list — see
   renderCardFreshness() in app.js. 12px floor, muted unless stale/provisional. */
.ob-freshness { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:0 14px 8px; font-size:12px; color:var(--text-muted); }
.ob-freshness-text.ob-stale { color:var(--accent-amber); }
.ob-provisional { color:var(--accent-amber); font-weight:600; }

/* overflow-x is a floor, not the plan — the mobile block below makes the grid
   fit 375px outright, because an element that scrolls inside a container still
   reports a right edge past the viewport. */
.ob-box-wrap { overflow-x:auto; }
.ob-box { width:100%; border-collapse:collapse; }
.ob-box thead th { font-family:var(--font-mono); font-size:var(--text-xs); font-weight:700; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-secondary); padding:8px; text-align:center; }
.ob-box tr.ob-subhead th { font-family:var(--font-mono); font-size:var(--text-xs); font-weight:400; letter-spacing:0; text-transform:none; color:var(--text-muted); padding:0 8px 8px; border-bottom:1px solid var(--border); }
/* Each word wears the colour of the value it names — the only in-place
   explanation of what the green number is. Keep these in step with
   .ob-line/.ob-price/.ob-prob below or the legend starts lying. */
.ob-k-line, .ob-k-price, .ob-k-prob { color:var(--text-primary); }
.ob-k-prob { font-weight:700; }
.ob-box td { padding:7px 8px; text-align:center; border-bottom:1px solid var(--border-subtle); }
.ob-box tbody tr:last-child td, .ob-box tbody tr:last-child .ob-team { border-bottom:none; }
.ob-team { text-align:left; padding:7px 8px 7px 14px; font-size:var(--text-base); font-weight:600; color:var(--text-primary); border-bottom:1px solid var(--border-subtle); }
.ob-cell { display:inline-flex; flex-direction:column; align-items:center; gap:1px; min-width:76px; padding:4px 8px; border:1px solid transparent; border-radius:var(--radius-sm); }
.ob-cell-pick { background:var(--accent-green-bg); border-color:var(--accent); }
.ob-line { font-family:var(--font-mono); font-size:var(--text-base); font-weight:700; color:var(--text-primary); line-height:1.25; }
.ob-price { font-family:var(--font-mono); font-size:var(--text-xs); color:var(--text-primary); line-height:1.25; }
.ob-prob { font-family:var(--font-mono); font-size:var(--text-sm); font-weight:700; color:var(--text-primary); line-height:1.25; }

.ob-strip { padding:8px 14px; border-top:1px solid var(--border-subtle); font-size:var(--text-base); color:var(--text-primary); }
.ob-strip-k { font-family:var(--font-mono); font-size:var(--text-xs); font-weight:700; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-secondary); margin-right:8px; }
.ob-pct { font-family:var(--font-mono); font-weight:700; color:var(--accent); }
/* Two figures used to share .ob-pct: a probability and the recommendation's
   EV. Green now means the recommendation, so they had to part company.
   .ob-pct stays green -- carried since 2026-09-06 by the bubble note
   ("missed our confidence floor by 0.4pp"), the "Where we see value"
   strip that used to be its example having been removed as a restatement
   of the verdict line; this is the probability ("Most likely to win
   58.0%"). */
.ob-pct-prob { font-family:var(--font-mono); font-weight:700; color:var(--text-primary); }
.ob-strip-bubble .ob-pct { color:var(--accent-amber); }
/* 'info' is the common case (an Over/Under play takes no side on the winner);
   only a genuine conflict earns amber. */
.ob-note { font-size:var(--text-sm); }
/* --text-secondary, not --text-muted: this note is meaning-carrying copy ("takes
   no side on the winner"), and the block header above requires the readable rung
   for anything carrying meaning. It is the COMMON case on MLB, so it must not be
   the least legible text on the card. */
.ob-note-info { color:var(--text-secondary); }
.ob-note-warn { color:var(--accent-amber); }
/* ADDED 2026-09-06 (NCAAF Phase 2a): opener-to-current line move strip. "with" =
   the market moved toward the side we recommend; against reuses ob-note-warn. */
.ob-move-with { color:var(--accent-green, #3fb950); }
/* ADDED 2026-09-06 (NCAAF Phase 2b): drawer line-history sparklines. */
.line-spark-wrap { margin-top:6px; }
.line-spark-row { display:flex; align-items:center; gap:8px; padding:2px 0; }
.line-spark-k { font-family:var(--font-mono); font-size:var(--text-xs); font-weight:700; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-secondary); min-width:52px; }
.line-spark { display:block; max-width:100%; height:auto; }
.line-spark-legend { font-size:var(--text-xs); color:var(--text-muted); margin-top:2px; }

/* The card opens the full drilldown takeover (2026-08-07, re-pointed
   2026-08-29 — see Slate.showGameDrilldown in static/js/slate.js). */
.ob-card[role="button"] { cursor:pointer; }
.ob-card[role="button"]:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
/* BLOCKING 1 fix (2026-08-29, unified board review round 2): j/k keyboard
   nav moves across board cards now (see slate.js:_attachKeyboardShortcuts —
   the slate rows this replaced are dead markup, tracked separately). Give
   that a focus ring distinct from mouse hover. */
.ob-card.kbd-focus { outline:2px solid var(--accent); outline-offset:-2px; }

/* 2026-09-06: the collapsed "no play" disclosure. Same rules at both
   viewports -- no new media block -- because the row is already a single
   truncating line and has nothing left to shed on a narrow screen.
   Font sizes stay at or above the 12px floor (tests/test_juice_font_floor.py,
   var(--text-xs) resolves to 12px). */
/* min-height:44px keeps the row a comfortable touch target -- the repo raised
   board controls to 44px in the 2026-08-11 review; do not regress that. */
/* min-width:0 is load-bearing: a flex item will not shrink below its content
   without it, which is the exact bug fixed in the takeover header on
   2026-09-05 -- without this the state label gets pushed off the row. */

@media (max-width: 768px) {
  .ob-box { table-layout:fixed; }
  .ob-box thead th, .ob-box td { padding:6px 3px; }
  .ob-box thead th:first-child { width:29%; }
  .ob-head, .ob-strip, .ob-locked { padding-left:10px; padding-right:10px; }
  .ob-team { padding:6px 4px 6px 10px; font-size:var(--text-sm); white-space:normal; overflow-wrap:anywhere; }
  .ob-cell { min-width:0; width:100%; padding:4px 2px; }
  .ob-line, .ob-price, .ob-prob { max-width:100%; overflow:hidden; text-overflow:ellipsis; }
  /* FIX 2026-08-05: the GLOBAL `th { white-space:nowrap }` (main.css:401) also
     catches this sub-header, so "line · price · win prob" was held on one line
     and forced the table to 422px inside a 325px phone column — pushing the
     Over/Under column, MLB's only unsuppressed market, off-screen by default.
     Letting it wrap brings the table to 333px. Same global rule was already
     fixed once for team-name headers (see the note at main.css:1999). The
     sub-header is the only in-place explanation of what the green number
     means, so it wraps rather than being shortened or dropped. */
  .ob-box tr.ob-subhead th { white-space:normal; line-height:1.3; }
}

/* At the narrowest phones, drop the win-probability span to reduce per-cell
   density — the price and the line are the numbers a bettor compares across
   books; win prob is supporting detail. The grid stays THREE columns: one
   column would break the price comparison that makes this a board. */
@media (max-width: 375px) {
  .ob-prob { display: none; }
}

/* Verdict strip (2026-08-08) — markup: renderVerdictStrip() in app.js.
   Same 12px floor as everything else here (tests/test_type_scale_floor.py). */
.ob-verdict { display:flex; justify-content:space-between; align-items:center;
  gap:8px; flex-wrap:wrap; padding:7px 10px; border-top:1px solid var(--border);
  font-size:13px; color:var(--text-secondary); }
.ob-verdict-play { border-top-color:var(--accent); color:var(--text-primary); }
.ob-verdict-ev { color:var(--accent); font-weight:600; }
.ob-verdict-none { color:var(--text-secondary); }
/* A visual affordance, not a control: the whole .ob-card is already
   role="button" with its own click and Enter/Space handlers, so this stays a
   <span>. A <button> here would nest an interactive element inside
   role="button".
   The border is what carries the contrast, not the fill: 16% white
   composited over the --bg-elevated fill measures ~1.74:1 against the card,
   up from 1.30:1 at 10% and 1.22:1 at 8% -- the fill alone is ~1.06:1,
   effectively imperceptible on its own. The fill's job is to separate this
   from the card surface (--bg-surface #111113) so the border has a distinct
   layer to sit on top of; a hairline alone against a near-identical
   background is invisible, which is exactly what shipped on 2026-08-31.
   .how-pill -- the precedent this was copied from -- works because ITS fill
   lifts it off the #09090b page. Same tokens, wrong surface. Pinned by
   tests/test_signal_stack_css.py. */
.ob-verdict-go { font-size:12px; color:var(--text-secondary); white-space:nowrap;
  display:inline-flex; align-items:center; padding:3px 10px;
  background:var(--bg-elevated);
  border:1px solid var(--border-strong); border-radius:999px; }

/* ── Mobile input zoom-prevention: .br-input (intra-file collision) ─────
   iOS Safari zooms the viewport when a focused input renders below 16px and
   does not zoom back out. .br-input's own plain declaration earlier in this
   file (font-size: var(--text-base), the measured 15px) was added
   2026-07-12, two months after the "Mobile input zoom-prevention (iOS
   Safari zooms on focus when input <16px)" guard above, and it sits AFTER
   that guard in source order. Both rules are specificity (0,1,0), so
   the LATER rule wins regardless of the media query -- an entry for
   .br-input inside that guard block is a silent no-op and was removed as
   dead weight (found the same way the .parlay-stake-input/slate.css case
   was: the sentinel resolves the actual cascade winner, not list
   membership). Same defect, this time inside one file instead of across
   two. This block is the one that actually fixes it: it is genuinely last
   in the file. Guarded by tests/test_input_zoom_guard.py. */
@media (max-width: 639px) {
  .br-input {
    font-size: 16px;
  }
}

/* ── Analytics yardstick ─────────────────────────────────────────────────
   Shown above the Analytics figures, not below them: after the numbers it
   reads as an excuse, before them it is a legend for the chart. */
.analytics-yardstick {
  position: relative;
  grid-column: 1 / -1;
  border: 1px solid var(--border-default);
  border-left: 3px solid var(--accent);
  background: var(--bg-card);
  border-radius: 6px;
  padding: 1.1rem 2.4rem 1rem 1.2rem;
  margin: 0 0 1rem;
  max-width: 46rem;
}
.analytics-yardstick h3 { margin: 0 0 0.5rem; font-size: 1.05rem; }
.analytics-yardstick p { margin: 0 0 0.6rem; line-height: 1.55; }
.analytics-yardstick .ay-lead { font-weight: 700; font-size: 1.05rem; }
.analytics-yardstick .ay-punch { font-weight: 700; margin-top: 0.8rem; }
.analytics-yardstick .ay-meta { color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 0; }
.analytics-yardstick .ay-ledger {
  border-collapse: collapse;
  margin: 0.6rem 0 0.9rem;
  font-variant-numeric: tabular-nums;
  width: 100%;
  max-width: 26rem;
}
.analytics-yardstick .ay-ledger td {
  padding: 0.32rem 0.8rem 0.32rem 0;
  border-bottom: 1px solid var(--border-default);
}
.analytics-yardstick .ay-ledger .num {
  text-align: right;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.analytics-yardstick .ay-ledger .pos { color: var(--ev-pos); }
.analytics-yardstick .ay-ledger .neg { color: var(--ev-neg); }
.analytics-yardstick .ay-ledger tr.tot td {
  font-weight: 700;
  border-bottom: none;
  border-top: 2px solid var(--border-default);
}
.analytics-yardstick .ay-close {
  position: absolute; top: 0.5rem; right: 0.6rem;
  background: none; border: none; cursor: pointer;
  color: var(--text-secondary); font-size: 1.4rem; line-height: 1;
  padding: 0.2rem 0.4rem;
}
.analytics-yardstick .ay-close:hover { color: var(--text-primary); }
.yardstick-reopen {
  background: none; border: none; padding: 0;
  color: var(--accent); cursor: pointer; font-size: 0.9rem;
  font-family: inherit; text-decoration: underline;
}
@media (max-width: 640px) {
  .analytics-yardstick { padding: 1rem 2.2rem 0.9rem 1rem; }
  .analytics-yardstick .ay-ledger { font-size: 0.95rem; }
}

/* ── Yardstick callout (board) ────────────────────────────────────────────
   Was a slim one-line strip and it disappeared -- understated to the point of
   invisible on the surface where it has the most work to do. It now reads as
   a card with a number in it, and the whole card is the tap target. */
.yardstick-strip {
  display: flex;
  align-items: stretch;
  gap: 0;
  border: 1px solid var(--accent);
  border-left: 4px solid var(--accent);
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent) 10%, var(--bg-card)) 0%,
    var(--bg-card) 55%
  );
  border-radius: 8px;
  margin: 0 0 12px;
  overflow: hidden;
}
.yardstick-strip .ys-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: flex-start;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  color: var(--text-primary);
  padding: 12px 14px;
}
.yardstick-strip .ys-body:hover { background: rgba(255, 255, 255, 0.03); }
.yardstick-strip .ys-eyebrow {
  /* 12px is the project's accessibility floor (tests/test_type_scale_floor.py).
     Shrinking an eyebrow to fit is the wrong trade on a product whose users
     include people with vision and motor impairments. */
  font-size: 12px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 700;
}
.yardstick-strip .ys-hook {
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.35;
}
.yardstick-strip .ys-loss { color: var(--ev-neg); }
.yardstick-strip .ys-cta {
  font-size: 0.88rem;
  color: var(--accent);
  font-weight: 600;
}
.yardstick-strip .ys-dismiss {
  align-self: flex-start;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 1.25rem;
  line-height: 1;
  padding: 10px 12px;
}
.yardstick-strip .ys-dismiss:hover { color: var(--text-primary); }
@media (max-width: 640px) {
  /* The strip costs the board its fold on a phone, so on mobile it keeps the
     argument and drops the packaging.

     Measured on prod 2026-09-09 at 375x812: the board starts at 221px -- the
     figure the mobile-fold work bought -- and this strip added 106px on top,
     putting the first card at 339px. With a 541px card that needs 880px
     against an 812px viewport, so NO game fitted on one screen: the exact
     condition the 2026-09-06 board spec called a failure.

     Of those 106px, the hook is 42 and carries the whole point. The eyebrow is
     a label for an argument that states itself, and the CTA is a cue rather
     than the control -- the whole card is the control (see the markup comment
     in templates/index.html), so hiding the CTA text costs an affordance, not
     an action. A chevron keeps the affordance at no extra height.

     Type is NOT shrunk to buy this: 12px is the project's accessibility floor
     and the hook stays at its readable size. Height comes from removing
     elements, not from making the remaining ones harder to read. */
  .yardstick-strip { margin-bottom: 8px; }
  .yardstick-strip .ys-body { padding: 9px 12px; gap: 0; }
  .yardstick-strip .ys-hook { font-size: 0.97rem; }
  .yardstick-strip .ys-eyebrow { display: none; }
  .yardstick-strip .ys-cta { display: none; }
  /* The affordance the hidden CTA carried, folded into the hook's own line. */
  .yardstick-strip .ys-hook::after {
    content: ' \2192';
    color: var(--accent);
    font-weight: 700;
    white-space: nowrap;
  }
}
}

/* Sport chip above a card on the All Sports board (2026-09-09). Only that
   mixed board renders it; on a single-sport board the tab already says which
   sport it is. 12px is the floor the push gate enforces -- anything smaller
   fails tests/test_css_type_floor.py, so shorten the text, never the type. */
.ob-sport-chip {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.72;
  margin: 18px 0 4px;
}

/* Kickoff on a board card (2026-09-09): date AND start time, in ET, always
   both. Asked for after a card gave no way to tell tonight.s game from
   Sunday.s without opening it. 12px is the floor the push gate enforces --
   shorten the text, never the type. */
.ob-when {
  font-size: 12px;
  font-weight: 500;
  opacity: 0.68;
  margin-left: 10px;
  white-space: nowrap;
}
