/* ═══════════════════════════════════════════════════════════════
   BETIST.AI — "Scoreboard" skin (design 1b: paper, ink, amber)
   Loads AFTER styles.css and reskins it: light editorial-sportsbook,
   Archivo + JetBrains Mono, 2px ink rules, dark ink panels.
   ═══════════════════════════════════════════════════════════════ */

/* ── Tokens: override both theme branches ── */
:root, html[data-theme], html[data-theme="light"], html[data-theme="dark"] {
  --bg: #f7f4ed;        /* paper */
  --sf: #ffffff;        /* card */
  --sf2: #f3efe6;       /* raised / hover */
  --bdr: #e8e4d8;       /* sand border */
  --tx: #141310;        /* ink */
  --mu: #6b675e;        /* muted */
  --ac: #f2a012;        /* amber brand */
  --ac-fg: #141310;
  --safe: #1f7a3d;
  --normal: #3d4e7a;
  --risk: #9a6a08;
  --won: #1f9e4e;
  --lost: #cd3f32;
  --ink: #141310;
  --ink-2: #33302a;     /* hairline on ink */
  --ink-mu: #7c776b;    /* muted on ink */
  --ink-tx: #fdfbf6;    /* text on ink */
  --cream: #fdeecd;     /* amber chip bg */
  --safe-bg: #e8f3ec;
  --normal-bg: #eef0f6;
  --risk-bg: #fdeecd;
  --font-ui: 'Archivo', system-ui, sans-serif;
  --font-num: 'JetBrains Mono', monospace;
  --radius: 12px;
  /* LIST-VIEW ROW STRIPE (owner-approved 2026-08-14): a faint ~5% ink wash on
     alternate PICK rows of every .tp-table (picks/daily/performance/dashboard).
     One token so the shade is defined once. The scoreboard skin forces the paper
     palette on every theme branch (see the :root selector list above), so this
     dark-ink-over-paper value reads in light AND the dark toggle alike. */
  --stripe: rgba(20, 19, 16, .05);

  /* ── ONE page shell (2026-07-19) ───────────────────────────────
     Every section used to invent its own width: header 1320, sections
     1180, hero full-bleed, .section 1100, .trust-section 760. At 1440px
     that put three different left edges on one page (0 / 52 / 122), which
     is what read as "each block is its own thing" and left the wide-screen
     margins and the clipped 4th proof card. All of them now resolve to
     these two numbers. Change the width HERE, never per section. */
  --shell-max: 1320px;
  --gutter: clamp(20px, 3.2vw, 48px);
}
/* Very wide screens: one shared step up so the page uses the display instead
   of sitting in a narrow column between two large empty margins. Every
   section takes it together, so section edges stay aligned. */
@media (min-width: 1800px) {
  :root, html[data-theme], html[data-theme="light"], html[data-theme="dark"] {
    --shell-max: 1560px;
  }
}
@media (min-width: 2300px) {
  :root, html[data-theme], html[data-theme="light"], html[data-theme="dark"] {
    --shell-max: 1760px;
  }
}


body { background: var(--bg); color: var(--tx); font-family: var(--font-ui); }

/* The shell. Every top-level block on every page uses this geometry, so
   section edges line up vertically all the way down the page. */
.container,
.page.container,
.sb-section,
.sb-page-hero,
.sb-provenance,
.sb-legal,
.section,
.trust-section,
.contact-page,
.shell {
  width: 100%;
  max-width: var(--shell-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* keep each block's own VERTICAL rhythm — the shell only owns horizontal */
.sb-section    { padding-block: 40px 8px; }
.sb-page-hero  { padding-block: 48px 8px; }
.sb-provenance { padding-block: 8px 0; }
.sb-legal      { margin-block: 30px 0; padding-block: 0; }
.section       { padding-block: 80px; }
.trust-section { padding-block: 72px; }
.contact-page  { padding-block: 80px; }

/* Full-bleed bands (the hero) keep their background edge-to-edge while the
   CONTENT inside them still aligns to the shell. */
.bleed { width: 100%; }
.bleed > .shell-inner {
  width: 100%; max-width: var(--shell-max);
  margin-inline: auto; padding-inline: var(--gutter);
}

/* ── Type ── */
h1, h2, .section-title {
  font-weight: 900; text-transform: uppercase; letter-spacing: -0.02em;
  color: var(--tx);
}
.sb-h1 { font-weight: 900; font-size: clamp(38px, 5.4vw, 72px); line-height: .99;
  letter-spacing: -0.03em; text-transform: uppercase; text-wrap: balance; margin: 0; }
.sb-kicker { display: inline-flex; align-items: center; gap: 8px;
  font: 700 11.5px var(--font-num); letter-spacing: .1em; text-transform: uppercase;
  color: var(--tx); background: var(--cream); border-radius: 6px; padding: 7px 12px; }
.sb-kicker.on-ink { background: transparent; color: var(--ac); padding: 0; }
.sb-sub { font-size: 15.5px; line-height: 1.55; color: var(--mu); max-width: 520px; }
.mono-label { font: 500 11px var(--font-num); color: #8a8579; }

/* ── Header ── */
.site-header { background: var(--sf); border-bottom: 2px solid var(--ink);
  box-shadow: none; backdrop-filter: none; position: sticky; top: 0; }
.site-header .container { padding-top: 14px; padding-bottom: 14px; }
.logo { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.sb-logo-b { width: 28px; height: 28px; background: var(--ac); color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  font: 800 15px var(--font-ui); border-radius: 6px; flex: 0 0 auto; }
.logo .word, .logo .dot, .logo .tld { font-weight: 800; font-size: 17px;
  letter-spacing: -0.02em; color: var(--tx) !important; }
/* burger button (injected by app.js) — hidden except on phones */
.nav-burger { display: none; flex-direction: column; justify-content: center;
  gap: 4px; width: 42px; height: 38px; padding: 9px 10px; flex: 0 0 auto;
  border: 1px solid var(--bdr); border-radius: 9px; background: var(--sf);
  cursor: pointer; }
.nav-burger span { display: block; height: 2px; background: var(--tx);
  border-radius: 2px; }
.site-header.nav-open .nav-burger { border-color: var(--ac); }
.main-nav a { font-size: 13px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--mu); border-bottom: 2px solid transparent;
  padding-bottom: 4px; }
.main-nav a.active, .main-nav a:hover { color: var(--tx);
  border-bottom-color: var(--ac); background: transparent; }
.theme-toggle { display: none !important; }
.select-wrap select { background: var(--sf); color: var(--tx);
  border: 2px solid var(--bdr); border-radius: 8px; font: 600 12px var(--font-num); }
/* HIDDEN GLOBALLY: this "TODAY'S PICKS ->" button duplicates the nav's own
   Today's Picks link + the hero CTA, and it is the ~170px that pushed the
   header content past the fixed .container box (max-width:1320px) in the
   signed-out state (wider "SIGN IN / REGISTER"), clipping DASHBOARD. Because
   the box is capped regardless of viewport width, it can't fit at ANY width
   when signed out -- so it's removed, not just hidden at a breakpoint. */
.sb-header-cta { display: none; text-decoration: none; background: var(--ink); color: #fff !important;
  font-weight: 700; font-size: 12.5px; letter-spacing: .05em; text-transform: uppercase;
  padding: 11px 20px; border-radius: 8px; white-space: nowrap; }
.sb-header-cta:hover { background: #2a2822; }

/* ── Buttons ── */
.btn-primary { background: var(--ac); color: var(--ink); font-weight: 800;
  font-size: 14px; letter-spacing: .02em; text-transform: uppercase;
  padding: 16px 26px; border-radius: 10px; border: none; box-shadow: none;
  animation: none; }
.btn-primary:hover { background: #ffb32a; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(242,160,18,.35); }
.btn-primary::after { display: none; }
.btn-secondary, .btn-small { background: transparent; color: var(--tx);
  font-weight: 700; text-transform: uppercase; border: 2px solid var(--ink);
  border-radius: 10px; }
.btn-secondary { font-size: 14px; padding: 15px 26px; }
.btn-small { font-size: 12px; padding: 10px 18px; }
.btn-secondary:hover, .btn-small:hover { background: var(--ink); color: var(--ink-tx); border-color: var(--ink); }

/* ── Chips / badges ── */
/* ITEM 6 (2026-07-22, owner color roles): risk tiers are NEUTRAL — an
   outline chip in the label voice. Green/red are reserved for WON/LOST (and
   signed money) exclusively; the old green "safe" chip put a result color
   on unsettled picks. Amber stays interactive/brand only. The .safe/.normal/
   .risky classes remain on the markup as identity hooks; they no longer
   carry color. */
.tier-badge { font: 500 10.5px var(--font-num); text-transform: uppercase;
  border-radius: 5px; padding: 3px 8px; letter-spacing: .06em;
  background: transparent; border: 1px solid var(--bdr);
  color: color-mix(in srgb, var(--tx) 62%, transparent); }
.tier-badge.lg { font-size: 12px; padding: 5px 10px; }
/* ── ITEM 16 EXT (2026-07-23): history is no longer a bespoke table ──
   It renders through the SAME shared builder as picks/dashboard
   (tpRowHTML/tpTableGroups/tpTableHeadHTML — see app.js), so it inherits
   every .tpt-* and .tp-table rule below for free: mobile collapse, column
   clustering, group-header hierarchy, one-bold rule. The item-9 CSS that
   used to hand-style a separate .ht-teams/.hth-league/56px-track layout
   is GONE — it targeted classes the unified markup no longer emits, and
   worse, its higher-specificity `.history-table tbody tr:not(.hth-league)`
   selector kept matching (since .hth-league no longer exists, :not()
   always matched) and was silently forcing the shared .tpt-league GROUP
   HEADER row into the wrong grid track on mobile. `.history-table` stays
   on the `<table>` only as an external test hook (zero CSS attached). */
/* owner defect fix: the view toggle must be VISIBLE and tappable on phones
   on BOTH pages — >=44px targets, near the section top, never behind a
   sheet. OWNER (2026-07-24, position audit): the toolbar used to reach "top
   of section" via a CSS order:-1 flex trick inside .pick-controls, which
   only reordered it above the FILTERS — stat tiles / the member strip still
   sat above it. Superseded by an actual DOM move (picks.html, dashboard.html,
   daily.js) so the toolbar is now the toolbar's real position, not a flex
   reorder; .pick-controls is retired, nothing references it any more. */
@media (max-width: 640px) {
  .view-seg button { min-width: 44px; min-height: 44px; }
  /* same rule for the performance page's grid/table toggle */
  #layout-toggle button { min-height: 44px; }
}

/* item 8a: country qualifier on an ambiguous league name — inherits the
   label voice it sits in, slightly quieter than the name itself */
.lg-qual { opacity: .75; white-space: nowrap; }
.since-badge { font: 700 11px var(--font-num); letter-spacing: .06em;
  text-transform: uppercase; background: var(--cream); color: var(--tx);
  border: none; border-radius: 6px; padding: 6px 11px; }
.pulse-dot { background: var(--won); }
.live-label { color: var(--won); font: 700 11px var(--font-num); text-transform: uppercase; }
.sb-live { font: 700 10.5px var(--font-num); color: #fff; background: var(--won);
  border-radius: 5px; padding: 4px 8px; vertical-align: middle; }
/* Marketing tooltip on the LIVE badge (owner 2026-08-05): shown on hover + focus
   (focus = tap on mobile). Bubble below the badge, capped width, theme-aware. */
.sb-live[data-tip] { position: relative; cursor: help; outline: none; }
.sb-live[data-tip]::after {
  content: attr(data-tip);
  position: absolute; top: calc(100% + 8px); left: 0;
  width: max-content; max-width: 280px;
  font: 600 11.5px var(--font-ui, inherit); letter-spacing: 0; text-transform: none;
  color: var(--sf); background: var(--ink); border-radius: 8px;
  padding: 8px 11px; line-height: 1.35; white-space: normal;
  box-shadow: 0 10px 30px -12px rgba(0,0,0,.45);
  opacity: 0; transform: translateY(-3px); pointer-events: none;
  transition: opacity .14s ease, transform .14s ease; z-index: 40; }
.sb-live[data-tip]:hover::after, .sb-live[data-tip]:focus-visible::after {
  opacity: 1; transform: translateY(0); }

/* Table-view veil teaser (owner 2026-08-05): the locked row's "fun fact" — the
   same insight line the grid card shows, sized for a table cell. Renders only
   when the pick actually carries an insight (never fabricated). */
.tpt-veil-fact { display: flex; align-items: center; gap: 5px; margin-top: 6px;
  font: 500 11px var(--font-ui, inherit); color: var(--mu); line-height: 1.25; }
.tpt-veil-fact svg { color: var(--risk); flex: 0 0 auto; }
.tpt-veil-fact > span { min-width: 0; }

/* Visible marketing hook (owner 2026-08-05) — the "live, continuously-refreshed
   slate" line shown as TEXT on the picks hero (#slate-hook) and the home hero
   (#hero-hook). Amber so it reads as a positive live signal, not body copy. */
.sb-hook { margin: 10px 0 0; max-width: 62ch;
  font: 600 13px var(--font-ui, inherit); line-height: 1.45; color: var(--risk); }
.sb-hero-copy .sb-hook { margin: 8px 0 2px; font-size: 13.5px; color: var(--risk); }

/* Final-score chip next to a settled outcome (BE added `score` 2026-08-05) —
   muted, mono, sits after the W/L + P/L so a reader can check what happened. */
.res-score { font: 600 11px var(--font-num, monospace); color: var(--mu);
  margin-left: 6px; white-space: nowrap; }

/* ── Stat tiles (picks page + generic) ── */
.stat-tile { background: var(--sf); border: 2px solid var(--bdr); border-radius: var(--radius); }
.stat-tile .label { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.stat-tile .value { font: 700 26px var(--font-num); color: var(--tx); }
.stat-tile .value.ac { color: var(--risk); }
.stat-tile .value.pos { color: var(--won); } .stat-tile .value.neg { color: var(--lost); }
.stat-tile .sub, .kick-tile .kick-tip { color: var(--mu); }
.kick-tile .kick-match { font-weight: 800; }

/* ── Bet cards ── */
.bet-card { background: var(--sf); border: 2px solid var(--bdr);
  border-radius: var(--radius); box-shadow: none; }
.bet-card:hover { border-color: #d5d0c2; transform: none; box-shadow: 0 8px 22px rgba(20,19,16,.06); }
.bet-card .competition { font: 500 11px var(--font-num); color: #8a8579; text-transform: uppercase; }
.bet-card .match { font-weight: 800; font-size: 17px; text-transform: uppercase; }
.bet-card.won  { border-color: #bfe0c9; background: #f7fcf8; }
.bet-card.lost { border-color: #ecc8c2; background: #fdf7f5; }

/* Tier group heads: chip + rule line */
.tier-group-head { display: flex; align-items: center; gap: 12px; }
.tier-group-head::after { content: ""; flex: 1; height: 2px; background: var(--bdr); }
.tier-group-head .sub { color: #8a8579; font-size: 12.5px; }

/* ── Dark ink panels (results chart, home track-record) ── */
.chart-panel, .sb-panel {
  background: var(--ink); color: var(--ink-tx);
  border: none; border-radius: 14px;
  /* re-scope tokens so charts & children render for dark ground */
  --bg: #141310; --sf: #1d1b17; --sf2: #26231e; --bdr: var(--ink-2);
  --mu: var(--ink-mu); --tx: var(--ink-tx);
}
.chart-title { font: 700 11.5px var(--font-num); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ac); }
.chart-caption { color: var(--ink-mu); }
.chart-panel .since-badge { background: transparent; color: var(--ink-mu); padding: 6px 0; }
.chart-legend .legend-item { color: var(--ink-mu); }
.chart-panel .segmented { border-color: var(--ink-2); }
.chart-stats .cs-tile { background: transparent; border: none;
  border-top: 1px solid var(--ink-2); border-radius: 0; padding: 16px 18px 2px 0; }
.chart-stats .cs-tile .label { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-mu); }
.chart-stats .cs-tile .value { font: 700 28px var(--font-num); color: var(--ink-tx); }
.chart-stats .cs-tile .value.pos { color: var(--won); }   /* green = good, matches the accuracy board (results page only) */
.chart-stats .cs-tile .value.neg { color: #e2685c; }
.chart-stats .cs-tile .sub { color: var(--ink-mu); }
/* Partial-day settled qualifier ("6/20 settled"): STAT-LABEL size, never
   footnote-tiny — the anti-bury rule (2026-07-22), asserted by the browser
   tier against the computed .label size. Sits FIRST in the sub line so the
   nowrap-ellipsis can never truncate the qualifier away. */
.chart-stats .cs-tile .q-settle { font: 700 10.5px var(--font-num);
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-mu); }

/* ── Performance reframe: core-band hero + per-band diverging bars ──
   Lives inside the dark .chart-panel, so it borrows the panel's amber-positive /
   soft-red-negative language (same as the .cs-tile values), NOT the green/red the
   accuracy bars use. Content-blind: .pos / .neg are set by the band's real sign. */
.perf-headline { margin-bottom: 18px; }
.perf-hero { border: 1px solid; border-radius: 12px; padding: 14px 16px; margin-bottom: 14px; }
.perf-hero.pos { border-color: color-mix(in srgb, var(--won) 38%, transparent);
  background: color-mix(in srgb, var(--won) 12%, transparent); }
.perf-hero.neg { border-color: color-mix(in srgb, #e2685c 36%, transparent);
  background: color-mix(in srgb, #e2685c 10%, transparent); }
.perf-hero .ph-label { font: 700 11px var(--font-num); letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-mu); }
.perf-hero .ph-value { font: 800 30px var(--font-num); line-height: 1.05; margin: 3px 0 5px; }
.perf-hero.pos .ph-value { color: var(--won); }
.perf-hero.neg .ph-value { color: #e2685c; }
.perf-hero .ph-sub { font: 600 12.5px var(--font-num); color: var(--ink-tx); opacity: .82; }
.perf-hero .ph-q { display: inline-block; margin-top: 7px; font: 700 10.5px var(--font-num);
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-mu); }
.perf-bands { display: flex; flex-direction: column; gap: 13px; }
.perf-bands .pb-cap { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-mu); margin-bottom: 2px; }
.pb-row { display: grid; grid-template-columns: 116px 1fr 58px; align-items: center; gap: 16px; }
.pb-namecell { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pb-namecell .pb-name { font: 700 12.5px var(--font-num); color: var(--ink-tx); text-transform: uppercase; }
.pb-namecell .pb-meta { font: 600 10.5px var(--font-num); color: var(--ink-mu); white-space: nowrap; }
.pb-track { position: relative; height: 14px; display: flex; }
.pb-track::before { content: ""; position: absolute; left: 50%; top: -3px; bottom: -3px;
  width: 1px; background: var(--ink-2); }
.pb-track.nosample::before { display: none; }   /* no zero-axis for a band with no bar */
.pb-nosample { margin: auto; font: 600 10px var(--font-num); color: var(--ink-mu);
  text-transform: uppercase; letter-spacing: .05em; }
.pb-half { width: 50%; height: 100%; display: flex; align-items: center; }
.pb-half.l { justify-content: flex-end; }
.pb-fill { height: 10px; border-radius: 3px; min-width: 2px; }
.pb-fill.pos { background: var(--won); }
.pb-fill.neg { background: #e2685c; }
.pb-roi { font: 800 13.5px var(--font-num); text-align: right; }
.pb-roi.pos { color: var(--won); }
.pb-roi.neg { color: #e2685c; }
.pb-roi.thin { color: var(--ink-mu); font-weight: 700; }
.pb-note { margin-top: 8px; font: 500 10.5px var(--font-num); line-height: 1.45;
  color: var(--ink-mu); opacity: .9; }
@media (max-width: 520px) {
  .pb-row { grid-template-columns: 108px 1fr 52px; gap: 11px; }
  .perf-hero .ph-value { font-size: 26px; }
}

/* ── Segmented controls ── */
.segmented { border: 2px solid var(--ink); border-radius: 10px; background: transparent; overflow: hidden; }
.segmented button { font-weight: 700; font-size: 12px; text-transform: uppercase;
  letter-spacing: .03em; color: var(--mu); background: transparent; border-radius: 0; }
.segmented button + button { border-left: 2px solid var(--ink); }
/* THIRD toggle-color report (2026-07-24) — the real bug, found by pixel
   sampling, not eyeballing: .segmented's container padding (3px, styles.css)
   plus its 2px border insets every button 5px from the rounded (10px) outer
   corner. overflow:hidden clips the CONTAINER to that curve, but a button
   sitting 5px inset with a square corner does not reach it — the sliver
   between the border and the button's own edge shows whatever is BEHIND the
   (transparent) container instead of the button's fill. Diagonal pixel scans
   (both corners, #layout-toggle and #time-range, 1440 and 390) all show the
   identical signature: ink border (~d3-4px), then ONE sample of page
   background rgb(247,244,237) at d~4.5-5px, then amber from d~5.5px on.
   This is what "orange not filling properly" reads as under a phone zoom.
   FIRST FIX ATTEMPT WAS WRONG, LEFT HERE AS A LESSON: giving the button its
   own border-radius (`:first-child { border-radius: 5px 0 0 5px }`) does
   NOT close a gap — border-radius only rounds an element's OWN corner
   INWARD off its OWN box, it cannot extend the element outward to reach a
   boundary it does not touch. Re-scanning after that "fix" showed the
   background sliver UNCHANGED (if anything wider, since the button's own
   corner was now additionally rounded away). The button was never too
   square; it was simply never CLOSE ENOUGH to the container's curve for
   overflow:hidden to clip it cleanly.
   REAL FIX: .view-seg was pixel-scanned identically (all 4 corners, both
   breakpoints) and is CLEAN — its container has padding:0 (a prior defect
   fix), so its buttons sit only the 2px border away from the outer curve,
   close enough that the container's own clip handles the corner with no
   visible gap. Applying the SAME padding:0 to .segmented (removing the
   container's own inset, not touching each button's internal 5px/11px text
   padding) makes every .segmented instance geometrically identical to the
   already-proven .view-seg — re-verified clean after this change, not
   assumed clean by analogy. */
.segmented { padding: 0; }
/* OWNER-CAUGHT (2026-07-24, computed-style verified): the date-range control
   (#time-range, inside .chart-panel) rendered amber active fill; the
   results.html grid/table toggle (#layout-toggle) rendered plain ink —
   getComputedStyle proved rgb(20,19,16) vs rgb(242,160,18) on two instances
   of the SAME .segmented class. Root cause: #layout-toggle is a legacy,
   never-migrated toggle (predates item 9's shared view_pref/.view-seg
   component that picks.html/dashboard use) with no scoping class of its own,
   so it fell through to whatever the BASE .segmented active rule said —
   which was ink, with amber only ever applied via a SCOPED override
   (.chart-panel .segmented). Fixed at the ROOT: amber is now the base
   .segmented active fill (amber is this site's ONE interactive/brand
   color — every segmented control should default to it, not need a
   per-page override to get it). The old .chart-panel-scoped override that
   restated the identical amber value is removed — it was the "second,
   more specific rule" the ink default was hiding behind; there is now
   exactly ONE rule for this state, and every segmented control gets it for
   free, including #layout-toggle, with no markup or JS change needed.
   (.view-seg button.active keeps its OWN explicit background/color restate
   deliberately — a prior defect fix (DEFECT 3, above) made it self-
   contained on purpose so it never again silently depends on what a
   same-named class elsewhere declares; that redundancy is intentional and
   is left alone. .chart-controls' mobile-grid .active override at ~line 298
   is also left alone — it is specificity-load-bearing there, beating the
   mobile grid's own per-button `background: var(--bg)` reset, not a
   duplicate of this rule.) */
.segmented button.active { background: var(--ac); color: var(--ink); }
.chart-panel .segmented { border-color: var(--ink-2); }
.chart-panel .segmented button { color: var(--ink-mu); }
.chart-panel .segmented button + button { border-left-color: var(--ink-2); }

/* A range with no distinct history yet (2026-07-20, item 6). Season is greyed
   during the off-season BY DESIGN — with 9 settled days it would draw exactly
   the board 30d and All already draw. Until now it had no style of its own and
   fell back to the browser's default disabled look, which is why it read as a
   dead control. It now states its own reason: a deliberate muted treatment, a
   localized tooltip, and the note below spelling out what opens it. */
.segmented button.disabled,
.chart-panel .segmented button.disabled {
  opacity: .38; cursor: not-allowed; font-style: italic;
  text-decoration: line-through; text-decoration-thickness: 1px;
}
.segmented button.disabled:hover { background: transparent; }
/* The selector carries six ranges since Yesterday landed. flex-wrap alone did
   NOTHING here: the base .segmented rule (styles.css) sets flex-shrink: 0, so
   as a flex item of .chart-head the control kept its max-content width — 437px
   in a 390px viewport — and body{overflow-x:hidden} then CLIPPED it, cutting
   "TÜM ZAMANLAR" to "TÜM Z" with 81px of the control unreachable. */
.chart-controls { min-width: 0; max-width: 100%; }
.chart-controls .segmented { flex-shrink: 1; min-width: 0; max-width: 100%; }
.chart-controls .segmented button { flex: 0 0 auto; white-space: nowrap; }
/* Mobile: a DELIBERATE 2x3 grid, not a wrap. The owner rejected free
   wrapping — five ranges on line one and TÜM ZAMANLAR stranded alone on line
   two reads as an accident. Six ranges tile 2 columns x 3 rows exactly: no
   orphan possible, every label on one line in all 11 locales (the longest,
   BG "За всички времена", needs ~115px and a 2-col cell gives ~165px at
   375). Chronological pairs per row: DÜN|BUGÜN / 7D|30D / SEZON|TÜM.
   Separators come from the 2px grid gap over the container background —
   the same ink-2 hairline the bordered variant draws — so the per-button
   border-left/top must be off or they would double. */
@media (max-width: 640px) {
  .chart-controls .segmented {
    display: grid; grid-template-columns: 1fr 1fr;
    width: 100%; gap: 2px; background: var(--ink-2);
    border-color: var(--ink-2);
  }
  .chart-controls .segmented button {
    background: var(--bg); border-left: 0 !important; margin: 0;
    padding: 11px 6px; text-align: center; min-width: 0;
    overflow: hidden;
  }
  .chart-controls .segmented button.active { background: var(--ac); }
}
.range-note {
  font: 500 11.5px/1.5 var(--font-num); color: var(--ink-mu);
  margin: 8px 0 0; max-width: 46ch;
}

/* ── Filters / inputs ── */
/* The search field is the FIFTH control in this row and must be the same
   component as the four .mselect controls beside it (2026-07-20, item 7).
   It already shared the border, radius and background here — but its
   min-height and font-size were set only inside the mobile media query, so
   it matched at 375-430px and rendered 34px tall against the mselects' 44px
   on every desktop width. Sizing belongs with the shared rule, not in a
   breakpoint, or the row silently stops being a row above 700px. */
/* .dash-prefs is listed alongside .filters everywhere below: the dashboard's
   filter row is its own container, so a search input added there (owner
   2026-07-29) would otherwise render as a raw browser box next to perfectly
   styled dropdowns. Same rules, one more selector — not a second definition. */
.filters input[type="text"], .dash-prefs input[type="text"], .mselect-ctl {
  background: var(--sf); border: 2px solid var(--bdr); border-radius: 10px;
  color: var(--tx); min-height: 44px; font-size: 16px; }
/* the input's own padding must not shrink it back below the shared height */
.filters input[type="text"],
.dash-prefs input[type="text"] { padding: 6px 12px; box-sizing: border-box; }
.filters input[type="text"]:focus,
.dash-prefs input[type="text"]:focus,
.mselect-ctl:focus-visible { border-color: var(--ac); outline: none; }
.mselect-panel { background: var(--sf); border: 2px solid var(--bdr); border-radius: 10px;
  box-shadow: 0 16px 40px rgba(20,19,16,.12); }
.mselect-item.sel { background: var(--cream); color: var(--tx); }
.mschip { background: var(--sf2); border: 1px solid var(--bdr); }

/* ── History table: fully dead now — item 16 ext moved it onto the shared
   .tp-table/.tpt-* system (see the note above .tier-badge.lg). .table-panel/
   .history-table thead th/.history-table td/.num targeted markup that no
   longer exists (the old bespoke <table class="history-table"> shell);
   removing them was the actual fix for the mobile overflow the owner
   reported (a live screenshot showed no odds box/stake/result — they were
   pushed off-viewport by these rules' stale grid-template-columns winning
   on specificity over the shared, correct one). .status-badge/.pnl are
   ALSO fully dead now — daily.js's day-expansion rows (defect 5) moved
   onto the shared .tpt-code/.tpt-oddsbox/.tpt-tier/.tpt-res classes
   (see the .dr-game block below), the last place either was used. */

/* ── Daily report ── */
.daily-panel { background: var(--sf); border: 2px solid var(--bdr); border-radius: var(--radius); }
.daily-row .bar { background: var(--ac); } .daily-row .bar.neg { background: var(--lost); }
.daily-row .profit.pos { color: var(--won); } .daily-row .profit.neg { color: var(--lost); }
.daily-more { color: var(--tx); font-weight: 700; text-transform: uppercase; }

/* ── Contact ── */
.contact-form input, .contact-form textarea {
  background: var(--sf); border: 2px solid var(--bdr); border-radius: 10px; color: var(--tx); }
.contact-form input:focus, .contact-form textarea:focus { border-color: var(--ac); outline: none; }
.cf-label { font: 700 10.5px var(--font-num); letter-spacing: .08em; text-transform: uppercase; color: #8a8579; }
.cf-status.err { color: var(--lost); }
.cf-success { color: var(--won); font-weight: 700; }
.contact-email { color: var(--tx); font-weight: 800; font-size: 18px; }
.eyebrow { font: 700 11.5px var(--font-num); letter-spacing: .1em; text-transform: uppercase; color: var(--risk); }

.sb-channel { display: flex; justify-content: space-between; align-items: center;
  border: 2px solid var(--bdr); border-radius: 12px; padding: 18px 22px;
  text-decoration: none; color: var(--tx); }
.sb-channel:hover { border-color: var(--ac); }
.sb-channel .k { font: 700 10.5px var(--font-num); color: #8a8579; letter-spacing: .08em; }
.sb-channel .v { font-weight: 700; font-size: 16px; margin-top: 4px; }
.sb-channel .arrow { font: 700 20px var(--font-num); color: var(--ac); }
.sb-faq { border: 2px solid var(--bdr); border-radius: 14px; padding: 24px 26px; background: var(--sf); }
.sb-faq.on-ink { background: var(--ink); color: var(--ink-tx); border: none; }
.sb-faq .q { font-weight: 800; font-size: 16px; }
.sb-faq .a { font-size: 13.5px; line-height: 1.6; color: var(--mu); margin-top: 6px; }
.sb-faq.on-ink .a { color: #a39d8e; }
.sb-faq .faq-kicker { font: 700 11px var(--font-num); color: var(--ac); letter-spacing: .1em; margin-bottom: 10px; }

/* ── Footer: black bar ── */
.site-footer { background: var(--ink); border-top: none; margin-top: 56px;
  padding-inline: 0; }   /* the shell inside owns the gutter */
/* footer inline padding comes from the shell */
.site-footer .container { display: block; }
.site-footer, .site-footer .copyright { color: var(--ink-mu); font: 500 11.5px var(--font-num); }
.site-footer .logo .word, .site-footer .logo .dot, .site-footer .logo .tld { color: var(--ink-tx) !important; }
.site-footer .footer-links a { color: var(--ink-mu); font: 500 11.5px var(--font-num); text-transform: uppercase; }
.site-footer .footer-links a:hover { color: var(--ac); }
.sb-18 { color: var(--ac) !important; }

/* ── Home: split hero ──────────────────────────────────────────
   The hero is a FULL-BLEED band: the paper half and the ink panel both run
   to the true viewport edge (that is what fixes the white strip beside the
   black panel), while the copy and the panel's CONTENTS align to the same
   shell edges as every section below. The trick is asymmetric padding
   computed from the shell, not a container around the band. */
/* A full-bleed GRID: the outer columns are the bleed, the inner two carry the
   shell. The copy lands exactly on the shell's left edge (percentages here
   resolve against the hero, which spans the full content width — unlike a
   custom property, which would resolve against each child) while the ink
   panel keeps running to the true right edge. */
.sb-hero { display: grid; gap: 0;
  grid-template-columns:
    [bleed-start] minmax(var(--gutter), 1fr)
    [shell-start] minmax(0, calc((var(--shell-max) - var(--gutter) * 2) * 0.54))
    [mid] minmax(0, calc((var(--shell-max) - var(--gutter) * 2) * 0.46))
    [shell-end] minmax(var(--gutter), 1fr) [bleed-end];
  background: var(--sf); border-bottom: 2px solid var(--ink); }
.sb-hero-copy  { grid-column: shell-start / mid; }
/* The ink panel STOPS at the shell's right edge (2026-07-20). It used to run
   to bleed-end, so the black panel touched the browser edge while every
   section below it — provenance deck, slate, footer — stopped at the shell.
   The left column happened to align with the shell, so one half of the hero
   looked contained and the other looked like it had escaped. One edge, top to
   bottom: the panel's right edge now lines up with the cards underneath it. */
.sb-hero-panel { grid-column: mid / shell-end; }
/* left edge of the copy == left edge of the shell */
.sb-hero-copy { padding: clamp(56px, 6vw, 88px) 48px clamp(48px, 5vw, 72px)
    var(--shell-inset);
  max-width: none; margin-left: 0; display: flex; flex-direction: column;
  justify-content: center; }
.sb-hero-copy .sb-kicker { margin-bottom: 26px; }
.sb-hero-copy p { font-size: 16.5px; line-height: 1.55; color: var(--mu);
  max-width: 480px; margin: 24px 0 0; }
.sb-hero-ctas { display: flex; gap: 12px; margin-top: 32px; flex-wrap: wrap; }
.sb-trust-row { display: flex; gap: 26px; margin-top: 42px;
  font: 500 12px var(--font-num); color: #8a8579; flex-wrap: wrap; }
/* The panel's own right edge is now the shell's right edge, so its right
   padding is ordinary internal padding (matched to the left) rather than the
   --gutter that previously held content clear of a bleeding edge. */
.sb-hero-panel { background: var(--ink); color: var(--ink-tx);
  padding: clamp(40px, 4vw, 56px) clamp(32px, 3vw, 48px)
    clamp(32px, 3.5vw, 44px) clamp(32px, 3vw, 48px);
  display: grid; grid-template-rows: auto 1fr auto; gap: 26px;
  --bg: #141310; --bdr: var(--ink-2); --mu: var(--ink-mu); }
/* THE FIGURE BLOCK: head, number, scope, slice and the three mini-stats are
   one deliberate unit with a single rhythm, not four stacked leftovers. */
.sb-panel-figure { display: flex; flex-direction: column; }
.sb-panel-head { display: flex; justify-content: space-between; align-items: baseline; }
.sb-panel-head .k { font: 700 11.5px var(--font-num); letter-spacing: .12em; color: var(--ac); }
.sb-panel-head .s { font: 500 11px var(--font-num); color: var(--ink-mu); }
.sb-big { font: 700 clamp(48px, 6vw, 80px) var(--font-num); letter-spacing: -0.05em;
  line-height: 1; margin-top: 24px; color: var(--ac); }
.sb-big-label { font-size: 13.5px; color: #a39d8e; margin-top: 6px; }
/* The headline is the honest combined figure and CAN be negative (owner
   decision 2026-07-19). A losing week is stated plainly, not hidden: muted
   red, never alarm-red, and never smaller than a winning week. */
.sb-big.is-neg { color: #c9736a; }
.sb-big.is-pos { color: var(--ac); }
/* Secondary slice figure — deliberately quiet so it cannot read as the
   headline. It is a named slice, not the record. */
.sb-big-second { font-size: 12.5px; color: #8d8778; margin-top: 4px;
  letter-spacing: 0.01em; }
.sb-big-second[hidden] { display: none !important; }
/* item 11: the plaque's pivot link is the ONLY amber on the plaque — the
   site's AA amber-link idiom (same mix as .tpt-unlock); the fake fortune
   above renders ink (it is not a result, so never won-green/amber). */
.sb-big-second a { color: color-mix(in srgb, var(--risk) 90%, var(--tx));
  font-weight: 700; text-decoration: none; white-space: nowrap; }
.sb-big-second a:hover { text-decoration: underline; }
.sb-mini-grid { display: grid; grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto; margin-top: 28px;
  border-top: 1px solid var(--ink-2); }
.sb-mini-grid > div { display: grid; grid-template-rows: subgrid; grid-row: span 2; }
.sb-mini-grid > div { padding: 16px 14px 0 0; }
.sb-mini-grid > div + div { border-left: 1px solid var(--ink-2); padding-left: 16px; }
.sb-mini-grid .v { font: 700 24px var(--font-num); color: var(--ink-tx); }
.sb-mini-grid .l { font-size: 11.5px; color: var(--ink-mu); margin-top: 3px; }
/* The profit chart earns its own plot area instead of being a thin strip of
   bars flush against the bottom edge: a labelled band with a baseline rule,
   real height, and air under the caption. */
.sb-chart-zone { border-top: 1px solid var(--ink-2); padding-top: 18px; }
.sb-bars { display: flex; gap: 6px; align-items: flex-end;
  min-height: clamp(88px, 11vh, 132px); padding-bottom: 10px;
  border-bottom: 1px solid var(--ink-2); }
.sb-bars > div { flex: 1; border-radius: 3px 3px 0 0; min-height: 4px; }

/* ── Home: slate section ── */
/* width + gutters come from the shell block at the top of this file */
.sb-section { padding-block: 40px 8px; }
.sb-section-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 18px; }
.sb-section-head h2 { font-size: 26px; letter-spacing: -0.02em; margin: 0; }
.sb-section-head .more { font-size: 13px; font-weight: 700; color: #8a8579;
  text-transform: uppercase; letter-spacing: .04em; text-decoration: none; }
.sb-section-head .more:hover { color: var(--tx); }
.sb-slate-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
/* The slate now renders the SHARED card (home.js routes through betCardHTML so
   the home page inherits the AI pill, the flip and the free-tier gate instead
   of silently missing every card feature added elsewhere). It keeps the slate's
   own padding and border weight, and the grid needs the flip perspective the
   .bet-grid rule normally supplies. */
/* align-items: STRETCH (owner 2026-08-03: "same standard, big ones look good").
   The slate mixes open picks (~240px) with a taller veiled CTA card (~310px); at
   align-items:start each sized to its own content, so an open card next to the
   veiled one looked half-height. Stretch makes every card in the row take the
   tallest sibling's height — the same content-driven row equalisation the
   dashboard/history grids already use (a card pins its foot to its bottom edge,
   so the extra height reads as balance, not dead space). NOT a blanket min-height
   (that was reverted for padding short cards) — cards only grow to a REAL taller
   neighbour. */
.sb-slate-grid { perspective: 1400px; align-items: stretch; }
.sb-slate-grid .bet-card { border: 2px solid var(--bdr); border-radius: 12px;
  padding: 20px 22px; background: var(--sf); }
@media (max-width: 860px) { .sb-slate-grid { grid-template-columns: 1fr; } }
.sb-slate-card { border: 2px solid var(--bdr); border-radius: 12px; padding: 20px 22px; background: var(--sf); }
.sb-slate-card .row1 { display: flex; justify-content: space-between; align-items: center; }
.sb-slate-card .when { font: 500 11px var(--font-num); color: #8a8579; text-transform: uppercase; }
.sb-slate-card .match { font-weight: 800; font-size: 17px; margin: 14px 0 3px; text-transform: uppercase; }
.sb-slate-card .row2 { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.sb-slate-card .mkt { font-size: 13px; color: var(--mu); }
.sb-slate-card .odds { font: 700 18px var(--font-num); }

/* ── Shared page heroes (picks/results/contact) ── */
.sb-page-hero { padding-block: 48px 8px; }
.sb-page-hero .sb-kicker { margin-bottom: 18px; }
.sb-page-hero p.intro { font-size: 15px; color: var(--mu); max-width: 560px; margin: 14px 0 0; line-height: 1.55; }
.sb-note { border: 2px dashed #d8d3c7; border-radius: 12px; padding: 16px 22px;
  display: flex; justify-content: space-between; align-items: center; gap: 14px;
  margin: 28px 0 8px; flex-wrap: wrap; }
.sb-note .txt { font-size: 13.5px; color: var(--mu); }
.sb-note .tag { font: 700 11px var(--font-num); color: #8a8579; white-space: nowrap; }

/* ── Legal strip ── */
.sb-legal { margin-block: 30px 0;
  font-size: 12.5px; color: #8a8579; line-height: 1.6; }

/* ── Responsive ── */
@media (max-width: 900px) {
  .sb-hero { grid-template-columns: 1fr; }
  .sb-hero-copy { padding: 44px 22px 36px; margin: 0; }
  .sb-hero-panel { padding: 36px 22px; }
  .sb-slate-grid { grid-template-columns: 1fr; }
  .sb-header-cta { display: none; }
  .sb-note { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 640px) {
  .sb-h1 { font-size: clamp(34px, 10vw, 44px); }
  .sb-mini-grid .v { font-size: 19px; }
  .chart-stats .cs-tile .value { font-size: 22px; }

  /* Stat tiles: the two columns did not share baselines — the left column sat
     ~16px lower. Two causes, both fixed here rather than by matching numbers:
     1. styles.css gives only :nth-child(even) its own padding, overriding the
        16px padding-top this file sets on every tile, so odd and even columns
        started at different offsets. Both columns now declare the same
        vertical padding and differ ONLY horizontally.
     2. a long label (BG/DE/RU) wrapping in one column and not the other still
        pushed that cell's value down. The label box reserves two lines, so
        the value and sub-line sit on the same baseline either way. */
  .chart-stats { align-items: start; }
  .chart-stats .cs-tile,
  .chart-stats .cs-tile:nth-child(even) { padding-top: 16px; padding-bottom: 2px; }
  .chart-stats .cs-tile { padding-right: 12px; padding-left: 0; }
  .chart-stats .cs-tile:nth-child(even) { padding-right: 0; padding-left: 14px; }
  .chart-stats .label {
    white-space: normal;            /* let long locales wrap instead of clip */
    min-height: 2.2em; line-height: 1.1;
    display: flex; align-items: flex-start;
  }
  /* Reserving two lines still leaves a few px of drift when one label wraps
     and its neighbour does not, because a wrapped box is not exactly 2.2em.
     Subgrid makes the three rows (label / value / sub) SHARED tracks across
     all four tiles, so they align by construction at any label length. The
     min-height above stays as the fallback where subgrid is unavailable. */
  @supports (grid-template-rows: subgrid) {
    .chart-stats { grid-template-rows: auto auto auto; }
    .chart-stats .cs-tile {
      display: grid; grid-template-rows: subgrid; grid-row: span 3;
    }
    .chart-stats .cs-tile > *:nth-child(n+4) { grid-row: 3; align-self: end; }
    .chart-stats .label { min-height: 0; }
  }
}

/* ── Filter stack: ONE width, ONE pair of edges ────────────────────
   These were flex children with different flex bases — measured at 390px,
   "All competitions" came out 350px wide and "All results" 163px, on
   consecutive rows, aligned at neither edge, with the search field a third
   width again. Sibling controls at ragged widths is what makes a page look
   unlaid-out, so the stack is now a GRID: every control is one cell, so they
   share a width and both edges by construction rather than by luck. */
.filters { display: grid; gap: 8px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.filters > * { min-width: 0; }
.filters input[type="text"] { max-width: none; width: 100%; }
.filters .mselect .mselect-ctl { width: 100%; }
@media (max-width: 640px) {
  /* one column: every control full width, one left edge, one right edge */
  .filters { grid-template-columns: 1fr; }
  .mselect-placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .mselect-chips { flex-wrap: nowrap; overflow: hidden; }
}

/* ── Bankroll simulator (inside the dark chart panel) ── */
.sb-sim { display: flex; align-items: center; gap: 22px; margin-top: 22px;
  padding: 18px 20px; border: 1px solid var(--ink-2); border-radius: 12px;
  background: #1a1815; flex-wrap: wrap; }
.sb-sim .sim-label { display: block; font: 700 10.5px var(--font-num);
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-mu); margin-bottom: 8px; }
.sb-sim .sim-input-wrap { display: flex; align-items: baseline; gap: 6px;
  border: 2px solid var(--ink-2); border-radius: 10px; padding: 8px 14px; background: #141310; }
.sb-sim .sim-input-wrap:focus-within { border-color: var(--ac); }
.sb-sim .sim-input-wrap > span { font: 700 18px var(--font-num); color: var(--ink-mu); }
.sb-sim .sim-input-wrap input { background: transparent; border: none; outline: none;
  color: var(--ink-tx); font: 700 22px var(--font-num); width: 110px;
  -moz-appearance: textfield; appearance: textfield; }
.sb-sim .sim-input-wrap input::-webkit-outer-spin-button,
.sb-sim .sim-input-wrap input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sb-sim .sim-arrow { font: 700 26px var(--font-num); color: var(--ink-mu); }
.sb-sim .sim-out { font: 700 34px var(--font-num); letter-spacing: -0.03em; line-height: 1; }
.sb-sim .sim-out.pos { color: var(--ac); }
.sb-sim .sim-out.neg { color: #e2685c; }
.sb-sim .sim-sub { font-size: 11.5px; color: var(--ink-mu); margin-top: 6px; max-width: 480px; line-height: 1.5; }
@media (max-width: 640px) {
  .sb-sim { gap: 12px; }
  .sb-sim .sim-arrow { display: none; }
  .sb-sim .sim-left, .sb-sim .sim-right { flex: 1 1 100%; }
  .sb-sim .sim-out { font-size: 28px; }
}

/* Simulator consistency indicator: tiny mono chips, non-distracting */
.sim-detail { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px;
  font: 500 10.5px var(--font-num); letter-spacing: .05em; color: var(--ink-mu); }
.sim-detail b { color: var(--ink-tx); font-weight: 700; margin-left: 4px; }
.sim-detail b.pos { color: var(--ac); }
.sim-detail b.neg { color: #e2685c; }

/* Home panel: real cumulative chart replaces the decorative bars */
.sb-panel-chart { margin-top: 26px; }
.sb-panel-chart svg { width: 100%; height: auto; display: block; }
.sb-chart-caption { display: flex; justify-content: space-between; gap: 10px;
  font: 500 10px var(--font-num); letter-spacing: .06em; color: var(--ink-mu);
  margin-top: 10px; }

/* Results: profit bar board (home-panel aesthetic) */
.bar-board { display: flex; gap: 5px; align-items: stretch; margin-top: 18px; min-height: 190px; }
.bar-board .bb-col { flex: 1; display: flex; flex-direction: column; min-width: 0; cursor: default; }
.bar-board .bb-up { height: 118px; display: flex; align-items: flex-end; }
.bar-board .bb-dn { height: 62px; border-top: 1px dashed #57503e; }
.bar-board i { display: block; width: 100%; border-radius: 3px; }
.bar-board .bb-col:hover i { filter: brightness(1.25); }
.bb-caption { display: flex; justify-content: space-between; gap: 10px;
  font: 500 10px var(--font-num); letter-spacing: .06em; color: var(--ink-mu); margin-top: 8px; }
/* tap-to-inspect (owner 2026-08-06): which bet a tapped bar is — mobile has no
   hover, so a tap surfaces the same detail the desktop title tooltip shows */
.bb-tap { margin-top: 6px; padding: 6px 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  font: 600 12px var(--font-num); color: var(--ink); text-align: center; }
@media (max-width: 640px) {
  .bar-board { gap: 3px; min-height: 150px; }
  .bar-board .bb-up { height: 92px; }
  .bar-board .bb-dn { height: 48px; }
}

/* Bar board: centered, capped widths (few bars never become slabs), value labels */
.bar-board { justify-content: center; }
.bar-board .bb-col { max-width: 84px; }
.bar-board.labelled .bb-col { min-width: 44px; }
.bar-board .bb-up { display: flex; flex-direction: column; justify-content: flex-end; }
.bar-board .bb-dn { display: flex; flex-direction: column; justify-content: flex-start; }
.bb-val { display: block; text-align: center; font: 700 10.5px var(--font-num);
  letter-spacing: .02em; margin: 4px 0; white-space: nowrap; }
.bb-val.pos { color: var(--ac); }
.bb-val.neg { color: #e2685c; }

/* Stat tiles → EDITORIAL (owner 2026-08-05): match the Performance cs-tile —
   borderless, a thin top rule, sized to content so a lone number never floats in
   a boxed void. Supersedes the old boxed "equal-square"/subgrid experiments. */
/* Equal-height tiles (owner 2026-08-05): all rows implicit + 1fr + stretch, so
   every tile matches the tallest whether the row has 1, 2 or 4 across. The value
   stays bottom-anchored (margin-top:auto) so the numbers still share a baseline. */
.stat-strip { align-items: stretch !important; grid-template-rows: none !important; grid-auto-rows: 1fr !important; }
.stat-tile { align-self: start; }
.kick-tile .kick-match { -webkit-line-clamp: 2; }

/* Next-Kickoff tip: wrap to 2 lines instead of one-line ellipsis, so the actual
   pick (e.g. "… — Yes") is never cut off on narrow mobile tiles. */
.kick-tile .kick-tip {
  white-space: normal !important;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; text-overflow: clip; line-height: 1.3;
}

/* Disabled time-range buttons (not enough ledger history yet) */
.segmented button.disabled, .segmented button:disabled {
  opacity: .38; cursor: not-allowed; pointer-events: none;
}

/* Daily Report — expandable rows revealing that day's games */
.daily-row { width: 100%; background: transparent; border: none; border-bottom: 1px solid var(--bdr);
  text-align: left; cursor: pointer; font: inherit; color: inherit; }
.daily-row .dr-chevron { font: 600 11px var(--font-num); color: var(--ac); width: 12px; flex-shrink: 0; }
.daily-detail { border-bottom: 1px solid var(--bdr); background: color-mix(in srgb, var(--sf2) 55%, transparent); }
.daily-detail[hidden] { display: none; }
/* OWNER (2026-07-23): grid mode inside an expanded day had ZERO breathing
   room -- .bet-grid carries no padding of its own (by design, so it can sit
   flush inside pages that already pad their container), but .daily-detail
   ALSO has none, so cards touched the day bar above, and the container's
   left/right/bottom edges, on every screen size. Table mode never had this
   problem because .dr-game pads itself per row. */
.daily-detail .bet-grid { padding: 16px 18px; }
@media (max-width: 640px) {
  .daily-detail .bet-grid { padding: 14px 16px; }
}
/* DEFECT 5: the expanded per-pick rows speak the VIEW SYSTEM STANDARD
   grammar — [teams] · [pick(+tier)] ||| [boxed odds] · [result]. The pick
   code (.tpt-code) is the one bold; teams demote to 500 (were 700, a second
   bold); odds ride the shared .tpt-oddsbox; result is .tpt-res (✓/✗ +
   locale letter). Same left-cluster / right-numeric split as the picks
   table; the day-aggregate .daily-row above stays bespoke. */
.dr-game { display: grid;
  grid-template-columns: minmax(0,1.3fr) minmax(0,1fr) auto auto;
  gap: 3px 14px; align-items: center;
  padding: 9px 18px 9px 44px; border-top: 1px solid color-mix(in srgb, var(--bdr) 55%, transparent); }
.dr-game:first-child { border-top: none; }
.dr-game .dr-match { display: flex; align-items: baseline; gap: 7px; min-width: 0;
  font-weight: 500; font-size: 13px; color: var(--tx); }
.dr-game .dr-match .tt-names { min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.dr-game .dr-match .ft-score { flex: 0 0 auto; font-weight: 500; }
.dr-game .dr-pick { display: inline-flex; align-items: baseline; gap: 8px; min-width: 0; }
.dr-game .dr-odds { justify-self: end; }
.dr-game .dr-res { justify-self: end; white-space: nowrap; }
.dr-empty { padding: 12px 18px 12px 44px; color: var(--mu); font-size: 13px; }
/* OWNER (2026-07-25 fix): the per-day .dr-toolbar (one toggle rendered
   INSIDE every expanded .daily-detail panel) is GONE — "there should be
   global toggle not local to trigger". The toggle is now the ONE
   #daily-toolbar-top control at the page level (site/daily.js), reusing
   the plain .pick-toolbar shape (see the margin-bottom rule below) same as
   picks.html's #pick-toolbar-top. No .dr-toolbar-scoped CSS is needed. */
@media (max-width: 640px) {
  /* the SAME two-line collapse as the picks table: line 1 teams + result,
     line 2 pick code + tier + odds */
  .dr-game { grid-template-columns: minmax(0,1fr) auto; column-gap: 12px; row-gap: 3px;
    padding: 9px 16px 9px 30px; }
  .dr-game .dr-match { grid-column: 1; grid-row: 1; }
  .dr-game .dr-res { grid-column: 2; grid-row: 1; }
  .dr-game .dr-pick { grid-column: 1; grid-row: 2; }
  .dr-game .dr-odds { grid-column: 2; grid-row: 2; }
}

/* ═══════════════════════════════════════════════════════════════
   SUBSCRIPTIONS — accounts, locked picks, pricing, checkout
   (same tokens: paper/ink/amber, Archivo + JetBrains Mono, 2px rules)
   ═══════════════════════════════════════════════════════════════ */

/* ── Header: subtle Account / Sign-in entry ── */
.account-link { display: inline-flex; align-items: center; gap: 6px;
  text-decoration: none; color: var(--mu); font: 700 12px var(--font-num);
  letter-spacing: .05em; text-transform: uppercase;
  border: 2px solid var(--bdr); border-radius: 8px; padding: 8px 13px;
  white-space: nowrap; transition: color .15s, border-color .15s; }
.account-link:hover { color: var(--tx); border-color: var(--ink); }
.account-link.authed { color: var(--tx); border-color: var(--ac);
  background: var(--cream); }
/* The header account label has TWO forms and CSS picks one — no JS on resize.
   Below 900px the full dual label ("Anmelden / Registrieren") cannot sit in a
   row that already clips DASHBOARD into a scroll strip, so the SHORT form
   shows instead. It used to hide the text entirely, which made registration
   invisible on phones — the one place the owner tests first. */
.account-link .al-short { display: none; }
@media (max-width: 900px) {
  .account-link .al-txt { display: none; }
  .account-link .al-short { display: inline; }
  .account-link { padding: 8px 10px; gap: 6px; }
}
@media (max-width: 400px) {
  /* 375px is the tightest real device; keep the label legible but compact
     rather than letting the row scroll */
  .account-link { padding: 8px 8px; font-size: 11px; }
}

/* ══ THE CARD/TABLE HIERARCHY — site-wide (2026-07-22, owner-approved) ═════
   One bold thing per card. Piloted on picks.html behind body.typo-pilot,
   PROMOTED to the shared level when the owner approved the pilot going
   site-wide (item 5): every card surface (picks, dashboard MY DAY, results
   history grid, homepage slate) renders this hierarchy, so the scope class
   is gone rather than sprayed onto every body tag.

   THE LABEL STYLE — defined once, used for every caption/label/chip on the
   pilot page (FanDuel's rule: labels never compete with values). */
.tp-label {
  font-family: var(--font-num); font-size: 9.5px; font-weight: 500;
  letter-spacing: .11em; text-transform: uppercase;
  color: color-mix(in srgb, var(--tx) 40%, transparent);
}
.tp-card { display: flex; flex-direction: column; }
.tp-top { display: flex; justify-content: space-between;
  gap: 10px; }
/* 3 — teams: identification, not emphasis */
.tp-teams { font-size: 14.5px; font-weight: 500; line-height: 1.25;
  margin: 9px 0 10px; color: var(--tx); letter-spacing: 0;
  /* reserve 2 lines so a 1-line name and a 2-line name build the SAME height
     card (owner 2026-08-05: cards must not vary by title length). */
  min-height: 2.5em; }
/* 1 — THE PICK: the only size jump on the card, stated once */
.tp-pickrow { display: flex; align-items: center;
  justify-content: space-between; gap: 10px; margin-bottom: 11px; }
.tp-pick { font-size: 23px; font-weight: 800;
  letter-spacing: -.01em; line-height: 1.05; min-width: 0;
  overflow-wrap: anywhere;
  /* reserve 2 lines: a short market ("Over 5.5 Cards: No") and a long one
     ("Ev Sahibi veya Deplasman: Evet") build the SAME height card, so the grid
     stays uniform in every language (owner 2026-08-05). */
  min-height: 2.1em; display: flex; align-items: center; }
/* Expanded-market names ("Both Teams to Score – 2nd Half", "Home to Score in
   Both Halves") are full phrases, not the short goal codes the 23px hero was
   sized for — they wrap to 3-4 ragged lines and crowd the risk badge. Right-size
   them so they sit on 1-2 tidy lines and stay legible in every language. */
.tp-card.xmarket .tp-pick { font-size: 16.5px; line-height: 1.2; font-weight: 700; min-height: 2.4em; }
/* 5 — the record slot (risk tier until a per-stream figure ships) */
.tp-record { font-family: var(--font-num); font-size: 11px;
  font-weight: 500; white-space: nowrap;
  /* ITEM 6: neutral — tier identity is words, not a result color */
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
/* 2 — odds in a boxed price cell, stake muted beside */
.tp-price { display: flex; align-items: center; gap: 8px; }
.tp-odds { font-family: var(--font-num);
  font-variant-numeric: tabular-nums; font-size: 19px; font-weight: 700;
  border: 1.5px solid var(--tx); border-radius: 6px; padding: 5px 11px; }
.tp-stake { font-family: var(--font-num);
  font-variant-numeric: tabular-nums; font-size: 13px; color: var(--mu); }
/* 4/6 — the quiet foot: chip left, kickoff/result right. Kickoff NEVER
   glows — muted mono, no amber, no pulse. */
.tp-foot { border-top: 1px solid var(--bdr); padding-top: 9px;
  margin-top: 11px; display: flex; justify-content: space-between;
  align-items: center; gap: 10px; }
/* THE ORACLE'S REACTION on a settled card (owner 2026-07-30: "implement it into
   game results"). It sits in the foot immediately before the result, so the
   picture and the outcome read as one statement: a celebration next to +₺11, the
   head in the hands next to -₺30. margin-left:auto pushes the pair right, so an
   upcoming card (no portrait) keeps the exact same foot geometry.

   44px is the floor at which the cap, beard and gesture resolve — the lesson
   from the 26px blob in the analysis head. Decorative only: alt="" upstream, and
   aria-hidden is unnecessary because the result text beside it carries meaning. */
/* LEFT cluster: portrait then the pick's tag, reading as one unit (owner:
   "avatar on left, text next to it"). The result keeps the right edge. */
.tp-foot-l { display: flex; align-items: center; gap: 9px; min-width: 0; }
.tp-foot-l .tp-label { min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.tp-oracle { width: 44px; height: 44px; flex: none;
  border-radius: 50%; object-fit: cover; object-position: top;
  border: 1.5px solid var(--ink); background: var(--bg); }
/* the foot's own gap already spaces the pair; the result keeps its own weight */
@media (max-width: 480px) { .tp-oracle { width: 38px; height: 38px; } }
/* right cluster: the AI pill rides beside the kickoff/result — same right-side
   placement as the table rows (owner 2026-07-30: consistent everywhere) */
.tp-foot-r { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
/* the record row: win rate on the left, the AI pill at the VERY RIGHT of the
   same line, sitting just above the foot's countdown (owner 2026-07-30). Cards
   without a record keep the row so the pill's position never moves. */
.tp-recrow { display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin: 8px 0 11px; }
.tp-recrow .tp-mrec { margin: 0; flex: 1 1 auto; min-width: 0; }
.tp-recrow .ai-trigger { margin-right: 0; flex: none; }
.tp-ko { font-family: var(--font-num); font-size: 11px;
  white-space: nowrap;
  color: color-mix(in srgb, var(--tx) 40%, transparent); }
.tp-res { font-family: var(--font-num); font-size: 13px;
  /* item 6: 600, not 700 — the settled amount is prominent but THE PICK
     stays the card's one bold (the measured probe counts >=700) */
  font-weight: 600; }
.tp-res.won { color: var(--won); }
.tp-res.lost { color: var(--lost); }

/* ══ EXPANDED MARKETS (members-only) — record-strength badge ═══════════════
   Replaces the stake-tier badge in the record slot on expanded market cards.
   Owner 2026-07-26: this is a RELIABILITY signal (how well the market's record
   holds), NOT a danger warning — so it reads as plain record strength with a
   calm green→grey fade and NO red. Strong = green, fading to neutral grey for
   thin/new; "New" gets a dashed outline (record still building). Compound
   selectors (.risk-band.rb-*) out-specify .tp-record's muted color. */
/* ONE neutral look for every accuracy level (owner 2026-07-26: no colour codes) —
   same plain outlined pill as the tier badge; the WORD carries the ranking. "New"
   keeps only a dashed edge to say "not rated yet", still colourless. */
.risk-band { border: 1px solid var(--bdr); border-radius: 5px; padding: 3px 8px;
  font-family: var(--font-num); font-size: 10.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .05em; white-space: nowrap;
  background: transparent; color: color-mix(in srgb, var(--tx) 62%, transparent);
  cursor: pointer; }
.risk-band.rb-unrated { border-style: dashed; }
/* ── MEMBER TAG SYSTEM (additive markers) ──────────────────────────────────
   Tags live in their OWN dedicated, SINGLE-LINE, NON-WRAPPING row (.tp-mtags),
   NOT in the pick row's badge cluster (that wrapped and grew cards raggedly by
   +5–59px). The row is emitted only on tagged cards; its height is CONSTANT for
   1/2/3 pills (they never wrap — extras clip), so per-card growth is uniform and
   the card grid stays even via equalizeCardHeights / align-items:stretch. */
.tp-mtags { display: flex; flex-wrap: nowrap; align-items: center; gap: 5px;
  margin: 0 0 8px; min-width: 0; overflow: hidden;
  /* RESERVE the row on EVERY card (memberTagsRowHTML always emits it, empty or not)
     so a TAGGED card no longer exceeds an untagged one — the markers used to add a
     row to only some cards, leaving the grid ragged (owner 2026-08-12; measured:
     card-height delta 27px -> 5px). */
  min-height: 19px; }
.pt-tags { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 5px;
  min-width: 0; overflow: hidden; }
.pt-tag { display: inline-flex; align-items: center; gap: 3px; flex: none;
  font-family: var(--font-num); font-size: 9px; font-weight: 700; line-height: 1;
  letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
  padding: 3px 6px; border-radius: 5px;
  background: var(--ac); color: var(--ac-fg); border: 1px solid var(--ac); }
.pt-tag.ghost { background: transparent; color: var(--risk); border-color: var(--ac); }
.pt-tag svg { width: 10px; height: 10px; flex: none; }
/* PRESTIGE badge — solid GOLD pill, ink text, members-only. Same box metrics as
   .pt-tag so it shares the fixed-height single-line row (no geometry change);
   rendered FIRST so on a tight/clipping row the prestige is what survives. */
.pt-badge { display: inline-flex; align-items: center; gap: 3px; flex: none;
  font-family: var(--font-num); font-size: 9px; font-weight: 800; line-height: 1;
  letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
  padding: 3px 6px; border-radius: 5px; color: var(--ink);
  background: linear-gradient(180deg, #f8d571 0%, #eeb23a 55%, #e0a018 100%);
  border: 1px solid #cf9412; box-shadow: 0 1px 1px rgba(20,19,16,.14); }
.pt-badge svg { width: 10px; height: 10px; flex: none; }
.tpt-pickwrap .pt-badge { padding: 2px; gap: 0; }
@media (max-width: 560px) { .pt-badge { padding: 3px; gap: 0; } }
/* tight widths: drop the label so 2–3 pills always fit one line — the icon alone
   still reads (owner's "icon-only in the tight spot"). */
@media (max-width: 560px) { .pt-tag-tx { display: none; } .pt-tag { padding: 3px; gap: 0; } }
/* table/list view: ICON-ONLY pills, inline in the pick cell, nowrap — they must
   never wrap the row or change row height (the label rides on the title tooltip). */
.tpt-pick .pt-tags { margin-left: 4px; vertical-align: middle; }
.tpt-pick .pt-tag, .tpt-pick .pt-badge { padding: 2px; gap: 0; }
.tpt-pick .pt-tag-tx { display: none; }
/* SIZE-MATCH to the AI pill (owner 2026-08-11, SIZE ONLY). Where the gold badges /
   amber tags sit alongside the AI pill (.ai-trigger ≈ 20px tall) — the list pick
   row (.tpt-pickwrap, next to the AI pill in .tpt-aigrp) and the card tag row
   (.tp-mtags) — bump the icon + chip padding so the chips read the SAME height as
   the AI pill. Colours, gradient, border, shape and labels are ALL unchanged; only
   size moves. Scoped to these two contexts, so the veiled-table icon pills and the
   ≤560px tight rules elsewhere keep their own sizing. Placed AFTER the .tpt-pick
   icon rule above (equal specificity) so it wins for the list pick row. */
.tpt-pickwrap .pt-badge, .tpt-pickwrap .pt-tag { padding: 3px; gap: 0; }
.tpt-pickwrap .pt-badge svg, .tpt-pickwrap .pt-tag svg,
.tp-mtags .pt-badge svg, .tp-mtags .pt-tag svg { width: 12px; height: 12px; }
/* MEMBERS CORNER RIBBON — the members marker. An absolutely-positioned overlay
   clipped to a small top-right corner wrapper: ZERO layout impact, so a member
   card is the EXACT same size as a non-member one. The clip is scoped to the
   corner wrapper (NOT overflow:hidden on the card) so card tooltips/panels are
   untouched. */
/* Each card is its OWN stacking context (z-index on a positioned element; NOT
   isolation/opacity/filter — those would FLATTEN the flip's preserve-3d). Without
   this, a flipping card's 3D compositing layer painted OVER the corner ribbon of
   the card directly below it on iOS Safari (owner 2026-08-13, mobile video). A
   contained stacking context keeps each flip's 3D to its own card, so the next
   card's ribbon paints normally on top. */
.tp-card, .bet-card { position: relative; z-index: 0; }
.tp-ribbon-wrap { position: absolute; top: 0; right: 0; width: 88px; height: 88px;
  overflow: hidden; pointer-events: none; z-index: 2;
  border-top-right-radius: var(--radius); }
.tp-ribbon { position: absolute; top: 13px; right: -30px; width: 120px;
  transform: rotate(45deg); text-align: center;
  background: var(--ac); color: var(--ac-fg);
  font: 800 8.5px/1 var(--font-num); letter-spacing: .09em; text-transform: uppercase;
  padding: 4px 0; box-shadow: 0 1px 2px rgba(20,19,16,.22); }
/* Long-locale ribbon fit: the "Members" word varies a lot by language (RU
   "Для участников" is ~2x EN "Members"), so at the fixed 8.5px base long words
   overfill the 45deg band and read oversized. app.js fitMemberRibbons() measures
   each ribbon's text and scales its font-size DOWN to a consistent fill (short
   locales stay at the 8.5px base). Font-size only — no colour/shape/position/label
   change, no card-height change — so nothing here overrides the base rule above;
   the old per-locale :lang(ru)/:lang(de) hardcode is replaced by that universal fit. */
/* On a ribbon (member) card, reserve the top-right corner so the single-line tags row
   never slides UNDER the ribbon. Uses MARGIN (not padding): margin shrinks the row's box so
   its own overflow:hidden clips before the ribbon — padding wouldn't (overflow clips at the
   padding box, letting tags render into it). Safe: .tp-mtags is nowrap + overflow:hidden, so
   this only clips a rightmost pill earlier — it cannot change card height. Match title + risk
   chips already sit clear of the corner (measured), so only the tags row needs it. */
.bet-card:has(.tp-ribbon-wrap) .tp-mtags,
.tp-card:has(.tp-ribbon-wrap) .tp-mtags { margin-right: 46px; }
/* Same for the top row (league LEFT + kickoff time RIGHT): the kickoff time is right-aligned
   and sat directly under the ribbon. padding-right pulls it left, clear of the corner. Single
   line, so no height change. */
.bet-card:has(.tp-ribbon-wrap) .tp-top,
.tp-card:has(.tp-ribbon-wrap) .tp-top { padding-right: 46px; }
/* Veiled (locked) member card: its top row is `.top` (meta + tier badge, badge
   RIGHT) — pull it clear of the ribbon corner too. Single line, no height change. */
.bet-card:has(.tp-ribbon-wrap) .top { padding-right: 46px; }
/* The ribbon strip is TAPPABLE (its own tooltip); the wrap stays click-through so
   it never blocks the card. */
.tp-ribbon-wrap { pointer-events: none; }
.tp-ribbon { pointer-events: auto; cursor: pointer; }
/* #4 (owner 2026-08-09, FIX v2 2026-08-11): the ribbon is a child of the flipping
   .tp-card, so on the rotateY(180deg) flip it showed MIRRORED on the AI-analysis
   (back) face, over the heading. The first fix (backface-visibility:hidden on the
   wrap) FAILED live — the wrap's overflow:hidden + absolute positioning defeats
   backface-visibility inside the preserve-3d card. Reliable fix: HIDE the ribbon
   whenever the card is flipped (the card carries `flipcard flipped` on flip). The
   ribbon belongs to the pick/front face only; the analysis face never needs it.
   This also resolves the DE/RU "ribbon text doesn't fit" report — that was the
   SAME mirrored ribbon spilling; the front-face ribbon fits in every locale. */
.tp-card.flipcard.flipped .tp-ribbon-wrap { display: none; }
/* Every vocabulary pill / gem taps to its explainer. */
.pt-badge, .pt-tag, .tpt-mmark { cursor: pointer; }
/* RESULT BADGE READS AS AN INTERACTIVE TAG (owner 2026-08-20, replaces the rejected
   underline that looked like a hyperlink). Same chip box as the tier / .risk-band /
   .pt-tag pills — hairline border, 5px radius, tight padding, nowrap, pointer cursor
   — but TINTED IN ITS OWN RESULT COLOUR (won green / lost red / void muted) via the
   same color-mix language as .perf-hero, since result colour is meaningful here. The
   box metrics match the in-table tier chip (.tpt-tier.risk-band: 2px 6px) so it never
   grows the row. Only when there IS a detail to reveal (data-rtip). */
.tp-res[data-rtip], .tpt-res[data-rtip] {
  display: inline-flex; align-items: center; white-space: nowrap; cursor: pointer;
  border: 1px solid color-mix(in srgb, currentColor 34%, transparent);
  background: color-mix(in srgb, currentColor 10%, transparent);
  border-radius: 5px; }
.tpt-res[data-rtip] { padding: 2px 6px; }   /* table: tier-chip metrics, row-safe */
.tp-res[data-rtip] { padding: 3px 8px; }    /* card foot: .risk-band metrics */
/* Tap-to-explain popup for the vocabulary pills — mirrors .risk-pop. */
.pt-pop { position: fixed; z-index: 60; max-width: 240px;
  background: var(--sf); border: 1px solid var(--ink); border-radius: 8px;
  padding: 9px 11px; box-shadow: 0 6px 22px rgba(0, 0, 0, .18);
  font-family: var(--font-num); font-size: 12px; line-height: 1.45; color: var(--tx); }
/* RESULT-DETAIL popover (owner 2026-08-20): CENTERED under its badge with a caret
   pointing up at it, so it unmistakably belongs to that result (the JS sets `left`
   to centre on the badge — viewport-clamped — and the caret's `left` to the badge
   centre within the popover). */
.pt-pop.res-pop { text-align: center; }
.res-pop-caret { position: absolute; top: -6px; left: 50%; width: 0; height: 0;
  transform: translateX(-50%);
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-bottom: 6px solid var(--ink); }
.res-pop-caret::before { content: ''; position: absolute; top: 1px; left: -5px;
  width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-bottom: 5px solid var(--sf); }
/* TOP MARKETS section — the per-market tier board (window.XMARKETS): a list of
   RECORDS (market name + risk band + "66% · 259 games"), never fixtures. Additive;
   its own card, does not touch the pick grid. */
.section-title.tm-title { display: inline-flex; align-items: center; gap: 7px;
  text-transform: none; }   /* not uppercased — sidesteps the Turkish İ casing gotcha */
.tm-title svg { width: 15px; height: 15px; color: var(--ac); flex: none; }
#tm-list { display: flex; flex-direction: column; gap: 6px; }
.tm-row { display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap;
  padding: 9px 12px; border: 1px solid var(--bdr); border-radius: 8px; background: var(--sf); }
.tm-name { font: 700 14px var(--font-num); color: var(--tx); min-width: 0; }
.tm-name .tm-act { font-weight: 500; color: var(--mu); }
.tm-marks { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tm-row .tp-mrec { margin-left: auto; }
/* table/list members marker: amber gem inline at the START of the teams cell,
   immediately before the match name (owner 2026-08-14), no row growth. It sits
   inside the flex .tt name line as a non-shrinking leading item so long RU/DE
   names ellipsize without ever squeezing or hiding the gem. */
.tpt-mmark { display: inline-flex; vertical-align: middle; margin-right: 5px; color: var(--ac); }
.tpt-mmark svg { width: 12px; height: 12px; }
.tpt-teams .tt > .tpt-mmark { flex: 0 0 auto; margin-right: 6px; }
/* LIST-VIEW MEMBERS MARKER: amber left-edge on the row + a leading amber gem at the
   START of the teams cell, right before the match name (owner 2026-08-14: moved out
   of the time cell so the crystal is the first thing seen on each member-only row).
   The edge is an inset box-shadow / border-segment (zero layout impact). */
/* DESKTOP: draw the amber as a segment OF the band-card's left border, not a
   stripe floating 12px inside it (owner 2026-08-12: "put the yellow lane over
   the table border / the border should be that colour"). A pseudo on the row's
   first cell, pulled left across the card's 12px padding + 1px border, sits the
   amber ON the card edge and covers the grey border for that row. Per-row, so a
   MIXED band (member + free picks in one .tp-tablewrap) stays correct. Keep the
   -13px in step with .tp-tablewrap padding-left(12) + border(1). */
.tpt-member .tpt-time { white-space: nowrap; }
.tpt-member > td:first-child { position: relative; }
.tpt-member > td:first-child::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: -13px;
  width: 3px; background: var(--ac); pointer-events: none; }
/* MOBILE (owner 2026-08-08): the row collapses to a grid where the time cell is short (~17px) and
   mid-row, so the edge on that cell renders as an ugly stub next to the gem. Move it to the FULL
   row (the grid row spans full height) so it's a clean left stripe, not a floating stub. */
@media (max-width: 640px) {
  .tpt-member > td:first-child::before { display: none; }   /* desktop border-segment off; mobile keeps the full-row stripe below */
  .tpt-member .tpt-time { box-shadow: none; }
  .tpt-member { box-shadow: inset 3px 0 0 var(--ac); }
}
/* tap-to-explain tooltip: why a market is rated this level + its own record.
   Click-triggered so it works on touch; dismisses on the next click / Escape. */
.risk-pop { position: fixed; z-index: 60; max-width: 260px;
  background: var(--sf); border: 1px solid var(--ink); border-radius: 8px;
  padding: 9px 11px; box-shadow: 0 6px 22px rgba(0, 0, 0, .18);
  font-family: var(--font-num); font-size: 12px; line-height: 1.45; color: var(--tx);
  display: flex; flex-direction: column; gap: 4px; }
.risk-pop .risk-pop-r { font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--tx) 62%, transparent); }

/* ══ AI review — trigger pill + expandable panel (table row / card section) ══ */
.ai-trigger { display: inline-flex; align-items: center; gap: 4px;
  font: 600 10px var(--font-num); text-transform: uppercase; letter-spacing: .05em;
  padding: 3px 8px; border: 1px solid var(--bdr); border-radius: 20px; margin-right: 8px;
  background: transparent; color: color-mix(in srgb, var(--tx) 58%, transparent);
  cursor: pointer; white-space: nowrap; vertical-align: middle; }
.ai-trigger:hover, .ai-trigger.on { border-color: var(--ac); color: var(--tx); }
/* desktop: the whole row/card toggles the analysis, so it reads clickable */
.tpt-row.has-ai, .bet-card.has-ai { cursor: pointer; }
/* A .bet-grid is a CSS grid, so its default align-items:stretch made EVERY card
   in a row grow to match the one whose AI panel had just expanded — the owner
   saw "click one and all cards open, empty". Only the dashboard grid can expand
   panels, so scope the fix there: each card keeps its own height. */
/* .dash-wrap align-items:start REMOVED (owner 2026-07-31, screenshot of a
   ragged dashboard row): it existed so a growing flipped card would not
   stretch its neighbours, but the flip no longer changes card size — so the
   override only produced ragged bottoms whenever a long team name wrapped.
   Cards stretch to the row again, and the pins below put every card's foot
   on its bottom edge, so tops, bottoms AND foot lines align; name-wrap is
   absorbed in the middle of the card where it cannot misalign anything. */
.tp-card .tpc-front { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.tp-card .tp-foot, .tpc-front .tp-foot { margin-top: auto; }
.ai-trigger svg { color: var(--ac); }
.tpt-airev > td { padding: 0 10px 12px; background: transparent; }
.tp-airev { margin-top: 10px; }
.ai-review-inner { border: 1px solid var(--bdr); border-left: 3px solid var(--ac);
  border-radius: 8px; padding: 11px 13px;
  background: color-mix(in srgb, var(--ac) 6%, var(--sf)); }
.ai-review-head { display: flex; align-items: center; gap: 6px;
  font: 700 11px var(--font-num); text-transform: uppercase; letter-spacing: .06em;
  color: var(--tx); margin-bottom: 6px; }
.ai-review-head svg { color: var(--ac); }
/* the outcome-matched oracle avatar (owner 2026-07-30). object-position:top
   because the generated portraits are waist-up — a centred crop would cut the
   face off at this size. 26px keeps the head one line tall, so no panel,
   card back or table row changes height. */
/* The .ai-face avatar is GONE from the analysis head (owner, three passes on
   this icon: 26px was a blob, 40px still read as clutter). The head is an
   information surface — label + three sections of text — and a decorative
   portrait there is never legible enough to earn its space. The rule is kept,
   scoped to nothing, so any future reuse starts from a sane size rather than
   re-deriving it. The oracle lives on the LOCKED GATE (96px, unchanged — owner:
   "free tier can keep same, it is good") and next on settled RESULTS. */
.ai-face { width: 40px; height: 40px; flex: none;
  border-radius: 50%; object-fit: cover; object-position: top;
  border: 1.5px solid var(--ink); background: var(--bg); }
.ai-review-tag { margin-left: auto; font-weight: 500; font-size: 9px; letter-spacing: .04em;
  color: var(--mu); border: 1px dashed var(--bdr); border-radius: 4px; padding: 1px 5px; }
.ai-review-inner p { margin: 0; font: 400 12.5px/1.55 var(--font-num); color: var(--tx); }
/* the three sections: the numbers / team news / why this pick */
.ai-sec + .ai-sec { margin-top: 8px; }
.ai-sec-k { display: block; font: 700 9.5px var(--font-num); text-transform: uppercase;
  letter-spacing: .07em; color: color-mix(in srgb, var(--tx) 45%, transparent);
  margin-bottom: 2px; }
.ai-review-inner p b { font-weight: 700; }
/* Settled-outcome metric ("8 cards"): the one fact the results row lacks. Value
   reads big, the metric noun quiet beside it. */
.ai-settle-num { display: flex; align-items: baseline; gap: .35em; }
.ai-settle-num .asn-v { font: 700 26px/1 var(--font-num); color: var(--tx); }
.ai-settle-num .asn-k { font: 400 12.5px var(--font-num); color: color-mix(in srgb, var(--tx) 55%, transparent); }
/* Pitch-veil Nostradamus line: a FIXED text block so every veil card is the same
   height regardless of which (variable-length) line it drew — short lines pad to
   the block, long lines clamp (owner 2026-08-05, uneven veil cards). */
.ail-fact { min-height: 4.65em; display: -webkit-box; -webkit-line-clamp: 3;
  -webkit-box-orient: vertical; overflow: hidden; }
.ai-review-stats { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.ai-review-stats span { font: 600 10.5px var(--font-num); font-variant-numeric: tabular-nums;
  border: 1px solid var(--bdr); border-radius: 5px; padding: 3px 7px; color: var(--mu); }

/* ── GRID AI: 3D CARD FLIP (owner 2026-07-29) ─────────────────────────────
   Front = the pick, back = the analysis. The card's footprint never changes,
   so the grid never reflows (the expand version stretched every row). The
   back face is a pre-rotated absolute overlay; long analyses scroll inside. */
/* PER-CARD perspective, NOT grid-wide (owner 2026-08-13). A grid-wide
   `.bet-grid { perspective }` put every card — including the locked/veiled ones —
   into ONE shared 3D context; on mobile, flipping a sibling re-layered that context
   and CLIPPED the locked cards' absolutely-positioned corner ribbons (they vanished
   until the next paint). Moving perspective into each flip card's own transform
   isolates the 3D to the card being flipped, so a flip can't touch siblings' ribbons.
   Flip look is unchanged (single card, single vanishing point). */
.tp-card.flipcard { position: relative; transform-style: preserve-3d;
  transform: perspective(1400px);
  transition: transform .55s cubic-bezier(.35,.1,.25,1); }
.tp-card.flipcard.flipped { transform: perspective(1400px) rotateY(180deg); }
/* RESERVE THE ROOM, DON'T GROW INTO IT (owner 2026-07-30: "when I click expand
   why are all the others shifting?"). The card used to grow on flip to fit the
   analysis — measured, 231 -> 290px, which pushed every card in the rows below
   down 48px. A card that changes size when you interact with it makes the whole
   grid jump under the reader's cursor.

   The space is reserved up front instead: an AI-capable card is tall enough for
   a typical analysis before anyone flips it, so flipping changes nothing at all.
   A genuinely long analysis still scrolls inside the back, from the top.

   REVERTED the blanket min-height (owner, next screenshot): reserving 290px on
   every card padded the ones that did not need it, leaving ~100px of empty
   space under short cards across the whole grid. Trading visible dead space on
   EVERY card for a scrollbar on the FEW long analyses is the wrong trade. The
   card keeps its natural height, the flip still never resizes it (the growth in
   fitFlippedCard is gone), and an analysis longer than the card scrolls inside
   the back face — starting at the top, which was the actual complaint that
   started this. */
.tp-card.flipcard .tpc-front { backface-visibility: hidden; -webkit-backface-visibility: hidden; }
/* BACK FACE v2 (owner 2026-07-30): clean full-bleed surface — NO accent bar, no
   panel-in-a-panel. Fixed head + tabs, the section body scrolls inside when the
   text is longer than the card, stats pinned under it. Tapping the face flips
   back (the Back chip is the visible affordance, styled like every site chip). */
.tp-card.flipcard .tp-airev.tpc-back { position: absolute; inset: 0; margin: 0;
  transform: rotateY(180deg); backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border-radius: inherit; background: var(--sf); padding: 0; overflow: hidden; cursor: pointer; }
.tpc-back .ai-review-inner { height: 100%; min-height: 0; display: flex;
  flex-direction: column; border: none; background: transparent; border-radius: 0;
  padding: 12px 13px; }
/* the head clears the flip chip; 62px was the old "← BACK" word, the icon
   button needs 28 + its 11px offset + a little air */
.tpc-back .ai-review-head { padding-right: 44px; flex: none; }
/* ICON, NOT A WORD (owner 2026-07-30): a rotate glyph reads as "turn the card
   over" in every language — the chip no longer carries a translated word, and
   the freed width stops it crowding the section tabs. Square touch target
   (28px, the site's minimum for a secondary control); the label lives on
   aria-label + title. */
.ai-flipback { position: absolute; top: 10px; right: 11px; z-index: 2;
  width: 28px; height: 28px; padding: 0; display: inline-flex;
  align-items: center; justify-content: center;
  border: 2px solid var(--ink); border-radius: 50%;
  background: var(--sf); color: var(--tx); cursor: pointer;
  transition: background .12s ease, color .12s ease, transform .18s ease; }
.ai-flipback svg { display: block; }
.ai-flipback:hover { background: var(--ink); color: var(--ink-tx); }
/* the glyph itself turns on hover — the affordance demonstrates the action */
.ai-flipback:hover svg { transform: rotate(-180deg); }
.ai-flipback svg { transition: transform .3s ease; }
.ai-flipback:focus-visible { outline: 2px solid var(--ac); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .ai-flipback, .ai-flipback svg { transition: none; }
  .ai-flipback:hover svg { transform: none; }
}
@media (prefers-reduced-motion: reduce) { .tp-card.flipcard { transition: none; } }
/* CARD-BACK TABS (owner 2026-07-30): the back face can't host three stacked
   paragraphs — mini-tabs show ONE section at a time. The table row keeps the
   stacked layout: tabs render in the DOM but stay hidden outside .tpc-back. */
.ai-tabs { display: none; }
/* the site's own segmented-control idiom (2px ink frame, amber active) */
/* owner 2026-07-30: "space a bit between why this pick etc and rotate button" —
   the round flip chip sits directly above the right-hand tab and 7px read as a
   collision. The margin runs from the HEAD's bottom, not the chip's, so 14px
   bought only 9px of real air (measured) — 20px gives ~15px, and the back face
   still doesn't overflow because the section scrolls inside it by design. */
.tpc-back .ai-tabs { display: flex; flex: none; border: 2px solid var(--ink);
  border-radius: 9px; overflow: hidden; margin: 20px 0 11px; }
.tpc-back .ai-tab { flex: 1; padding: 6px 2px; border: 0; border-radius: 0;
  background: transparent; color: var(--tx); font: 600 9.5px var(--font-num);
  text-transform: uppercase; letter-spacing: .04em; cursor: pointer; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpc-back .ai-tab + .ai-tab { border-left: 2px solid var(--ink); }
/* NARROW CARDS (owner 2026-07-30: "on performance page why has this design
   broken, not like dashboard?"). Measured: the dashboard grid gives a card
   401px and the three tabs 121px each — nothing truncates. The PERFORMANCE
   grid packs 5 columns, so the same card is 239px and each tab gets 67px:
   two of three labels collapsed to "THE NUMBE…". The back face was designed
   against a width it was never promised.

   Below 300px of card the segmented control stops pretending it can hold three
   words: the label shrinks, the padding tightens, and the text wraps to two
   short lines instead of ellipsizing mid-word.

   TWO PARTS. (1) the label WRAPS rather than ellipsizes — applied to every
   card back, because a truncated control is wrong at any width and wrapping
   costs a wide card nothing. (2) .history-grid widens the Performance grid's
   columns so its cards stop being 40% narrower than the dashboard's in the
   first place. */
.tpc-back .ai-tab { white-space: normal; line-height: 1.15; padding: 5px 3px; }
/* ONE CARD PROPORTION EVERYWHERE (owner 2026-07-30: "why not use the same as
   we do in the dashboard for daily" — meaning badge SIZE, not logic).

   Measured: the badges are already byte-identical across the three surfaces
   (AI pill 10px/48x21, pick 23px, odds 19px/41h). What differed was the CARD —
   the dashboard gives it 401px, while Daily and Performance inherited the base
   grid's 230px min and packed 5 columns, so the same badges sat in a card 42%
   narrower and read as oversized. Both pages now use the DASHBOARD's own rule,
   so a card is a card wherever it appears. */
.history-grid, .daily-grid {
  /* SAME grid as Today's Picks #tier-groups. EQUAL-HEIGHT cards via
     align-items:stretch (owner 2026-08-14, extends the picks-page standard to
     Daily + Performance). Stretch alone was reverted 2026-08-12 because on the
     veil+open mix it opened an empty gap above short cards' pinned footers — the
     fix is DISTRIBUTION (justify-content:space-between on the card body, rules
     just below), the same technique that made stretch stick on #tier-groups. */
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 12px; align-items: stretch; justify-items: start; }
/* Sparse risk bands live ONLY on the picks page (#tier-groups): a band holding
   1–2 picks used to leave two empty phantom columns to the right of a lone card
   (auto-FILL keeps empty tracks). Scoped here so the fix never touches the
   History/Performance/Daily/Dashboard grids, which are always full. auto-FIT
   collapses the empty tracks; the fixed 420px track max keeps a lone card a
   normal width, left-aligned, and every card the SAME width band-to-band. */
#tier-groups .bet-grid {
  /* FLEX-WRAP, CENTERED ROWS (owner 2026-08-06): a partial/lone row CENTRES
     instead of leaving a right-hand hole; full rows grow edge-to-edge. */
  /* EQUAL-HEIGHT cards via align-items:stretch (owner 2026-08-14, REVERSES the
     2026-08-05 natural-height decision): every card in a row grows to the row's
     tallest, so multi-column rows read even instead of ragged.
     WHY THIS STICKS WHERE 2026-08-12 DID NOT: stretch alone was reverted then
     because it collected ALL the slack in one EMPTY GAP above the short cards'
     pinned foot (owner arrowed it). The fix is DISTRIBUTION — the card body uses
     justify-content:space-between (rules below), so top stays at the top edge,
     foot/kickoff at the bottom, and the middle rows get even gaps: a taller card
     reads as generous, not empty. The foot's site-wide `margin-top:auto` pin and
     the veil `.locked-zone` pin are neutralised IN THIS SCOPE so space-between
     governs; every other grid (home slate, dashboard, results) keeps its own
     natural-height rhythm (incl. the 2026-07-19 locked-divider decision). */
  display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch; gap: 8px; }
#tier-groups .bet-grid > .bet-card {
  flex: 1 1 340px; max-width: 480px; min-width: 0; }
/* DISTRIBUTE THE STRETCH SLACK (Task A, owner 2026-08-14). space-between spreads
   any extra height as even gaps between the body rows rather than one void above
   the foot. No-slack cards are unchanged (space-between == flex-start with no free
   space), so a lone card in a band still looks identical to before. */
#tier-groups .bet-grid > .bet-card.tp-card { justify-content: space-between; }
#tier-groups .bet-grid > .bet-card.flipcard .tpc-front { justify-content: space-between; }
/* undo the site-wide foot pin here so space-between (not the auto margin) owns the
   slack; 11px keeps the foot's natural gap when there is no slack to distribute */
#tier-groups .bet-grid > .bet-card.tp-card .tp-foot { margin-top: 11px; }
/* the veil card distributes the same way; restore the natural inter-block gap the
   global `.locked-zone{margin-top:auto}` pin (scoreboard.css ~3078) would otherwise
   swallow, so a no-slack veil still breathes and a stretched one spreads evenly */
#tier-groups .bet-grid > .bet-card.locked { justify-content: space-between; }
#tier-groups .bet-grid > .bet-card.locked .locked-zone { margin-top: 10px; }
@media (max-width: 640px) {
  #tier-groups .bet-grid > .bet-card.locked .locked-zone { margin-top: 8px; }
}
/* EQUAL-HEIGHT extended to Daily / Performance / Dashboard (owner 2026-08-14) —
   the EXACT #tier-groups technique above, reused: align-items:stretch levels each
   grid ROW (set on .history-grid/.daily-grid above; .dash-wrap .bet-grid is already
   stretch), and the card DISTRIBUTES its slack via justify-content:space-between so
   the extra height reads as even inter-row gaps, NOT one void above the pinned foot.
   LOCKED cards are DELIBERATELY EXCLUDED here: the 2026-07-19 locked-divider
   decision (.bet-card.locked .locked-zone{margin-top:auto}, ~3097) is HONORED — the
   veil keeps taking the slack above its explainer, its internal rhythm unchanged;
   the card still equalises to the row because align-items:stretch grows the outer
   .bet-card regardless of its inner distribution. */
.history-grid > .bet-card.tp-card,
.daily-grid > .bet-card.tp-card,
.dash-wrap .bet-grid > .bet-card.tp-card { justify-content: space-between; }
.history-grid > .bet-card.flipcard .tpc-front,
.daily-grid > .bet-card.flipcard .tpc-front,
.dash-wrap .bet-grid > .bet-card.flipcard .tpc-front { justify-content: space-between; }
.history-grid > .bet-card.tp-card .tp-foot,
.daily-grid > .bet-card.tp-card .tp-foot,
.dash-wrap .bet-grid > .bet-card.tp-card .tp-foot { margin-top: 11px; }
.history-grid > .bet-card, .daily-grid > .bet-card,
.dash-wrap .bet-grid > .bet-card { max-width: 480px; width: 100%; }
/* veil card match name to one line so a long name doesn't make that veil taller
   than its row-mates (owner 2026-08-05). */
.bet-card.locked .match { min-height: 2.2em; }  /* reserve 2 lines so the veil title area matches a regular card's, keeping the veiled card the same height in the grid */
/* THE STAKE IS BACK ON TODAY'S PICKS (owner 2026-08-27, superseding the
   2026-08-03 "stake is performance-only" rule for the main slate ONLY).
   Reasoning: Results and Daily already state the same figure, so the slate
   withholding it made the site read inconsistently — what is at stake BEFORE
   the match, what it became after. Not a new disclosure; the same number where
   the reader expects it. The value was always rendered (stakeCellText at
   app.js:7277 card / :7589 row) and only hidden here, so this is a CSS-only
   reveal — no new money path, one unit basis (activeUnit) for every figure.

   #tier-groups is therefore NO LONGER in this rule.

   IN PLAY JOINS IT (owner 2026-08-28: "in play also, you don't write these
   numbers — I would like to see it there as well"). A live pick is the SAME
   actionable pre-settlement pick the slate shows; it only moved into its own
   section on 2026-08-23, and the section inherited the hide by construction, not
   by a decision. Withholding the figure there made the stake appear, vanish at
   kickoff, and reappear as money on Performance after full time — the exact
   inconsistency the 08-27 reveal was meant to end. #in-play-host renders through
   the SAME card/row renderer, so this is the same CSS-only reveal: no new money
   path, one unit basis (activeUnit) for every figure.

   Measured cost at 375x812 (UAT, TRY, member): card view +0px per card (the
   stake rides inside the .tp-price flex row, whose height the 41px odds box
   already sets); table view +18px per row, matching the +17.8px the main slate
   paid on 08-27. Card height is unchanged, so the veil-coverage guarantee keeps
   its dependency.

   The other two hosts stay hidden — each is its own owner decision, not given:
     #next-up-host    tomorrow's picks, not actionable yet (and next-up renders
                      next-day FUNDED picks through the same renderer outside
                      #tier-groups, so it would leak a stake for a day nobody
                      can act on);
     #played-today-host  settled-free, money-free by owner decision 2026-08-20.
                      NOTE the resulting split, put to the owner 2026-08-28: a
                      pick that settles TODAY states its stake and profit on
                      Performance/Results while Played Today shows the bare
                      won/lost word. His call, not FE's.
   A VEILED stub never carries a stake on any of them: the server withholds it
   (a locked stub carries no stake_units/unit_mult/odds field at all — verified
   against the live public feed) and tpRowHTML hardcodes "—" in the locked row
   (app.js:7540) while lockedCardHTML emits no stake element at all. */
/* PLAYED TODAY NOW SHOWS THE MONEY (owner 2026-08-28) — reverses the 2026-08-20
   money-free rule for this host only. Reason: once Today's Picks and In Play
   state the stake, hiding it here made the figure VANISH when a game finished,
   on the same page, while Performance and Daily still stated it. The owner's
   call, asked and answered explicitly.
   #next-up-host stays hidden: it is the thin-slate FALLBACK carrying the NEXT
   day's picks, not today's allocation — a separate question, not yet asked. */
#next-up-host .tp-stake, #next-up-host .tpt-stake { display: none; }

/* ══ IN PLAY (owner 2026-08-23) — live games MOVED out of the main slate into
   their own section (see #in-play-host in picks.js). Renders live funded picks
   through the SAME card/row renderer (stake hidden above, like the sibling
   hosts), so its head reads live and its cards keep the shipped LIVE badge. */
#in-play-host { margin-top: 14px; }
#in-play-host .in-play-head { align-items: baseline; flex-wrap: wrap; }
/* the section head's LIVE badge — the brand LIVE green (var(--won)) + a pulsing
   dot, matching the per-card ko-live pill and the scoreboard hero .sb-live. */
.tier-badge.live {
  color: var(--won);
  border-color: color-mix(in srgb, var(--won) 40%, transparent);
  background: color-mix(in srgb, var(--won) 12%, transparent);
  display: inline-flex; align-items: center; gap: 6px;
}
.tier-badge.live::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: var(--won); animation: ko-live-pulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .tier-badge.live::before { animation: none; } }

/* ══ PLAYED TODAY (owner 2026-08-20) — the separate, secondary results-so-far
   section below the live slate. Reuses the pick card/row + result badge; the
   result-badge money is already suppressed in JS (settledFree), the stake cell
   is hidden above. Distinct + SECONDARY: dimmed, a muted head, and its own grid
   class (NOT .bet-grid, so the veil-coverage guard's `.bet-grid .bet-card` scan
   never counts these always-open cards against per-screen veil coverage). */
#played-today-host { margin-top: 14px; }
#played-today-host .played-today { opacity: .82; }
#played-today-host .played-head { align-items: baseline; flex-wrap: wrap; }
#played-today-host .played-sub { flex-basis: 100%; margin-top: 2px; }
/* same flex-wrap rhythm as #tier-groups .bet-grid, on a distinct class */
#played-today-host .played-grid {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch; gap: 8px; }
#played-today-host .played-grid > .bet-card {
  flex: 1 1 340px; max-width: 480px; min-width: 0; }
#played-today-host .played-grid > .bet-card.tp-card { justify-content: space-between; }
#played-today-host .played-grid > .bet-card.flipcard .tpc-front { justify-content: space-between; }
#played-today-host .played-grid > .bet-card.tp-card .tp-foot { margin-top: 11px; }
/* The settled MEASURED DETAIL (resultWording #471) is no longer an always-visible
   line (owner 2026-08-20): it rides the result badge as a hover title= + tap
   popover (see .pt-pop / the tap-to-explain handler in app.js). Nothing to style
   here — the .tp-res / .tpt-res badge already carries it. */
/* ══ POSTPONED NOTICES (owner 2026-08-21) — early (pre-kickoff) voids surfaced on
   the UPCOMING surface as a muted, NON-ACTIONABLE notice list (match postponed/
   cancelled, stake returned). Reads as a quiet, settled-void state, never a
   bettable card: no odds/stake/CTA, dimmed like Played-today, and the void badge
   + flag borrow the muted result colour (var(--mu), same as .tp-res.void). Its
   own host + list class (not .bet-grid) so the veil-coverage scan never counts
   these always-open rows. */
#postponed-host, #dash-postponed-host { margin-top: 14px; }
.postponed-group { opacity: .82; }
.postponed-head { align-items: baseline; flex-wrap: wrap; }
/* muted void variant of the section badge — same shape, result-void colour */
.tier-badge.void { color: var(--mu); border-color: var(--bdr); }
.pp-list { list-style: none; margin: 8px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px; }
.pp-item { display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 6px 12px;
  padding: 9px 12px; border: 1px solid var(--bdr); border-radius: 8px;
  background: color-mix(in srgb, var(--mu) 6%, transparent); }
.pp-item-main { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; min-width: 0; }
.pp-item-teams { font-weight: 600; color: var(--tx); }
.pp-item-league { font: 500 11px var(--font-num); text-transform: uppercase;
  letter-spacing: .04em; color: var(--mu); }
.pp-item-market { font-size: 13px; color: var(--mu); }
.pp-item-flag .tp-res.void { font: 600 11.5px var(--font-num); color: var(--mu);
  white-space: normal; }
.tpc-back .ai-tab.on { background: var(--ac); color: var(--ink); }
/* A scrolling section used to end mid-word with nothing to say so (owner's
   screenshot: "...keeping three" and then a hard edge). The card no longer
   grows to swallow long text, so the OVERFLOW has to be legible: a short fade
   at the foot reads as "there is more", where a hard cut reads as broken. */
.tpc-back .ai-sec.on { -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 22px), transparent);
          mask-image: linear-gradient(180deg, #000 calc(100% - 22px), transparent); }
/* no fade once the reader has hit the bottom — nothing is hidden there */
.tpc-back .ai-sec.on.at-end { -webkit-mask-image: none; mask-image: none; }
.tpc-back .ai-sec { display: none; }
/* the overflow answer: the SECTION scrolls inside the fixed card — head and
   tabs stay pinned, a thin scrollbar says "there's more" */
.tpc-back .ai-sec.on { display: block; flex: 1 1 auto; min-height: 0;
  overflow-y: auto; scrollbar-width: thin; padding-right: 2px; }
.tpc-back .ai-sec .ai-sec-k { display: none; }   /* the active tab already names it */
.tpc-back .ai-sec p { font-size: 12.5px; line-height: 1.65; margin: 0; }
.tpc-back .ai-review-stats { flex: none; margin-top: 9px; }
/* PHONES READ BIGGER (owner 2026-07-30 screenshot: "maybe we should do cards
   bigger on mobile for readability?"). On mobile the card already spans the
   screen, so "bigger" means the TYPE: 12.5px mono analysis text is squint
   territory on a phone held at arm's length. Only the card BACK changes — the
   pick face's hierarchy (big pick, boxed odds) was sized for mobile already. */
@media (max-width: 640px) {
  .tpc-back .ai-sec p { font-size: 14.5px; line-height: 1.7; }
  .tpc-back .ai-tab { font-size: 11px; padding: 8px 3px; }
  .ai-review-stats span { font-size: 12px; }
}
/* FREE READER on the card back (owner 2026-07-30: "improve it"): the wide
   two-column layout is for table rows — on the card the oracle takes CENTER
   stage as a circular portrait with the lock riding its edge, copy centered
   under it, and the CTA as a full-width filled button. Scoped to .tpc-back so
   the table-row version is untouched. */
.tpc-back .ai-review-inner.ai-locked { display: flex; flex-direction: column;
  align-items: center; text-align: center; overflow-y: auto; scrollbar-width: thin; }
.tpc-back .ai-locked .ail-art { flex: none; width: 92px; height: 92px;
  min-height: 0; margin: 22px 0 4px; align-self: center; }
.tpc-back .ai-locked .ail-art img { width: 92px; height: 92px; max-height: none;
  object-fit: cover; object-position: top; border-radius: 50%;
  border: 2px solid var(--ink); display: block; }
.tpc-back .ai-locked .ail-lockdot { right: -4px; bottom: 0; }
.tpc-back .ai-locked .ail-copy { display: flex; flex-direction: column;
  align-items: center; width: 100%; min-height: 0; }
.tpc-back .ai-locked .ail-copy .ai-review-head { padding-right: 0; }
.tpc-back .ai-locked .ail-secs { grid-template-columns: 1fr; width: 76%; }
.tpc-back .ai-locked .ail-blur, .tpc-back .ai-locked .ail-blur.short { margin-left: auto; margin-right: auto; }
.tpc-back .ai-locked .ai-cta { display: block; width: 100%; text-align: center;
  margin-top: 12px; background: var(--ac); color: var(--ink); border: none;
  padding: 10px; border-radius: 9px; }
.tpc-back .ai-locked .ai-cta:hover { background: var(--ink); color: var(--ink-tx); }

/* ── SLIDE TO UNLOCK (owner 2026-07-29; veiled table rows, UAT flag) ──────
   The classic iPhone affordance: shimmering label, draggable thumb. A plain
   tap navigates too — the slide is the fun path, never the only path. */
.slide-unlock { position: relative; display: inline-flex; align-items: center;
  width: 168px; height: 32px; border-radius: 16px; background: var(--ink);
  overflow: hidden; cursor: pointer; vertical-align: middle; }
.slide-unlock .su-text { flex: 1; text-align: center; padding: 0 10px 0 34px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font: 600 10.5px var(--font-num); letter-spacing: .09em; text-transform: uppercase;
  background: linear-gradient(90deg, rgba(253,251,246,.45), #fff 50%, rgba(253,251,246,.45));
  background-size: 200% 100%; -webkit-background-clip: text; background-clip: text;
  color: transparent; animation: su-shimmer 2.4s linear infinite; }
@keyframes su-shimmer { from { background-position: 160% 0; } to { background-position: -60% 0; } }
.slide-unlock .su-thumb { position: absolute; left: 3px; top: 3px; width: 26px; height: 26px;
  border-radius: 50%; background: var(--ac); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 13px; touch-action: none; user-select: none;
  transition: transform .28s cubic-bezier(.3,.1,.3,1); box-shadow: 0 1px 4px rgba(0,0,0,.25); }
.slide-unlock .su-thumb.dragging { transition: none; }
.slide-unlock.su-done { opacity: .65; }
@media (prefers-reduced-motion: reduce) {
  .slide-unlock .su-text { animation: none; background: none; color: var(--ink-tx); }
}
/* ── FREE READER'S PANEL ──────────────────────────────────────────────────
   Owner 2026-07-29: "maybe we can use nostradamus photo etc and giving lock
   icon members. It should be better not that simple."

   So: the oracle's portrait on the left (the same face as the members teaser
   and the maintenance page) with a lock disc on his shoulder, and on the right
   the three REAL section headings with blurred bars under each — the shape of
   the analysis, visible, with the words left out. The bars are pure texture:
   no invented sentences, and no member text is on the wire to leak. */
.ai-review-inner.ai-locked { border-left-color: var(--ink); display: grid;
  grid-template-columns: 92px 1fr; gap: 14px; align-items: center;
  padding: 13px 15px; overflow: hidden; position: relative;
  background:
    radial-gradient(120% 140% at 0% 50%, color-mix(in srgb, var(--ac) 13%, transparent), transparent 62%),
    color-mix(in srgb, var(--ac) 5%, var(--sf)); }
.ail-art { position: relative; align-self: stretch; display: flex;
  align-items: flex-end; justify-content: center; min-height: 104px; }
.ail-art img { width: 100%; height: 100%; max-height: 128px; object-fit: cover;
  object-position: 50% 12%; border-radius: 8px; display: block;
  /* the portrait sits UNDER the copy, not beside it as an equal — it is
     atmosphere, so it stays desaturated and slightly veiled */
  filter: grayscale(.35) contrast(1.04); opacity: .92;
  -webkit-mask-image: linear-gradient(180deg, #000 62%, transparent);
          mask-image: linear-gradient(180deg, #000 62%, transparent); }
.ail-lockdot { position: absolute; right: -6px; bottom: 10px;
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: var(--ac); color: var(--ink);
  box-shadow: 0 0 0 3px var(--sf), 0 2px 6px rgba(0,0,0,.16); }
.ail-copy { min-width: 0; }
.ai-review-inner.ai-locked .ail-sub { color: var(--mu); font-size: 12px; margin: 0 0 10px; }
/* auto-fit, NOT a fixed 3-up: this panel is as wide as the table row on a
   desktop but only ~320px inside a grid card, and a viewport media query
   cannot tell those apart — it stays three columns in the card and the
   headings crush to ~73px. Letting the tracks wrap on their own minimum
   handles table view, grid view and phones with one rule. */
.ail-secs { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px 12px; margin-bottom: 4px; }
.ail-sec { min-width: 0; }
.ail-blur { display: block; height: 7px; border-radius: 4px; margin-top: 4px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--tx) 20%, transparent),
    color-mix(in srgb, var(--tx) 9%, transparent));
  filter: blur(1.4px); }
.ail-blur.short { width: 62%; }
@media (max-width: 640px) {
  .ai-review-inner.ai-locked { grid-template-columns: 62px 1fr; gap: 11px; padding: 11px; }
  .ail-art { min-height: 78px; }
  .ail-art img { max-height: 92px; }
  .ail-lockdot { width: 24px; height: 24px; right: -4px; }
  .ail-lockdot svg { width: 12px; height: 12px; }
  .ail-secs { gap: 7px 10px; }
  .ail-blur.short { width: 44%; }
}
.ai-cta { display: inline-block; margin-top: 9px; font: 700 11px var(--font-num);
  text-transform: uppercase; letter-spacing: .05em; text-decoration: none;
  background: var(--ac); color: var(--ink); border-radius: 8px; padding: 6px 14px; }
.ai-cta:hover { background: var(--ink); color: var(--ink-tx); }
/* table view: the badge is compact in the pick cell, win rate on its own line */
.tpt-tier.risk-band { font-size: 9.5px; padding: 2px 6px; }
.tpt-mrec { display: block; margin-top: 3px; font-family: var(--font-num);
  font-variant-numeric: tabular-nums; font-size: 10.5px;
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
.tpt-mrec b { color: var(--tx); font-weight: 700; }
/* the one-line record under the price: edge over the book, ROI, sample */
/* ══ Date-range control (createDateRange) — one calendar, mselect grammar ══ */
.dselect .mselect-panel { min-width: 296px; }
.dr-quick { display: flex; gap: 6px; padding: 12px 12px 4px; }
.dr-quick button { flex: 1; font: 600 11px var(--font-num); text-transform: uppercase;
  letter-spacing: .04em; padding: 6px 4px; border: 1px solid var(--bdr);
  border-radius: 8px; background: transparent; color: var(--tx); cursor: pointer;
  white-space: nowrap; }
/* active = the site's segmented-control active (amber + ink), not ink-on-ink */
.dr-quick button.on { background: var(--ac); color: var(--ink); border-color: var(--ac); }
.dr-nav { display: flex; align-items: center; justify-content: space-between;
  padding: 8px 12px 0; }
.dr-nav button { border: none; background: none; cursor: pointer; color: var(--tx);
  font: 700 17px/1 var(--font-num); padding: 2px 10px; }
.dr-nav button:disabled { color: color-mix(in srgb, var(--tx) 22%, transparent); cursor: default; }
.dr-month { font: 700 13px var(--font-num); }
.dr-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
  padding: 6px 12px 8px; }
.dr-dow { font: 600 9.5px var(--font-num); color: var(--mu); text-align: center;
  padding: 2px 0; text-transform: uppercase; }
.dr-day { font: 600 12.5px var(--font-num); text-align: center; padding: 7px 0;
  border: none; background: none; border-radius: 8px; cursor: pointer; color: var(--tx); }
.dr-day:hover:not(:disabled) { background: color-mix(in srgb, var(--tx) 8%, transparent); }
.dr-day:disabled { color: color-mix(in srgb, var(--tx) 25%, transparent); cursor: default; }
.dr-day.in { background: color-mix(in srgb, var(--ac) 24%, transparent); border-radius: 0; }
.dr-day.edge { background: var(--ac); color: var(--ink); font-weight: 700; }
.dr-foot { display: flex; align-items: center; border-top: 1px solid var(--bdr);
  padding: 8px 12px; }
.dr-foot[hidden] { display: none; }
.dr-note { font: 600 11.5px var(--font-num); color: var(--mu); }

.tp-mrec { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 7px;
  margin: 8px 0 11px; font-family: var(--font-num);
  font-variant-numeric: tabular-nums; font-size: 11.5px; color: var(--tx); }
.tp-mrec em { font-style: normal; text-transform: uppercase; letter-spacing: .05em;
  font-size: 9.5px; color: color-mix(in srgb, var(--tx) 42%, transparent); }
.tp-mrec b { font-weight: 700; }
.tp-mrec .mrec-dot { color: color-mix(in srgb, var(--tx) 28%, transparent); }
/* ── UNFUNDED PICK STATES ITS REASON (owner 2026-08-28) ─────────────────────
   "Analysis only" on a pick we show but do not stake. MUTED and hairline —
   this is a statement about OUR discipline (we stake only what we can verify
   independently), never a defect on the pick, so it must not borrow the
   risk-band, result or warning vocabulary. The dashed edge advertises the
   hover/tap explanation, which is the SAME .pt-pop popover the result badge
   uses (see _RTIP_SEL in app.js) rather than a second tooltip language.
   It rides INSIDE the existing .tpt-mrec / .tp-mrec meta line, so it adds no
   row height — the 375px measurements are recorded at unfundedChipHTML.
   `cursor: help` + tabindex make the explanation reachable by pointer,
   keyboard and touch alike. */
.gp-nostake { display: inline-block; font-family: var(--font-num);
  font-size: 10.5px; font-weight: 500; line-height: 1.5; color: var(--mu);
  border: 1px dashed var(--bdr); border-radius: 999px; padding: 0 6px;
  margin-left: 6px; cursor: help; white-space: nowrap; vertical-align: baseline; }
.gp-nostake:focus-visible { outline: 2px solid var(--tx); outline-offset: 1px; }
.tp-res.void { color: var(--mu); }
/* item 6: kickoff never glows — countdown elements render muted everywhere
   (the amber .fig-value.countdown rule died with the classic card): */
.tp-ko.countdown, [data-countdown] {
  color: color-mix(in srgb, var(--tx) 40%, transparent); }

/* ── view toolbar + the pilot TABLE view (table is the DEFAULT) ─────────────
   Same hierarchy in row form: league group headers in the label style (the
   league never repeats per row), the pick code is the row's ONE bold element,
   odds in the boxed mono price cell sharing a right edge down the column,
   time/stake muted mono, hairline rules, no zebra. */
/* OWNER (2026-07-24, position audit): .pick-controls (the wrapper that
   wrapped filters+toolbar together, and did the mobile order:-1 reorder) is
   RETIRED — dashboard.html places the toolbar as its own real DOM block
   right under the heading, so no wrapper-level margin or reorder is needed.
   picks.html reverted to trailing the stat tiles on 2026-07-25 (named
   exception, see CLAUDE.md) but ALSO keeps its own real #pick-toolbar-top
   DOM node rather than the old order:-1 wrapper trick — the retirement
   itself still stands. .filters carries its own spacing directly,
   standalone, wherever it appears (picks.html, results.html, dashboard.html,
   daily.html all use the plain `.filters` class the same way now). */
.filters { margin-bottom: 14px; }
/* the Daily page shares the same filters shape (gated filters, no sort —
   sort moved into #daily-toolbar-top/.pick-toolbar alongside the view
   toggle, owner 2026-07-23, matching picks.js's #pick-toolbar-top) */
.daily-controls { margin: 0 0 14px; }
.pick-toolbar { display: flex; justify-content: flex-end;
  align-items: center; gap: 10px; }
.pk-sort-wrap select { max-width: 240px; }
/* IN PLAY stat tile as a clickable, member-gated filter (owner 2026-08-03):
   click to narrow the slate to the live picks. Pressable for members; locked
   with the members badge for everyone else (tap → membership CTA). */
.inplay-tile { cursor: pointer; position: relative;
  transition: box-shadow .12s ease; }
.inplay-tile:hover { box-shadow: 0 0 0 1.5px #b8860b inset; }
.inplay-tile.inplay-on { box-shadow: 0 0 0 2px #b8860b inset; }
/* Badge pinned to the tile's TOP-right, clear of the "picks running now" sub
   (owner 2026-08-04: the MEMBERS badge overlapped the sub on mobile). On the
   mobile stat-strip the tile is a subgrid and the nth-child(n+4) rule was
   pinning this appended badge to grid-row 3 (the bottom), so its top:8px landed
   inside the last row over the sub. grid-row:1/-1 spans the whole tile so top:8px
   is measured from the tile top; transform:none cancels the base center shift. */
/* The MEMBERS badge REPLACES the "picks running now" sub in its slot (owner
   2026-08-04, repeated overlap). Root cause: the tile is a 3-row subgrid; the
   appended badge and the sub both land in row 3, so any position for the badge
   overlapped the sub. When the tile is LOCKED (non-member) we hide the sub and
   drop the badge into that same row-3 slot — they never coexist, so they can't
   overlap. A member sees the sub and no badge (clearSliceLock removes it). */
.inplay-tile.slice-locked .sub { visibility: hidden; }  /* keep the box so the value stays baseline-aligned with the other tiles */
.inplay-tile .slice-lock-badge {
  position: static !important; transform: none !important;
  grid-column: 1 / -1 !important; grid-row: 3 !important;
  justify-self: center; align-self: center; margin: 0 auto; }
/* margin-BELOW, not above, on every one of these — always correct regardless
   of what comes next (filters, content) because margin-bottom pushes space
   AFTER the element rather than assuming a fixed neighbour above it. On
   daily.html the toolbar leads (heading -> toolbar -> filters/sort ->
   content), matching results.html's .history-head, which supplies the same
   gap via its own margin-bottom. picks.html is the named exception
   (2026-07-25): #pick-toolbar-top now trails #pick-stats instead
   (heading -> stats -> toolbar -> filters -> content) — the SAME
   margin-bottom rule still supplies the right gap before .filters either
   way, and .stat-strip's own margin-bottom (styles.css) supplies the gap
   above the toolbar on picks.html without any change needed here.
   .myday-toolbar is NOT in this list (2026-07-25, orphaned-toggle fix): it
   no longer sits on its own row — it now shares .myday-head with the MY DAY
   heading (see dashboard.html + the rule below), and .myday-head itself
   supplies the trailing gap via the shared .history-head margin-bottom.
   Giving myday-toolbar its OWN margin-bottom too would double the gap
   inside that row and desync its cross-axis position from the heading. */
#pick-toolbar-top, #daily-toolbar-top { margin-bottom: 16px; }
/* OWNER (2026-07-25, orphaned-toggle fix — bug 1): MY DAY's heading and its
   toolbar (sort + view toggle) now render in ONE .history-head row, the
   exact pattern results.html's History section already used successfully —
   title text fills the left side, so the row is never "just a toggle"
   floating with dead space beside it, even inside the bordered .acct-card.
   flex-start (not .history-head's default center) because MY DAY's left
   side is a two-line heading+subtitle stack, not History's single line —
   the toolbar should align to the TOP of that stack, not its vertical
   middle. Wraps under the heading at narrow widths via the inherited
   flex-wrap:wrap, same as results.html. */
.myday-head { align-items: flex-start; }
/* OWNER (2026-07-23, card removal fallout): the heading had ZERO margin of
   its own -- it always relied on .acct-card's padding for the gap before
   the bankroll strip below it. Gone card, gone gap: heading and strip were
   touching directly. Explicit margin restores it independent of any card. */
.myday-heading { min-width: 0; margin-bottom: 20px; }
/* The dropdown shows in BOTH views (owner 2026-08-05): the desktop table also
   sorts by its clickable column headers, but users expect the dropdown to be
   present too, so we keep it — syncToolbar keeps its value in lock-step with a
   header click, so the two controls never disagree. (Was hidden in desktop
   table view before; owner asked for it back.) */
/* sortable header row — label style, arrow included; never loud */
.tpt-head th { text-align: left; padding: 4px 12px 8px 0;
  border-bottom: 1px solid var(--bdr); white-space: nowrap; }
.tpt-head th.r { text-align: right; }
.tpt-head th:last-child { padding-right: 0; }
.tpt-head th.sortable { cursor: pointer; user-select: none; }
.tpt-head th.sortable:hover { color: var(--tx); }
.tpt-arrow { font-size: 8px; margin-left: 3px; }
@media (max-width: 640px) {
  .tp-table thead { display: none; }
}
/* flat (odds-sorted) rows carry the league INSIDE the teams cell, muted —
   same treatment as the results table's league line */
.tpt-league-inline { display: block; font: 500 10px var(--font-num);
  color: #8a8579; text-transform: uppercase; letter-spacing: .06em;
  margin-bottom: 2px; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
/* DEFECT 3 (owner, live screenshot): the toggle markup carries BOTH
   .segmented (generic, styles.css: padding:3px, gap:2px, per-button
   border-radius:7px, its OWN .active fill) and .view-seg (this component).
   With scoreboard.css loaded AFTER styles.css, .view-seg won the FILL
   COLOR (amber) but styles.css's padding:3px still applied uncontested
   (no property collision to win), inserting a 3px inset between the
   button and the container's rounded 10px corner — since padding(3) <
   radius(10), the button's own SQUARE corner sat inside the rounded arc,
   showing a cream wedge of the page background at each corner behind the
   fill: the "not filling properly" the owner saw. Zeroed here, self-
   contained, so .view-seg never again depends on what a same-named
   generic class elsewhere happens to declare — the buttons sit flush
   with the container edges and its own overflow:hidden clips their
   outer corners cleanly to the 10px radius, no gap possible. */
/* OWNER (2026-07-24, 4th toggle report): results.html's own #layout-toggle
   (icon + localized "GRID"/"TABLE" text) is now the desktop reference for
   EVERY view toggle — picks.html and dashboard's icon-only .view-seg were
   the divergent ones. Fixed at the component level: viewToggleHTML() now
   renders an icon AND a .view-seg-lbl text span (localized, short form,
   the exact words already live on results.html) on every button; CSS shows
   both at desktop, drops to icon-only ONLY below the site's one shared
   mobile breakpoint (640px, the same threshold every other component on
   this site collapses at — never a page-specific number). #layout-toggle
   itself is migrated onto this SAME component (results.js) so there is
   exactly one markup source for this control, not a lookalike. */
.view-seg { display: inline-flex; padding: 0; gap: 0; }
.view-seg button { display: inline-flex; align-items: center;
  justify-content: center; gap: 5px; height: 30px; padding: 0 12px;
  border: none; border-radius: 0; cursor: pointer; white-space: nowrap; }
.view-seg button.active { background: var(--ac); color: var(--ink); }
.view-seg-lbl { font: 700 11px var(--font-num); text-transform: uppercase;
  letter-spacing: .04em; }
@media (max-width: 640px) {
  .view-seg button { padding: 0; width: 44px; }
  .view-seg-lbl { display: none; }
}
.tp-tablewrap { background: var(--sf); border: 1px solid var(--bdr);
  border-radius: 14px; padding: 2px 16px 10px; }
.tp-table { width: 100%; border-collapse: collapse; }
.tpt-league th { text-align: left; padding: 16px 0 6px; }
/* ITEM 16.2: GROUP headers must out-rank COLUMN headers at a glance — the
   group speaks ink at 700/11px over a spanning rule; the column header row
   keeps the quiet 9.5px label voice anchored by its hairline. Computed
   styles differ in size AND weight AND color (asserted). */
.tpt-league th, .hth-league th { font-size: 11px; font-weight: 700;
  letter-spacing: .1em; color: var(--tx);
  border-bottom: 1px solid var(--bdr); }
.tpt-row > td { border-top: 1px solid var(--bdr);
  /* 11px, not 12: the boxed odds cell is ~27px tall, so 12px landed the row
     at 53-54px — just past the 44-52px band the table spec sets. Measured. */
  padding: 11px 12px 11px 0; vertical-align: middle; }
.tpt-row > td:last-child { padding-right: 0; }
.tpt-row:hover > td {
  background: color-mix(in srgb, var(--tx) 3%, transparent); }
/* LIST-VIEW ZEBRA (owner-approved 2026-08-14): a faint ink wash on alternate PICK
   rows. KEYED BY PICK, not raw <tr>: the render tags every other pick's row with
   .tpt-stripe and copies that class onto the pick's own .tpt-airev sub-row, so an
   open AI panel shares its pick's shade instead of counting as its own stripe; the
   .tpt-league group headers carry no stripe and never consume a parity slot, so the
   alternation runs unbroken across groups. Member rows keep their amber left accent
   — it is an inset box-shadow / an absolutely-positioned pseudo, both painted OVER
   this background.
   PAINT THE ROW, NOT THE CELLS (owner 2026-08-14, mobile fix): on DESKTOP the <tr>
   background paints beneath the transparent <td> cells and fills the row. On MOBILE
   (<=640px) the row is display:grid and .tpt-pick is `display:contents` (~1970) — a
   contents element generates NO box, so a per-cell background left the pick column
   (grid rows 2-3: pick text + record line) unshaded. A row-level background fills
   the whole grid area in both layouts. Hover (`.tpt-row:hover > td`, a td bg) still
   layers cleanly over this on desktop and is disabled on mobile. */
.tpt-row.tpt-stripe,
.tpt-airev.tpt-stripe { background: var(--stripe); }
.tpt-time { font-family: var(--font-num); font-size: 11px;
  width: 48px; white-space: nowrap;
  color: color-mix(in srgb, var(--tx) 40%, transparent); }
.tpt-teams { max-width: 0; width: 38%; }
/* the ellipsis clips the NAMES span only — the final-score chip stays
   visible beside it (a chip inside the nowrap clip vanished on long RU/DE
   names and its rect crossed the odds box; tier 3b). Flex, with the names
   in their own min-width:0 child, keeps inter-word spacing intact. */
.tpt-teams .tt { display: flex; align-items: center;
  overflow: hidden; font-size: 14px;
  font-weight: 500; color: var(--tx); letter-spacing: 0; }
.tpt-teams .tt-names { min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.tpt-teams .ft-score { flex: 0 0 auto; }
.tpt-pick { max-width: 0; width: 30%; }
/* TIER-INLINE FIX (owner, 2026-07-24): the tier tag must ride the SAME line
   as the pick code ("tier joins the pick cell as quiet label text alongside
   the stake" — the original item-16-ext spec). .tpt-code was `display:block`,
   so its following sibling .tpt-tier (an inline span) wrapped onto its own
   orphaned line below — full width, forced by the block box, on EVERY
   history row. .tpt-pickwrap is a flex row (the same pattern daily.js's
   .dr-pick already used correctly): the code shrinks+ellipsizes via
   min-width:0, the tier tag never shrinks and always sits inline after it. */
.tpt-pickwrap { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
/* #3 (owner 2026-08-09): keep the AI pill + its badges as ONE non-breaking unit so the
   flex-wrap pickwrap can never split them onto separate lines (they landed 21px apart).
   Same items, same look — just glued on one line. */
.tpt-aigrp { display: inline-flex; align-items: center; gap: 4px; flex-wrap: nowrap; white-space: nowrap; }
.tpt-code { font-size: 14px; font-weight: 700;
  letter-spacing: -.01em; display: block; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }
.tpt-pickwrap .tpt-tier { flex: 0 0 auto; }
.tpt-odds { text-align: right; white-space: nowrap; }
.tpt-oddsbox { display: inline-block; font-family: var(--font-num);
  font-variant-numeric: tabular-nums; font-size: 13.5px; font-weight: 700;
  border: 1.5px solid var(--tx); border-radius: 6px; padding: 3px 9px; }
.tpt-oddsbox.veiled { color: var(--mu); border-color: var(--bdr);
  font-weight: 500; }
.tpt-stake { font-family: var(--font-num); font-size: 12px;
  text-align: right; white-space: nowrap; color: var(--mu); }
.tpt-status { text-align: right; white-space: nowrap; }
.tpt-status .tpt-ko { font-family: var(--font-num);
  font-size: 11px; color: color-mix(in srgb, var(--tx) 40%, transparent); }
/* the final-score chip rides the teams cell on graded rows; globally it is
   700, which handed settled rows a SECOND bold (caught by the measured
   one-bold-per-row check, not by eye) — inside the table it is information,
   not emphasis */
.tp-table .ft-score { font-weight: 500; }
/* settled rows carry ✓/✗ + W/L — never color alone; weight stays BELOW bold
   so the pick code remains the row's one bold element */
.tpt-res { font-family: var(--font-num); font-size: 12px;
  font-weight: 600; }
.tpt-res.won { color: var(--won); }
.tpt-res.lost { color: var(--lost); }
.tpt-res.void { color: var(--mu); }
/* THE RETURNED AMOUNT on a settled void (owner 2026-08-27). It is a MONEY
   statement, so it takes the numeric face and the same 600 weight the +/-
   amounts carry — but it stays MUTED like the void chip it sits beside: a void
   is neither a win nor a loss and must never borrow the win/lose colour. nowrap
   so "↩ $5" can never break across two lines inside the result cell (the 375px
   budget the void chip was measured on). */
.res-returned { font-family: var(--font-num); font-size: 12px; font-weight: 600;
  color: var(--mu); white-space: nowrap; }
.tp-res.void + .res-returned { font-size: 13px; }   /* card foot matches .tp-res */
/* ── LIVE BADGE (task 2026-08-22) ────────────────────────────────────────────
   A genuinely-live pick (koIsLive: in_play AND < 150 min) reads as a distinct
   VISUAL STATE, not just the word "Live" in the muted time slot. Same tinted-chip
   language as the result badge (.tp-res[data-rtip]) — hairline border + faint
   fill in its own colour, 5px radius, tight padding — tinted the brand LIVE green
   (var(--won), matching .sb-live / .live-label on the scoreboard hero). A small
   pulsing dot leads the word (the universal "live now" cue). Applied ONLY while
   the foot reads LIVE; the 30s ticker toggles .ko-live so a game crossing the
   window drops the pill with the word. Compound/descendant selectors out-specify
   the muted .tp-ko / [data-countdown] / .tpt-status .tpt-ko colour rules. Shown on
   OPEN cards, TABLE rows, and veiled/locked stubs alike (a live game behind the
   veil is a scarcity signal — kept on purpose). */
.tp-ko.ko-live,
.locked-count.ko-live,
.tpt-status .tpt-ko.ko-live {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--won);
  font-family: var(--font-num); font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--won) 40%, transparent);
  background: color-mix(in srgb, var(--won) 12%, transparent);
  border-radius: 5px; padding: 2px 7px;
}
.tp-ko.ko-live::before,
.locked-count.ko-live::before,
.tpt-status .tpt-ko.ko-live::before {
  content: ""; flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%;
  background: var(--won); animation: ko-live-pulse 1.6s ease-in-out infinite;
}
@keyframes ko-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .tp-ko.ko-live::before,
  .locked-count.ko-live::before,
  .tpt-status .tpt-ko.ko-live::before { animation: none; }
}
/* veiled premium row: the veil sits on the pick cell only; the row stays
   readable. Tag (--mu) and unlock (--risk) are AA-MEASURED on --sf, not
   eyeballed — re-measure if any of the three tokens move. */
.tpt-veil { display: inline-flex; flex-wrap: wrap;
  align-items: center; gap: 4px 7px; }
/* THE ORACLE GUARDS THE PICK (owner 2026-07-31: "you can add our Nostradamus
   designs here — I don't want a workaround, I want a real solution so people
   can't reach it").

   The real solution was never visual: the server does not send `pick`, `odds`,
   `market` or `selection` to a non-member — gate_picks() strips them and
   LOCKED_FIELDS is the whitelist, so there is nothing in the DOM, in a JS
   variable, or in any API response to uncover. A blur is decoration and always
   was; anyone can delete a CSS filter.

   So the slot no longer pretends to hide text. It shows the oracle, outcome-
   matched like everywhere else — honest (nothing fabricated to "unblur"), on
   brand, and it turns a dead grey smear into the thing that sells the
   membership. */
.veil-oracle { display: inline-flex; flex: 0 0 auto; }
/* BETTER DESIGN (owner 2026-07-31): the bare circle read as unfinished. The
   veil now speaks the SAME idiom as the approved free-tier gate — portrait
   with the amber lockdot riding its edge — so every locked surface on the
   site is one recognizable object, not three ad-hoc ones. .veil-orb is the
   relative anchor the dot pins to; the dot reuses .ail-lockdot wholesale and
   is only rescaled to the portrait it decorates. */
.veil-orb { position: relative; display: inline-block; }
.veil-oracle img { width: 40px; height: 40px; border-radius: 50%;
  object-fit: cover; object-position: top; border: 1.5px solid var(--ink);
  background: var(--bg); display: block; }
.veil-oracle .ail-lockdot { right: -5px; bottom: -3px; width: 18px; height: 18px; }
.veil-oracle .ail-lockdot svg { width: 10px; height: 10px; }
.veil-oracle.card img { width: 72px; height: 72px; }
.veil-oracle.card .ail-lockdot { width: 24px; height: 24px; right: -4px; bottom: 0; }
.veil-oracle.card .ail-lockdot svg { width: 12px; height: 12px; }
.veil-oracle.card { justify-content: center; padding: 6px 0 2px; }
.tpt-veil .locked-blur { filter: blur(6px); opacity: .5;
  font-weight: 700; font-size: 13px; }
.tpt-veil svg { color: var(--risk); flex: 0 0 auto; }
/* the category tag is a LABEL (weight 500, labels never compete) — the
   veiled row's one bold element is the unlock CTA standing in the pick slot */
.tpt-cat { font-family: var(--font-num); font-size: 10.5px;
  font-weight: 500; letter-spacing: .07em; text-transform: uppercase;
  color: var(--mu); }
/* --risk on --sf measures 4.71:1 but ~4.4:1 over the row-hover tint; the
   10% ink mix takes both states past 4.5:1 (computed, and re-measured live).
   ITEM 4 (owner: "unlocking membership maybe more attractive should be" —
   stronger pull, no dark patterns, amber stays the one interactive color):
   was plain underlined text, easy to miss beside boxed odds and mono data.
   Now a small filled chip (same --cream/amber family as every other
   interactive affordance on the page — .lock-badge, .insight-line, the
   filter-row unlock pill), so it reads as a CONTROL, not a caption. Contrast
   re-computed for the NEW backgrounds (not eyeballed): text on --cream
   4.75:1, on the hover fill 4.58:1 — both clear the 4.5:1 floor. */
.tpt-unlock { display: inline-flex; align-items: center; gap: 3px;
  font-size: 12px; font-weight: 700;
  color: color-mix(in srgb, var(--risk) 90%, var(--tx));
  background: var(--cream); border-radius: 6px; padding: 3px 9px;
  text-decoration: none; transition: background-color .15s ease; }
.tpt-unlock:hover { text-decoration: none; background: #ffe9b8; }
/* WIDE unlock: with the "extra member pick" label gone (owner 2026-08-04), the
   Unlock button grows to fill the veil row beside the oracle, so it reads as one
   confident CTA instead of a small link next to empty space. */
.tpt-unlock.wide { flex: 1 1 auto; justify-content: center; padding: 6px 12px; }
.slide-unlock.wide { flex: 1 1 auto; min-width: 0; }
/* MOBILE ≤640: two-line collapse — line 1 teams + odds box right; line 2
   pick code (bold) + time + stake muted, status right. The page never
   scrolls sideways: teams truncate, the pick code may wrap. */
@media (max-width: 640px) {
  .tp-tablewrap { padding: 0 12px 8px; }
  .tp-table, .tp-table tbody { display: block;
    width: 100%; }
  .tpt-league { display: block; }
  .tpt-league th { display: block; padding: 13px 0 5px 10px; }   /* match the row left gutter */
  .tpt-row { display: grid; align-items: center;
    /* col 1 (pick text) is the flexible/wrapping track; the meta+result
       columns stay max-content and pinned right. Previously col 1 was
       max-content, so a long market (e.g. "Home Asian Handicap -1.5: No")
       forced the track wide and shoved the result (+$52) off the right edge. */
    grid-template-columns: minmax(0, 1fr) max-content max-content max-content;
    column-gap: 10px; row-gap: 3px;
    /* LEFT GUTTER (owner 2026-08-09): reserve 10px on EVERY row so the amber
       members left-edge (.tpt-member inset box-shadow, below) sits in the gutter
       instead of over "Consensus (N)"/the pick text. On all rows so member and
       non-member stay left-aligned. */
    border-top: 1px solid var(--bdr); padding: 9px 0 9px 10px; }
  .tpt-row > td { display: block; border: none; padding: 0;
    width: auto; max-width: none; }
  .tpt-row:hover > td { background: transparent; }
  .tpt-teams { grid-row: 1; grid-column: 1 / 4; min-width: 0; }
  .tpt-odds { grid-row: 1; grid-column: 4; justify-self: end; }
  .tpt-pick { grid-row: 2; grid-column: 1; min-width: 0; }
  .tpt-pick, .tpt-pickwrap, .tpt-code,
  .tpt-veil { white-space: normal; overflow-wrap: anywhere; }
  /* the pick code may wrap to a second line on a long/narrow phone; the
     tier tag wraps WITH it (flex-wrap) rather than forcing its own row */
  .tpt-pickwrap { flex-wrap: wrap; row-gap: 1px; }
  .tpt-time { grid-row: 2; grid-column: 2; width: auto; }
  .tpt-stake { grid-row: 2; grid-column: 3; text-align: left; }
  .tpt-status { grid-row: 2; grid-column: 4; justify-self: end; }
  /* RETURNED AMOUNT TAKES ITS OWN LINE (owner 2026-08-27, measured at 375px).
     Column 4 is max-content and column 1 (the pick text) is the flexible track,
     so anything that widens the result cell steals width from the pick and tips
     a boundary row into an extra line — the exact regression that cost us a day.
     Inline, "Ertelendi ↩ ₺1.4k" pushed col 4 from 128px to 135px and the row
     from 128.6px to 150.3px. Stacked, col 4 is max(reason, amount) = the reason
     word, i.e. EXACTLY its pre-change width, so the pick column is untouched and
     the row height is identical to before at every amount. Only ever a second
     line inside a cell that is already the row's short one. */
  .tpt-status .res-returned { display: block; }
  /* CONSENSUS/VALUE LINE its OWN full-width row (owner 2026-08-04: it was
     squeezed into the narrow pick column and "Strong value" wrapped on some rows
     and not others). display:contents lets the pick code and the meta line be
     placed independently in the row grid — the meta spans all columns on row 3,
     so "Consensus (N) · Strong value" always sits on ONE line, aligned across
     every row. The record line (.tpt-mrec) that ledger picks carry gets the same
     treatment. */
  .tpt-row > .tpt-pick { display: contents; }
  .tpt-pick > .tpt-pickwrap { grid-row: 2; grid-column: 1; min-width: 0; }
  .tpt-pick > .tpt-mrec { grid-row: 3; grid-column: 1 / -1;
    white-space: nowrap; margin-top: 1px; }
  /* VEILED/LOCKED row (owner verify item): the pick cell is a rich veil
     (lock + category + "Unlock →"), whose max-content width forced grid
     track 1 to ~297px and shoved teams/countdown ~118px off the right edge
     (clipped by the body, so the countdown + odds "—" were cut off — the
     overflow the owner saw at 375/390/430). Locked rows get their OWN
     two-line anatomy: line 1 = teams + countdown right; line 2 = the veil
     spanning full width so it WRAPS in place; the meaningless "—" odds/
     stake and the redundant time are dropped. No child exceeds the row. */
  .tpt-row.locked { grid-template-columns: minmax(0,1fr) max-content; }
  .tpt-row.locked .tpt-teams { grid-row: 1; grid-column: 1; }
  .tpt-row.locked .tpt-status { grid-row: 1; grid-column: 2; justify-self: end; }
  .tpt-row.locked .tpt-pick { grid-row: 2; grid-column: 1 / -1; min-width: 0; }
  .tpt-row.locked .tpt-time,
  .tpt-row.locked .tpt-odds,
  .tpt-row.locked .tpt-stake { display: none; }
  /* MOBILE unlock CTA (owner 2026-08-05): .tpt-pick is display:contents here, so
     the veil + its fact are the real grid items — span them full width so the
     slide-unlock button gets the whole row and the label fits without clipping. */
  .tpt-row.locked .tpt-veil { width: 100%; min-width: 0; grid-column: 1 / -1; }
  .tpt-row.locked .tpt-veil-fact { grid-column: 1 / -1; }
}

/* ── THE AI PILL SITS WITH THE PICK, NOT WITH THE RESULT (owner 2026-07-30:
   "table view not good on web and mobile") ──
   On mobile the teams cell spans grid tracks 1–3 while the status cell IS
   track 4 (max-content), so the pill's 55px came straight out of the TEAM
   NAMES: "Pittsburgh Riverhounds v FC Tulsa" (218px of text) ellipsized in a
   171px slot. Shrinking the pill bought only 26px back, and moving the odds
   box down to free the names starved the flexible track instead (pick cell
   13px wide, rows 188px tall — measured, reverted).
   The fix is which CELL the pill lives in: `tpRowHTML` now emits it in the
   PICK cell, which on mobile is the FLEXIBLE track — a track that takes what
   is left over, so the pill costs the names nothing and track 4 is the result
   alone. It also reads better: the AI affordance sits beside the pick it
   explains rather than beside the profit. GRID CARDS ARE UNCHANGED (the owner
   placed that pill deliberately: same line as the win rate, far right, above
   the countdown). */
.tpt-pickwrap .ai-trigger { margin-right: 0; margin-left: 2px; align-self: center; }
@media (max-width: 640px) {
  /* The pick cell is the flexible track (~130px on a 375 phone), and a padded
     pill overran it: code 55 + tier 20 + pill 50 + gaps = 137, so the pill
     wrapped to a line of its own and every row grew 12px. Compact here — the
     whole row is tappable on mobile, so the pill is an affordance rather than
     the only target. Of the two things inside it the WORD survives and the
     sparkle goes: "AI" is self-evident at a glance, a lone glyph is a guess. */
  .tpt-pickwrap .ai-trigger { font-size: 9px; padding: 3px 7px; gap: 0; }
  .tpt-pickwrap .ai-trigger svg { display: none; }
}

/* ── Locked (members-only) cards — ONE component everywhere ──
   Canonical treatment (picks, results, home slate, home motif): amber
   border, blurred placeholder content, black lock pill, amber Unlock CTA. */
.bet-card.locked { background: linear-gradient(180deg, var(--sf) 0%, #fffaf0 100%);
  border-color: var(--ac); }
.bet-card.locked:hover { border-color: #d98f0e; }
.locked-zone { position: relative; }
.locked-blur { filter: blur(7px); opacity: .45; user-select: none;
  pointer-events: none; }
.locked-overlay { position: absolute; inset: 0; display: flex;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; }
/* CARD VEIL: lay the oracle + UNLOCK button out in a ROW so the absolutely-centered
   CTA can't overlap the oracle at phone widths (owner 2026-08-13: at ~390px the
   centered button landed ~22px inside the oracle portrait). Scoped to .bet-card.locked
   so the homepage slate-veil (.slate-veil .locked-overlay) is untouched. Verified:
   at 390px a 21px gap, at 1280px an 86px gap — no overlap, CTA still centred in the
   remaining space. */
.bet-card.locked .locked-zone { display: flex; align-items: center; gap: 14px; }
.bet-card.locked .locked-overlay { position: static; inset: auto; flex: 1 1 auto; }
.lock-badge { display: inline-flex; align-items: center; gap: 7px;
  background: var(--ink); color: var(--ink-tx);
  font: 700 10.5px var(--font-num); letter-spacing: .07em;
  text-transform: uppercase; border-radius: 6px; padding: 6px 12px; }
.lock-badge svg { color: var(--ac); flex: 0 0 auto; }
/* ITEM 4 (owner: make the unlock pull stronger, no dark patterns): a slight
   size step, a soft amber-tinted shadow so the pill lifts off the blurred
   veil instead of sitting flush with it, and a calm hover lift (no motion
   loop, no blink — a static affordance, same idiom as any card hover on
   this site). Color and copy are untouched by this: amber stays the only
   interactive hue, no countdown/scarcity, "Premium" still never appears. */
.unlock-cta { text-decoration: none; background: var(--ac); color: var(--ink);
  font: 800 12px var(--font-ui); letter-spacing: .04em;
  text-transform: uppercase; border-radius: 8px; padding: 10px 20px;
  box-shadow: 0 3px 10px color-mix(in srgb, var(--ac) 45%, transparent);
  transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease; }
.unlock-cta:hover { background: #ffb32a; transform: translateY(-1px);
  box-shadow: 0 5px 14px color-mix(in srgb, var(--ac) 55%, transparent); }
.locked-meta { display: flex; align-items: baseline; gap: 8px; margin-top: 10px; }
.locked-meta .locked-count { font: 700 13px var(--font-num); color: var(--tx); }

/* Picks page: members strip under the stats */
.unlock-note { border-color: var(--ac); background: #fffaf0; }
.unlock-note .txt b { font-family: var(--font-num); }

/* ── Sign-in modal ── */
.si-overlay { position: fixed; inset: 0; background: rgba(20,19,16,.55);
  display: flex; align-items: center; justify-content: center; z-index: 300;
  padding: 20px; }
.si-modal { position: relative; background: var(--sf); border-radius: 16px;
  border-top: 4px solid var(--ac); max-width: 430px; width: 100%;
  padding: 30px 30px 26px; box-shadow: 0 30px 80px rgba(20,19,16,.35); }
.si-close { position: absolute; top: 12px; right: 14px; background: none;
  border: none; font: 400 26px var(--font-ui); color: var(--mu);
  cursor: pointer; line-height: 1; }
.si-close:hover { color: var(--tx); }
.si-kicker { font: 700 10.5px var(--font-num); letter-spacing: .12em;
  color: var(--risk); margin-bottom: 12px; }
.si-modal h3 { margin: 0 0 6px; font-weight: 900; text-transform: uppercase;
  letter-spacing: -0.02em; font-size: 22px; }
.si-sub { margin: 0 0 18px; font-size: 13.5px; color: var(--mu); line-height: 1.5; }
/* ══ Brand non-affiliation disclaimer (2026-07-20, legal) ═══════════════════
   Colours are FIXED rather than themed. A legal notice must not depend on a
   token that flips between light and dark — it is stated once, with a contrast
   ratio that holds in both (#efeada on #141310 ≈ 14:1, well past WCAG AA).
   z-index 250 puts it above the fixed language chip (.lang-hint, 220), which
   has covered body text before, while staying below modal scrims (300/400)
   that are transient and user-initiated. No close control by design. */
.brand-disclaimer {
  position: relative; z-index: 250;
  background: #141310; color: #efeada;
  border-top: 2px solid #2a2822;
  font: 500 12px/1.5 var(--font-num, monospace);
  padding: 10px clamp(16px, 3.2vw, 48px);
  text-align: center; text-wrap: pretty;
}
@media (max-width: 560px) {
  /* long-text locales (RU/DE/BG/MK) wrap to 3 lines here — keep it readable
     rather than shrinking it out of sight */
  .brand-disclaimer { font-size: 11.5px; padding: 10px 16px; text-align: left; }
}
/* the full statement, at every point of conversion and in the footer */
.disclaimer-full {
  font: 500 12px/1.6 var(--font-num, monospace);
  color: var(--mu); border: 1px solid var(--bdr); border-radius: 8px;
  padding: 10px 12px; margin: 14px 0; text-wrap: pretty;
}

.si-form { display: flex; flex-direction: column; gap: 10px; }
/* The identity-mode switch (2026-07-20, item 3). Deliberately quiet: EMAIL is
   the path a new visitor should take, so the username alternative reads as a
   secondary option rather than a competing button. */
.si-label { font: 700 10.5px var(--font-num); letter-spacing: .12em;
  text-transform: uppercase; color: var(--mu); }
.si-toggle { display: inline-block; margin-top: 10px; padding: 0;
  background: none; border: none; font: 500 12.5px var(--font-ui);
  color: var(--mu); text-decoration: underline; text-underline-offset: 3px;
  cursor: pointer; }
.si-toggle:hover { color: var(--tx); }
.si-email, .acct-si-email { background: var(--sf); border: 2px solid var(--bdr);
  border-radius: 10px; padding: 13px 15px; font: 600 14px var(--font-num);
  color: var(--tx); width: 100%; }
.si-email:focus, .acct-si-email:focus { border-color: var(--ac); outline: none; }
.si-send, .acct-si-send { background: var(--ink); color: #fff; border: none;
  border-radius: 10px; padding: 14px 18px; font: 800 13px var(--font-ui);
  letter-spacing: .04em; text-transform: uppercase; cursor: pointer; }
.si-send:hover, .acct-si-send:hover { background: #2a2822; }
.si-send:disabled, .acct-si-send:disabled { opacity: .6; cursor: default; }
.si-status { margin-top: 12px; font-size: 13px; min-height: 18px; }
/* the dashboard save-confirmation sits between the filter row and the picks:
   its reserved height must match its real line-height (20px), or the text
   appearing shifts everything below by the 2px difference on every save */
#prefs-status { min-height: 20px; }
.si-status .err, .err { color: var(--lost); font-weight: 600; }
.oksent { color: var(--won); font-weight: 700; }
.si-uat { border: 2px dashed var(--ac); border-radius: 10px; padding: 13px 15px;
  background: #fffaf0; }
.si-uat-note { font: 500 11px var(--font-num); color: var(--mu);
  letter-spacing: .02em; margin-bottom: 8px; }
.si-uat-link { display: inline-block; font: 800 13px var(--font-ui);
  color: var(--ink); text-decoration: none; background: var(--ac);
  border-radius: 7px; padding: 8px 14px; word-break: break-all; }
.si-uat-link:hover { background: #ffb32a; }

/* ── Pricing page ── */
.plan-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
  margin-top: 26px; }
.plan-card { background: var(--sf); border: 2px solid var(--bdr);
  border-radius: 14px; padding: 26px 26px 24px; display: flex;
  flex-direction: column; position: relative; }
.plan-card.highlight { border-color: var(--ink); }
.plan-card .plan-flag { position: absolute; top: -12px; left: 24px;
  background: var(--ac); color: var(--ink); font: 700 10px var(--font-num);
  letter-spacing: .08em; text-transform: uppercase; border-radius: 5px;
  padding: 4px 9px; }
.plan-card.current { border-color: var(--ac); background: #fffdf6; }
.plan-name { font-weight: 900; text-transform: uppercase; font-size: 20px;
  letter-spacing: -0.01em; }
.plan-tag { font: 500 11px var(--font-num); color: #8a8579;
  text-transform: uppercase; letter-spacing: .06em; margin-top: 3px; }
.plan-price { margin: 18px 0 4px; }
.plan-price .amt { font: 700 40px var(--font-num); letter-spacing: -0.04em; }
.plan-price .per { font: 500 12px var(--font-num); color: var(--mu); }
.plan-intro { display: inline-block; margin: 2px 0 4px;
  font: 700 11px var(--font-num); letter-spacing: 0.02em;
  color: var(--ink); background: var(--ac);
  padding: 3px 8px; border-radius: 4px; }
.plan-feats { list-style: none; margin: 14px 0 22px; padding: 0;
  display: flex; flex-direction: column; gap: 9px; flex: 1; }
.plan-feats li { font-size: 13.5px; color: var(--mu); line-height: 1.45;
  padding-left: 20px; position: relative; }
.plan-feats li::before { content: "✓"; position: absolute; left: 0;
  color: var(--ac); font: 800 13px var(--font-num); }
.plan-feats li.na { opacity: .45; }
.plan-feats li.na::before { content: "—"; color: var(--mu); }
.plan-cta { background: var(--ink); color: #fff; border: none;
  border-radius: 10px; padding: 15px 18px; font: 800 13px var(--font-ui);
  letter-spacing: .04em; text-transform: uppercase; cursor: pointer; }
.plan-cta:hover { background: #2a2822; }
.plan-card.highlight .plan-cta { background: var(--ac); color: var(--ink); }
.plan-card.highlight .plan-cta:hover { background: #ffb32a; }
.plan-cta.is-current { background: var(--cream); color: var(--tx);
  cursor: default; }
.feat-chip { display: inline-block; background: var(--cream); color: var(--tx);
  font: 700 11px var(--font-num); border-radius: 5px; padding: 4px 9px;
  margin: 2px 4px 2px 0; }
.feat-note { font: 500 10.5px var(--font-num); color: #8a8579;
  margin-left: 4px; letter-spacing: .05em; text-transform: uppercase; }
@media (max-width: 900px) { .plan-grid { grid-template-columns: 1fr; } }

/* ── League picker ── */
.lp-panel { border: 2px solid var(--ink); border-radius: 14px;
  padding: 22px 24px; margin-top: 18px; background: var(--sf); }
.lp-head { display: flex; justify-content: space-between; align-items: baseline; }
.lp-title { font: 800 14px var(--font-ui); text-transform: uppercase;
  letter-spacing: .02em; }
.lp-count { font: 700 13px var(--font-num); color: var(--risk); }
.lp-hint { font-size: 12.5px; color: var(--mu); margin-top: 4px; }
.lp-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.lp-chip { display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 1px; background: var(--sf); border: 2px solid var(--bdr);
  border-radius: 9px; padding: 8px 13px; cursor: pointer; font: inherit;
  text-align: left; transition: border-color .12s, background .12s; }
.lp-chip:hover { border-color: var(--ink); }
.lp-chip.sel { border-color: var(--ac); background: var(--cream); }
.lp-chip .lp-name { font: 700 13px var(--font-ui); color: var(--tx); }
.lp-chip .lp-n { font: 500 10px var(--font-num); color: #8a8579;
  letter-spacing: .04em; text-transform: uppercase; }

/* ── Account page ── */
.acct-wrap { max-width: 680px; margin: 0 auto; }
.acct-flash { border-radius: 10px; padding: 13px 17px; margin-bottom: 18px;
  font: 700 13px var(--font-ui); border: 2px solid; }
.acct-flash.ok { color: var(--won); border-color: #bfe0c9; background: #f7fcf8; }
.acct-flash.err { color: var(--lost); border-color: #ecc8c2; background: #fdf7f5; }
/* ONE-SHOT (owner 2026-07-30): the flash retires itself after ~8s and can be
   dismissed by hand. Padding-right clears the close button so a long
   confirmation line never runs under it. */
.acct-flash { position: relative; padding-right: 42px; transition: opacity .4s ease, transform .4s ease; }
.acct-flash.flash-out { opacity: 0; transform: translateY(-6px); }
.acct-flash-x { position: absolute; top: 50%; right: 10px; transform: translateY(-50%);
  width: 26px; height: 26px; display: grid; place-items: center; padding: 0;
  border: none; background: transparent; color: inherit; opacity: .55;
  font: 400 19px/1 var(--font-ui); cursor: pointer; border-radius: 6px; }
.acct-flash-x:hover { opacity: 1; background: rgba(0, 0, 0, .06); }
@media (prefers-reduced-motion: reduce) { .acct-flash { transition: none; } }
/* BODY-LEVEL TOAST (#16, 2026-08-01): the confirmation now mounts in a fixed
   tray so no page render can wipe it. Top-centre, above everything, slides in. */
#flash-tray { position: fixed; top: 16px; left: 50%; transform: translateX(-50%);
  z-index: 1000; display: flex; flex-direction: column; gap: 8px;
  width: min(560px, calc(100vw - 24px)); pointer-events: none; }
#flash-tray .flash-toast { pointer-events: auto; margin: 0;
  box-shadow: 0 6px 24px rgba(20, 19, 16, .16); animation: flash-in .32s ease; }
@keyframes flash-in { from { opacity: 0; transform: translateY(-10px); } }
@media (prefers-reduced-motion: reduce) { #flash-tray .flash-toast { animation: none; } }
.acct-card { background: var(--sf); border: 2px solid var(--bdr);
  border-radius: 14px; padding: 26px 28px; margin-bottom: 16px; }
/* OWNER (2026-07-23): My Day (#dash-body) drops the card chrome entirely --
   compared directly against Today's Slate, which has NO bordered/backed
   wrapper anywhere; it sits flat on the plain page background. Scoped to
   #dash-body only: #dash-locked's teaser and account.html's settings rows
   are genuinely card-like content (an upsell box, a settings form) and
   keep the base .acct-card look. This also RETIRES the previous fix here
   (a mobile-only negative-margin bleed to match the card's inner edge) --
   with no card padding left to counteract, the picks list needs no
   compensation at all, same as Daily/Picks. */
#dash-body .acct-card { background: none; border: none; border-radius: 0;
  padding: 0; }
.acct-card h2 { margin: 0 0 16px; font-size: 17px; }
.acct-row { display: flex; justify-content: space-between; align-items: center;
  gap: 12px; padding: 12px 0; border-top: 1px solid var(--bdr); flex-wrap: wrap; }
.acct-row:first-of-type { border-top: none; }
.acct-row .k { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.acct-row .v { font: 700 15px var(--font-num); }
.acct-plan-badge { font: 800 13px var(--font-ui); text-transform: uppercase;
  border-radius: 6px; padding: 5px 12px; }
.acct-plan-badge.plan-free { background: var(--sf2); color: var(--mu); }
.acct-plan-badge.plan-myleagues { background: var(--normal-bg); color: var(--normal); }
.acct-plan-badge.plan-pro { background: var(--ink); color: var(--ac); }
.acct-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.acct-btn { background: transparent; color: var(--tx); font: 700 12px var(--font-ui);
  text-transform: uppercase; letter-spacing: .03em; border: 2px solid var(--ink);
  border-radius: 9px; padding: 11px 18px; cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; }
.acct-btn:hover { background: var(--ink); color: var(--ink-tx); }
.acct-btn.danger { border-color: #ecc8c2; color: var(--lost); }
.acct-btn.danger:hover { background: var(--lost); border-color: var(--lost); color: #fff; }
.acct-si-form { display: flex; flex-direction: column; gap: 10px; max-width: 420px; }

/* ── Checkout page ── */
.co-wrap { max-width: 520px; margin: 0 auto; }
.co-card { background: var(--sf); border: 2px solid var(--ink); border-radius: 16px;
  padding: 30px 32px; }
.co-card .co-kicker { font: 700 10.5px var(--font-num); letter-spacing: .12em;
  color: var(--risk); text-transform: uppercase; margin-bottom: 14px; }
.co-line { display: flex; justify-content: space-between; align-items: baseline;
  padding: 13px 0; border-top: 1px solid var(--bdr); gap: 10px; flex-wrap: wrap; }
.co-line:first-of-type { border-top: none; }
.co-line .k { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.co-line .v { font: 700 16px var(--font-num); }
.co-line .v.big { font-size: 30px; letter-spacing: -0.03em; }
.co-line .v.big .per { font-size: 12px; color: var(--mu); letter-spacing: 0; }
.co-mut { font-size: 12.5px; color: var(--mu); }
.co-note { border: 2px dashed var(--ac); background: #fffaf0; border-radius: 10px;
  padding: 12px 16px; font: 600 12px var(--font-num); color: var(--tx);
  letter-spacing: .02em; margin: 18px 0; text-transform: uppercase; }
/* Stripe Elements mount point -- the element's OWN internal look comes
   from the appearance API passed in account.js (matching this same token
   set); this just gives it the same breathing room .co-note had. */
#payment-element { margin: 18px 0; }
/* EMBEDDED CHECKOUT composition (owner 2026-07-30, "mobile something not
   right"): the Stripe frame is a full checkout with its own padding + summary,
   so inside our padded 2px-ink card it read as a cramped frame-in-a-frame with
   the price stated twice. When the embedded flow is active (.co-embedded on
   <body>): our summary header stays (the brand moment), the pre-VAT charge
   line hides (the frame states the true total incl. VAT), and the frame
   BLEEDS to the card edges so it gets the full width — critical at 375px. */
.co-embedded .co-card { padding: 22px 0 10px; }
.co-embedded .co-card .co-kicker,
.co-embedded .co-card .co-line,
.co-embedded .co-card .co-charge,
.co-embedded .co-card .disclaimer-full,
.co-embedded .co-card #co-status,
.co-embedded .co-card #co-card-err { margin-left: 24px; margin-right: 24px; }
.co-embedded #payment-element { margin: 14px 0 6px; }
.co-embedded .co-card .disclaimer-full { font-size: 10.5px; }
@media (max-width: 640px) {
  .co-embedded .co-wrap { max-width: none; }
  .co-embedded .co-card { border-left: none; border-right: none; border-radius: 0; }
}

/* ── FREE READER v3 (owner 2026-07-30): Photo · ONE sentence · CTA — all
   visible at once, NO scrolling, both views. Later rules override the earlier
   grid/teaser layouts wholesale. Table rows lay the three out in a ROW;
   the card back stacks them centered with the CTA pinned full-width. */
.ai-review-inner.ai-locked { display: flex; align-items: center; gap: 14px;
  text-align: left; overflow: hidden; }
.ai-locked .ail-art { flex: none; position: relative; width: 56px; height: 56px;
  min-height: 0; margin: 0; align-self: center; }
/* PORTRAIT TONE (owner #10): the oracle art carries a paper-cream background,
   so on the white plaque the circle read as a "dirty disc". A cream ring
   (--bg) behind the portrait matches the art, so the disc blends into one
   clean object; the ink border still defines it. */
.ai-locked .ail-art img { width: 56px; height: 56px; max-height: none;
  object-fit: cover; object-position: top; border-radius: 50%;
  border: 2px solid var(--ink); background: var(--bg); display: block; }
.ai-locked .ail-lockdot { position: absolute; right: -5px; bottom: -3px; }
.ai-locked .ail-fact { flex: 1 1 auto; min-width: 0; margin: 0;
  font: 500 12.5px/1.5 var(--font-num); color: var(--tx); }
/* CTA TONE (owner #10: "amber CTA is the loudest thing in the row"): quieted
   to the site's chip idiom — ink outline on paper, amber reserved for the
   hover/commit state. Reads as a control, not a billboard. */
.ai-locked .ai-cta { flex: none; margin: 0; background: transparent;
  color: var(--ink); border: 1.5px solid var(--ink); padding: 8px 14px;
  border-radius: 9px; white-space: nowrap; font-weight: 700;
  transition: background .14s ease, color .14s ease; }
.ai-locked .ai-cta:hover { background: var(--ac); color: var(--ink);
  border-color: var(--ac); }
.tpc-back .ai-review-inner.ai-locked { flex-direction: column;
  text-align: center; justify-content: center; gap: 10px; overflow: hidden; }
.tpc-back .ai-locked .ail-art { width: 72px; height: 72px; margin: 4px 0 0; }
.tpc-back .ai-locked .ail-art img { width: 72px; height: 72px; }
.tpc-back .ai-locked .ail-fact { flex: none; max-width: 34ch;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden; }
.tpc-back .ai-locked .ai-cta { width: 100%; margin-top: 2px; }
/* table rows (owner 2026-07-30: "in table mode not showing that good"): the
   trio was stretched across the row's full width — photo far left, button far
   right, dead space between. It reads as a compact centered plaque instead:
   capped width, clean neutral border (no amber bar), a touch more presence. */
/* TABLE-ROW PLAQUE, REBUILT (owner 2026-07-30, screenshot: "too small, not
   centered, very amateur"). Measured before: on mobile the 64px portrait sat
   ALONE on its line with 189px of dead space beside it (73% of the line empty),
   then a 5-line paragraph, then a full-bleed amber slab — three left-aligned
   blocks of unequal weight with nothing anchoring the eye. On desktop the same
   markup read as a browser notification bar.

   Root cause: detailed waist-up artwork displayed at 64px is a smudge, and one
   horizontal composition was being forced through a 253px phone column.

   Owner's instinct — bigger photo, let the button ride the text — is right
   WHERE THERE IS ROOM: at 253px of usable width a 96px photo leaves 151px for
   the text, about three words a line. So the composition ADAPTS: horizontal
   with a bigger portrait on desktop, centred stack on phones. Same parts, same
   look, no second design to maintain. */
/* ALIGNED, NOT FLOATING (owner 2026-07-30, 2nd desktop screenshot): capping the
   plaque at 720px and centring it fixed the earlier "stretched across the row"
   complaint but created a new one — the island sat under the PICK column with a
   dead region under KICKOFF and COMPETITION, aligned to nothing.

   Both complaints have ONE answer: the panel spans the row like the members'
   analysis panel does (so it reads as that row expanding, and the locked and
   unlocked states finally share a container), while the CONTENT stays clustered
   left instead of being spread edge to edge. The dead space the first complaint
   was about came from `justify-content: space-between` behaviour, not from the
   width. */
.tpt-airev .ai-review-inner.ai-locked { max-width: none; margin: 6px 0;
  padding: 18px 22px; border: 1px solid var(--bdr); border-left: 1px solid var(--bdr);
  background: var(--sf); border-radius: 12px; gap: 20px; justify-content: flex-start; }
/* the copy stops growing to fill the row — it keeps a readable measure and the
   button sits right after it, so the cluster reads as one object */
.tpt-airev .ai-locked .ail-fact { flex: 0 1 auto; max-width: 62ch; }
.tpt-airev .ai-locked .ai-cta { margin-left: 4px; }
/* 96px (was 64): the gesture that makes each of the 44 images worth having only
   reads above ~90px. The plaque widens to 720 so the copy still drops to 2
   lines rather than paying for the portrait in text height. */
.tpt-airev .ai-locked .ail-art { width: 96px; height: 96px; }
.tpt-airev .ai-locked .ail-art img { width: 96px; height: 96px; }
.tpt-airev .ai-locked .ail-fact { font-size: 13px; }

/* PHONES: one centred column. The portrait has no "beside it", so the 189px of
   dead space cannot exist; the CTA stops being a full-width slab and becomes a
   pill sized to its own words. */
@media (max-width: 560px) {
  .tpt-airev .ai-review-inner.ai-locked { flex-direction: column; text-align: center;
    align-items: center; gap: 12px; padding: 20px 16px; max-width: 380px; }
  .tpt-airev .ai-locked .ail-art { width: 104px; height: 104px; }
  .tpt-airev .ai-locked .ail-art img { width: 104px; height: 104px; }
  .tpt-airev .ai-locked .ail-fact { flex: none; max-width: 40ch; }
  .tpt-airev .ai-locked .ai-cta { flex: none; width: auto; padding: 11px 22px; }
}
@media (max-width: 480px) {
  /* the generic row-wrap fallback still serves any OTHER .ai-locked host; the
     table plaque and the card back both opt out of it above/below. */
  .ai-review-inner.ai-locked { flex-wrap: wrap; }
  .ai-locked .ai-cta { flex: 1 1 100%; text-align: center; }
  /* the ROW-wrap trick above must not leak into the card-back COLUMN, where
     grow:1 inflates the button to fill the card's whole free height */
  .tpc-back .ai-review-inner.ai-locked { flex-wrap: nowrap; }
  .tpc-back .ai-locked .ai-cta { flex: none; }
  /* ...nor into the table plaque, which is now its own centred column */
  .tpt-airev .ai-review-inner.ai-locked { flex-wrap: nowrap; }
  .tpt-airev .ai-locked .ai-cta { flex: none; width: auto; }
}
#co-card-err { margin: 0 0 14px; font-size: 12.5px; }
#co-pay { width: 100%; background: var(--ac); color: var(--ink); border: none;
  border-radius: 11px; padding: 17px 20px; font: 800 15px var(--font-ui);
  letter-spacing: .02em; text-transform: uppercase; cursor: pointer; }
#co-pay:hover { background: #ffb32a; }
#co-pay:disabled { opacity: .6; cursor: default; }
#co-status { margin-top: 12px; font-size: 13px; min-height: 18px; text-align: center; }
.co-back { display: inline-block; margin-top: 16px; font: 700 12px var(--font-num);
  color: var(--mu); text-decoration: none; text-transform: uppercase;
  letter-spacing: .05em; }
.co-back:hover { color: var(--tx); }

/* The [hidden] attribute must beat the display rules of these components */
/* .slate-note added 2026-07-30: home.js already set hint.hidden = (count === 0),
   but `.slate-note { display: block }` outranks the attribute's UA display:none,
   so the home page advertised "0 extra flagged picks in today's slate" (owner
   screenshot). The JS was right; the CSS was beating it. */
.slate-note[hidden],
.acct-btn[hidden], .plan-cta[hidden], .lp-panel[hidden], .co-line[hidden],
#acct-anon[hidden], #acct-signedin[hidden], #acct-leagues-section[hidden],
#acct-leagues-picker[hidden] { display: none !important; }

/* ── Home: membership section (ink panel + locked-card motif) ── */
.sb-members { display: grid; grid-template-columns: 1.25fr 1fr; gap: 40px;
  align-items: center; background: var(--ink); border-radius: 16px;
  padding: 46px 48px; color: var(--ink-tx);
  /* re-scope tokens so the motif card renders for dark ground */
  --bg: #141310; --sf: #1d1b17; --sf2: #26231e; --bdr: var(--ink-2);
  --mu: var(--ink-mu); --tx: var(--ink-tx); }
.sb-members-copy h2 { margin: 14px 0 0; font-size: clamp(26px, 3.2vw, 38px);
  line-height: 1.04; color: var(--ink-tx); }
.sb-members-copy p { color: #a39d8e; font-size: 15px; line-height: 1.6;
  margin: 16px 0 0; max-width: 440px; }
.sb-members-ctas { display: flex; gap: 18px; align-items: center;
  margin-top: 26px; flex-wrap: wrap; }
.sb-members-more { font: 700 12.5px var(--font-num); letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-mu); text-decoration: none; }
.sb-members-more:hover { color: var(--ac); }
.sb-members-motif { max-width: 360px; justify-self: center; width: 100%; }
.sb-members-motif .bet-card.locked { background: var(--sf);
  box-shadow: 0 24px 60px rgba(0,0,0,.45);   /* amber border from the base */
  transform: rotate(1.5deg); padding: 20px 22px; }
.sb-members-motif .bet-card.locked .match { color: var(--ink-tx); }
@media (max-width: 900px) {
  .sb-members { grid-template-columns: 1fr; padding: 34px 24px; gap: 28px; }
  .sb-members-motif { max-width: 320px; }
}

/* Slate-section Member hint — DEFECT 1 (owner, live screenshot): this used
   to sit INSIDE .sb-section-head beside the h2/LIVE badge as a same-row
   flex item; with justify-content:space-between it landed as a rival pill
   floating above/beside the wrapped mobile heading (clipped at the
   viewport edge). It is now its OWN block line, right under the section
   head and above the slate grid — subordinate to "Today's slate", stating
   the real count, in the quiet label voice (no pill chrome, no caps
   shouting). Never the loudest thing near the h2. */
.slate-note { display: block; font: 500 12.5px var(--font-num);
  color: var(--mu); text-decoration: none; margin: -6px 0 14px; }
.slate-note:hover { color: var(--tx); text-decoration: underline; }

/* MEMBER CTA — prominent "more picks with membership" box (owner 2026-08-02:
   "state it everywhere, make it prominent"). A full-width amber bar the eye
   can't miss, on every pick surface. Reuses the members_more_cta string. */
#member-cta-box { margin: 0 0 16px; }
.member-cta { display: flex; align-items: center; justify-content: center; gap: 9px;
  background: var(--amber); color: #141310; text-decoration: none;
  font: 700 15px var(--font-ui, inherit); letter-spacing: -.01em;
  padding: 14px 18px; border-radius: 12px; border: 1.5px solid var(--amber);
  box-shadow: 0 8px 22px -12px rgba(242,160,18,.6); transition: filter .12s ease, transform .06s ease; }
.member-cta:hover { filter: brightness(.96); }
.member-cta:active { transform: translateY(1px); }
.member-cta .member-cta-ic { display: inline-flex; }
.member-cta .member-cta-ic svg { width: 16px; height: 16px; }
.member-cta .member-cta-tx { text-align: center; }
/* scheduled-downgrade banner above the plan grid */
.plan-pending-note { margin: 0 0 18px; display: flex; gap: 12px; align-items: center;
  justify-content: center; flex-wrap: wrap; text-align: center; }
.plan-pending-note .ppn-cancel { flex: none; font: inherit; font-weight: 700; font-size: 13px;
  padding: 8px 14px; border-radius: 9px; border: 1.5px solid currentColor;
  background: transparent; color: inherit; cursor: pointer; }
.plan-pending-note .ppn-cancel:hover { background: rgba(0,0,0,.06); }

/* Home slate: veiled premium teaser card — the SAME locked component,
   size-adapted (horizontal overlay, smaller pill + CTA, same blur/amber) */
.sb-slate-card.slate-locked { border-color: var(--ac);
  background: linear-gradient(180deg, var(--sf) 0%, #fffaf0 100%); }
.slate-veil { margin-top: 12px; }
.slate-veil .locked-blur { padding: 6px 0 4px; }
.slate-veil .locked-blur .row2 { margin: 0; }
.slate-veil .locked-overlay { flex-direction: row; gap: 10px; flex-wrap: wrap; }
.slate-veil .lock-badge { font-size: 9.5px; padding: 5px 10px; }
.slate-veil .unlock-cta { font-size: 10.5px; padding: 7px 13px; }

/* Pricing: the grid now holds however many plans the config defines */
.plan-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  max-width: 780px; margin-left: auto; margin-right: auto; }

/* Premium extras: members see the full card with a quiet Premium mark */
.bet-card .badges { display: inline-flex; gap: 6px; align-items: center; flex-shrink: 0; }
.tier-badge.premium { background: var(--ink); color: var(--ac); }

/* ── Profile hub (Dashboard / Account / Membership) ── */
.profile-wrap { max-width: 860px; margin: 0 auto; }
/* DASHBOARD spans the page shell (2026-07-22, owner). The 860px cap that
   suits account.html's forms left the MY DAY card ~950px centered in a
   2000px viewport — dead margins both sides and the h1 (which aligns to the
   SHELL edge, like every hero) visually disconnected from its own card.
   max-width:none hands the width back to .page.container, i.e. the ONE
   shell system (1320 / 1560@1800+ / 1760@2300+), so title and card share an
   edge at every size and wide monitors are actually used. */
.dash-wrap { max-width: none; }
/* Denser pick cards would fit 5-6 slivers at shell width with the base
   230px minimum; the owner wants FULL columns — 3 at ~1440-1600, 4 at
   ~1900-2000 — with ~340px cards. auto-fill wraps instead of clipping, and
   min(340px,100%) keeps narrow viewports safe. Scoped to >=1024px so
   phone/tablet keep today's base .bet-grid behaviour exactly. */
@media (min-width: 1024px) {
  .dash-wrap .bet-grid {
    /* SAME grid rule as Today's Picks (#tier-groups) — see the .history-grid/
       .daily-grid note (owner 2026-08-05: one card proportion on every page). */
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
    gap: 12px; align-items: stretch; justify-items: start;
  }
}
.profile-nav { display: flex; gap: 0; border: 2px solid var(--ink);
  border-radius: 10px; overflow: hidden; margin-bottom: 18px; width: fit-content; }
.profile-nav button { background: transparent; border: none; cursor: pointer;
  font: 700 12.5px var(--font-ui); letter-spacing: .04em; text-transform: uppercase;
  color: var(--mu); padding: 12px 22px; }
.profile-nav button + button { border-left: 2px solid var(--ink); }
.profile-nav button.active { background: var(--ink); color: #fff; }
.dash-prefs { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; }
.dp-field { display: flex; flex-direction: column; gap: 6px; min-width: 180px; }
.dp-label { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.dash-teaser { display: grid; grid-template-columns: 1.2fr 1fr; gap: 28px;
  align-items: center; }
.dt-motif .bet-card.locked { transform: rotate(1.5deg); }
@media (max-width: 720px) { .dash-teaser { grid-template-columns: 1fr; } }

/* "House oracle" plaque on the dashboard teaser — the homepage graded-prophecy
   coupon, reused. Stacked (art beside coupon), so it reads right WITH or
   WITHOUT the portrait: the <img> self-removes when the file is absent and the
   coupon simply takes the row. */
.dash-oracle { margin-top: 18px; display: flex; gap: 22px; align-items: center;
  flex-wrap: wrap; }
.dash-oracle-art { flex: 0 0 auto; width: 150px; height: auto; align-self: center;
  border: 2px solid var(--ink); border-radius: 10px; background: var(--sf2);
  box-shadow: 4px 4px 0 rgba(20,19,16,.10); }
.dash-oracle-coupon { flex: 1 1 260px; min-width: 240px; font-family: var(--font-num);
  border: 1px solid var(--bdr); border-radius: 10px; padding: 14px 16px;
  background: var(--sf2); }
.dash-oracle-coupon .cp-title { font: 700 11px var(--font-num); letter-spacing: .12em;
  color: var(--mu); margin: 0 0 10px; }
.dash-oracle-coupon .cp-row { display: flex; align-items: baseline; gap: 10px;
  padding: 7px 0; border-top: 1px solid var(--bdr); }
.dash-oracle-coupon .cp-row:first-of-type { border-top: 0; }
.dash-oracle-coupon .cp-ev { flex: 1 1 auto; min-width: 0; font-size: 12.5px;
  color: var(--tx); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-oracle-coupon .cp-odds { font-weight: 700; color: var(--tx); font-size: 12.5px; }
.dash-oracle-coupon .tpt-res { font-size: 11px; font-weight: 700; white-space: nowrap; }
.dash-oracle-coupon .cp-total-row { display: flex; justify-content: space-between;
  align-items: baseline; margin-top: 10px; padding-top: 10px;
  border-top: 2px solid var(--ink); }
.dash-oracle-coupon .cp-total-row .tp-label { color: var(--mu); font-size: 11px; }
.dash-oracle-coupon .cp-total { font: 700 15px var(--font-num); color: var(--tx); }
.dash-oracle-coupon .cp-marker { font: 500 10px var(--font-num); color: var(--mu);
  margin-top: 4px; line-height: 1.35; }
@media (max-width: 520px) { .dash-oracle { justify-content: center; } }
.br-row { display: flex; gap: 22px; align-items: flex-end; flex-wrap: wrap; }
.br-input-wrap { display: flex; align-items: baseline; gap: 6px;
  border: 2px solid var(--bdr); border-radius: 10px; padding: 10px 14px;
  background: var(--sf); width: fit-content; }
.br-input-wrap:focus-within { border-color: var(--ac); }
.br-input-wrap > span { font: 700 16px var(--font-num); color: var(--mu); }
.br-input-wrap input { background: transparent; border: none; outline: none;
  color: var(--tx); font: 700 20px var(--font-num); width: 130px;
  -moz-appearance: textfield; appearance: textfield; }
.br-input-wrap input::-webkit-outer-spin-button,
.br-input-wrap input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.br-unit { font: 500 13px var(--font-num); color: var(--mu); padding-bottom: 12px; }
.br-unit b { color: var(--tx); }
.br-hist { border: 2px dashed var(--bdr); border-radius: 12px;
  padding: 16px 20px; margin-top: 16px; }
.bh-k { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.bh-v { font: 700 24px var(--font-num); margin-top: 4px; }
.bh-v.pos { color: var(--won); } .bh-v.neg { color: var(--lost); }
.bh-note { font-size: 12px; color: var(--mu); margin-top: 6px; }
.sj-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.sj-grid .stat-tile { padding: 16px 18px; }
.sj-grid .stat-tile .value.pos { color: var(--won); }
.sj-grid .stat-tile .value.neg { color: var(--lost); }
@media (max-width: 640px) { .sj-grid { grid-template-columns: 1fr; } }
.sj-best-line { display: flex; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; border-top: 1px solid var(--bdr); margin-top: 14px;
  padding-top: 12px; font-size: 13.5px; }
.sj-best-line .k { font: 700 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
.sj-best-line .v b.pos { color: var(--won); font-family: var(--font-num); }

/* Single-session notice (quiet, self-dismissing). DEFECT A: this used to be
   position:sticky;top:0;z-index:250 while ALSO sitting ahead of the header
   in DOM order (body.prepend) — two sticky top:0 siblings fight for the same
   pinned slot, and the higher z-index one (this bar) wins, rendering as a
   stray strip above/over the white nav header. Now mounted AFTER the header
   (account.js) and left in normal flow (no sticky, no z-index) — it renders
   once, directly under the header, and scrolls away with the page like any
   other banner; it is short-lived (12s) so staying pinned adds nothing. */
.session-notice { background: var(--cream);
  color: var(--tx); font: 600 12.5px var(--font-ui); text-align: center;
  padding: 10px 16px; border-bottom: 2px solid var(--ac); }

/* ── Insight teaser line on veiled cards (the hook) ──
   Factual, computed server-side over the full ledger history. Reads as a
   quiet data note, not a promise: mono type, amber mark, no exclamation. */
.insight-line { display: flex; align-items: flex-start; gap: 7px;
  margin: 12px 0 2px; padding: 9px 11px; border-radius: 9px;
  background: var(--cream); color: var(--tx);
  font: 600 11.5px var(--font-ui); line-height: 1.4; }
.insight-line svg { color: var(--risk); flex: 0 0 auto; margin-top: 1px; }
/* The two motifs sit on OPPOSITE grounds and must not share a text colour.
   .sb-members-motif lives inside .sb-members, which re-scopes the tokens for
   dark ground (--sf: #1d1b17), so --ink-tx is right there: 13.5:1.
   .dt-motif (dashboard teaser) has no such re-scoping — its card is the
   default white — so the shared rule painted #fdfbf6 on white at 1.06:1 and
   the insight sentence was invisible. The hook is the one thing a locked card
   must keep readable; only the pick and odds are veiled. */
.sb-members-motif .insight-line {
  background: rgba(242,160,18,.12); color: var(--ink-tx); }   /* 13.54:1 */
.dt-motif .insight-line {
  background: rgba(242,160,18,.12); color: var(--tx); }        /* 17.01:1 */

/* Duration ladder: effective per-day price + how the charge behaves */
.plan-perday { font: 600 11.5px var(--font-num); color: var(--mu);
  letter-spacing: .02em; margin-top: 2px; }
.plan-kind { color: var(--risk); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; font-size: 10.5px; }
.plan-grid { max-width: none; }
@media (min-width: 901px) {
  .plan-grid { grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); }
}

/* ── Veiled-card content hierarchy (informative veil) ──
   Reads top-to-bottom: WHY it's premium (category) → WHAT the pattern is
   (insight) → the veil + Unlock → WHAT this is (explainer + info link).
   VIEW SYSTEM STANDARD (2026-07-23): the table's locked ROW already
   established the rule — the unlock CTA is the row's ONE bold, the
   category tag is a LABEL. This shared CARD template (picks/results/
   dashboard grid + the home slate + both static motifs) never followed
   it: .veil-cat AND .veil-info both carried 700, so a locked card showed
   THREE bold leaves once a real veiled pick existed to measure (tier-3
   caught it live: "3 bold leaves [SPAN,unlock-cta,veil-info]"). Both
   demoted to label weight; .unlock-cta stays the one bold. */
.veil-cat { display: inline-flex; align-items: center; gap: 7px;
  background: var(--ink); color: var(--ink-tx); border-radius: 6px;
  padding: 6px 11px; margin: 12px 0 0;
  font: 500 10.5px var(--font-num); letter-spacing: .06em;
  text-transform: uppercase; }
.veil-cat svg { color: var(--ac); flex: 0 0 auto; }
.bet-card.locked .insight-line { margin: 9px 0 2px; font-size: 12.5px; }
.veil-why { margin-top: 12px; font-size: 11.5px; line-height: 1.45;
  color: var(--mu); }
.veil-info { display: inline-block; margin-left: 4px; color: var(--risk);
  font-weight: 500; text-decoration: underline;
  text-underline-offset: 2px; white-space: nowrap; }
.veil-info:hover { color: var(--tx); }
/* the veil zone now carries only the CTA, so it needs less headroom */
.bet-card.locked .locked-zone { margin-top: 10px; }
.bet-card.locked .locked-overlay { gap: 0; }

/* Home slate teaser: same hierarchy, compact */
.sb-slate-card .veil-cat { margin: 10px 0 0; font-size: 9.5px; padding: 5px 9px; }
.sb-slate-card .insight-line { margin: 8px 0 0; padding: 7px 9px; font-size: 11px; }
.sb-slate-card .veil-why { margin-top: 8px; }

/* Motif cards (home members section, dashboard teaser) */
/* Same split for the tag chip. --ac (#f2a012) on the light teaser card is
   1.90:1 — the owner flagged this one too. On the dark members card it is
   6.05:1 and stays. #8a5e07 is the darkest-amber that clears AA on the light
   ground (5.06:1) while still reading as the brand amber rather than brown. */
.sb-members-motif .veil-cat {
  background: rgba(242,160,18,.16); color: var(--ac); }        /* 6.05:1 */
.dt-motif .veil-cat {
  background: rgba(242,160,18,.16); color: #8a5e07; }          /* 5.06:1 */

@media (max-width: 640px) {
  .veil-cat { font-size: 9.5px; padding: 5px 9px; margin-top: 10px; }
  .bet-card.locked .insight-line { font-size: 12px; padding: 8px 10px; }
  .veil-why { font-size: 11px; margin-top: 10px; }
  .bet-card.locked .locked-zone { margin-top: 8px; }
}

/* "What is a premium pick?" — the honesty story on the picks page */
.premium-explain { border: 2px dashed var(--bdr); border-radius: 14px;
  padding: 22px 26px; margin: 28px 0 8px; background: var(--sf); }
.premium-explain h2 { font-size: 17px; margin: 0 0 10px; }
.premium-explain p { font-size: 13.5px; line-height: 1.6; color: var(--mu);
  margin: 0 0 8px; max-width: 760px; }
.premium-explain p:last-child { margin-bottom: 0; }
.premium-explain[hidden] { display: none !important; }
@media (max-width: 640px) {
  .premium-explain { padding: 18px 18px; }
  .premium-explain p { font-size: 13px; }
}

/* Unlocked flagged pick (member view): the SAME descriptive tag the veiled
   card showed, in a lighter treatment — nothing is hidden anymore, so it
   reads as a note rather than a gate. Wording is identical either way. */
.veil-cat.open-cat { background: var(--cream); color: var(--tx);
  margin: 10px 0 0; }
.veil-cat.open-cat svg { color: var(--risk); }
.bet-card:not(.locked) .insight-line { margin: 8px 0 0; font-size: 12px; }
@media (max-width: 640px) {
  .veil-cat.open-cat { font-size: 9.5px; }
}

/* Reason-tag weights: strong evidence keeps ink+amber, secondary reasons take
   a calmer slate accent, a reason-less card stays neutral. Same component,
   readable hierarchy — the design language does not change. */
.veil-cat.secondary { background: var(--normal-bg); color: var(--normal); }
.veil-cat.secondary svg { color: var(--normal); }
.veil-cat.plain { background: var(--sf2); color: var(--mu); }
.veil-cat.plain svg { color: var(--mu); }
.veil-cat.open-cat.secondary { background: var(--normal-bg); color: var(--normal); }
.veil-cat.open-cat.plain { background: var(--sf2); color: var(--mu); }


/* ── Soft language suggestion chip ──
   Fixed, small, out of the flow (never reflows the page on mobile), no
   urgency, no modal: one tap to switch, one tap to dismiss for good. */
.lang-hint { position: fixed; left: 16px; bottom: 16px; z-index: 220;
  display: inline-flex; align-items: stretch; overflow: hidden;
  background: var(--sf); border: 2px solid var(--ink); border-radius: 10px;
  box-shadow: 0 10px 30px rgba(20,19,16,.16); max-width: calc(100vw - 32px); }
.lang-hint .lh-go { display: inline-flex; align-items: center;
  padding: 10px 14px; text-decoration: none; color: var(--tx);
  font: 700 12.5px var(--font-ui); letter-spacing: .01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lang-hint .lh-go:hover { background: var(--cream); }
.lang-hint .lh-x { border: none; border-left: 2px solid var(--bdr);
  background: transparent; color: var(--mu); cursor: pointer;
  font: 400 18px var(--font-ui); line-height: 1; padding: 0 12px; }
.lang-hint .lh-x:hover { color: var(--tx); background: var(--sf2); }
@media (max-width: 640px) {
  .lang-hint { left: 12px; right: 12px; bottom: 12px; max-width: none; }
  .lang-hint .lh-go { flex: 1; font-size: 12px; padding: 10px 12px; }
}

/* Per-currency pricing: local amount + what actually gets charged */
.plan-charge { font: 600 11px var(--font-num); color: var(--mu);
  letter-spacing: .02em; margin-top: 4px; }
.co-charge { font: 700 13.5px var(--font-ui); color: var(--tx);
  border-top: 1px solid var(--bdr); padding-top: 14px; margin-top: 4px; }
.co-charge.fallback { color: var(--risk); }
/* which country's VAT is in that total — quiet, but present, because the field
   that used to imply it is gone from the card form (owner bug, 2026-07-29) */
.co-vat-country { font: 600 11.5px var(--font-num); color: var(--mu);
  margin: 0 0 12px; letter-spacing: .01em; }
.co-vat-country a { color: var(--mu); text-underline-offset: 2px; }
.co-vat-country a:hover { color: var(--tx); }

/* Bar board: geometry is computed in JS from the measured container (shared
   up/down scale, reserved label row, fitted column width). CSS must not
   impose fixed heights or minimum widths or the chart clips on mobile. */
.bar-board { flex-wrap: nowrap; overflow: hidden; }
.bar-board .bb-col { max-width: none; }
.bar-board.labelled .bb-col { min-width: 0; }
.bar-board .bb-up, .bar-board .bb-dn { height: auto; }
.bb-val { font-size: 10px; line-height: 14px; overflow: hidden;
  text-overflow: ellipsis; }
@media (max-width: 640px) {
  .bar-board { min-height: 0; }
  .bar-board .bb-up, .bar-board .bb-dn { height: auto; }
}

/* Language chip: never overlap content, and respect the iOS home indicator.
   The body reserves the chip's height while it is on screen, so the last
   paragraph (the provenance block is where this bit) can always be read. */
.lang-hint { bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
body.has-lang-hint { padding-bottom: calc(var(--lang-hint-h, 56px)
  + 24px + env(safe-area-inset-bottom, 0px)); }
@media (max-width: 640px) {
  .lang-hint { bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
  body.has-lang-hint { padding-bottom: calc(var(--lang-hint-h, 52px)
    + 16px + env(safe-area-inset-bottom, 0px)); }
}

/* Both money inputs are the SAME component — the unit field previously fell
   back to a browser-default white box because the styling was keyed to one id. */
.sb-sim .sim-left { min-width: 0; }
.sb-sim .sim-input-wrap input { width: 100%; min-width: 90px; max-width: 150px; }
.br-input-wrap input { width: 100%; min-width: 90px; max-width: 150px; }
@media (max-width: 640px) {
  .sb-sim .sim-left { flex: 1 1 46%; }
  .sb-sim .sim-input-wrap input { font-size: 19px; }
}

/* ── Provenance proof deck — a real swipeable component, same tokens ──
   Replaces the old wall of text: five cards on a scroll-snap rail with dots,
   arrows and keyboard support. Touch swipe is native scrolling (no JS
   dragging), so it feels right on iOS. */
.sb-provenance { padding-block: 8px 0; }
.prov-card { border: 2px solid var(--bdr); border-radius: 14px;
  padding: 26px 0 20px; background: var(--sf); }
.prov-head { padding: 0 22px; margin-bottom: 18px; }
.prov-head .k { display: block; font: 700 10.5px var(--font-num);
  letter-spacing: .1em; text-transform: uppercase; color: var(--risk); }
.prov-head h2 { margin: 8px 0 0; font-size: clamp(20px, 2.6vw, 30px);
  line-height: 1.08; }
.prov-rail { --slides: 3; --slide-gap: 14px;
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc((100% - (var(--slides) - 1) * var(--slide-gap)) / var(--slides));
  gap: var(--slide-gap); padding: 2px 22px 6px;
  overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; scrollbar-width: none; outline: none; }
/* whole cards at every width — never a slide cut mid-word at rest */
@media (max-width: 700px)  { .prov-rail { --slides: 1; } }
@media (min-width: 701px)  { .prov-rail { --slides: 2; } }
@media (min-width: 1060px) { .prov-rail { --slides: 3; } }
@media (min-width: 1500px) { .prov-rail { --slides: 4; } }
.prov-rail::-webkit-scrollbar { display: none; }
.prov-slide { min-width: 0; scroll-snap-align: start;
  background: var(--sf2); border: 2px solid var(--bdr); border-radius: 12px;
  padding: 20px 22px; display: flex; flex-direction: column; }
.prov-slide:last-child { border-color: var(--ink); background: var(--ink);
  color: var(--ink-tx); }
.ps-num { font: 700 10.5px var(--font-num); letter-spacing: .12em;
  color: var(--risk); }
.prov-slide:last-child .ps-num { color: var(--ac); }
.ps-title { margin: 10px 0 8px; font-size: 17px; font-weight: 800;
  text-transform: uppercase; letter-spacing: -0.01em; }
.ps-body { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--mu); }
.prov-slide:last-child .ps-body { color: #a39d8e; }
.prov-link { align-self: flex-start; margin-top: 12px; text-decoration: none;
  font: 700 16px var(--font-num); color: var(--ac); }
.prov-nav { display: flex; align-items: center; gap: 10px;
  padding: 14px 28px 0; }
.prov-arrow { width: 34px; height: 34px; flex: 0 0 34px; cursor: pointer;
  background: transparent; border: 2px solid var(--ink); border-radius: 9px;
  color: var(--tx); font: 700 14px var(--font-num); line-height: 1; }
.prov-arrow:hover { background: var(--ink); color: var(--ink-tx); }
.prov-arrow[disabled] { opacity: .3; cursor: default; }
.prov-arrow[disabled]:hover { background: transparent; color: var(--tx); }
.prov-dots { display: flex; gap: 7px; flex: 1; }
.prov-dot { width: 8px; height: 8px; padding: 0; border-radius: 50%;
  border: none; background: var(--bdr); cursor: pointer; }
.prov-dot.on { background: var(--ac); width: 22px; border-radius: 4px; }
.prov-hint { font: 500 10.5px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; color: #8a8579; }
@media (max-width: 640px) {
  .sb-provenance { padding: 8px 0 0; }
  .prov-card { border-radius: 0; border-left: none; border-right: none; }
  .prov-head { padding: 0 20px; }
  .prov-rail { padding: 2px 20px 6px; gap: 10px; }
  .prov-slide { padding: 18px 18px; }
  .prov-nav { padding: 12px 20px 0; }
  .prov-arrow { display: none; }          /* thumbs swipe; arrows are desktop */
  .prov-hint { margin-left: auto; }
}

/* ── No horizontal overflow, ever ──
   Long-language nav items (RU/DE/MK/BG are far longer than EN) forced the
   header flex row wider than the viewport, which scrolled the whole document
   sideways and clipped the logo, title and banners. Flex children default to
   min-width:auto, so they refuse to shrink below their content: give them
   min-width:0 and let the nav scroll inside its own strip. */
html, body { max-width: 100%; overflow-x: hidden; }
.site-header .container, .site-footer .container { min-width: 0; }
.main-nav { min-width: 0; }
.header-controls { min-width: 0; flex-shrink: 0; }
/* Level the header row: the language + currency selects and the sign-in link
   share ONE height (the selects relied on the browser default, the sign-in on
   its own padding, so they landed at different heights) — owner 2026-08-05. */
.header-controls .select-wrap select,
.header-controls .account-link {
  height: 36px; box-sizing: border-box;
  padding-top: 0; padding-bottom: 0;   /* size purely by height so both render identical */
}
.header-controls .select-wrap { height: 36px; }
.header-controls .account-link { align-items: center; }   /* keep its label vertically centred */
.logo { min-width: 0; flex-shrink: 0; }
/* The single-row header (logo · nav · controls) only reflowed at <=640px, but
   logo + the 6 uppercase nav links + the 4 controls stop fitting one row around
   ~1230px -- so on any window between there and full desktop the nav overflowed
   and collided with the language selector ("CONTACT" clipped). Fix: give the nav
   its own full-width row below logo+controls BEFORE that happens, reusing the
   mobile scroll-strip pattern; overflow-x:auto is a belt-and-braces guard so the
   nav can never overlap the controls at any in-between width. */
.main-nav { overflow-x: auto; scrollbar-width: none; }
.main-nav::-webkit-scrollbar { display: none; }
/* a nav link must never wrap its own label ("TODAY'S / PICKS" on two lines) --
   in the single-row layout that made the header two lines tall and shoved the
   last link into the controls. */
.main-nav a { white-space: nowrap; }
/* 1450, not 1280: the SIGNED-OUT header is wider than signed-in ("Sign in /
   register" ≫ "Account"), so the single row runs out of space sooner. Reflow to
   two rows before it gets cramped; genuinely-wide desktops (>1450) keep one row. */
@media (max-width: 1450px) {
  /* styles.css pins .site-header to a fixed height:58px; a two-row header
     overflows and clips (top cut off). Let it grow, as the <=640px block does.
     Row 1 = logo + controls; row 2 = the full-width nav. `flex: 1 0 100%`
     (shrink 0) forces the nav onto its OWN row deterministically -- with the
     old flex-basis:100% it kept flex-shrink:1 from `flex:1`, so at some widths
     it half-shared row 1 and the last link (CONTACT) wrapped under the
     controls instead. */
  .site-header { height: auto; }
  .site-header .container { flex-wrap: wrap; align-items: center; row-gap: 10px; }
  .logo { margin-right: auto; }
  .header-controls { order: 2; }
  /* the big "Today's picks ->" header button is redundant here with the nav's
     own Today's Picks link + the hero CTA; hiding it keeps row 1 uncrowded so
     the language/currency/account controls never wrap. */
  .sb-header-cta { display: none; }
  .main-nav { order: 3; flex: 1 0 100%; width: 100%; padding-bottom: 2px; }
}
.sb-page-hero, .page, .profile-wrap, .acct-wrap, .co-wrap { min-width: 0; }
/* long single words (Cyrillic headings, emails) must wrap, not push */
.sb-h1, .section-title, .acct-row .v, .prov-head h2, .plan-name,
.bet-card .match, .acct-flash { overflow-wrap: anywhere; }
@media (max-width: 640px) {
  /* HAMBURGER (owner 2026-07-31). The scroll-strip nav is RETIRED on phones —
     with the scrollbar hidden, the tail links effectively did not exist on
     small screens ("can't see all menu properly"). The burger (injected by
     app.js on every page) opens the same links as a stacked in-flow panel
     that pushes content down — no overlay stacking, closes on link / outside
     tap / Escape. The strip remains for 641–900 where it fits. */
  .site-header .main-nav { display: none; }
  .nav-burger { display: inline-flex; }
  .site-header.nav-open .main-nav {
    display: flex; flex-direction: column; order: 3; flex: 1 0 100%;
    overflow: visible; -webkit-mask-image: none; mask-image: none;
    border: 1px solid var(--bdr); border-radius: 12px; background: var(--sf);
    padding: 6px; margin-top: 4px; }
  .site-header.nav-open .main-nav a {
    padding: 12px; font-size: 14px; border-radius: 0; flex: 1 1 auto; }
  .site-header.nav-open .main-nav a + a { border-top: 1px solid var(--bdr); }
  .site-header.nav-open .main-nav a:first-child { border-radius: 8px 8px 0 0; }
  .site-header.nav-open .main-nav a:last-child { border-radius: 0 0 8px 8px; }
  /* Hero CTAs: the base .btn-primary/.btn-secondary carry white-space:nowrap
     (styles.css), so the TR label "Günün ücretsiz tahminlerini gör" set the
     hero grid track's min-content to 384px inside a 375px viewport — the
     whole homepage then clipped 9px of itself behind overflow-x:hidden.
     On phones a hero button wraps its text instead of widening the page. */
  .sb-hero-ctas a { white-space: normal; text-align: center; min-width: 0;
    max-width: 100%; }
  .main-nav { flex-wrap: nowrap; }
  .main-nav a { flex: 0 0 auto; }
  .header-controls { flex-wrap: wrap; gap: 6px; }
  .header-controls .select-wrap select { max-width: 44vw; }
  /* ITEM 8b (owner, 2026-07-22): the stacked header rows TOUCHED — measured
     at 390: logo row bottom 50px, controls row top 50px (0px gap), nav 4px.
     One row-gap on the spacing scale gives the stack an intentional rhythm
     (logo → 10 → controls → 10 → nav); the nav's ad-hoc 4px margin dies
     with it. */
  .site-header .container { row-gap: 10px; }
  .site-header .main-nav { margin-top: 0; }
}

/* ══ A3 · Picks summary tiles ══════════════════════════════════════
   The 4-tile row was a flex strip: each tile sized itself, so the
   Next-Kickoff tile (two-line match + tip) stood much taller than its
   neighbours and the row stopped short, leaving dead space on the right.
   Same fix as the results stat tiles — equal columns that fill the row, and
   shared row tracks so label / value / sub sit on common baselines however
   tall any one tile's content is. */
.stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  grid-template-rows: auto auto auto;
  gap: 12px;
  align-items: stretch;
}
.stat-strip > .stat-tile {
  display: flex; flex-direction: column;
  align-self: stretch;        /* beat the global `.stat-tile{align-self:start}` so
                                 every tile fills the 1fr row → equal heights */
  min-width: 0;               /* long league names must not widen the track */
  background: var(--sf); border: 1px solid var(--bdr); border-radius: 12px;
  padding: 15px 16px; min-height: 96px;
}
.stat-strip > .stat-tile .label { min-height: 0; }
/* value anchored to the bottom (label top), so every tile shares one baseline and
   a lone number never floats — 4 uniform single-value tiles (owner 2026-08-05). */
.stat-strip > .stat-tile .value { font-size: 32px; line-height: 1; margin-top: auto; }
.stat-strip > .stat-tile .sub { margin-top: 5px; padding-top: 0; min-height: 1.55em; line-height: 1.55; }  /* full-line slot so an empty sub reserves the same box as a text one → values share a baseline */
/* every tile reserves a sub slot so the big values share one baseline (owner
   2026-08-05). The closest-game tile gets a small corner lock (members peek). */
.stat-strip > .stat-tile { position: relative; }
.stat-strip > .stat-tile .tile-lock { position: absolute; top: 13px; right: 14px;
  color: var(--risk); opacity: .85; display: inline-flex; }
/* A tile with MORE children than the span silently auto-places the extras ON
   TOP of the last track — that is how the Next-Kickoff tile rendered its
   market line and its countdown in the same space. Any child past the third
   is pushed into the last row rather than overlapping it, so a structural
   mistake degrades to "cramped" instead of "unreadable". */
.stat-strip > .stat-tile > *:nth-child(n+4) { grid-row: 3; align-self: end; }
/* the kick tile's two middle lines share ONE value slot */
.stat-strip .kick-value { min-width: 0; }
.stat-strip .kick-value .kick-match,
.stat-strip .kick-value .kick-tip { overflow-wrap: anywhere; }
@supports not (grid-template-rows: subgrid) {
  .stat-strip > .stat-tile { display: flex; flex-direction: column; }
  .stat-strip > .stat-tile .label { min-height: 2.1em; }
}
/* the kick tile no longer sets its own width — the grid owns the track */
.stat-strip .kick-tile { min-width: 0; max-width: none; }
.stat-strip .stat-tile .sub { margin-top: auto; padding-top: 5px; }

/* ══ A4 · Sticky footer ════════════════════════════════════════════
   Short pages (contact, checkout, an empty slate) left a large dead band of
   page background below the footer because nothing forced the document to
   fill the viewport. */
html { min-height: 100%; }
body { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
body > main, body > .page, body > .container { flex: 1 0 auto; }
body > .site-footer { flex: 0 0 auto; margin-top: auto; }

/* ══ A4 · Footer with substance ════════════════════════════════════
   A site bottom carrying one line of copyright read as unfinished. Same
   design language (ink band, mono caps, amber accents), more structure. */
.site-footer { background: var(--ink); border-top: none; margin-top: 56px;
  padding-inline: 0; }   /* the shell inside owns the gutter */
.site-footer .container { padding-block: 40px 22px; }
.sb-foot-grid {
  display: grid; gap: 30px 40px;
  /* Two link columns since the Settings column went (2026-07-20, item 4).
     The brand column absorbs the freed width rather than leaving a hole on
     the right, so the row still spans the shell edge to edge. */
  grid-template-columns: minmax(220px, 2.4fr) repeat(2, minmax(130px, 1fr));
  padding-bottom: 28px; border-bottom: 1px solid var(--ink-2);
}
.sb-foot-brand .logo { margin-bottom: 12px; display: inline-flex; }
.sb-foot-brand p { font: 500 12px/1.6 var(--font-num); color: var(--ink-mu);
  margin: 0; max-width: 320px; }
.sb-foot-col h3 { font: 700 10.5px var(--font-num); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ac); margin: 0 0 12px; }
.sb-foot-col a, .sb-foot-col span { display: block; color: var(--ink-mu);
  font: 500 12.5px var(--font-num); text-decoration: none; margin-bottom: 8px; }
.sb-foot-col a:hover { color: var(--ink-tx); }
.sb-foot-col select { width: 100%; max-width: 190px; background: #1c1a16;
  color: var(--ink-tx); border: 1px solid var(--ink-2); border-radius: 8px;
  padding: 7px 10px; font: 500 12.5px var(--font-num); }
/* the not-a-bookmaker line carries real weight at the site bottom */
.sb-foot-statement { padding: 22px 0 0; font: 700 13px/1.6 var(--font-num);
  color: var(--ink-tx); letter-spacing: .01em; }
.sb-foot-statement .sb-18 { color: var(--ac) !important; }
.sb-foot-base { display: flex; flex-wrap: wrap; gap: 10px 26px;
  justify-content: space-between; align-items: center; padding-top: 16px; }
.sb-foot-base .copyright { font: 500 11.5px var(--font-num); color: var(--ink-mu); }
/* E2 · the odds-timing sentence keeps its substance, loses its weight */
.sb-foot-note { font: 500 11px/1.55 var(--font-num); color: #5f5b53;
  max-width: 640px; margin-top: 4px; }
@media (max-width: 860px) {
  .sb-foot-grid { grid-template-columns: 1fr 1fr; }
  .sb-foot-brand { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .sb-foot-grid { grid-template-columns: 1fr; gap: 24px; }
}

/* A5 · drag affordance on the proof deck */
.prov-rail { cursor: grab; }
.prov-rail.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.prov-rail.is-dragging a { pointer-events: none; }

/* A3 (mobile) · styles.css's 640px rule resets grid-template-rows, which kills
   the subgrid and let the tiles drift apart again (30px spread at 390px). */
@media (max-width: 640px) {
  .stat-strip { grid-template-columns: 1fr 1fr; grid-auto-rows: auto; }
  .stat-strip > .stat-tile { grid-row: auto; }
}

/* C1 · reserve the header controls' space so nothing reflows when the live
   values land. The account word swaps between "Sign in" and "Account" (and
   their translations), so the control is sized for the longer of the two. */
.account-link { min-width: 104px; justify-content: center; }
@media (max-width: 900px) { .account-link { min-width: 0; } }
.select-wrap select#currency-select { min-width: 68px; }
.select-wrap select#lang-select { min-width: 96px; }

/* ══ (2) Pick-card internal alignment ══════════════════════════════
   Cards in a grid row stretch to the tallest, but their INSIDES sat at
   natural heights, so the odds/stake/kickoff row and its divider landed at
   different heights across one row — and with a three-line match name the
   "Pick:" line could run into the divider above the figures.

   The card is a flex column with the foot pinned to the bottom. Every card in
   a row is the same height, so every foot (and its divider) lands on the same
   line, whatever the match name does. Deliberately NOT subgrid: the card has
   optional sections (category chip, insight line), and a variable child count
   against a fixed span is exactly what made the Next-Kickoff tile overlap. */
.bet-card { display: flex; flex-direction: column; min-width: 0; }
.bet-card > .top { flex: 0 0 auto; }
/* the gap a short card would leave collects ABOVE the divider, never inside
   the text, so nothing can overlap the rule */
.bet-card .match { overflow-wrap: anywhere; }
/* The figures row collided in German: "Einsatz" and "Anpfiff in" are far wider
   than "Stake"/"Kickoff in", and a nowrap flex row with no shrink budget let
   the columns run into each other (caught at 1440px in DE, invisible in EN).
   The row may wrap, each column may shrink, and only the NUMBERS stay nowrap. */

/* OWNER DECISION 2026-07-19 — LEAVE THIS AS IT IS. Do not "fix" it later.
   A locked card's figures row
   sits inside the blurred decoy with the "why is this hidden" explainer BELOW
   it, so its divider lands ~43px above the dividers of open cards in the same
   row, whose figures row is the last element. Every card in the row is the same
   height and the same top — this is one element of extra content, not a
   layout bug, and it cannot be closed by CSS alone without either moving the
   explainer above the veil (which breaks the deliberate read order: why → what
   → what you get → what this is) or reserving dead space on every open card.
   The owner's call: locked cards already read as deliberately different
   (amber border, veil, unlock CTA), so a slightly different internal rhythm
   reads as INTENT rather than error. Neither alternative is worth it —
   breaking the read order, or reserving dead space on every open card. The
   locked zone still takes the slack so the gap sits above the explainer
   rather than inside the card body. */
.bet-card.locked .locked-zone { margin-top: auto; }
.bet-card.locked .veil-why { flex: 0 0 auto; }

/* ══ C4 · Cancellation modal ═══════════════════════════════════════
   COMPLIANCE, expressed in CSS: the two actions are IDENTICAL in size,
   weight, padding and colour weight. Cancelling is not smaller, greyer,
   lower, or slower to reach than keeping. Do not "improve conversion" by
   changing this — EU rules require cancelling to be as easy as subscribing,
   and the pair below is the evidence that it is. */
.cx-overlay { position: fixed; inset: 0; z-index: 400; display: flex;
  align-items: center; justify-content: center; padding: 20px;
  background: rgba(20,19,16,.55); }
.cx-modal { background: var(--sf); border: 2px solid var(--ink);
  border-radius: 14px; padding: 28px 26px 24px; width: 100%; max-width: 470px;
  position: relative; max-height: 90vh; overflow-y: auto; }
.cx-modal h3 { margin: 0 0 14px; font-size: 21px; }
.cx-modal p { margin: 0 0 12px; font-size: 14px; line-height: 1.6; color: var(--mu); }
.cx-modal .cx-keeps { color: var(--tx); font-weight: 600; }
.cx-offer { border: 2px dashed var(--bdr); border-radius: 12px;
  padding: 16px 18px; margin: 16px 0 18px; background: var(--sf2); }
.cx-offer-t { font: 700 13px var(--font-num); color: var(--risk);
  text-transform: uppercase; letter-spacing: .05em; margin-bottom: 8px; }
.cx-offer p { margin-bottom: 12px; }
.cx-offer[hidden] { display: none !important; }
/* the two actions: same box, same type, same emphasis */
.cx-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-top: 18px; }
.cx-actions[hidden] { display: none !important; }
.cx-btn { font: 700 14px/1 var(--font-ui); padding: 14px 12px;
  border-radius: 10px; cursor: pointer; border: 2px solid var(--ink);
  min-height: 48px; text-align: center; }
.cx-cancel { background: var(--sf); color: var(--tx); }
.cx-cancel:hover { background: var(--sf2); }
.cx-keep { background: var(--ink); color: var(--ink-tx); }
.cx-keep:hover { background: #23211c; }
.cx-btn:disabled { opacity: .6; cursor: default; }
.cx-msg { margin-top: 14px; }
.cx-msg[hidden] { display: none !important; }
@media (max-width: 460px) {
  .cx-actions { grid-template-columns: 1fr; }
}

/* D1 · final score beside the match, so a reader can verify the grading */
.ft-score { display: inline-block; margin-left: 8px; padding: 1px 7px;
  border-radius: 5px; background: var(--sf2); border: 1px solid var(--bdr);
  font: 700 12px var(--font-num); color: var(--tx); white-space: nowrap;
  vertical-align: middle; }

/* (grouped filter rows are styled in the multi-select block below) */

/* ══ Nickname badge + explanation ══════════════════════════════════
   SUBORDINATE by construction: smaller than the pick line, lighter weight,
   muted colour, and it sits BELOW the market/pick so it can never be read as
   the market itself. */
.nick-chip { display: inline-flex; align-items: center; gap: 6px;
  /* the card is a flex COLUMN, so a child stretches to full width by
     default — the chip was rendering as a full-width bar rather than a
     badge, and it also made every phrase measure the same width. */
  align-self: flex-start; width: auto;
  margin: 8px 0 0; padding: 5px 11px 5px 9px;
  border: 1px solid color-mix(in srgb, var(--ac) 38%, var(--bdr));
  border-radius: 999px; background: var(--cream);
  font: 600 11.5px var(--font-ui); color: var(--risk);
  letter-spacing: .015em; cursor: pointer; max-width: 100%;
  overflow-wrap: anywhere; text-align: left; line-height: 1.3; }
/* a small mark so the chip reads as a NAME, not a stray label */
.nick-chip::before { content: ""; flex: 0 0 auto; width: 5px; height: 5px;
  border-radius: 50%; background: var(--ac); }
.nick-chip:hover, .nick-chip:focus-visible { color: var(--ink);
  border-color: var(--ac); background: #fbe3b4; outline: none; }
.nick-chip:focus-visible { box-shadow: 0 0 0 2px var(--ink); }
/* the one-line explanation: anchored BELOW the chip, so it never covers the
   market or the pick, and it wraps rather than pushing the card wider */
.nick-pop { margin-top: 6px; padding: 8px 10px; border-radius: 8px;
  background: var(--ink); color: var(--ink-tx); font-size: 11.5px;
  line-height: 1.5; display: flex; flex-direction: column; gap: 6px;
  max-width: 100%; overflow-wrap: anywhere; }
.nick-more { align-self: flex-start; background: transparent; border: none;
  color: var(--ac); font: 700 11px var(--font-ui); cursor: pointer; padding: 0;
  text-decoration: underline; }

/* ══════════════════════════════════════════════════════════════════
   MULTI-SELECT — rebuilt 2026-07-19
   The previous control was a dropdown whose row did four jobs in one cramped
   line with two absolutely-positioned children at hand-tuned offsets. It read
   as a default component dropped into the page rather than part of this
   product.

   Built from the existing system only: paper/ink/amber tokens, mono numerals
   for counts, the tier-badge pill treatment, and the 2px-ink border / 10-14px
   radius conventions the cards already use. Nothing new invented.
   ══════════════════════════════════════════════════════════════════ */

/* ── Closed control: the state a user sees most ── */
/* DEFECT 2 (2026-07-20): the trigger is ONE row, always 44px, whatever is
   selected. Chips used to wrap to as many rows as needed — 5 leagues made it
   ~3x taller, pushed the profit panel down the page, and left controls in the
   same row at different heights. Height is now fixed and JS collapses the
   overflow into a "+N" counter; the open panel still shows everything. */
.mselect-ctl { gap: 6px; background: var(--sf); border: 2px solid var(--bdr);
  border-radius: 10px; padding: 6px 34px 6px 10px;
  min-height: 44px; height: 44px; max-height: 44px; overflow: hidden; }
.mselect-chips { display: flex; flex-wrap: nowrap; overflow: hidden;
  align-items: center; gap: 6px; min-width: 0; flex: 1 1 auto; }
.mselect-chips .mschip { flex: 0 0 auto; max-width: 100%; }
.msmore { flex: 0 0 auto; font: 700 11px var(--font-num); color: var(--mu);
  background: var(--sf2); border-radius: 6px; padding: 3px 6px;
  white-space: nowrap; }
.mselect-ctl:hover { border-color: #d5d0c2; }
.mselect.open .mselect-ctl { border-color: var(--ink); }
.mselect-ctl .caret { right: 11px; color: var(--mu); display: inline-flex;
  transition: transform .18s ease; }
.mselect.open .mselect-ctl .caret { transform: translateY(-50%) rotate(180deg); }
.mselect-placeholder { font-weight: 600; font-size: 14px; }
/* chips carry the same weight and rhythm as the tier badges */
.mschip { background: var(--cream);
  border: 1px solid color-mix(in srgb, var(--ac) 40%, var(--bdr));
  border-radius: 8px; padding: 3px 4px 3px 9px; font: 600 13px var(--font-ui);
  color: var(--tx); gap: 2px; max-width: 190px; }
.mschip .x { display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 6px; color: var(--risk); padding: 0; }
.mschip .x:hover { background: color-mix(in srgb, var(--ac) 22%, transparent);
  color: var(--ink); }

/* ── Surface ── */
.mselect-scrim { display: none; }
/* The panel sizes to its CONTENT, independently of the trigger (2026-07-20).
   The trigger became a fixed 44px summary; the panel had no min-width, so it
   inherited the trigger's 239px and league names truncated to "Pri...",
   "Seri..." — two different entries rendering identically. A filter you cannot
   read is a filter you cannot use.

   max-height was 70vh, which ignores how far down the page the trigger sits:
   a 630px panel opening at y=382 ran to 1012 in a 900px viewport. Capping at
   400px keeps it on screen; the list scrolls inside with the search pinned. */
.mselect-panel { padding: 0; border: 2px solid var(--ink); border-radius: 14px;
  box-shadow: 0 18px 44px rgba(20,19,16,.18);
  width: max-content; min-width: 100%;
  max-width: min(420px, calc(100vw - 24px));
  /* Height: sized to the SPACE ACTUALLY AVAILABLE, not a fixed px cap.
     A 400px cap left a tall monitor showing ~7 rows with the 8th cut and
     hundreds of wasted pixels — the fade is for genuine overflow, not the
     first impression on a big screen. setOpen() measures the distance from
     the panel's top to the viewport bottom and sets an inline max-height;
     this rule is only the pre-measure fallback. */
  max-height: min(72vh, 560px);
  display: none; flex-direction: column; overflow: hidden; }
/* a control near the right edge opens leftwards so a wider panel cannot
   overflow the viewport — the market filter sits at the end of the row */
.mselect-panel.align-right { left: auto; right: 0; }
/* wide monitors: let long league names breathe — the 420px cap was sized for
   laptop widths */
@media (min-width: 1800px) {
  .mselect-panel { max-width: min(520px, calc(100vw - 24px)); }
}
/* the search stays reachable while the list scrolls under it */
.mselect-searchwrap { position: sticky; top: 0; z-index: 2; }
/* full option names, wrapped rather than truncated, at desktop widths */
.mselect-item .lbl { white-space: normal; overflow: visible;
  text-overflow: clip; }
.mselect.open .mselect-panel { display: flex; }
.mselect-head { display: none; }
.mselect-searchwrap { padding: 10px 10px 6px; border-bottom: 1px solid var(--bdr);
  background: var(--sf); }
.mselect-panel input.mselect-search { margin: 0; border: 2px solid var(--bdr);
  border-radius: 9px; min-height: 40px; font-size: 15px; }
.mselect-list { overflow-y: auto; padding: 6px; -webkit-overflow-scrolling: touch;
  /* ONE scrollbar treatment on every panel, both pages (2026-07-21): thin,
     no arrow buttons. The dashboard panel was tall enough to show the
     platform default — arrows and all — beside panels that never scrolled,
     which read as two different components. */
  scrollbar-width: thin; scrollbar-color: var(--bdr) transparent; }
.mselect-list::-webkit-scrollbar { width: 8px; }
.mselect-list::-webkit-scrollbar-track { background: transparent; }
.mselect-list::-webkit-scrollbar-thumb { background: var(--bdr); border-radius: 4px; }
.mselect-list::-webkit-scrollbar-thumb:hover { background: var(--mu); }
.mselect-list::-webkit-scrollbar-button { display: none; height: 0; }
/* Edge fades (2026-07-21): a partially visible row at a scroll edge reads as
   "continues" instead of "broken". State classes come from syncFades() in
   app.js — a static mask would fade the first row even at rest. Chosen over
   snapping max-height to a row multiple because rows are not uniform (the
   market panel mixes one-line rows, two-line grouped rows and headers, and
   the search filter changes the list live). The three rules exist because
   mask-image does not compose across classes. */
.mselect-list.fade-bottom {
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 26px), transparent);
  mask-image: linear-gradient(180deg, #000 calc(100% - 26px), transparent);
}
.mselect-list.fade-top {
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 26px);
  mask-image: linear-gradient(180deg, transparent, #000 26px);
}
.mselect-list.fade-top.fade-bottom {
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 26px,
    #000 calc(100% - 26px), transparent);
  mask-image: linear-gradient(180deg, transparent, #000 26px,
    #000 calc(100% - 26px), transparent);
}
.mselect-sep { height: 1px; background: var(--bdr); margin: 6px 4px; }

/* ── Rows: a GRID, so nothing is placed by hand-tuned offset ── */
/* The expander column is reserved on EVERY row, not just rows that have one:
   otherwise the tick sits at a different x on rows with and without an
   expander, and the column of ticks goes ragged — the same "siblings that do
   not line up" problem as the filter stack itself, one level down. */
.msrowwrap { display: grid; grid-template-columns: 1fr 44px;
  align-items: stretch; gap: 2px; }
.msrowwrap > .mselect-item { grid-column: 1; }
.msrowwrap > .msexpand { grid-column: 2; }
/* The tick LEADS the row. It used to sit hard right with the label hard
   left, leaving a wide empty gulf at 390px so the row read as two unrelated
   things at opposite edges. A leading control is also the native multi-select
   pattern, and it puts every tick in one column regardless of row content. */
.mselect-item { display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto; align-items: center;
  gap: 11px; width: 100%; min-height: 48px; padding: 9px 12px 9px 10px;
  border-radius: 10px; text-align: left; border: 2px solid transparent; }
.mselect-item .mstick { grid-column: 1; }
.mselect-item .swatch { grid-column: 2; }
.mselect-item .mstext { grid-column: 3; display: flex; flex-direction: column;
  gap: 3px; min-width: 0; }
.mselect-item .lbl { font: 600 14.5px/1.25 var(--font-ui); color: var(--tx);
  overflow-wrap: anywhere; }
/* the nickname: clearly secondary, but legible — not a whisper */
.mselect-item .msub { font: 500 12.5px/1.3 var(--font-ui); color: var(--mu);
  overflow-wrap: anywhere; }
/* the count is a designed element, in the mono numerals used site-wide */
.mselect-item .mscount { grid-column: 4; justify-self: end;
  font: 600 11.5px var(--font-num); color: var(--mu); background: var(--sf2);
  border: 1px solid var(--bdr); border-radius: 999px; padding: 3px 8px;
  min-width: 26px; text-align: center; font-variant-numeric: tabular-nums; }
.mselect-item .mstick { display: inline-flex; align-items: center;
  justify-content: center; width: 22px; height: 22px; border-radius: 7px;
  border: 2px solid var(--bdr); color: transparent; background: var(--sf);
  flex: 0 0 auto; }
/* Distinct idle / hover / focus states (2026-07-21). Hover and focus shared
   one grey background, so a row that kept programmatic or keyboard focus
   looked permanently hovered ("Team to score" stuck grey with the cursor
   elsewhere). Hover is now gated to devices that actually hover, and focus
   is an ink ring — related but unmistakably not the same state. */
@media (hover: hover) {
  .mselect-item:hover { background: var(--sf2); }
}
.mselect-item:focus-visible { outline: 2px solid var(--ink);
  outline-offset: -2px; background: none; border-color: var(--ink); }

/* ── Selected: unmistakable on a phone in daylight ── */
.mselect-item.sel { background: var(--cream);
  border-color: color-mix(in srgb, var(--ac) 55%, var(--bdr)); }
.mselect-item.sel .lbl { color: var(--ink); }
.mselect-item.sel .mstick { background: var(--ac); border-color: var(--ac);
  color: var(--ink); }
.mselect-item.sel .mscount { background: var(--sf);
  border-color: color-mix(in srgb, var(--ac) 45%, var(--bdr)); color: var(--risk); }

/* ── Expander: a real 44px control on the grid, not an absolute glyph ── */
.msexpand { display: inline-flex; align-items: center; justify-content: center;
  width: 44px; min-height: 48px; padding: 0; border: 2px solid transparent;
  border-radius: 10px; background: transparent; color: var(--mu);
  cursor: pointer; transition: transform .18s ease, background .15s ease; }
.msexpand:hover { background: var(--sf2); color: var(--tx); }
.msexpand:focus-visible { outline: none; border-color: var(--ink); color: var(--tx); }
.msexpand[aria-expanded="true"] { transform: rotate(180deg);
  background: var(--sf2); color: var(--ink); }

/* ── Nested specifics: read as a group, not as indented siblings ── */
.msdetail { margin: 2px 0 6px 12px; padding: 4px 0 4px 12px;
  border-left: 2px solid color-mix(in srgb, var(--ac) 35%, var(--bdr));
  background: color-mix(in srgb, var(--sf2) 60%, transparent);
  border-radius: 0 10px 10px 0; }
.msdetail[hidden] { display: none !important; }
.msdetail .mselect-item { min-height: 42px; padding: 7px 10px; }
.msdetail .mselect-item .lbl { font-size: 13.5px; font-weight: 500; color: var(--mu); }
.msdetail .mselect-item.sel .lbl { color: var(--ink); font-weight: 600; }

/* ══ MOBILE: a bottom sheet, not a dropdown ══════════════════════
   A dropdown fights a phone viewport and was the single biggest reason this
   felt improvised. A sheet is the pattern people expect on a phone, and it
   gives rows room to be 56px without feeling cramped. */
@media (max-width: 700px) {
  .mselect-scrim { display: block; position: fixed; inset: 0; z-index: 300;
    background: rgba(20,19,16,.68); backdrop-filter: saturate(.85) blur(1.5px);
    -webkit-backdrop-filter: saturate(.85) blur(1.5px); }
  .mselect-scrim[hidden] { display: none; }
  .mselect-panel { position: fixed; z-index: 301;
    left: 0; right: 0; bottom: 0; top: auto;
    /* explicitly reset the desktop content-sizing so it cannot leak into the
       bottom sheet, which must stay full-width */
    width: 100%; min-width: 0; max-width: none; max-height: 82vh;
    border-radius: 18px 18px 0 0; border-width: 2px 0 0;
    box-shadow: 0 -12px 40px rgba(20,19,16,.28);
    animation: ms-rise .22s cubic-bezier(.22,.9,.3,1);
    padding-bottom: env(safe-area-inset-bottom, 0); }
  .mselect-panel.align-right { left: 0; right: 0; }
  /* a real header: what this filter is, and a way out */
  .mselect-head { display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 18px 16px 12px; border-bottom: 2px solid var(--ink);
    background: var(--sf); position: relative; }
  .mselect-head::before { content: ""; position: absolute; top: 7px; left: 50%;
    transform: translateX(-50%); width: 38px; height: 4px; border-radius: 2px;
    background: var(--bdr); }
  .mselect-title { font: 800 15px var(--font-ui); text-transform: uppercase;
    letter-spacing: .02em; color: var(--tx); }
  /* a quiet exit: the loudest element on screen should never be the way out */
  .mselect-done { font: 700 13.5px var(--font-ui); color: var(--mu);
    background: transparent; border: 2px solid var(--bdr); border-radius: 9px;
    padding: 9px 16px; min-height: 44px; cursor: pointer; }
  .mselect-done:hover, .mselect-done:focus-visible { color: var(--ink);
    border-color: var(--ink); background: var(--sf2); outline: none; }
  .mselect-searchwrap { padding: 12px 16px 10px; }
  .mselect-list { padding: 8px 12px calc(28px + env(safe-area-inset-bottom, 0px));
    scroll-padding-bottom: 24px; }
  /* a soft fade at the bottom edge so a mid-row cut reads as more content
     below rather than as a rendering fault (and clears the home indicator) */
  .mselect-panel::after { content: ""; position: absolute; left: 0; right: 0;
    bottom: 0; height: calc(28px + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(to top, var(--sf), rgba(255,255,255,0));
    pointer-events: none; }
  .mselect-item { min-height: 56px; padding: 12px; gap: 12px; }
  .mselect-item .lbl { font-size: 15.5px; }
  .mselect-item .msub { font-size: 13px; }
  .msrowwrap { grid-template-columns: 1fr 48px; }
  .msexpand { min-height: 56px; width: 48px; }
  .msdetail .mselect-item { min-height: 48px; }
  body.ms-sheet-open { overflow: hidden; }
}
@keyframes ms-rise { from { transform: translateY(14%); opacity: .7; } }
@media (prefers-reduced-motion: reduce) {
  .mselect-panel { animation: none !important; }
}

/* ══ Dashboard filter stack ════════════════════════════════════════
   The four multi-selects and the SORT control were two different control
   types in one stack: the sort was a bare native <select> with its own
   smaller chevron, its own border and a narrower box. Same problem as the
   results filters — sibling controls that do not share a width or an edge.
   The stack is a grid (one column on mobile) and the sort select is dressed
   in the multi-select shell: same height, border, radius and chevron. */
.dash-prefs { display: grid; gap: 10px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.dp-field { min-width: 0; }
.dp-field .select-wrap { width: 100%; }
/* the shell, matched to .mselect-ctl exactly */
.dp-field .select-wrap select {
  width: 100%; min-height: 44px; padding: 6px 34px 6px 10px;
  background: var(--sf); color: var(--tx);
  border: 2px solid var(--bdr); border-radius: 10px;
  font: 600 14px var(--font-ui); cursor: pointer;
  appearance: none; -webkit-appearance: none; }
.dp-field .select-wrap select:hover { border-color: #d5d0c2; }
.dp-field .select-wrap select:focus-visible { border-color: var(--ink); outline: none; }
/* the same chevron the multi-select uses, at the same offset */
.dp-field .select-wrap::after {
  content: ""; position: absolute; right: 11px; top: 50%; width: 15px; height: 15px;
  transform: translateY(-50%); pointer-events: none;
  background: currentColor; color: var(--mu);
  -webkit-mask: no-repeat center/15px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  mask: no-repeat center/15px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  font-size: 0; }
@media (max-width: 640px) {
  .dash-prefs { grid-template-columns: 1fr; }
}

/* ══ Username field + prompt ═══════════════════════════════════════ */
.acct-row.un-row .un-btn { margin-left: auto; padding: 6px 14px; min-height: 36px;
  font: 700 12.5px var(--font-ui); }
.acct-row .v.un-unset { color: var(--mu); font-style: italic; }
.acct-row.un-editrow { display: block; }
.acct-row.un-editrow[hidden] { display: none !important; }
.un-field { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.un-field input { width: 100%; min-height: 44px; padding: 10px 12px;
  border: 2px solid var(--bdr); border-radius: 10px; background: var(--sf);
  color: var(--tx); font: 600 15px var(--font-ui); outline: none; }
.un-field input:focus { border-color: var(--ink); }
.un-help { font: 500 12px/1.45 var(--font-ui); color: var(--mu); }
.un-status { font: 600 12.5px var(--font-ui); min-height: 1.2em; }
.un-status.ok { color: var(--safe); }
.un-status.err { color: var(--lost); }
.un-status.checking { color: var(--mu); }
.un-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.acct-btn.primary { background: var(--ac); border-color: var(--ink); color: var(--ink); }
.acct-btn.primary:hover { background: #ffb32a; }
/* the prompt: skipping must be as easy to reach as saving */
.un-overlay .si-input { width: 100%; min-height: 44px; padding: 10px 12px;
  margin-top: 14px; border: 2px solid var(--bdr); border-radius: 10px;
  background: var(--sf); color: var(--tx); font: 600 15px var(--font-ui); outline: none; }
.un-overlay .si-input:focus { border-color: var(--ink); }
.un-overlay .un-help { margin-top: 6px; }
.un-skip { display: block; width: 100%; margin-top: 10px; padding: 12px;
  background: transparent; border: 2px solid var(--bdr); border-radius: 10px;
  color: var(--mu); font: 700 13.5px var(--font-ui); cursor: pointer; min-height: 44px; }
.un-skip:hover { color: var(--tx); border-color: var(--ink); }

/* ══ Member-only slicing controls ══════════════════════════════════
   The SAME locked language as a veiled pick card — amber border, lock, and a
   clear "Members" affordance — NOT a greyed-out disabled control. A locked
   control that looks broken sells nothing; one that looks withheld sells the
   membership. The controls stay in the DOM for exactly that reason.
   The TIME RANGE is deliberately absent from this list: it stays public. */
.search-wrap { position: relative; display: block; min-width: 0; }
.search-wrap input { width: 100%; }
.slice-locked { position: relative; cursor: pointer; border-radius: 10px; }
/* the control itself stops receiving pointer events; the wrapper handles the
   tap and opens the upgrade path instead of the filter sheet */
.slice-locked > .mselect-ctl,
.slice-locked > input,
.slice-locked .mselect-panel { pointer-events: none; }
/* the input[type="text"] variant repeats the attribute so this rule OUTWEIGHS
   the base `.filters input[type="text"]` styling — without it the locked
   search box stayed white while every locked dropdown went amber (owner
   screenshot, 2026-07-29) */
.slice-locked > .mselect-ctl,
.slice-locked > input,
.slice-locked > input[type="text"] {
  border-color: color-mix(in srgb, var(--ac) 55%, var(--bdr));
  background: var(--cream);
  color: color-mix(in srgb, var(--tx) 62%, transparent);
  /* reserve the badge's footprint so the control text can never run
     underneath the MEMBERS pill; what does not fit ellipsizes */
  padding-right: 110px;
}
.slice-locked > .mselect-ctl .mselect-chips {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slice-locked > input, .slice-locked > input[type="text"] { text-overflow: ellipsis; }
.slice-locked .mselect-placeholder { color: color-mix(in srgb, var(--tx) 62%, transparent); }
.slice-locked > input::placeholder { color: color-mix(in srgb, var(--tx) 55%, transparent); }
.slice-locked .caret { opacity: .35; }
/* the lock affordance, in the pick-card idiom */
.slice-lock-badge { position: absolute; top: 50%; right: 10px;
  transform: translateY(-50%); display: inline-flex; align-items: center;
  gap: 5px; padding: 3px 9px 3px 7px; border-radius: 999px;
  background: var(--ink); color: var(--ac);
  font: 700 10.5px var(--font-ui); text-transform: uppercase;
  letter-spacing: .05em; pointer-events: none; white-space: nowrap; }
.slice-lock-badge svg { flex: 0 0 auto; }
.slice-locked:hover > .mselect-ctl,
.slice-locked:hover > input { border-color: var(--ac); }
.slice-locked:focus-visible { outline: none; }
.slice-locked:focus-visible > .mselect-ctl,
.slice-locked:focus-visible > input { border-color: var(--ink); }
/* the caret would collide with the badge on a narrow control */
.slice-locked .mselect-ctl { padding-right: 96px; }
.slice-locked > input { padding-right: 96px; }
/* the upgrade sheet's reassurance line reads quieter than the offer */
.gate-overlay .gate-open { color: var(--mu); font-size: 13px; }

/* ROI vs bankroll: each figure states its own denominator, and one sentence
   connects them. Existing tokens and type scale — no new component. */
.sim-assume { font: 500 11px/1.5 var(--font-num); color: var(--ink-mu);
  margin-top: 8px; letter-spacing: .01em; }
.sim-twoways { font: 500 11.5px/1.55 var(--font-ui); color: var(--ink-mu);
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--ink-2);
  max-width: 520px; }


/* ---- Hero art (all 11 locales) — the framed Nostradamus portrait replaces
   the data panel; the live headline number moves onto a museum plaque so
   the proof stays above the fold. Chart/mini stats intentionally absent
   here (home.js null-guards them). OWNER-DIRECTED — see CLAUDE.md. ---- */
.sb-hero-art { background: none; padding: 0; display: flex; flex-direction: column;
  gap: 14px; min-height: 0; }
/* The painting FITS the hero row (which the copy column sizes) instead of
   dictating it. width:auto keeps the aspect so the border hugs the frame. */
/* item 15 REDO (owner: "like bet slip on photo, photo bigger"): the STAGE
   owns the composition — portrait fills it edge to edge (the photo's own
   wall margins are the matte; mild cover-crop trims wall, never frame),
   and the paper slip sits ON the photo, rotated, like the approved ad
   mockup. The two scale as ONE unit at every width. */
.sb-hero-art .sb-art-stage { position: relative; width: 100%;
  aspect-ratio: 1100 / 1381; max-height: min(72vh, 720px);
  align-self: center; flex: 1 1 auto; min-height: 0; }
.sb-hero-art .sb-art-img { position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: center 30%;
  display: block; border-radius: 8px; border: 1px solid var(--bdr); }
@media (min-width: 901px) {
  .sb-hero-art { min-height: clamp(480px, 58vh, 640px); padding-top: clamp(20px, 2.5vw, 36px); }
}
@media (max-width: 900px) {
  .sb-hero-art .sb-art-img { flex: none; width: 100%; height: auto; align-self: auto; }
}
.sb-art-plaque { background: var(--sf); border: 1px solid var(--bdr);
  border-radius: 8px; padding: 16px 18px; }
.sb-plaque-label { font: 500 11px/1.2 var(--font-num); letter-spacing: .12em;
  color: var(--mu); }
.sb-plaque-title { font-weight: 700; font-size: 18px; line-height: 1.3;
  color: var(--ink); margin-top: 6px; }
.sb-plaque-live { margin-top: 10px; display: flex; align-items: baseline;
  gap: 10px; flex-wrap: wrap; }
.sb-art-plaque .sb-big { font: 700 26px/1 var(--font-num); letter-spacing: -0.02em;
  color: var(--ink); }
.sb-art-plaque .sb-big.is-pos { color: var(--ac-deep, #b8760a); }
.sb-art-plaque .sb-big.is-neg { color: var(--lost); }
.sb-art-plaque .sb-big-label { font-size: 12.5px; color: var(--mu); margin-top: 0; }
.sb-art-plaque .sb-big-second { color: var(--mu); }

/* ── LEGAL PAGES (terms/privacy/responsible, 2026-07-23) ──
   Long-form reading: one measure, label-voice kicker, quiet h2 rhythm.
   The copy is per-locale static; [ENTITY] is the owner's placeholder. */
.legal-page { max-width: 760px; margin: 0 auto; padding: 46px 20px 70px; }
.legal-head .legal-updated { font: 500 11px var(--font-num);
  letter-spacing: .08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--tx) 45%, transparent); margin-top: 14px; }
.legal-body { margin-top: 26px; }
.legal-sec { margin-bottom: 30px; }
.legal-sec h2 { font-size: 17px; font-weight: 700; margin-bottom: 10px;
  letter-spacing: -.01em; }
.legal-sec p { font-size: 14.5px; line-height: 1.65; color: var(--tx);
  margin-bottom: 10px; overflow-wrap: anywhere; }
.legal-sec a { font-weight: 600;
  color: color-mix(in srgb, var(--risk) 90%, var(--tx)); }
.legal-sec a:hover { text-decoration: underline; }
.sb-foot-legal { display: inline-flex; flex-wrap: wrap; gap: 4px 16px;
  margin-left: 18px; }
.sb-foot-legal a { font: 500 11px var(--font-num); letter-spacing: .06em;
  text-transform: uppercase; text-decoration: none;
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
.sb-foot-legal a:hover { color: var(--tx); text-decoration: underline; }
@media (max-width: 640px) {
  .legal-page { padding: 30px 16px 50px; }
  .sb-foot-legal { margin-left: 0; display: flex; width: 100%;
    margin-top: 8px; }
}

/* ── ITEM 14: the member strip — bankroll + tenure ABOVE the picks ──
   One quiet band in the label voice: captions small, numbers mono, no size
   jump — the PICK stays the page's loudest element. */
/* OWNER (2026-07-23): matched to the SAME card standard Today's Slate's
   .stat-tile uses (white --sf, 2px border, the shared --radius) instead of
   its own one-off values (--sf2 "raised/hover" tone, 1px border, a
   hardcoded 10px radius) -- not a new look, the existing one. */
.member-strip { display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 18px 22px; padding: 18px 20px; margin: 0 0 16px;
  border: 2px solid var(--bdr); border-radius: var(--radius); background: var(--sf); }
.member-strip .dp-field { margin: 0; }
/* DEFECT B (owner: bankroll section unclear) — the bankroll and unit inputs
   now share ONE block (.ms-flow) with the computed line + a plain-language
   sentence directly beneath, so editing either input and seeing its effect
   is one glance, not a hunt across the strip. .ms-flow gets more of the
   strip's width than the tenure cell — it is the block doing the explaining. */
.member-strip { grid-template-columns: minmax(300px, 1.3fr) minmax(220px, 1fr); }
.member-strip .ms-flow > .dp-label:first-child { display: block; margin-bottom: 8px; }
/* BUG 2 (owner, 2026-07-23 fresh report — "dense, cramped wall of text"):
   Bankroll Planner and Since You Joined read as one undifferentiated block
   because the grid's column GAP was the only thing between them — no line,
   no edge, nothing a reader's eye actually catches. A real vertical rule
   (desktop) / horizontal rule (stacked mobile, below) now marks the section
   boundary explicitly, and the caption ABOVE it (.dp-label:first-child)
   gets the same block+margin treatment .ms-flow already had, so both
   sub-sections open with an equally-weighted header. This is separation
   via a rule on the spacing scale, not a second bordered card — the strip
   stays ONE quiet band, just legibly divided in two. */
.member-strip .ms-tenure { border-left: 1px solid var(--bdr); padding-left: 24px; }
.member-strip .ms-tenure > .dp-label:first-child { display: block; margin-bottom: 8px; }
.member-strip .bk-inputs { display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; }
.member-strip .bk-inputs .dp-field { min-width: 128px; }
.member-strip .bk-arrow { font-size: 15px; color: var(--mu); flex: 0 0 auto;
  margin-top: 14px; /* roughly centers on the input row, under the label */ }
.member-strip .br-unit { font: 600 13px var(--font-num);
  font-variant-numeric: tabular-nums; margin-top: 10px; }
/* the human-language line: the actual "why" a member sees a stake size —
   never louder than a pick code (13px cap, ONE level below it) but ink, not
   muted, since this IS the strip's answer to "what is going on" */
.member-strip .bk-plain { font: 500 12.5px var(--font-ui); color: var(--tx);
  line-height: 1.4; margin-top: 3px; max-width: 46ch; }
.member-strip .ms-since { font: 500 12px var(--font-num); color: var(--mu);
  margin: 6px 0 4px; }
.member-strip .sj-grid { display: flex; flex-wrap: wrap; gap: 10px 20px;
  margin-top: 8px; }
.member-strip .sj-grid .stat-tile { border: none; background: transparent;
  border-radius: 0; padding: 0; }
.member-strip .sj-grid .stat-tile .value { font-size: 13px; }  /* < the 14px pick code — the strip never out-speaks a pick (measured red) */
/* BUG 2: every caption/sub in this strip floored at 11px (was 10-10.5px in
   three places below) — "nothing sub-11px" on mobile, applied everywhere
   since the strip renders identically at both breakpoints. 0.5-1px is
   inaudible as loudness but was failing the floor. */
.member-strip .dp-label { font-size: 11px; }
.member-strip .sj-grid .stat-tile .sub { font-size: 11px; }
.member-strip .br-hist { font-size: 11px; color: var(--mu); }
.member-strip .sj-best-line { font-size: 11px; color: var(--mu); margin-top: 8px; }
/* DEFECT 7 (owner, live screenshot): the Bankroll-Planner projection value
   (.bh-v) kept its STANDALONE bankroll-page scale INSIDE the strip —
   24px/700 red — so "-₺17.5k · 81W-39L · ROI -9.7%" shouted louder than a
   14px pick code. Item 14 calmed the neighbours (br-unit, sj-grid, since-
   joined) but never overrode .bh-v here. Now it speaks the strip's own
   label voice: one calm reference block, quiet caption, the number mono at
   13px (< the pick), the loss RED preserved (won/lost is a legitimate color
   role) but its SIZE no longer rivals the picks table.
   BUG 2 (2026-07-23 fresh report): the box itself used to sit at the SAME
   spacing as the plain sentence above it and the disclaimer line inside it
   (6px top margin, dashed hairline, transparent-on-transparent against the
   strip's own --sf2 fill) — nothing marked it as a distinct callout, so it
   read as more crammed text rather than a bounded reference box. It now
   sits on --sf (the acct-card's own white/card surface, one level "up" from
   the strip's --sf2 raised fill — the same alternating-surface convention
   .stat-tile already uses against --bg), with a SOLID border (dashed reads
   as draft/placeholder, not an intentional box) and real margin-top
   separating it from the sentence above. */
.member-strip .br-hist { border: 1px solid var(--bdr); border-radius: 8px;
  background: var(--sf); padding: 12px 14px; margin-top: 14px; }
.member-strip .bh-k { font-size: 11px; }
.member-strip .bh-v { font: 600 13px var(--font-num);
  font-variant-numeric: tabular-nums; margin-top: 4px; }
.member-strip .bh-note { font-size: 11px; margin-top: 5px; }
/* DEFECT 6 (owner, live desktop screenshot, 2026-07-23): sort+view used to
   live INSIDE .dash-prefs (the auto-fit filter grid), so the toggle — a
   small, fixed-size control — was force-stretched into a full 1fr grid
   column, leaving a trailing dead-space rectangle that grew with viewport
   width (measured 252px @1920, 302px @2560), plus an unlabelled sort
   <select> that could render as a bare chevron depending on view state.
   Fixed at the MARKUP level: sort+view moved out into their own row
   reusing picks.html's OWN .pick-toolbar/.pk-sort-wrap classes verbatim
   (see dashboard.html + account.js) — same component, not a lookalike.
   The .dash-prefs.view-table/.dp-view rules that used to live here are
   gone with the elements they targeted (a stray extra "}" that used to
   sit at the end of this block is also gone). */
@media (max-width: 640px) {
  .member-strip { grid-template-columns: 1fr; padding: 14px; gap: 20px; }
  .member-strip .bk-inputs .dp-field { min-width: 0; flex: 1 1 120px; }
  /* BUG 2 mobile: the vertical rule reads as a stray line once the grid
     stacks to one column — swap it for a horizontal rule ABOVE Since You
     Joined, with enough top space that it reads as a section break rather
     than a stray hairline crossing a wall of text. */
  .member-strip .ms-tenure { border-left: none; padding-left: 0;
    border-top: 1px solid var(--bdr); padding-top: 18px; margin-top: 2px; }
}

/* ── ITEM 16.1 (owner: stranded columns): DESKTOP table architecture ──
   Contentful columns CLUSTER LEFT with bounded, viewport-independent
   widths (identical px at 1440 and 1920 — asserted); ALL slack pools into
   the single elastic gap inside the odds column, between the pick and the
   right-aligned numeric block. Reading a row is one saccade, not a meter.
   Mobile (<=640) keeps its two-line collapse untouched. */
@media (min-width: 1024px) {
  .tp-table .tpt-time { width: 1%; white-space: nowrap; }
  .tp-table .tpt-teams { width: 360px; max-width: 360px; }
  /* PICK BUDGET (owner 2026-07-27, "pick not clear"): 240px fit the short codes
     ("O2.5") but the expanded-market full names + the two badges ("Away Team
     Clean Sheet: Yes · LOW · STRONG") ellipsized while the elastic gap sat empty.
     The budget grows with the viewport — bounded, so the cluster-left
     architecture holds and nothing overflows at 1024.

     HUGS ITS CONTENT (owner 2026-07-30, "table view not good on web"): a FIXED
     budget cannot serve 11 languages. Measured with the page's own font over
     the real market vocabulary, the longest pick headline is 203px in English
     and 443px in Bosnian — a 2.2x spread — so any single number is either a
     void or a truncation. It was both: English carried 183px of dead space in
     every dashboard row (40% of the column) while bs/sr, needing ~517px with
     badges, ellipsized against the same cap. width:1% hugs the content and the
     max-width only catches a runaway, so each locale takes what it needs and
     the slack pools where this block wants it — the one elastic gap in the
     odds column. Same mechanism the history table above uses.
     Measured 2026-07-30 (EN, 1440): pick 460 -> 296px, dead 183 -> 19px, zero
     ellipsis, row heights unchanged. */
  .tp-table .tpt-pick { width: 1%; max-width: 340px; }
  .tp-table .tpt-odds { width: auto; }        /* the ONE elastic gap */
  .tp-table .tpt-stake, .tp-table .tpt-status { width: 1%; white-space: nowrap; }
  /* the history table clusters the same way */
}
@media (min-width: 1280px) {
  /* the cap rises with the viewport; the cell still hugs, so this only ever
     buys room for the long-locale headlines (bs/sr ~517px with badges) that
     used to ellipsize against a 460px wall. */
  .tp-table .tpt-pick { width: 1%; max-width: 560px; }
  /* The history table needs no rule of its own: its picks are short codes
     ("Over 1.5" = 55px, widest measured 95px) with no record line, so hugging
     is exactly what the rule above already gives it — the ~365px void that
     the old fixed 460px budget opened inside its pick cell is gone with the
     budget. (Two rules briefly existed here, from two sessions fixing the
     same complaint; this is the survivor.) */
}
/* (A 1080px table cap briefly lived here, 2026-07-30 — owner: "we have fixed
   wide screen problems previously", i.e. ITEM 16.1's full-width architecture
   IS the approved wide-screen design. Reverted the same day; do not re-add.) */
/* ITEM 16.3: the kickoff cell is left-aligned mono — times align down the
   column by construction; the day marker trails after the time. The cell
   never wraps and never exceeds its shrink-to-fit width. */
.tpt-time { font-variant-numeric: tabular-nums; }

/* ── ITEM 15 REDO: the paper betslip ON the portrait (approved mockup) ──
   Paper white, hairline border, drop shadow, -7deg; ALL text ink-on-paper
   (the first pass rendered faint grey — rejected; these colors are
   explicit and AA-measured). The amber rule under the header is BRAND
   accent; the total stays INK (never won-green, never a real figure). */
.sb-art-coupon { position: absolute; right: 5%; bottom: 6%;
  width: clamp(158px, 56%, 244px); transform: rotate(-7deg);
  background: #FDFBF6; border: 1px solid #E8E4D8;
  border-radius: 3px; box-shadow: 0 10px 24px rgba(20, 19, 16, .45);
  padding: 10px 12px 9px; }
.sb-art-coupon .cp-title { font: 700 11px var(--font-num);
  letter-spacing: .12em; text-transform: uppercase; color: #141310;
  border-bottom: 2px solid var(--ac); padding-bottom: 6px; margin-bottom: 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-art-coupon .cp-row { display: flex; align-items: baseline; gap: 8px;
  font-family: var(--font-num); font-size: 10.5px; color: #33302A;
  padding: 3px 0; border: none; }
.sb-art-coupon .cp-ev { flex: 1 1 auto; min-width: 0; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.sb-art-coupon .cp-odds { font-weight: 700; color: #141310;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.sb-art-coupon .tpt-res { font-size: 10.5px; white-space: nowrap; }
.sb-art-coupon .cp-total-row { display: flex; justify-content: space-between;
  align-items: baseline; gap: 8px; border-top: 1px dashed #E8E4D8;
  margin-top: 5px; padding-top: 6px; }
.sb-art-coupon .cp-total-row .tp-label { color: #6B675E; }
.sb-art-coupon .cp-total { font: 700 14px var(--font-num);
  font-variant-numeric: tabular-nums; color: #141310; white-space: nowrap; }
.sb-art-coupon .cp-marker { font: 500 9.5px var(--font-num); color: #55524B;
  margin-top: 4px; line-height: 1.4; }
@media (max-width: 900px) {
  .sb-art-coupon { right: 4%; bottom: 5%; }
}

/* item 16 ext: the tier tag inside the pick cell — quiet label voice but
   AA-legible (62% ink, the neutral-chip mix; the old standalone 40%-ink
   column was the ghost-grey disease the owner flagged) */
.tpt-tier { font: 500 10px var(--font-num); letter-spacing: .08em;
  text-transform: uppercase; white-space: nowrap;
  color: color-mix(in srgb, var(--tx) 62%, transparent); }

/* ══ Good Picks page (good-picks.js): risk-band sections + pick cards ══
   Member "Today's Picks" from good_picks.json — funded picks featured, unfunded
   greyed, over-30%-cap picks veiled for free viewers. Paper/ink/amber theme. */
.gp-band { margin: 0 0 22px; }
.gp-band-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px;
  padding-bottom: 6px; border-bottom: 1px solid color-mix(in srgb, var(--tx) 14%, transparent); }
.gp-band-name { font: 700 15px var(--font-num); letter-spacing: .02em; color: var(--tx); }
.gp-band-sub { font: 400 12px system-ui, sans-serif;
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
.gp-band-count { margin-left: auto; font: 600 13px var(--font-num);
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
.gp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.gp-card { border: 1px solid color-mix(in srgb, var(--tx) 14%, transparent);
  border-radius: 12px; padding: 12px 14px; background: var(--sf);
  display: flex; flex-direction: column; gap: 8px; }
.gp-card.gp-funded { border-left: 3px solid #b8860b; }
.gp-card.gp-unfunded { opacity: .6; }
/* veiled cards reuse the shared .bet-card.locked veil (app.js lockedCardHTML) */
.gp-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font: 600 11px system-ui, sans-serif; color: color-mix(in srgb, var(--tx) 55%, transparent); }
.gp-league { text-transform: uppercase; letter-spacing: .03em; }
.gp-kick { margin-left: auto; white-space: nowrap; }
.gp-tag { font: 700 10px system-ui, sans-serif; text-transform: uppercase;
  letter-spacing: .04em; padding: 2px 7px; border-radius: 999px; }
.gp-today { background: #b8860b; color: #fff; }
.gp-notfunded { background: color-mix(in srgb, var(--tx) 10%, transparent);
  color: color-mix(in srgb, var(--tx) 70%, transparent); }
.gp-match { font: 700 15px system-ui, sans-serif; color: var(--tx); line-height: 1.25; }
.gp-pick { font: 600 14px system-ui, sans-serif;
  color: color-mix(in srgb, var(--tx) 90%, transparent); }
.gp-metrics { display: flex; gap: 14px; flex-wrap: wrap; }
.gp-metric { display: flex; flex-direction: column; }
.gp-metric b { font: 700 15px var(--font-num); color: var(--tx); }
.gp-metric i { font: 400 10px system-ui, sans-serif; font-style: normal;
  text-transform: uppercase; letter-spacing: .03em;
  color: color-mix(in srgb, var(--tx) 55%, transparent); }
.gp-stake b { color: #b8860b; }
.gp-foot { display: flex; gap: 6px; flex-wrap: wrap; }
.gp-badge { font: 600 11px system-ui, sans-serif; border-radius: 999px; padding: 3px 9px;
  background: color-mix(in srgb, var(--tx) 8%, transparent);
  color: color-mix(in srgb, var(--tx) 65%, transparent); }
.gp-consensus { background: #1f2238; color: #fff; }
.gp-toggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font: 600 13px system-ui, sans-serif; color: var(--tx); user-select: none; }
.gp-toggle input { accent-color: #b8860b; width: 15px; height: 15px; cursor: pointer; }
/* the members-more CTA box is the SHARED #member-cta-box (app.js mountMemberCTA)
   — its styles live with the rest of the member-CTA CSS, nothing to add here. */

/* ── "Split my money" total input (app.js mountMoneySplit) — the total-amount
   staking model; the visitor's total is split across the funded picks. ── */
#money-split-host { margin: 0 0 14px; }
.money-split { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font: 600 13px system-ui, sans-serif; color: var(--tx); }
.money-split .ms-label { text-transform: uppercase; letter-spacing: .04em;
  font-size: 11px; color: color-mix(in srgb, var(--tx) 60%, transparent); }
.money-split .ms-field { display: inline-flex; align-items: center; gap: 4px;
  border: 1.5px solid color-mix(in srgb, var(--tx) 22%, transparent);
  border-radius: 10px; padding: 6px 10px; background: var(--sf); }
.money-split .ms-field:focus-within { border-color: #b8860b; }
.money-split .ms-sym { color: color-mix(in srgb, var(--tx) 60%, transparent); }
.money-split input { border: 0; background: transparent; color: var(--tx);
  font: 700 15px var(--font-num); width: 6.5ch; outline: none; padding: 0;
  -moz-appearance: textfield; }
.money-split input::-webkit-outer-spin-button,
.money-split input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.money-split .ms-hint { font-weight: 400; font-size: 11px;
  color: color-mix(in srgb, var(--tx) 55%, transparent); flex: 1 1 180px; }

/* Split-my-money input above the Dashboard My Day list */
.myday-money { margin: 6px 0 10px; }

/* good_picks value edge shown IN WORDS (owner 2026-08-04): a positive-tone tag
   so the reader gets "good bet" with no %. gp-topmodel class reserved for a
   top-model accent. */
.gp-value { color: var(--won); font-weight: 700; white-space: nowrap; }

.ai-facts{margin:0;padding-left:18px}
.ai-facts li{margin:5px 0;line-height:1.4}
