/* ── Fonts: self-hosted (#417) ───────────────────────────────────────────────
   This file used to open with @import url('https://fonts.googleapis.com/...') — which is why
   removing the <link> from login.html did NOT make the login path third-party-free, and my
   claim that it did was wrong. TC-08 only read the HTML and never looked here.

   Now all four faces ship from /assets/fonts. That kills a third-party request on every page
   (a privacy leak, and a render-blocking dependency on someone else's uptime), and an @import
   is the worst way to load a font besides: the browser cannot even discover it until this
   stylesheet has downloaded and parsed.

   All four are SIL Open Font License, which explicitly permits self-hosting. Each file is the
   VARIABLE font — one file covers every weight, which is why there are 4 files and not 12
   (Google serves the same bytes for :wght@400 and :wght@700; they were byte-identical).

   font-display: swap — text paints immediately in the fallback and re-renders when the face
   arrives. Never invisible, at the cost of a reflow.
   ------------------------------------------------------------------------- */
/* #1182 THREE @font-face BLOCKS REMOVED (DM Sans, Montserrat, Great Vibes) — all three were dead.
   No font-family rule anywhere in client/ consumed them: --font-head and --mono both alias --font
   (Inter), and Great Vibes was superseded when the Prime Vectors wordmark replaced the cursive
   (see the note further down this file). Their .woff2 files are gone with them.

   ⚠ Deleting them is HYGIENE, not a speed-up — a browser never downloads an @font-face it has no
   rule for, so these cost nothing at runtime. What they cost was 118KB in the tree and a false
   premise: the critical-path audit for #1018 counted four fonts as blocking, when only one is. The
   real win in this change is the preload of inter.woff2, which IS on the critical path. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;               /* variable: the whole range from one file */
  font-display: swap;
  src: url('/assets/fonts/inter.woff2') format('woff2');
}

* { margin: 0; padding: 0; box-sizing: border-box; }
/* Native form controls (button/input/select/textarea) don't inherit font-family — browsers apply
   their own default (Arial-ish), so any control that doesn't explicitly set a font "reverts" off the
   app face (e.g. the runner odds button). Force them onto --font once, globally, so nothing drifts.
   font-family only (not `font:`) so each control keeps its own size/weight. */
button, input, select, textarea, optgroup { font-family: inherit; }

:root {
  /* #234 shared chip metrics — EVERY badge/chip family on the runner card consumes
     these so heights align on a shared row (colour/border stay per-family). */
  --chip-h: 20px;
  /* #235 single source for page gutters — consumed by .page-main (+ 640px override). */
  /* #239 phase 1: ALL white overlays route through this base triplet, so a future
     light theme flips ONE knob (e.g. to 16,18,24) instead of hunting 39 literals. */
  --brand-vo-green: #3fe9ad;          /* the VectorOdds mark — a BRAND, so it is the same on */
  --brand-vo-green-2: #2fcf98;        /* every theme. Declared once here, not typed per page. */
  --brand-vo-purple: #4b3ff0;
  --brand-vo-purple-2: #6d5cff;
  /* #417 The exchange's back/lay convention: blue is BACK, pink is LAY, on every betting
     exchange there is. Like the rug colours, that is a fact about the domain — a theme that
     recoloured them would be lying to the user — so they are declared once and inherited by
     every palette rather than typed into the screener. */
  --exch-back: #1e88e5;
  --exch-back-rgb: 30, 136, 229;
  --exch-lay: #e53935;
  --exch-lay-rgb: 229, 57, 53;
  /* the modal scrim: it DIMS the page, so it is black on a light theme too */
  --scrim-rgb: 0, 0, 0;
  --video-mat: #000;                  /* letterbox behind <video> — black by convention, not by theme */
  --overlay-rgb: 255, 255, 255;
  --page-pad-x: 1rem;
  --page-pad-x-m: 0.55rem;
  --chip-fs: var(--fs-2xs);
  --chip-px: 0.4rem;
  --bg: #101218;
  --bg-card: #191c26;
  --bg-hover: #1f2330;
  --bg-input: #191c26;
  --border: #282d3e;
  --border-light: #333849;
  --bg-light: #3b4152;
  /* Page backdrop behind the content panels — kept separate from --bg-light so
     it can be tuned without affecting chips that use --bg-light. */
  --bg-page: var(--bg);
  --text: #e8e9ed;
  /* #133 grey text lightened site-wide for easier reading (was b0b3c0 / 9da1b8 / 767a94). */
  --text-mid: #c2c5d2;
  --text-muted: #b4b8cc;
  --text-dim: #9296b0;
  --accent: #7c6cf0;
  --accent-btn: #6a5ad4;  /* a11y (#168): accent shade for fills BEHIND WHITE TEXT — clears AA
                             (5.22:1). Plain --accent (#7c6cf0) is only 3.99:1 and stays for
                             text-less fills (bars, toggles, dots) + accent-coloured text. */
  /* #416 Text ON --accent-btn. White works here because the accent is a deep purple. A LIGHT
     accent (the mint CTA in the indigo trial) needs DARK text — every theme sets its own. */
  --on-accent: #ffffff;
  --accent-light: #b0a4ff;
  --win: #f5c542;
  --win-bg: rgba(245, 197, 66, 0.07);
  --ew: #2dd4bf;
  --ew-bg: rgba(45, 212, 191, 0.07);
  --avoid: #6b7280;
  --avoid-bg: rgba(107, 114, 128, 0.05);
  --danger: #f87171;
  --green: #34d399;
  /* Race-type colour coding — single source of truth, reused site-wide. */
  --type-horse: var(--accent);
  --type-grey: var(--green);
  --type-harness: var(--win);
  --radius: 10px;
  --header-h: 89px; /* two equal rows: top bar + next-to-jump (44px each + 1px divider) */
  --race-pills-h: 58px; /* #301 approx height of the sticky meet-nav pills row (mobile) — the hero stacks below it */
  --header-top-h: 44px;
  --gutter: 0px;
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  /* #417 --font-head lets a theme pair a DISPLAY face with its body face — the classic move
     #148 had no way to express, since one --font meant one voice. It defaults to --font, so
     every theme that does not set it looks exactly as it did: this is a new capability, not a
     restyle. Consumed by h1–h6/.text-h*/.section-title. */
  --font-head: var(--font);
  /* #148 One font throughout — --mono now aliases --font (was a separate monospace).
     Numbers keep column alignment via font-variant-numeric on .mono/[--mono] users.
     The ONLY exception is the Tulloch's Tips heading (Great Vibes, see .tt-title). */
  --mono: var(--font);
  /* #417 --font-code is the REAL monospace, and it is not a relapse of #148. --mono aliases
     --font, so it is a promise about NUMBER ALIGNMENT (kept via font-variant-numeric), not
     about fixed width. Log output and stack traces need actual fixed width — their columns
     are load-bearing — and the logs view had to hardcode a stack because no token said
     "monospace" any more. One place to change it, and a theme can still swap it. */
  --font-code: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* #148 Consolidated type scale — 8 named steps (was ~42 ad-hoc rem sizes). Use the
     --fs-* tokens or the h1–h6 / .text-* classes; the ≤640px block below shifts the
     larger steps down for mobile. */
  --fs-h1: 1.5rem;
  --fs-h2: 1.25rem;
  --fs-h3: 1.05rem;
  --fs-h4: 0.9rem;
  --fs-body: 0.85rem;
  --fs-sm: 0.78rem;
  --fs-xs: 0.75rem;   /* a11y (#168): raised legibility floor ~12px — was 0.68rem/10.9px */
  --fs-2xs: 0.7rem;   /* a11y (#168): raised legibility floor ~11px — was 0.6rem/9.6px */
  /* #416 --x-rgb companions. CSS cannot do rgba(var(--win), .12) -- a hex var is not
     three numbers -- so a tint could never follow the theme and every one was hardcoded.
     Derived from this theme's own colours; --overlay-rgb already set the precedent. */
  --accent-rgb:       124, 108, 240;
  --win-rgb:          245, 197, 66;
  --ew-rgb:           45, 212, 191;
  --green-rgb:        52, 211, 153;
  --danger-rgb:       248, 113, 113;
  --text-rgb:         232, 233, 237;
  --avoid-rgb:        107, 114, 128;
  /* #NEW canonical RESULT-STATE palette — ONE source for every outcome badge/pill across the site
     (results list, Best Bets, prediction cards, tulloch). Fixed, semantic colours (a win is green,
     a place orange, a miss red, upcoming grey) — deliberately the SAME in every theme so the win/
     place/miss meaning never drifts. Use var(--result-*) on outcome surfaces, never a raw hex. */
  --result-win:    #2fcf98;  --result-win-rgb:   47, 207, 152;
  --result-place:  #ffc15e;  --result-place-rgb: 255, 193, 94;
  --result-miss:   #d31f1e;  --result-miss-rgb:  211, 31, 30;
  --result-upcoming: var(--text-muted);
  /* #416 Tulloch's Tips carries its own small palette: the card is a premium slab with its
     OWN ground and ink, deliberately not the page's. Every one of these used to be a literal
     (#3a2b8c, rgba(176,164,255)…) so the card looked identical in every theme. */
  --tulloch:      #b0a4ff;            /* the signature: border, glow, crest, accent bar */
  --tulloch-rgb:  176, 164, 255;
  --tulloch-g1:   #3a2b8c;            /* the slab gradient, light -> dark */
  --tulloch-g2:   #241b63;
  --tulloch-g3:   #171833;
  --tulloch-ink:  #e9e5ff;            /* text ON the slab (12.1:1 on g2) */
  --tulloch-well-rgb: 10, 10, 24;   /* the inset wells, a darkening of the slab itself */
  /* Panel-LOCAL tokens so Prime Vectors reads its OWN palette (ink/hairlines/cards/statuses),
     not the page's — this is what lets the light theme flip the panel to a dark "terminal" slab
     without any text turning illegible. Every default below reproduces the dark look 1:1; the
     light theme re-skins them to the Dark-Terminal brand. */
  --tulloch-panel:       var(--tulloch-g1);              /* slab ground (light: Midnight Violet) */
  --tulloch-ink-rgb:     233, 229, 255;                  /* light-on-slab text + hairlines (≈#e9e5ff) */
  --tulloch-edge:        rgba(var(--tulloch-rgb), 0.45); /* slab hairline */
  --tulloch-card:        rgba(var(--tulloch-well-rgb), 0.32); /* pick-row ground */
  --tulloch-card-hover:  rgba(var(--tulloch-well-rgb), 0.5);
  --tulloch-card-radius: 8px;                            /* pick-row + (light) slab corners */
  --tulloch-win:   var(--win);                           /* WON status */
  --tulloch-place: var(--accent-light);                  /* PLACED status */
  --tulloch-miss:  var(--text-muted);                    /* Unplaced status */
  --tulloch-muted: var(--text-muted);                    /* UPCOMING status */
  --tulloch-live:  var(--win);                           /* RUNNING/LIVE status (light: Flag Red) */
  --tulloch-pos:   var(--green);                          /* P&L profit ink */
  --tulloch-neg:   var(--danger);                         /* P&L loss ink */
  /* #417 chart lines — the bright set: drawn on the DARK ground */
  --chart-1: #3b82f6;  --chart-2: #f97316;  --chart-3: #22c55e;
  --chart-4: #a855f7;  --chart-5: #ef4444;  --chart-6: #14b8a6;
  --brand-betfair: #f59e0b;           /* Betfair's amber — a BRAND, kept recognisable */
  --chart-1-rgb: 59, 130, 246;        /* companion for tints of the info blue */
}

/* #250 responsible-gambling footer — every page, theme-var driven. */
/* #424 site footer — column-driven links + brand, above the compliance strip. */
.site-footer { display: grid; grid-template-columns: minmax(180px, 1fr) 2fr; gap: 1.5rem 2rem; padding: 2rem var(--page-pad-x) 1.5rem; margin-top: 2rem; border-top: 1px solid var(--border); color: var(--text-muted); font-size: var(--fs-xs); }
.sf-brand { display: flex; flex-direction: column; gap: 0.6rem; }
.sf-logo { height: 30px; width: auto; max-width: 150px; object-fit: contain; display: block; align-self: flex-start; }
.sf-wordmark { font-weight: 700; color: var(--text-mid); letter-spacing: 0.01em; }
.sf-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1.25rem 1.5rem; }
.sf-heading { margin: 0 0 0.5rem; font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-mid); }
.sf-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.sf-links a { color: var(--text-muted); text-decoration: none; }
.sf-links a:hover { color: var(--text-mid); text-decoration: underline; }
.sf-social { display: flex; gap: 0.75rem; }
.sf-social a { color: var(--text-muted); }
.sf-social a:hover { color: var(--accent); }
@media (max-width: 640px) { .site-footer { grid-template-columns: 1fr; gap: 1.25rem; } }

.rg-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.5rem 0.9rem; padding: 0.8rem var(--page-pad-x) calc(0.8rem + env(safe-area-inset-bottom)); margin-top: 1.5rem; border-top: 1px solid var(--border); font-size: var(--fs-2xs); color: var(--text-muted); text-align: center; }
.rg-footer a { color: var(--text-mid); text-decoration: underline; }
.rg-18 { display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 20px; padding: 0 4px; border: 1.5px solid var(--text-muted); border-radius: 50%; font-weight: 800; }
.rg-tagline { font-weight: 700; color: var(--text-mid); text-transform: uppercase; letter-spacing: 0.03em; }
@media (max-width: 640px) { .rg-footer { margin-bottom: 56px; } } /* clear the bottom tabs */
/* #515 pin the universal footer to the bottom on short app pages: make the shell page a min-height
   column so the footer sits at the bottom of the viewport (short pages) or below the fold (long
   pages), full width in both. Scoped to app-shell pages so marketing/auth layouts are untouched. */
body:has(.app-shell) { min-height: 100dvh; display: flex; flex-direction: column; }
body:has(.app-shell) > .app-shell { flex: 1 0 auto; }
body:has(.app-shell) > footer { flex-shrink: 0; }

/* #257 Admin-host chrome — a simple, admin-only shell on the dedicated admin host.
   Distinct compact header + a flat admin nav; consumer chrome is not built (these rules
   are belt-and-braces in case a page ships its own copy). Scoped to :root.admin-host so
   normal hosts are untouched. */
:root.admin-host .site-header,
:root.admin-host .bottom-nav,
:root.admin-host .rg-footer,
:root.admin-host .ai-upgrade-strip { display: none !important; }
/* a distinct, businesslike admin bar (tinted from the accent, flatter than the consumer nav) */
:root.admin-host .top-nav {
  background: linear-gradient(180deg, #1a1436, #14112b);
  border-bottom: 2px solid var(--accent);
}
/* a small "ADMIN" tag next to the brand so the environment is unmistakable */
:root.admin-host .tn-brand::after {
  content: "ADMIN"; margin-left: 0.55rem; padding: 0.1rem 0.4rem;
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.12em;
  color: var(--accent-light); border: 1px solid var(--accent); border-radius: 4px;
  align-self: center;
}
/* the admin nav is flat + always visible (no consumer curation) */
:root.admin-host .app-shell { padding-top: 0.5rem; }

/* #271 (+ admin cleanup 2026-07-15) Admin chrome = the mobile-style STICKY TOP BAR with the
   VectorOdds brand at EVERY width. Originally these rules were mobile-only (the consumer
   header/bottom bar that open the drawer are hidden on admin pages); the desktop admin nav
   was the consumer left sidebar. Now the same top-bar strip applies on desktop too — one
   admin chrome everywhere. The desktop sidebar rules (body padding-left, hidden brand) are
   neutralised below at higher specificity, so consumer pages are untouched. */
:root.admin-host body { padding-top: 0; padding-left: 0; }
:root.admin-host .top-nav {
  position: sticky; top: 0; left: 0; right: 0; bottom: auto;
  width: 100%; height: auto; max-height: none;
  transform: none !important;            /* never parked off-screen */
  will-change: auto !important;          /* CRITICAL: transform/will-change make .top-nav the
                                            containing block for position:fixed descendants,
                                            trapping the dropdowns inside its overflow clip. */
  border-right: 0; border-bottom: 2px solid var(--accent);
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
  z-index: 300; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
:root.admin-host .tn-inner {
  flex-direction: row; align-items: center; height: auto;
  padding: 0.35rem 0.55rem; gap: 0.2rem; flex-wrap: nowrap;
  width: max-content; min-width: 100%;
}
/* the brand stays visible at every width (desktop consumer nav hides it in the sidebar) */
:root.admin-host .top-nav .tn-brand { display: flex; margin: 0 0.4rem 0 0.15rem; flex: 0 0 auto; white-space: nowrap; }
:root.admin-host .top-nav .tn-user-area { display: flex; flex: 0 0 auto; }
:root.admin-host .tn-menu { flex-direction: row; align-items: center; gap: 0.2rem; flex-wrap: nowrap; }
:root.admin-host .tn-dropdown { width: auto; flex: 0 0 auto; }
:root.admin-host .tn-link { width: auto; white-space: nowrap; padding: 0.4rem 0.7rem; }
:root.admin-host .tn-divider { display: none; }          /* dividers make no sense in a row strip */
:root.admin-host .app-shell { padding-top: 0.6rem; }
:root.admin-host .nav-scrim { display: none !important; } /* pinned open → never scrim */
/* Grouped sub-menus in the strip: the strip clips overflow (overflow-x:auto/overflow-y:
   hidden), so absolute menus would be cut off — fixed positioning escapes the clip (no
   transformed ancestor: the strip forces transform:none). auto offsets keep the menu under
   its trigger. */
:root.admin-host .tn-drop-menu { position: fixed; top: auto; left: auto; z-index: 400; max-height: 70vh; overflow-y: auto; }
:root.admin-host .tn-drop-header { display: flex; align-items: center; }

/* #260 18+ age gate (blocking modal) + the account "18+" section. Mobile-first. */
.age-gate-lead { font-size: var(--fs-body); line-height: 1.5; margin: 0 0 1rem; }
.age-gate-lead.age-gate-block { color: #ff9d9d; }
.age-gate-form { display: flex; flex-direction: column; gap: 0.5rem; }
.age-gate-form label { font-size: var(--fs-sm); color: var(--text-mid); }
.age-gate-form input[type="date"] {
  width: 100%; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; padding: 0.55rem 0.65rem;
  font-size: var(--fs-body); margin-bottom: 0.35rem;
}
.age-gate-form input[type="date"]:focus { outline: none; border-color: var(--accent); }
.age-gate-err { color: #ff9d9d; font-size: var(--fs-sm); min-height: 1.1em; }
.age-gate-help { font-size: var(--fs-2xs); color: var(--text-muted); line-height: 1.5; margin: 0.9rem 0 0; }
.age-gate-help a { color: var(--text-mid); text-decoration: underline; }
.acct-age { display: flex; align-items: flex-start; gap: 0.6rem; }
.acct-age input[type="checkbox"] { margin-top: 0.2rem; width: 1.05rem; height: 1.05rem; flex: none; accent-color: var(--accent); }

/* #244 locked-field chip — same metrics as every other chip (#234). */
.locked-field { display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; gap: 0.2rem; border-radius: 5px; border: 1px dashed var(--border-light); color: var(--text-dim); text-decoration: none; font-weight: 700; }
.locked-field:hover { color: var(--accent-light); border-color: var(--accent); }

/* #243 segmented control (UI.segmented) — theme-var driven. */
/* #416 WAS: `inline-flex` + `overflow: hidden`, which silently CLIPPED any option past the
   container width -- the buttons rendered, sat outside the box, and could not be clicked. It
   held 3 short labels so nobody hit it; adding two longer ones (theme trials) made it
   unusable, and the failure is invisible (no scrollbar, no ellipsis -- the option is just gone).
   A row of unknown length must never be `overflow: hidden`.
   max-width + overflow-x keeps the joined-pill look and makes every option reachable at any
   width; overflow-y stays hidden so the children still clip to the radius. */
.ui-seg {
  display: inline-flex; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-card);
  max-width: 100%; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin;
}
.ui-seg-btn { white-space: nowrap; flex: none; }
.ui-seg-btn { border: 0; background: transparent; color: var(--text-mid); font: inherit; font-size: var(--fs-xs); font-weight: 700; padding: 0.35rem 0.85rem; cursor: pointer; }
.ui-seg-btn + .ui-seg-btn { border-left: 1px solid var(--border); }
.ui-seg-btn.seg-on { background: var(--accent-btn); color: var(--on-accent); }
.ui-seg-btn:not(.seg-on):hover { background: var(--bg-hover); }

/* ── LIGHT — the "Editorial" financial-data-journal palette, now THE light theme ─────
   (The former mint/indigo/emerald trial palettes + the earlier plain light theme were
   retired 2026-07-18; the app ships exactly Light (this) / Dark (:root) / Auto.)
   A luxury print / top-tier data-journal aesthetic, NOT another app skin. The design
   is BORDER-FIRST: hairline silver-ink rules + negative space carry structure, with
   drop shadows removed entirely (panels sit flat on an off-white canvas). Green is
   heavily restricted to cure the monotonous-highlight look — deep FOREST INK
   (#0b2f1d) does all the interactive-text / nav / CTA work, and one clean MINT
   (#00a854) is reserved strictly for the "Live" hero eyebrow + win indicators, never
   general borders/fills. Headings are a serif display in midnight black; body copy is
   muted charcoal. A crisp corner HIERARCHY (10px structural / 2px tactical inner / 0px
   global frame) reads as a printed page married to modern device design. Account ->
   Appearance -> "Light", or localStorage.theme = "light". */
:root[data-theme="light"] {
  --overlay-rgb: 0, 0, 0;
  --bg: #f4f4f5; --bg-page: #f4f4f5;      /* crisp neutral off-white canvas */
  --bg-card: #ffffff;                     /* pure white data panels + charts */
  --bg-input: #ffffff;
  --bg-hover: #eceef1;                     /* faint slate-tinted focus shift */
  --bg-light: #e5e7eb;                     /* faint neutral fill + donut EMPTY TRACK */
  --border: #e5e7eb;                      /* LIGHT SILVER — ultra-thin structural hairline; slate stays for TEXT/icons/hover, never a static box */
  --border-light: #c9d1da;                /* lighter slate for subtle dividers */
  --text: #3b1f2b;                        /* MIDNIGHT VIOLET — headings + bold metrics (15.9:1) */
  --text-mid: #2b2c4d;                    /* DEEP INDIGO — body/labels/times stepped one shade darker (was #383961) for stronger contrast */
  --text-dim: #2b2c4d;                    /* deep indigo — matches --text-mid */
  /* #237 — darkened from #8b97a8 (2.63:1 on white, axe color-contrast on every page using it — see
     the #1194 comment above on .tp-upd-from, which moved ONE component off this same value for the
     same reason). #5c6b7e clears AA at 5.4:1+ on both --bg (#fafafa) and --bg-card (#ffffff) while
     staying visibly lighter than --text-mid, so the dimmest-of-three-tiers hierarchy still reads. */
  --text-muted: #5c6b7e;                  /* lighter slate — dimmest only, still AA-passing */
  --text-rgb: 59, 31, 43;
  --accent: #383961;                      /* TWILIGHT INDIGO — links, active nav, interactive text (9.7:1) */
  --accent-light: #383961;
  --accent-btn: #383961;                  /* indigo fill — active-nav block + primary buttons */
  --accent-hover: #2b2c4d;                /* a shade deeper on hover */
  --on-accent: #ffffff;                   /* white on indigo — 9.7:1 */
  --accent-rgb: 56, 57, 97;
  --green-ink: #383961; --mint-bg: #eef0f6; /* result banner: indigo placements on a faint indigo field */
  --mint: #383961; --mint-rgb: 56, 57, 97; /* eyebrow status = indigo (red is reserved for live alerts) */
  --avoid: #8b97a8; --avoid-bg: rgba(139, 151, 168, 0.10); --avoid-rgb: 139, 151, 168;
  --danger: #d31f1e; --danger-rgb: 211, 31, 30;  /* FLAG RED — live alerts, badges, <5m countdowns */
  --green: #383961; --green-rgb: 56, 57, 97; /* success (WON/PLACED) = indigo, never green */
  --win: #383961;                         /* winning metric / top pick = indigo */
  --win-bg: rgba(56, 57, 97, 0.10);
  --win-rgb: 56, 57, 97;
  --ew: #d31f1e;                          /* each-way = flag red (donut segment 3) */
  --ew-bg: rgba(211, 31, 30, 0.10);
  --ew-rgb: 211, 31, 30;
  --on-status: #ffffff;
  /* Prime Vectors — a deliberately DARK "terminal" panel even in light mode (brand contrast):
     Twilight Indigo GROUND with Midnight Violet cards on it (sharp 0px corners), Vanilla Custard
     ink, Slate Grey hairlines, Flag Red reserved for the LIVE status. This is the same ground as
     the full-side rail panel, so Prime Vectors reads as one clean surface with violet tiles.
     g1/g2/g3 + --tulloch-border stay custard because .badge-toprated borrows them. */
  --tulloch: #5f758e; --tulloch-rgb: 95, 117, 142;                    /* Slate Grey — accent seed + glow */
  --tulloch-g1: #dbdfac; --tulloch-g2: #d3d79f; --tulloch-g3: #c9ce90; /* (kept for .badge-toprated) */
  --tulloch-border: #b9be7e;                                           /* (kept for .badge-toprated) */
  --tulloch-ink: #dbdfac; --tulloch-ink-rgb: 219, 223, 172;            /* Vanilla Custard text */
  --tulloch-well-rgb: 59, 31, 43;
  --tulloch-panel: #383961;                                            /* Twilight Indigo — the ground */
  --tulloch-card: #3b1f2b; --tulloch-card-hover: #4d2c3a;              /* Midnight Violet tiles */
  --tulloch-card-radius: 0px;                                          /* sharp geometric corners */
  --tulloch-edge: #5f758e;                                             /* Slate Grey hairline */
  --tulloch-win: #dbdfac; --tulloch-place: #5f758e; --tulloch-miss: #8b97a8; --tulloch-muted: #8b97a8;
  --tulloch-live: #d31f1e; --tulloch-pos: #dbdfac; --tulloch-neg: #d31f1e;  /* Flag Red = LIVE / loss */
  /* charts — donut ring segments per the brief (winners/placed/each-way); empty track = faint neutral. */
  --chart-1: #383961;  /* winners — indigo */
  --chart-2: #5f758e;  /* placed — slate */
  --chart-3: #d31f1e;  /* each-way — flag red */
  --chart-4: #3b1f2b;  /* midnight violet */
  --chart-5: #8b97a8; --chart-6: #b9be7e;
  --chart-1-rgb: 56, 57, 97; --brand-betfair: #383961;
  --exch-back: #383961; --exch-lay: #d31f1e; --exch-back-rgb: 56, 57, 97; --exch-lay-rgb: 211, 31, 30;
  --video-mat: #000000;
  /* Inter is the editorial geometric display voice (brief: "moving exclusively to Inter") —
     high-contrast, precise; kept as the header face for the sharp Vector identity. */
  --font-head: var(--font);
  /* VECTOR identity — completely SHARP geometry: zero radius on every surface (was 8/8/6px).
     Structure comes from the hairline frame + spacing, never rounding. Drives every
     border-radius:var(--radius|--r-card|--r-inner) across the shared components at once. */
  --radius: 0;
  --r-card: 0;                            /* cards / result / warning blocks — square */
  --r-inner: 0;                           /* buttons, chips, pills — square block labels */
}
/* ── VECTOR (editorial) — sharp geometry + block labels ─────────────────────────
   Several shared components carry a HARDCODED corner radius (buttons 7px, badges/chips 4–5px,
   pills 999px) the token override above can't reach. Square them here in ONE theme-scoped block
   (not per-component patches), and give status chips a precise block-caps treatment — the
   analytical Vector voice. No colour literals (theme tokens own colour). */
:root[data-theme="light"] button,
:root[data-theme="light"] [class*="btn"],
:root[data-theme="light"] .hero-cta,
:root[data-theme="light"] [class*="badge"],
:root[data-theme="light"] .status-pill,
:root[data-theme="light"] .pill,
:root[data-theme="light"] .chip,
:root[data-theme="light"] .tag,
:root[data-theme="light"] [class*="-tag"],     /* bespoke pill TAGS: uncov-tag, bb-code-tag, rm-tag-* … */
:root[data-theme="light"] [class*="-chip"],    /* bespoke CHIPS: mr-chip(s), rf-chip … */
:root[data-theme="light"] [class*="-count"],   /* count badges: arc-count, agh-count, osb-count … */
:root[data-theme="light"] [class*="-stance"],  /* market-stance pill */
:root[data-theme="light"] .tt-flag,            /* Prime Vectors flag pills */
:root[data-theme="light"] .hero-cov,           /* race-hero coverage tag */
:root[data-theme="light"] .os-value,
:root[data-theme="light"] .os-rg,              /* odds-source value/RG badges */
:root[data-theme="light"] .meet-nav-pill,     /* race-selector pills (R1, R2, …) */
:root[data-theme="light"] .skel-pill,          /* …and their loading skeleton, so no rounded flash */
:root[data-theme="light"] .back-link,          /* "Back to meets" */
:root[data-theme="light"] input,
:root[data-theme="light"] select,
:root[data-theme="light"] textarea,
:root[data-theme="light"] .card {
  border-radius: 0;                       /* ALL buttons + badges (+ chips/pills/nav) squared */
}
:root[data-theme="light"] .status-badge,
:root[data-theme="light"] .status-pill {
  text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700;
}
/* Mobile input safety — interactive controls keep a 44px+ touch target on small viewports,
   and squared corners must not clip their padding. */
@media (max-width: 640px) {
  :root[data-theme="light"] .btn-primary,
  :root[data-theme="light"] .btn-secondary,
  :root[data-theme="light"] .btn-outline,
  :root[data-theme="light"] .btn-danger,
  :root[data-theme="light"] .hero-cta,
  :root[data-theme="light"] .share-btn {
    min-height: 44px;
  }
}
/* BORDER-FIRST structural panels — hairline rule, crisp radius, ZERO shadow, flat on
   the canvas. Backgrounds are left alone so semantic tints (callouts, pred states) survive. */
:root[data-theme="light"] .landing-hero,
:root[data-theme="light"] .hiw-card,
:root[data-theme="light"] .sp-card,
:root[data-theme="light"] .tf-card,
:root[data-theme="light"] .bs-card,
:root[data-theme="light"] .notif-channel,
:root[data-theme="light"] .pred-box,
:root[data-theme="light"] .mr-item,
:root[data-theme="light"] .value-card,
:root[data-theme="light"] .stat-card,
:root[data-theme="light"] .callout,
:root[data-theme="light"] .hero {
  border-radius: var(--r-card);
  border: 1px solid var(--border);        /* soft silver hairline frame */
  box-shadow: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
/* The landing hero drops its tinted radial — structure comes from spacing, not gradient. */
:root[data-theme="light"] .landing-hero { background: var(--bg-card); }
/* Interactive focus: the hairline SNAPS to forest ink + a faint tint shift. No glow. */
:root[data-theme="light"] .hiw-card:hover,
:root[data-theme="light"] .sp-card:hover,
:root[data-theme="light"] .tf-card:hover,
:root[data-theme="light"] .bs-card:hover,
:root[data-theme="light"] .mr-item:hover,
:root[data-theme="light"] .value-card:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
  box-shadow: none;
}
/* Inner components — ultra-tactical, near-square corners (0–2px). */
:root[data-theme="light"] .hero-cta,
:root[data-theme="light"] .status-badge,
:root[data-theme="light"] .btn-primary,
:root[data-theme="light"] .btn-secondary,
:root[data-theme="light"] .btn-outline,
:root[data-theme="light"] .btn-danger,
:root[data-theme="light"] .jh-chip,
:root[data-theme="light"] .rf-chip,
:root[data-theme="light"] .badge,
:root[data-theme="light"] .horse-filter,
:root[data-theme="light"] .sb-field,
:root[data-theme="light"] .form-input,
:root[data-theme="light"] input,
:root[data-theme="light"] textarea,
:root[data-theme="light"] select {
  border-radius: var(--r-inner);
}
/* The CTA is FOREST INK (mint pulled off buttons); its hover deepens the ink — this also
   overrides the leftover indigo-purple :hover literal that would otherwise flash here. */
:root[data-theme="light"] .hero-cta { background: var(--accent-btn); color: var(--on-accent); }
:root[data-theme="light"] .hero-cta:hover { background: var(--accent-hover); }
/* The eyebrow is the ONE mint element — the "Live form intelligence" status marker. */
:root[data-theme="light"] .hero-eyebrow {
  color: var(--mint);
  background: rgba(var(--mint-rgb), 0.10);
  border-color: rgba(var(--mint-rgb), 0.35);
}
/* "Top rated" badge — the base yellow-on-yellow is unreadable on the light canvas.
   Swap to the palette's warm custard highlight with midnight-violet ink (high contrast)
   and a bronze hairline, matching the Tulloch "premium" treatment. */
:root[data-theme="light"] .badge.badge-toprated {
  background: var(--tulloch-g1);
  color: var(--text);
  border: 1px solid var(--tulloch-border);
}
/* Prime Vectors panel — the Dark-Terminal slab: flat Midnight Violet ground, Slate Grey
   hairline, sharp 0px corners, no glow. All ink/cards come from the panel-local tokens above. */
:root[data-theme="light"] .tulloch {
  background: var(--tulloch-panel);       /* Midnight Violet — strips the base radial+linear gradient */
  border-radius: var(--tulloch-card-radius);
  border: 1px solid var(--tulloch-edge);
  box-shadow: none;                       /* strips the base drop + inset shadow */
}
/* ── The whole right rail is ONE uninterrupted Dark-Terminal column (light theme) ──────
   Live alerts + Prime Vectors flow into a single Midnight Violet slab — no inner card fills
   or pale gaps — separated only by a Slate Grey hairline, Vanilla Custard text throughout,
   Flag Red reserved for the live-alert count. Desktop-only (the rail is hidden on mobile).
   All colours come from the panel-local --tulloch-* tokens, so nothing is a one-off literal. */
:root[data-theme="light"] .app-rail:not(.pv-flat) {
  background: var(--tulloch-panel);              /* Twilight Indigo — merges with the full-side panel */
  border: 0;                                      /* no inner frame — Prime Vectors flows onto the panel */
  border-radius: 0;
  padding: 1rem 1rem 1.1rem;
  color: var(--tulloch-ink);                      /* Vanilla Custard base ink */
  z-index: 1;                                     /* sit above the full-side panel below */
}
/* Full-height dark-blue (Twilight Indigo) panel filling the WHOLE right side of the screen —
   from the rail's left edge to the right screen edge — behind the Prime Vectors rail (which
   stays a Midnight-Violet card on top of it). --rail-x is the rail's left X, set by nav.js on
   load/resize; unset (no rail / mobile) → offscreen so nothing paints. */
:root[data-theme="light"] .app-shell:not(:has(.app-rail.pv-flat))::after {
  content: "";
  position: fixed;
  top: var(--header-h); left: var(--rail-x, 100vw); right: 0; bottom: 0;
  background: var(--tulloch-panel);               /* Twilight Indigo — same ground as the rail */
  z-index: 0;
  pointer-events: none;
}
/* Live alerts: no card fill/frame — it flows into the slab, a Slate Grey hairline divides it
   from Prime Vectors below (a border, never a background fill, does the separating). */
:root[data-theme="light"] .app-rail:not(.pv-flat) .app-rail-card {
  background: transparent; border: 0; border-radius: 0;
  padding: 0 0 0.85rem; margin: 0 0 0.85rem;
  border-bottom: 1px solid var(--tulloch-edge);
  color: var(--tulloch-ink);
}
:root[data-theme="light"] .app-rail:not(.pv-flat) .arc-title,
:root[data-theme="light"] .app-rail:not(.pv-flat) .arc-chevron,
:root[data-theme="light"] .app-rail:not(.pv-flat) .arc-body,
:root[data-theme="light"] .app-rail:not(.pv-flat) .arc-more { color: var(--tulloch-ink); }
/* the live-alert count is the section's one critical indicator → Flag Red */
:root[data-theme="light"] .app-rail:not(.pv-flat) .arc-count {
  color: var(--tulloch-live); background: transparent; border-color: var(--tulloch-live);
}
/* individual alert rows flow in too — transparent, custard ink, sharp; their 🔴 badges stay. */
:root[data-theme="light"] .app-rail:not(.pv-flat) .alert-card {
  background: transparent; border: 1px solid var(--tulloch-edge); border-radius: 0;
}
:root[data-theme="light"] .app-rail:not(.pv-flat) .ac-horse { color: var(--tulloch-ink); }
:root[data-theme="light"] .app-rail:not(.pv-flat) .ac-detail,
:root[data-theme="light"] .app-rail:not(.pv-flat) .ac-race { color: rgba(var(--tulloch-ink-rgb), 0.72); }
/* Prime Vectors flows into the SAME slab — drop its own frame so there's no card-in-card. */
:root[data-theme="light"] .app-rail:not(.pv-flat) .tulloch {
  background: transparent; border: 0; border-radius: 0; box-shadow: none; padding: 0;
}
/* Prime Vectors tiles — Midnight-Violet picks on the Twilight-Indigo ground, each defined by a
   crisp Slate Grey hairline so they read as clean, separate tiles; the section dividers (sub +
   track record) use the same hairline for one consistent rule weight. (rgba is Slate Grey #5F758E.) */
:root[data-theme="light"] .tt-pick { border-color: rgba(95, 117, 142, 0.45); }
:root[data-theme="light"] .tt-pick:hover { border-color: rgba(95, 117, 142, 0.85); }
:root[data-theme="light"] .tt-sub { border-bottom-color: rgba(95, 117, 142, 0.4); }
:root[data-theme="light"] .tt-record { border-top-color: rgba(95, 117, 142, 0.4); }
:root[data-theme="light"] .tt-rec-cell + .tt-rec-cell { border-left-color: rgba(95, 117, 142, 0.4); }
/* Global frame — a flat, unrounded 0px canvas edge grounding the text-heavy sections. */
:root[data-theme="light"] .site-header { border-radius: 0; }
/* Whole top strip goes pure white (logo colours to be revisited): the black logo row
   AND the grey next-to-jump row both flip to the white card ground, so the header reads
   as one clean white band under a single silver hairline. */
:root[data-theme="light"] .sh-top { background: var(--bg-card); }
:root[data-theme="light"] .jump-header { background: var(--bg-card); }
/* Logo removed from the header for now (wordmark was tuned for the old black bar). */
/* Light theme: show the light logo IMAGE (like the dark theme) alongside the "VectorOdds"
   wordmark, set in the editorial display treatment — theme ink (twilight indigo, not the brand
   green), the Inter head font, tightened at 700. nav.js swaps the logo src per theme, so the
   light colour mark shows here and the dark mono mark in dark (on `themechange`). */
:root[data-theme="light"] .tn-brand-word {
  color: var(--accent);           /* Twilight Indigo — the editorial brand accent */
  font-family: var(--font-head);
  font-weight: 700;
  letter-spacing: -0.015em;
}
/* Safety net: never let the editorial page scroll sideways. A position:fixed header sizes
   itself to the DOCUMENT width when the page overflows horizontally, which shoves the
   right-aligned "My Account" button off-screen. overflow-x:clip caps the page at the
   viewport — and (verified) does NOT break the sticky race pills the way overflow:hidden would. */
:root[data-theme="light"] { overflow-x: clip; }
/* Active nav — a hard-edged INDIGO block with white text, not a pale pill. */
:root[data-theme="light"] .tn-link.tn-active,
:root[data-theme="light"] .acct-nav-link.active {
  background: var(--accent-btn);
  color: var(--on-accent);
  border-radius: var(--r-inner);
}
/* Active race pill (R6) — white text on the indigo block, no outer border; the dark
   fill alone defines its shape. Fixes the dark-on-dark time text (overlay-rgb is black
   in editorial, so the base rgba(overlay,0.65) rendered near-black on indigo). */
:root[data-theme="light"] .meet-nav-pill.active .mnp-num { color: var(--on-accent); }
:root[data-theme="light"] .meet-nav-pill.active .mnp-time { color: color-mix(in srgb, var(--on-accent) 72%, transparent); }
:root[data-theme="light"] .meet-nav-pill.active { border-color: transparent; }
/* Donut empty track — faint neutral so the indigo/slate/red ring segments pop. */
:root[data-theme="light"] .ps-ring .ps-track { stroke: var(--bg-light); }

/* ── Editorial — Race Detail shell + injected data components ─────────────────
   The dense page. Legibility and grid alignment over decoration: the platinum
   canvas flows through the transparent section wrappers (.home-section-primary is
   margin-only), and pure white is applied only to the injected data cards/tables. */

/* Dynamic data matrices (#raceRoot): a premium financial LEDGER. Numeric/grid data in a
   true tabular mono at muted charcoal; rows separated by hairline rules + negative space,
   NOT zebra striping; row focus is a neutral cool-grey wash (no green bleed). */
:root[data-theme="light"] { --mono: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; }
:root[data-theme="light"] .data-table { color: var(--text-mid); }
:root[data-theme="light"] .data-table tbody tr:hover { background: var(--bg-hover); }
/* Primary metric (top pick): a premium cream-paper block with a bronze hairline —
   flags it as strategic intelligence — with forest-ink accents, never a green fill.
   Kills the tinted/indigo gradient the base .top-pick paints. */
:root[data-theme="light"] .top-pick {
  background: var(--tulloch-g1);
  border: 1px solid var(--tulloch-border);
  border-radius: var(--r-card);
}
:root[data-theme="light"] .top-pick-name { color: var(--accent); }

/* Modal architecture (UI.modal — quaddies, media): dark-ink scrim, crisp hairline card,
   flat (no heavy shadow — the scrim carries the separation), unboxed midnight ✕. */
:root[data-theme="light"] .modal-overlay { background: rgba(var(--text-rgb), 0.6); }
:root[data-theme="light"] .modal-content {
  border-radius: var(--r-card);
  border: 1px solid var(--border);
  box-shadow: none;
}
:root[data-theme="light"] .modal-close { color: var(--text); }

/* Premium loading state (#loadingRace): no spinner — a subtle serif pulse, midnight fading
   to charcoal, centred in generous white space, building anticipation for the insight. */
@keyframes editorial-loading-pulse {
  0%, 100% { color: var(--text); opacity: 1; }
  50% { color: var(--text-mid); opacity: 0.55; }
}
:root[data-theme="light"] #loadingRace,
:root[data-theme="light"] .loading-msg {
  color: var(--text);
  font-family: var(--font-head);
  letter-spacing: 0.01em;
  animation: editorial-loading-pulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="light"] #loadingRace,
  :root[data-theme="light"] .loading-msg { animation: none; }
}

/* ── Editorial — Race Detail asymmetric 2-column grid ────────────────────────
   UI.racePage emits three wrappers under Editorial only: .race-hd (full-width race
   context), .race-main (65% field matrix) and .race-side (35% insights). Mobile-first:
   a single column with the INSIGHTS stacked ABOVE the dense field, so strategy reads
   before the scroll. At ≥1025px it becomes the 65/35 grid. Other themes never emit
   these wrappers, so this whole section is inert for them. */
:root[data-theme="light"] #raceRoot {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 1380px;
  margin: 0 auto;
}
:root[data-theme="light"] .race-hd { order: 0; border-bottom: 1px solid var(--border); padding-bottom: 0.75rem; }
:root[data-theme="light"] .race-side { order: 1; display: flex; flex-direction: column; gap: 24px; min-width: 0; }
:root[data-theme="light"] .race-main {
  order: 2; min-width: 0;
  display: flex; flex-direction: column; gap: 0.75rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem;
}
/* The field is ALWAYS unwrapped (display:contents at every breakpoint below), so .race-main's
   white panel never actually paints — give the runner rows their OWN white card ground at every
   width so DESKTOP matches MOBILE (previously white only ≤1024px, leaving desktop rows sitting
   grey on the page canvas). */
:root[data-theme="light"] .runner-row { background: var(--bg-card); }
/* The premium sidebar blocks (top pick, best bets) already carry the cream-paper treatment
   via their own rules; track bias and any other insight sits on a plain white hairline card. */
:root[data-theme="light"] .race-side .race-best-bets:not(:empty) {
  background: var(--tulloch-g1);
  border: 1px solid var(--tulloch-border);
  border-radius: var(--r-card);
  padding: 0.85rem 1rem;
}
/* Best-bets is desktop-hidden globally (shown elsewhere); in the Editorial sidebar it IS
   the strategy surface, so let a populated one show at the 2-column breakpoint. */
@media (min-width: 1025px) {
  /* Light desktop race page: ONE wide column like the dark theme (no 35% insights split).
     Unwrap the editorial wrappers so the header, field and feeds flow full-width in a single
     column; best-bets stays desktop-hidden (the rail carries it), exactly as in dark. */
  :root[data-theme="light"] #raceRoot { display: block; max-width: none; }
  :root[data-theme="light"] #raceRoot > .race-hd,
  :root[data-theme="light"] #raceRoot > .race-main,
  :root[data-theme="light"] #raceRoot > .race-side { display: contents; }
}

/* ── Editorial — Race Detail MOBILE reflow (≤1024px) ─────────────────────────
   The single-column stack, re-sequenced per feedback: the runner field comes
   first, insights next, then the best-bets / footer feeds, with Check Results
   dead last. Also unwraps the header so the sticky pills+hero aren't cut off,
   and widens the field to the screen edges. */
@media (max-width: 1024px) {
  /* Mobile: drop the editorial 3-column grid and flow everything in natural DOCUMENT order
     (header → field → insights → footer feeds → footer). display:contents on all three
     wrappers unwraps them, so: the sticky pills/hero pin against the whole page instead of
     a short wrapper; runners sit above insights above the footer; and there is no flex
     min-width blowout shoving content off-screen. */
  :root[data-theme="light"] #raceRoot { display: block; }
  :root[data-theme="light"] #raceRoot > .race-hd,
  :root[data-theme="light"] #raceRoot > .race-main,
  :root[data-theme="light"] #raceRoot > .race-side { display: contents; }
  /* THE overflow culprit: .meet-nav is a flex:1 scroll container but had min-width:auto, so
     its pills refused to shrink and blew the row (and the whole page) wider than the screen —
     which shifted the fixed header + account button off-screen and broke the sticky pins.
     min-width:0 lets its overflow-x:auto actually engage so the pills scroll internally. */
  :root[data-theme="light"] .meet-nav { min-width: 0; }
  /* Full-width runner cards, white on the grey canvas (race-main's white panel is gone in
     block flow), hard-contained so an open accordion can't leak past the edge. */
  :root[data-theme="light"] .runners-container { padding-left: 0; padding-right: 0; }
  :root[data-theme="light"] .runner-row { background: var(--bg-card); overflow: hidden; }
  /* Runner grid must shrink so the name line wraps and the odds column stays on-screen. */
  :root[data-theme="light"] .runner-compact { grid-template-columns: 50px minmax(0, 1fr) minmax(0, auto); }
  :root[data-theme="light"] .runner-compact > *,
  :root[data-theme="light"] .ui-accordion-body * { min-width: 0; }
}

/* The pre-prediction "Run a prediction to see…" prompt is removed under Editorial —
   the Get Prediction button already cues the action; this reclaims the empty block. */
:root[data-theme="light"] .po-prompt { display: none; }

/* Keep the runner-card "More" accordion strictly within the card: cap its width, let any
   wide inner grid/table scroll inside the body, and break long unbroken text/tokens. */
:root[data-theme="light"] .ui-accordion { max-width: 100%; }
:root[data-theme="light"] .ui-accordion-body { max-width: 100%; overflow-x: auto; overflow-wrap: anywhere; }

/* ── Editorial — Race Detail contrast refinements ────────────────────────────
   Higher-contrast hierarchy: white data containers pop off the platinum canvas,
   and the components the brief calls out get their legibility fixed. */

/* A. Result banner — a solid Twilight Indigo panel so the final result stands out
   against the grey/white page. All text goes white; the "RACE RESULT" eyebrow sits
   a touch translucent to keep the placings dominant. */
:root[data-theme="light"] .resulted-banner {
  background: var(--accent-btn);          /* solid twilight indigo */
  border: 1px solid var(--accent-btn);
  border-radius: var(--r-card);
}
:root[data-theme="light"] .resulted-banner .rb-positions,
:root[data-theme="light"] .resulted-banner .rb-pos strong { color: var(--on-accent); }
:root[data-theme="light"] .resulted-banner .rb-header { color: color-mix(in srgb, var(--on-accent) 75%, transparent); }

/* B. Next-to-jump track pills + filter chips — pure white so they don't bleed into the
   header bar; muted text that darkens to full charcoal on hover. */
:root[data-theme="light"] .jh-race,
:root[data-theme="light"] .jh-chip {
  background: var(--bg);                  /* subtle grey chip, not a white form field */
  border: 1px solid var(--border);        /* faint silver hairline */
  border-radius: 6px;
  color: var(--text-mid);
}
:root[data-theme="light"] .jh-race:hover,
:root[data-theme="light"] .jh-chip:hover { color: var(--text); border-color: var(--accent); }
/* #520 keep the solid-fill selected state legible in the light theme too. */
:root[data-theme="light"] .jh-chip.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* D. Field-matrix rows — hairline separators, no zebra, generous 12px vertical padding;
   column headers in the legible slate. */
:root[data-theme="light"] .data-table th { color: var(--text-mid); }
:root[data-theme="light"] .data-table td,
:root[data-theme="light"] .data-table th { padding-top: 0.75rem; padding-bottom: 0.75rem; }

/* E. Typography — now Inter drives the heads (no serif). Tighten the large display
   sizes so the sans headers read as sharp geometric editorial display, and pin the
   heavy metric weights (donut centre) that carry the number-forward voice. */
:root[data-theme="light"] h1,
:root[data-theme="light"] h2,
:root[data-theme="light"] .text-h1,
:root[data-theme="light"] .text-h2,
:root[data-theme="light"] .section-title,
:root[data-theme="light"] .hero-title { letter-spacing: -0.015em; font-weight: 700; }
:root[data-theme="light"] .ps-ring .ps-val { font-weight: 700; }

@media (max-width: 640px) {
  :root { --fs-h1: 1.35rem; --fs-h2: 1.15rem; --fs-h3: 1rem; --fs-sm: 0.76rem; --fs-xs: 0.72rem; --fs-2xs: 0.68rem; } /* a11y (#168): mobile floor ~11px, was 10.5/9.3px */
}
/* #148 standard heading classification + text-size utilities (all read the scale). */
/* #417 …and the heading FACE, from one rule. --font-head defaults to --font, so this changes
   nothing for a theme that does not set it — but a theme that wants a display face now has
   somewhere to put it. .tt-title (Great Vibes) sets its own family and still wins on
   specificity, so Tulloch's script is untouched. */
h1, h2, h3, h4, h5, h6,
.text-h1, .text-h2, .text-h3, .text-h4,
.section-title, .hero-title { font-family: var(--font-head); }
h1, .text-h1 { font-size: var(--fs-h1); }
h2, .text-h2 { font-size: var(--fs-h2); }
h3, .text-h3 { font-size: var(--fs-h3); }
h4, .text-h4 { font-size: var(--fs-h4); }
h5, h6 { font-size: var(--fs-body); }
.text-body { font-size: var(--fs-body); }
.text-sm { font-size: var(--fs-sm); }
.text-xs { font-size: var(--fs-xs); }
.text-2xs { font-size: var(--fs-2xs); }
/* Keep tabular alignment for numeric/odds text now that mono = the body font. */
.mono, [class*="mono"], .odds-row, .to-val, .runner-num, .pal-var, .pal-hex { font-variant-numeric: tabular-nums; }

body {
  background: var(--bg-page);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-h3);
  line-height: 1.55;
  padding-top: var(--header-h);
  min-height: 100vh;
  min-height: 100vh;
  /* Crisp editorial rendering: pair the two smoothing hints, and sit Inter a hair
     above 400 so body copy reads sharp. Numeric weight (not font-variation-settings)
     so every explicit font-weight:700/800 in the sheet still wins. */
  font-weight: 450;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

/* Main menu — an overlay drawer that slides in over the content from the left
   (default closed) at all sizes; the content never resizes. Opened by the header
   menu toggle; a scrim + any outside click closes it. */
.top-nav {
  position: fixed; top: var(--header-h); left: 0; bottom: 0;
  width: min(84vw, 300px);
  background: var(--bg-card); border-right: 1px solid var(--border);
  z-index: 250; overflow-y: auto; -webkit-overflow-scrolling: touch;
  transform: translateX(-100%);
  transition: transform 0.42s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
body.nav-open .top-nav { transform: translateX(0); }
/* Dim scrim behind the open drawer (below the header so the toggle stays live). */
.nav-scrim {
  position: fixed; inset: var(--header-h) 0 0 0; z-index: 240;
  background: rgba(0, 0, 0, 0.45); opacity: 0; visibility: hidden; pointer-events: none;
  /* Fully removed from hit-testing when closed so it can never block page scroll;
     visibility flips to hidden only after the fade-out completes. */
  transition: opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 0.42s;
}
body.nav-open .nav-scrim { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s; }

.tn-inner {
  max-width: none;
  margin: 0;
  height: 100%;
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
  display: flex;
  padding: 1rem 0.75rem;
}

.tn-brand {
  display: flex;
  align-items: center;
  margin: 0 0 0.6rem 0.25rem;
  opacity: 1;
  transition: opacity 0.15s;
}
.tn-brand:hover { opacity: 0.8; }
.tn-brand-live { display: flex; align-items: center; gap: 0.5rem; }
.tn-brand-img { height: 40px; width: auto; max-width: 120px; object-fit: contain; display: block; }
.tn-brand-word { font-size: var(--fs-h3); font-weight: 800; color: #33e0a1; letter-spacing: -0.3px; }

.tn-menu {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.1rem;
}
.tn-link { width: 100%; padding: 0.5rem 0.7rem; }
.tn-dropdown { position: relative; width: 100%; }
/* The in-nav hamburger is replaced by the header menu toggle. */
.tn-toggle { display: none !important; }
.tn-divider { height: 1px; background: var(--border); margin: 0.4rem 0.3rem; align-self: stretch; }

.tn-link {
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-mid);
  padding: 0.4rem 0.75rem;
  border-radius: 6px;
  transition: all 0.12s;
}

.tn-link:hover { color: var(--text); background: var(--bg-hover); }
.tn-link.tn-active { color: var(--accent-light); background: rgba(var(--accent-rgb), 0.08); font-weight: 600; }
/* #457 tier-locked menu item — greyed with a small lock; the whole row links to the pricing
   page (upsell). Applies to both the drawer (.tn-link/.tn-drop-item) and desktop bar
   (.dn-link/.dn-sub-item) since applyMenuLocks tags every gated anchor with .tn-locked. */
.tn-locked { opacity: 0.5; }
.tn-locked:hover { opacity: 0.72; }
/* #546 the padlock is the themed SVG that applyMenuLocks injects (.tn-lock-ico, #526) — the old
   emoji ::after was a SECOND lock on the same anchor (two padlocks). Removed; keep only the greyed
   state here. */

.tn-date {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-left: 0.2rem;
}

/* Navbar Dropdowns */

.tn-dropdown {
  position: relative;
}

.tn-drop-header {
  display: flex;
  align-items: center;
}

.tn-drop-link {
  border-radius: 6px 0 0 6px;
}
span.tn-drop-link[data-dropdown-label] { cursor: pointer; user-select: none; }
/* Hovering anywhere on a parent row highlights the whole row (label + caret),
   so dropdown parents behave like the plain top-level links. */
.tn-drop-header:hover .tn-drop-link,
.tn-drop-header:hover .tn-caret-btn { background: var(--bg-hover); color: var(--text); }

.tn-caret-btn {
  font-family: var(--font);
  font-size: var(--fs-2xs);
  color: var(--text-mid);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.4rem 0.45rem;
  border-radius: 0 6px 6px 0;
  transition: all 0.12s;
}

.tn-caret-btn:hover { color: var(--text); background: var(--bg-hover); }
.tn-caret-btn.tn-caret-active { color: var(--accent-light); background: rgba(var(--accent-rgb), 0.08); }

.tn-drop-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 200px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  z-index: 200;
  overflow: hidden;
}

.tn-drop-menu.tn-drop-open { display: block; }

.tn-drop-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  font-size: var(--fs-sm);
  color: var(--text-mid);
  border-top: 1px solid var(--border);
  transition: background 0.12s;
}

.tn-drop-item:first-child { border-top: none; }
.tn-drop-item:hover { background: var(--bg-hover); color: var(--text); }
.tn-drop-item-active { color: var(--accent-light); font-weight: 600; }
/* Group heading inside the admin dropdown (#50) */
.tn-drop-heading {
  padding: 0.5rem 0.75rem 0.25rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
}
.tn-drop-heading:first-child { border-top: none; }

.tn-di-course {
  font-weight: 600;
  color: var(--text);
}

.tn-di-meta {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.tn-drop-loading, .tn-drop-empty {
  padding: 0.6rem 0.75rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

/* Future dropdown: days -> expandable meets */
.tn-drop-menu-future { min-width: 240px; max-height: 72vh; overflow-y: auto; }
.tn-future-day { border-top: 1px solid var(--border); }
.tn-future-day:first-child { border-top: none; }
.tn-future-day-header {
  display: flex; align-items: stretch; gap: 0;
  width: 100%; color: var(--text); font-size: var(--fs-body); font-weight: 700;
}
.tn-fd-link {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  flex: 1 1 auto; min-width: 0; text-decoration: none; color: inherit;
  padding: 0.5rem 0.5rem 0.5rem 0.75rem;
}
.tn-fd-link:hover { background: var(--bg-hover); }
.tn-fd-caret-btn {
  flex: 0 0 auto; background: none; border: none; border-left: 1px solid var(--border);
  cursor: pointer; color: var(--text-dim); padding: 0.5rem 0.7rem;
}
.tn-fd-caret-btn:hover { background: var(--bg-hover); color: var(--text); }
.tn-fd-date { color: var(--text-dim); font-weight: 500; font-size: var(--fs-xs); }
.tn-fd-meta { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.tn-fd-caret { display: inline-block; transition: transform 0.15s; }
.tn-future-day.tn-fd-open .tn-fd-caret { transform: rotate(180deg); }
.tn-future-meets { display: none; background: var(--bg); }
.tn-future-day.tn-fd-open .tn-future-meets { display: block; }
.tn-future-meets .tn-drop-item { padding-left: 1.1rem; }

.tn-user-area {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: 0.5rem;
  padding-left: 0.5rem;
  border-left: 1px solid var(--border);
}

.tn-username {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tn-logout-btn {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.25rem 0.55rem;
  cursor: pointer;
  transition: all 0.12s;
}
.tn-logout-btn:hover { color: var(--danger); border-color: var(--danger); }

.tn-toggle {
  display: none;
  flex-direction: column;
  gap: 4px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.4rem;
}

.tn-toggle span {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--text-mid);
  border-radius: 1px;
  transition: all 0.2s;
}

@media (max-width: 640px) {
  body { font-size: 16px; }

  .tn-link {
    width: 100%;
    padding: 0.55rem 0.75rem;
    text-align: left;
  }

  .tn-dropdown { width: 100%; }

  .tn-drop-header { width: 100%; }
  .tn-drop-link { flex: 1; padding: 0.55rem 0.75rem; border-radius: 6px; }
  .tn-caret-btn { padding: 0.55rem 0.75rem; }

  .tn-drop-menu {
    position: static;
    box-shadow: none;
    border: none;
    border-radius: 0;
    background: var(--bg);
    margin: 0.15rem 0;
  }
}

/* Best bets in the main content — mobile only; the sticky right rail shows these
   on desktop, so hide the in-content copies there. (The loaders set an inline
   display, so override with !important.) */
.race-best-bets { margin: 1rem 0; }
.race-best-bets:empty { display: none; }
.race-best-bets .rbb-title { font-weight: 800; font-size: var(--fs-h4); margin-bottom: 0.5rem; }
@media (min-width: 900px) {
  .race-best-bets,
  #dayBestBetsSection { display: none !important; }
  /* #bestBetsSection is NOT hidden here: it's the dedicated /best-bets.html page's main
     content now (removed from home/day), so it must show at every width. */
}

/* Back link — "← Back to …" button (race pages → day schedule). */
.back-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: var(--fs-body); font-weight: 600; color: var(--text-mid);
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.4rem 0.7rem; text-decoration: none;
}
.back-link:hover { border-color: var(--accent); color: var(--text); }
.back-link-arrow { font-size: var(--fs-h3); line-height: 1; }

/* Menu collapse toggle — top-left button that slides the sidebar off-screen for
   more content room. Desktop-only (mobile uses the hamburger). */
.nav-toggle-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex-shrink: 0; padding: 0;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text-mid); cursor: pointer;
}
.nav-toggle-btn:hover { border-color: var(--accent); color: var(--text); }
.nav-toggle-btn svg { width: 18px; height: 18px; }

/* Desktop: left sidebar pinned to the page edge; the header spans full width; the
   page area (right of the rail) holds a centred content + sticky right-column shell. */
@media (min-width: 900px) {
  /* Scale all fonts on desktop: rem sizes are root-relative, so bumping the root
     font-size enlarges every rem-based font (and rem spacing) uniformly by 1.125x. */
  html { font-size: 112.5%; background: var(--bg-page); }
  /* Divided layout widths: main content (--content-w) + sticky right column
     (--side-w) sum to a fixed 1340px, so the right column can be widened by
     narrowing the main content by the same amount (combined width unchanged). */
  :root { --rail: 240px; --content-w: 980px; --side-w: 360px; }
  /* Content is full width — the menu drawer overlays it rather than pushing it. */
  body { margin-left: 0; max-width: none; background: var(--bg-page); padding-left: 0; padding-right: 0; }
  .app-shell { display: flex; justify-content: center; align-items: flex-start; gap: 0.9rem; padding: 1rem 0.9rem 0; }
  .app-content { flex: 1 1 auto; max-width: var(--content-w); min-width: 0; }
  /* Full-width template (admin): no rail, content spans the whole page width. */
  .app-shell-full .app-content { max-width: none; }
  .app-rail {
    display: block; flex: 0 1 var(--side-w); max-width: var(--side-w); min-width: 0;
    position: sticky; top: calc(var(--header-h) + 1rem); align-self: flex-start;
  }
  /* Desktop: the submenu slides OUT as a fixed panel at left:100% (below). Because
     .top-nav has transform/will-change it is the containing block for that fixed
     panel, so its overflow clips it — the #109 mobile-scroll fix (overflow-y:auto on
     the base rule) must NOT apply here or the submenu flashes out and is cut off,
     making every sub-page unreachable. On desktop the rail's own items are short and
     never overflow, so visible is safe (mobile keeps the base overflow-y:auto). */
  .top-nav { width: var(--rail); overflow: visible; }
  .tn-dropdown { width: 100%; }
  .tn-drop-header { width: 100%; }
  .tn-drop-link { flex: 1; }
  .tn-caret-btn { padding: 0.5rem 0.6rem; }
  /* On hover, the submenu slides OUT as a full-height panel pinned to the right
     edge of the sidebar (TAB-style), from below the header to the bottom of the
     viewport. It starts tucked behind the sidebar and slides right into view. */
  .tn-drop-menu {
    display: block; position: fixed; left: 100%; top: 0; bottom: 0;
    width: 260px; min-width: 0;
    background: var(--bg-card); border-right: 1px solid var(--border); border-radius: 0;
    box-shadow: 10px 0 30px rgba(0,0,0,0.45); margin: 0; padding: 0.6rem; z-index: 90;
    overflow-y: auto;
    opacity: 0; transform: translateX(-100%); pointer-events: none;
    will-change: transform, opacity;
    /* Slower, more organic slide: a gentle spring easing with a hint of overshoot
       so the panel eases out, drifts slightly past, then settles into place. The
       opacity fade trails the motion so it feels like it emerges, not snaps. */
    transition: opacity 0.4s ease-out, transform 0.6s cubic-bezier(0.34, 1.42, 0.5, 1);
  }
  .tn-drop-menu.tn-drop-open { opacity: 1; transform: translateX(0); pointer-events: auto; }
  .tn-user-area {
    flex-direction: column; align-items: stretch; gap: 0.4rem;
    margin: auto 0 0 0; padding: 0.75rem 0 0; border-left: none; border-top: 1px solid var(--border);
  }
  .tn-username { max-width: none; }
}

/* Two-line site header (fixed, full width, above everything). Line 1 = logo +
   menu toggle + user; line 2 = the "next to jump" row. */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; height: var(--header-h);
  z-index: 300; background: var(--bg-card); border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column;
}
.sh-top {
  display: flex; align-items: center; gap: 0.75rem; flex-shrink: 0;
  height: var(--header-top-h); padding: 0 0.9rem; border-bottom: 1px solid var(--border);
  background: #000; /* black top row; the next-to-jump row below is lighter */
}
.sh-logo { display: inline-flex; align-items: center; flex-shrink: 0; }
.sh-logo img { height: 28px; width: auto; max-width: 130px; object-fit: contain; display: block; }
.sh-logo-word { font-weight: 800; color: #33e0a1; font-size: var(--fs-h3); letter-spacing: -0.3px; }
.nav-toggle-labeled { width: auto; gap: 0.4rem; padding: 0 0.65rem; }
.ntb-label { font-size: var(--fs-body); font-weight: 600; }
.sh-user { margin-left: auto; display: flex; align-items: center; min-width: 0; }
.sh-user-name {
  font-size: var(--fs-body); font-weight: 600; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 999px;
  padding: 0.28rem 0.75rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40vw;
}

/* Betslip button relocated into the header top row (black bg) — was in the drawer. */
.sh-betslip {
  position: relative; display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  background: transparent; border: 1px solid rgba(var(--overlay-rgb), 0.16); color: var(--text);
  border-radius: 8px; height: 34px; padding: 0 9px; cursor: pointer;
}
.sh-betslip:hover { background: rgba(var(--overlay-rgb), 0.09); }
.sh-betslip .bs-star { fill: currentColor; }
.sh-betslip .betslip-badge:empty { display: none; }
.sh-betslip .betslip-badge {
  position: absolute; top: -5px; right: -5px; background: var(--accent-btn); color: var(--on-accent);
  border-radius: 999px; font-size: var(--fs-2xs); font-weight: 700; min-width: 16px; height: 16px;
  padding: 0 4px; display: inline-flex; align-items: center; justify-content: center;
}

/* Top-right "My Account" mini-menu (replaces the username label). */
.sh-acct { position: relative; }
.sh-acct-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 0.6rem;
  background: transparent; border: 1px solid rgba(var(--overlay-rgb), 0.16); border-radius: 8px;
  color: var(--text); font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; white-space: nowrap;
}
.sh-acct-btn:hover { background: rgba(var(--overlay-rgb), 0.09); }
.sh-acct-ico { font-size: 1rem; line-height: 1; }
.sh-acct-caret { font-size: 0.7em; opacity: 0.75; }
.sh-acct-menu {
  position: absolute; right: 0; top: calc(100% + 6px); min-width: 200px; z-index: 300;
  max-width: calc(100vw - 16px); /* #606 never overflow the viewport on small admin screens */
  display: flex; flex-direction: column;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 6px;
  box-shadow: 0 12px 32px -12px rgba(0,0,0,0.75);
}
.sh-acct-menu[hidden] { display: none; }
.sh-acct-item {
  display: block; text-align: left; padding: 0.5rem 0.6rem; border-radius: 6px;
  background: transparent; border: none; color: var(--text-mid); font: inherit; font-size: var(--fs-sm);
  text-decoration: none; white-space: nowrap; cursor: pointer;
}
.sh-acct-item:hover { background: var(--bg-hover); color: var(--text); }
.sh-acct-logout { color: var(--danger); }
.sh-acct-sep { height: 1px; background: var(--border); margin: 4px 2px; }
/* #606 collapse "My Account" to the icon earlier so the header row can't overflow on narrow
   admin pages (models.html etc.), where the sub-nav + controls make the top row tight. */
@media (max-width: 640px) { .sh-acct-lbl { display: none; } }
@media (max-width: 900px) { .sh-top { min-width: 0; } .sh-user { flex-shrink: 0; } }

/* Search box in the header top row. */
.sh-search { flex: 1 1 auto; min-width: 0; max-width: 440px; margin: 0 0.5rem; }
.sh-search .sb-field { height: 34px; background: rgba(var(--overlay-rgb), 0.06); border-color: rgba(var(--overlay-rgb), 0.14); }
.sh-search .sb-input, .sh-search .sb-ico { color: var(--text); }
.sh-search .sb-input::placeholder { color: rgba(var(--text-rgb), 0.55); }

/* #183 — mobile search: the inline header search shares the top row with the logo,
   betslip and account controls, which is cramped ≤700px. Below that width the inline
   box is hidden and replaced with a search-icon toggle that opens a full-width overlay
   (fixed, below the top bar) hosting its own UI.searchBox instance. */
.sh-search-toggle {
  display: none; flex-shrink: 0; align-items: center; justify-content: center;
  width: 34px; height: 34px; background: transparent; border: 1px solid rgba(var(--overlay-rgb), 0.16);
  border-radius: 8px; color: var(--text); cursor: pointer; padding: 0;
}
.sh-search-toggle:hover { background: rgba(var(--overlay-rgb), 0.09); }
.sh-search-toggle svg { width: 16px; height: 16px; }
.sh-search-overlay {
  position: fixed; left: 0; right: 0; top: var(--header-top-h); z-index: 350;
  background: var(--bg-card); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.75rem;
}
.sh-search-overlay[hidden] { display: none; }
.sh-search-overlay-mount { flex: 1; min-width: 0; }
.sh-search-overlay-mount .sb-results { position: fixed; left: 8px; right: 8px; top: calc(var(--header-top-h) + 58px); }
.sh-search-overlay-close {
  flex-shrink: 0; background: transparent; border: none; color: var(--text-mid);
  font-size: 1.6rem; line-height: 1; cursor: pointer; padding: 0 0.3rem;
}
@media (max-width: 700px) {
  .sh-search { display: none; }
  .sh-search-toggle { display: inline-flex; }
  /* Break the dropdown out to the viewport so results aren't squeezed into a narrow box. */
  .sh-search .sb-results { position: fixed; left: 8px; right: 8px; top: calc(var(--header-top-h) + 4px); }
}

/* Account page in "individual pages" mode (#—): the tab bar is hidden — each section is
   reached from the top-right My Account mini-menu and rendered on its own. */
.acct-solo .acct-nav { display: none; }
@media (max-width: 640px) {
  .ntb-label { display: none; }
  .nav-toggle-labeled { width: 34px; padding: 0; }
  .sh-logo img { height: 24px; }
}

/* The "next to jump" row now sits as the second line inside .site-header. */
.jump-header {
  position: static; z-index: auto; height: auto; flex: 1; min-height: 0;
  background: var(--bg-light); border-bottom: none;
  display: flex; align-items: center; gap: 0.75rem; padding: 0 0.9rem;
}
.jh-title { font-weight: 800; font-size: var(--fs-h4); color: var(--text); white-space: nowrap; flex-shrink: 0; }
.jh-filters { display: flex; align-items: center; gap: 0.3rem; flex-shrink: 0; }
.jh-chip {
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  padding: 0.28rem 0.5rem; font-size: var(--fs-body); line-height: 1; cursor: pointer;
  color: var(--text-mid); font-family: var(--font);
}
.jh-chip:hover { border-color: var(--border-light); }
/* #520 selected chip = a solid accent fill (was a 12% tint you couldn't distinguish from idle). */
.jh-chip.active { border-color: var(--accent); color: var(--on-accent); background: var(--accent); font-weight: 700; }
.jh-chip.jh-soon { opacity: 0.4; cursor: default; }
.jh-opt { font-weight: 700; font-size: var(--fs-xs); }
.jh-sep { width: 1px; height: 20px; background: var(--border); margin: 0 0.15rem; flex-shrink: 0; }
.jh-races { display: flex; align-items: center; gap: 0.5rem; overflow-x: auto; flex: 1; height: 100%; }
.jh-races::-webkit-scrollbar { height: 5px; }
.jh-races::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 3px; }
.jh-race {
  display: flex; align-items: center; gap: 0.4rem;
  flex: 1 1 0; min-width: 0; box-sizing: border-box;
  background: var(--bg); border: 1px solid var(--border); border-radius: 7px;
  padding: 0.18rem 0.55rem; text-decoration: none; white-space: nowrap;
}
.jh-race:hover { border-color: var(--accent); }
.jh-r-ico { font-size: var(--fs-h3); line-height: 1; flex-shrink: 0; }
.jh-r-track { flex: 1; min-width: 0; font-size: var(--fs-xs); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jh-r-track b { color: var(--text-muted); font-weight: 700; }
.jh-r-time {
  flex-shrink: 0; font-size: var(--fs-h4); font-family: var(--mono); font-weight: 800;
  color: var(--accent-light); background: var(--bg-card); border-radius: 5px;
  padding: 0.1rem 0.38rem; line-height: 1.1;
}
.jh-r-time.jh-off { color: var(--text-dim); }
.jh-empty { color: var(--text-muted); font-size: var(--fs-body); }
/* Compact filter dropdown on the next-to-jump bar. */
.jh-filter-dd { position: relative; flex-shrink: 0; }
.jh-filter-btn { display: inline-flex; align-items: center; gap: 0.35rem; }
.jh-fb-dots { display: inline-flex; gap: 3px; }
.jh-fb-caret { font-size: var(--fs-xs); opacity: 0.8; }
.jh-filter-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 320;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.45); padding: 0.4rem; min-width: 190px;
  display: flex; flex-direction: column; gap: 0.05rem;
}
.jh-filter-menu[hidden] { display: none; }
.jh-fm-row {
  display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.5rem;
  border-radius: 6px; font-size: var(--fs-body); color: var(--text); cursor: pointer; white-space: nowrap;
}
.jh-fm-row:hover { background: var(--bg-hover); }
.jh-fm-row input { accent-color: var(--accent); margin: 0; }
.jh-fm-disabled { opacity: 0.5; cursor: default; }
.jh-fm-disabled:hover { background: none; }
.jh-fm-soon { font-size: var(--fs-2xs); color: var(--text-dim); border: 1px solid var(--border); border-radius: 4px; padding: 0 0.25rem; margin-left: auto; }
.jh-fm-sep { height: 1px; background: var(--border); margin: 0.25rem 0.2rem; }
/* Mobile "next to jump": drop the label, tighten chips, and give race cards a
   fixed comfortable width so the row scrolls instead of squashing each card. */
@media (max-width: 640px) {
  .jump-header { gap: 0.4rem; padding: 0 0.5rem; }
  .jh-title { display: none; }
  .jh-filters { gap: 0.2rem; }
  .jh-chip { padding: 0.24rem 0.4rem; }
  .jh-sep { margin: 0 0.1rem; }
  .jh-race { flex: 0 0 auto; min-width: 138px; padding: 0.18rem 0.5rem; }
  .jh-r-track { font-size: var(--fs-xs); }
  .jh-r-time { font-size: var(--fs-h4); padding: 0.1rem 0.38rem; }
}

/* App shell: main content + a right-hand column. Base (mobile) = single column,
   rail hidden; desktop turns it into a centred two-column layout (see media query).
   NOTE: scoped to max-width:899px so these do NOT override the min-width:900px
   desktop rules above — media queries add no specificity, so an unscoped rule
   here would win by source order and hide the rail at all widths. */
/* Desktop default: the mobile Tulloch block is hidden (the rail shows Tulloch there).
   Declared BEFORE the media query so the query's override wins on mobile. */
.home-tulloch-mobile { display: none; }
@media (max-width: 899px) {
  .app-shell { display: block; }
  .app-rail { display: none; }
  /* On mobile the rail (with Tulloch's Tips + Best Bets) is hidden, so surface
     Tulloch's Tips in the main column in place of the how-it-works cards, and drop
     Best Bets from the page. */
  .home-tulloch-mobile { display: block; }
  .hiw-section { display: none; }
  /* NOTE: #bestBetsSection is intentionally NOT hidden — it's the dedicated /best-bets.html
     page's main content (no longer embedded on home/day), so it must render on mobile too. */
}
.app-rail-card {
  background: var(--bg); border: 1px solid var(--border); border-radius: 12px;
  padding: 1rem 1.1rem; color: var(--text-muted); font-size: var(--fs-body);
}
.app-rail-card + .app-rail-card { margin-top: 0.75rem; }
.arc-title { font-weight: 800; color: var(--text); font-size: var(--fs-h4); margin-bottom: 0.4rem; }
.arc-body { line-height: 1.6; }
/* Collapsible rail card. The home rail's rolled-up "Live alerts" card was its only user and is
   gone (2026-08-17); the mechanics stay because they are the shared pattern for any rail card
   that needs one. Its own `.arc-alerts` spacing rule went with the card. */
.arc-head { display: flex; align-items: center; gap: 0.5rem; width: 100%; background: none; border: none; cursor: pointer; color: inherit; font-family: inherit; padding: 0; }
.arc-chevron { color: var(--text-muted); transition: transform 0.15s; font-size: var(--fs-sm); }
.app-rail-card.collapsed .arc-chevron { transform: rotate(-90deg); }
.app-rail-card.collapsed .arc-body { display: none; }
.arc-count { margin-left: auto; font-size: var(--fs-xs); font-weight: 800; color: var(--accent-light); background: color-mix(in srgb, var(--accent) 16%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 999px; padding: 0.05rem 0.45rem; }
.arc-more { display: inline-block; margin-top: 0.35rem; font-size: var(--fs-sm); font-weight: 700; color: var(--accent-light); text-decoration: none; }
.arc-more:hover { text-decoration: underline; }

/* Home "How it works" strip — three linked cards; collapses to one column on mobile. */
.hiw-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0.85rem; }
.hiw-card { display: block; background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.1rem; text-decoration: none; color: var(--text); transition: border-color 0.15s, background 0.15s; }
.hiw-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.hiw-head { display: flex; align-items: center; gap: 0.55rem; }
.hiw-ico { font-size: var(--fs-h2); flex: none; line-height: 1; }
.hiw-t { font-weight: 700; font-size: var(--fs-h3); }
.hiw-d { font-size: var(--fs-body); color: var(--text-mid); line-height: 1.5; margin-top: 0.5rem; }
.hiw-link { display: inline-block; margin-top: 0.5rem; font-size: var(--fs-body); font-weight: 700; color: var(--accent-light); }
@media (max-width: 560px) { .hiw-grid { grid-template-columns: 1fr; } }

/* Legacy header (hidden by nav.js) */
header { display: none; }

/* Loading / Empty / Error */

.loading-msg {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--text-muted);
  font-size: var(--fs-h4);
  display: none;
}

/* Archive filter controls (reuses the horse-page control styles) */
.archive-controls { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.1rem; align-items: center; }

/* Horses page */
.horse-controls { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.1rem; align-items: center; }
.horse-search-input {
  flex: 1 1 260px; min-width: 200px; padding: 0.6rem 0.8rem; font-size: var(--fs-h4);
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; color: var(--text);
}
.horse-search-input:focus, .horse-filter:focus { outline: none; border-color: var(--accent); }
/* #237 — a real ring for keyboard focus. The `:focus` rule above only swapped the border colour,
   which a mouse click and a Tab press both trigger identically — a Tab landing here had no visible
   indicator at all beyond that thin border change. Matches the existing `:focus-visible` ring
   pattern already used elsewhere in this sheet (e.g. `.tab-odds.odds-btn`). */
.horse-search-input:focus-visible, .horse-filter:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.horse-filter {
  padding: 0.55rem 0.7rem; font-size: var(--fs-body); cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; color: var(--text);
}
/* Render the native date picker + calendar popup in dark mode and make the
   calendar icon clearly visible on the dark theme. */
input[type="date"].horse-filter { color-scheme: dark; }
input[type="date"].horse-filter::-webkit-calendar-picker-indicator {
  filter: invert(0.65); cursor: pointer; opacity: 0.9;
}
.horse-check {
  display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--fs-body);
  color: var(--text-muted); background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 8px; padding: 0.5rem 0.7rem; cursor: pointer;
}
.horse-check input { accent-color: var(--accent); }
.horse-layout { display: grid; grid-template-columns: 280px 1fr; gap: 1.25rem; align-items: start; }

/* ── Race-type colour coding (library) ───────────────────────────────────────
   Reusable helpers so every surface shows race types consistently. Apply
   .rt-<code> to expose the type colour as --rt-c, then use --rt-c for borders,
   text, dots, fills. .rt-dot renders the standalone colour swatch. */
.rt-horse   { --rt-c: var(--type-horse); }
.rt-grey    { --rt-c: var(--type-grey); }
.rt-harness { --rt-c: var(--type-harness); }
.rt-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--rt-c, var(--text-dim)); flex-shrink: 0; vertical-align: middle; }

/* Home "Today's meetings" matrix filters (AU / trials) beside the code tabs. */
.board-filters { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.1rem 0 0.85rem; }
/* Day page: race-type filter + AU/trials toggles on one row. */
.race-filter-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.25rem; margin-bottom: 1.1rem; }
.race-filter-row > #raceFilters { flex: 1 1 320px; }
.race-filter-row .race-filter-bar { margin-bottom: 0; }

/* Mobile: filter chips fill the row evenly (equal thirds) with compensated font
   sizing so the labels still fit; day-page filter row stacks vertically. */
@media (max-width: 640px) {
  .race-filter-bar { gap: 0.3rem; }
  .race-filter-bar .jh-chip { padding: 0.42rem 0.25rem; font-size: var(--fs-sm); }
  .rf-chip[data-type] { gap: 0.3rem; }
  .rf-chip[data-type]::before { width: 7px; height: 7px; }
  /* #202-followup: lay the toggles out as a neat 2-column grid so the switches line
     up in columns instead of trailing each label at a different offset. */
  .board-filters .toggle-stack, .race-filter-row #raceToggles .toggle-stack {
    display: grid; grid-template-columns: 1fr 1fr; align-items: center; margin-left: 0; gap: 0.5rem 1rem;
  }
  .board-filters .toggle-stack .toggle-label, .race-filter-row #raceToggles .toggle-stack .toggle-label {
    justify-content: space-between; width: 100%;
  }
  /* Stack the type filter over the toggles. Reset the 320px flex-basis — in a
     column it would apply to height and force a big gap. */
  .race-filter-row { flex-direction: column; align-items: stretch; gap: 0.5rem; }
  .race-filter-row > #raceFilters { flex: 0 0 auto; }
  .toggle-text { font-size: var(--fs-sm); }
}
@media (max-width: 380px) {
  .race-filter-bar .jh-chip { font-size: var(--fs-xs); padding: 0.42rem 0.15rem; }
}

/* Reusable race filter bar (UI.raceFilters) — chips reuse the .jh-chip look and
   share equal width, evenly dividing the full bar width. */
/* Chips dynamically size to the bar width — equal share of whatever the content
   width is, so the filter reads well on both mobile and desktop. */
.race-filter-bar { display: flex; flex-wrap: nowrap; align-items: stretch; gap: 0.4rem; margin-bottom: 1.1rem; }
.race-filter-bar .jh-chip { flex: 1 1 0; min-width: 0; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; text-align: center; white-space: nowrap; }
/* Labels truncate rather than force the bar to wrap or overflow — chips always
   shrink to fit a single row at any width. */
.race-filter-bar .rf-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Colour-code the type chips (no icons): a coloured dot + matching active fill. */
.rf-chip[data-type]::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--rt-c, var(--text-dim)); flex-shrink: 0; }
.rf-chip[data-type="horse"]   { --rt-c: var(--type-horse); }
.rf-chip[data-type="grey"]    { --rt-c: var(--type-grey); }
.rf-chip[data-type="harness"] { --rt-c: var(--type-harness); }
.rf-chip[data-type].active { border-color: var(--rt-c); color: var(--text); background: color-mix(in srgb, var(--rt-c) 16%, transparent); }

/* Flat, cross-code race list (UI-driven full day list). */
.all-races-list { display: flex; flex-direction: column; gap: 0.22rem; }
/* Grouped-by-track view: a track header above each track's rows. */
.arl-group { margin-bottom: 0.9rem; }
.arl-group-head {
  display: flex; align-items: center; gap: 0.4rem; padding: 0.3rem 0.2rem 0.4rem;
  font-weight: 800; font-size: var(--fs-h4); color: var(--text);
}
.arl-group-count { font-weight: 600; font-size: var(--fs-sm); color: var(--text-dim); }
.arl-group .arl-row { margin-bottom: 0.3rem; }

.arl-row {
  display: flex; align-items: center; gap: 0.55rem; padding: 0.32rem 0.7rem; font-size: var(--fs-body);
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 7px;
  text-decoration: none; color: var(--text);
}
.arl-row:hover { border-color: var(--accent); background: var(--bg-hover); }
.arl-row.arl-abn { opacity: 0.6; }
/* Colour-code the race type (no icons): each row expose its type colour as --rt-c
   and themes to it — left accent, subtle tinted background, and coloured time —
   so it's obvious which race type you're viewing. */
.arl-row.arl-horse   { --rt-c: var(--type-horse); }
.arl-row.arl-grey    { --rt-c: var(--type-grey); }
.arl-row.arl-harness { --rt-c: var(--type-harness); }
.arl-row.arl-horse, .arl-row.arl-grey, .arl-row.arl-harness {
  background: color-mix(in srgb, var(--rt-c) 8%, var(--bg-card));
}
.arl-row.arl-horse:hover, .arl-row.arl-grey:hover, .arl-row.arl-harness:hover {
  border-color: var(--rt-c);
  background: color-mix(in srgb, var(--rt-c) 16%, var(--bg-card));
}
.arl-time { font-family: var(--mono); font-weight: 800; color: var(--rt-c, var(--accent-light)); flex-shrink: 0; width: 66px; font-size: var(--fs-xs); white-space: nowrap; }
.arl-num { font-weight: 800; color: var(--text-muted); flex-shrink: 0; width: 32px; }
.arl-track { font-weight: 700; flex-shrink: 0; min-width: 120px; }
/* Desktop: transparent wrapper so track/name/meta stay direct flex children. */
.arl-main { display: contents; }
.arl-name { flex: 1; min-width: 0; color: var(--text-mid); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arl-meta { color: var(--text-dim); font-size: var(--fs-sm); flex-shrink: 0; white-space: nowrap; }
.arl-status { flex-shrink: 0; margin-left: auto; }
@media (max-width: 640px) {
  /* Stack track + race name on two lines so the race is identifiable, not just the
     track. Time stays left, status right. */
  .arl-main { display: flex; flex-direction: column; gap: 0.05rem; flex: 1 1 auto; min-width: 0; }
  .arl-main > span { flex: 0 0 auto; min-width: 0; }
  .arl-track { min-width: 0; font-size: var(--fs-sm); }
  .arl-name { display: block; font-size: var(--fs-sm); }
  .arl-meta { display: none; }
}

/* Race matrix (UI.raceMatrix) — meetings down the left, races across the top,
   tightly aligned by race number. Colour-coded by race type. Sticky venue col. */
.race-matrix {
  display: grid;
  /* Fixed race-column width so cells NEVER compress — the grid overflows and the
     container scrolls horizontally instead of cramming R#/time into 50px. The venue
     column is sticky (below) so it stays visible while the races scroll. */
  grid-template-columns: minmax(150px, 210px) repeat(var(--rm-cols, 1), 64px);
  gap: 4px; overflow-x: auto; background: var(--border); border: 1px solid var(--border);
  border-radius: 8px; font-size: var(--fs-sm);
  /* No visible scrollbar — pan by grab-dragging (mouse) or swiping (touch). */
  scrollbar-width: none; cursor: grab;
}
.race-matrix::-webkit-scrollbar { display: none; }
.race-matrix.rm-grabbing { cursor: grabbing; user-select: none; }
.race-matrix.rm-grabbing a { cursor: grabbing; }
.rm-corner, .rm-colhead { background: var(--bg); }
.rm-corner { position: sticky; left: 0; z-index: 3; } /* above the sticky venue */
.rm-colhead { text-align: center; font-weight: 800; color: var(--text-muted); padding: 0.35rem 0.2rem; }
.rm-venue {
  display: flex; align-items: center; gap: 0.4rem; padding: 0.7rem 0.55rem;
  background: var(--bg-card);
  text-decoration: none; color: var(--text); font-weight: 700;
  /* z-index 2: the sticky venue must paint ABOVE in-cell absolute content (the AI
     marker .rmc-ai sits at z-index 1) or badges bleed over the track name on scroll. */
  position: sticky; left: 0; z-index: 2;
  flex-wrap: wrap; row-gap: 0.3rem;
}
/* Forces the coverage tag onto its own line under the track name. */
.rm-vbreak { flex-basis: 100%; height: 0; margin: 0; }
/* Wrap the track name to up to two lines (then ellipsis) so it stays readable
   instead of being cut off — matters most in the narrow mobile venue column. */
.rm-venue-name { min-width: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.2; }
a.rm-venue:hover { background: var(--bg-hover); }
.rm-cell {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  padding: 0.6rem 0.35rem; background: var(--bg-card);
  border-top: 2px solid var(--rt-c, transparent); text-decoration: none; color: var(--text);
}
a.rm-cell:hover { background: var(--bg-hover); }
.rm-cell.rm-empty { background: transparent; border-top-color: transparent; }
/* Races with an AI prediction: soft accent tint + an accent-coloured race number,
   reinforced by a labelled "AI" corner tag (#169) so the signal survives without colour. */
.rm-cell.rm-pred { background: color-mix(in srgb, var(--accent) 9%, var(--bg-card)); }
a.rm-cell.rm-pred:hover { background: color-mix(in srgb, var(--accent) 16%, var(--bg-card)); }
.rm-cell.rm-pred .rmc-num { color: var(--accent-light); }
/* #169 labelled "AI" corner tag on a predicted race cell — a shape+text marker that
   reads without relying on the tint (colour-blind safe), replacing the old colour-only dot. */
.rmc-ai {
  position: absolute; top: 2px; right: 2px; z-index: 1;
  font-size: 8px; font-weight: 800; letter-spacing: 0.04em; line-height: 1;
  padding: 2px 3px; border-radius: 3px;
  background: var(--accent-btn); color: var(--on-accent);
}
/* Count of AI-predicted races beside the track/venue name. */
.rm-venue-preds {
  flex-shrink: 0; margin-left: auto; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.02em;
  color: var(--accent-light); background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 5px; padding: 0.05rem 0.3rem;
}
/* #430 specials marker on a matrix cell — bottom-left so it never collides with .rmc-ai (top-right). */
.rmc-special {
  position: absolute; bottom: 2px; left: 2px; z-index: 1;
  font-size: 9px; font-weight: 800; line-height: 1; padding: 1px 3px; border-radius: 3px;
  background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent-light);
}
/* #430 race-page hero special pill. */
.hero-special { margin-top: 0.4rem; }
.special-badge {
  display: inline-flex; align-items: center; gap: 0.2rem;
  font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
  color: var(--accent-light); background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 999px; padding: 0.12rem 0.55rem;
}
.special-badge:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.rmc-num { font-weight: 700; font-size: var(--fs-xs); color: var(--text-muted); line-height: 1; }
.rmc-time { font-family: var(--mono); font-size: var(--fs-xs); color: var(--accent-light); line-height: 1.1; white-space: nowrap; }
.rm-cell.rm-resulted { opacity: 0.5; }
.rm-cell.rm-abn { opacity: 0.4; }
/* Uncoverable meeting (not in TAB, never settled) — dim the whole row, no tips. */
.rm-venue.rm-uncov, .rm-cell.rm-uncov { opacity: 0.45; }
.rm-venue-tag { flex-shrink: 0; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; border-radius: 5px; padding: 0.05rem 0.3rem; margin-left: 0.35rem; white-space: nowrap; }
.rm-tag-uncov { color: var(--text-dim); background: rgba(157,161,184,0.14); border: 1px solid var(--border); }
.rm-tag-delay { color: var(--win); background: rgba(var(--win-rgb), 0.12); border: 1px solid rgba(var(--win-rgb), 0.3); }
/* Race-page coverage badge (in the hero badge row) + identifier banner */
.hero-cov { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; border-radius: 999px; padding: 0.12rem 0.5rem; white-space: nowrap; }
.hero-cov-uncov { color: var(--text-dim); background: rgba(157,161,184,0.14); border: 1px solid var(--border); }
.hero-cov-delay { color: var(--win); background: rgba(var(--win-rgb), 0.12); border: 1px solid rgba(var(--win-rgb), 0.3); }
.race-cov-note { margin-top: 0.75rem; padding: 0.6rem 0.9rem; border-radius: 8px; font-size: var(--fs-body); line-height: 1.45; }
.race-cov-uncov { color: var(--text-mid); background: rgba(157,161,184,0.1); border: 1px solid var(--text-dim); }
.race-cov-delay { color: var(--win); background: rgba(var(--win-rgb), 0.08); border: 1px solid var(--win); }
.rm-cell.rm-abn .rmc-time { text-decoration: line-through; }
.rm-cell.rm-live .rmc-time { color: var(--green); }

/* #560 — three visually DISTINCT race states, so resulted and trial-locked never read the same:
   • yet-to-run  → the default cell (full contrast, accent time, hover) — actionable.
   • resulted    → passive/DONE: dimmed + desaturated so it recedes into the past.
   • trial-locked→ an inviting "unlock": accent wash + lock glyph, NOT dead-grey — a call-to-action. */
.rm-cell.rm-resulted { opacity: 0.5; filter: grayscale(0.65); }
.rm-cell.rm-resulted .rmc-num, .rm-cell.rm-resulted .rmc-time { color: var(--text-muted); }
.rm-venue.rm-locked, .rm-cell.rm-locked { opacity: 1; background: color-mix(in srgb, var(--accent) 8%, var(--bg-card)); }
.rm-cell.rm-locked { border-top: 1px dashed color-mix(in srgb, var(--accent) 45%, var(--border)); }
a.rm-cell.rm-locked:hover, a.rm-venue.rm-locked:hover { background: color-mix(in srgb, var(--accent) 16%, var(--bg-card)); }
.rm-cell.rm-locked .rmc-num { color: var(--accent-light); }
.rm-cell.rm-locked .rmc-time { color: color-mix(in srgb, var(--accent-light) 65%, var(--text-muted)); }
/* the padlock is the themed RED lock (.lock-ico → var(--danger)), same as the locked nav-menu items. */
.rmc-lock { position: absolute; top: 2px; right: 3px; z-index: 1; line-height: 0; }
.rmc-lock svg { width: 12px; height: 12px; display: block; }
.rm-tag-upgrade { color: var(--accent-light); background: color-mix(in srgb, var(--accent) 16%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); display: inline-flex; align-items: center; gap: 2px; }
.rm-tag-upgrade svg { width: 10px; height: 10px; }

/* The same three-way distinction in the flat race LIST (raceRow). */
.arl-row.arl-locked { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); background: color-mix(in srgb, var(--accent) 6%, transparent); }
.arl-row.arl-locked:hover { background: color-mix(in srgb, var(--accent) 12%, var(--bg-hover)); }
.status-badge.sb-upgrade { color: var(--accent-light); background: color-mix(in srgb, var(--accent) 16%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); display: inline-flex; align-items: center; gap: 3px; }
.status-badge.sb-upgrade svg { width: 11px; height: 11px; }

@media (max-width: 640px) {
  /* Roomier cells on mobile so the matrix isn't cramped — it scrolls
     horizontally (as intended) rather than squeezing every race into view. */
  .race-matrix {
    grid-template-columns: minmax(136px, 1.5fr) repeat(var(--rm-cols, 1), minmax(58px, 1fr));
    font-size: var(--fs-sm); -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity;
  }
  .rm-venue { padding: 0.7rem 0.55rem; scroll-snap-align: start; }
  .rm-cell { padding: 0.7rem 0.4rem; gap: 3px; }
  .rmc-num { font-size: var(--fs-xs); }
  .rmc-time { font-size: var(--fs-sm); }
}
/* Stats page tabs */
.stat-tabs {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.25rem;
}
.stat-tab {
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-size: var(--fs-body); font-weight: 600;
  padding: 0.55rem 0.9rem; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.stat-tab:hover { color: var(--text); }
.stat-tab.active { color: var(--accent-light); border-bottom-color: var(--accent); }

/* Sample prompt display */
.prompt-block { margin-top: 0.9rem; }
.prompt-label {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-dim); margin-bottom: 0.35rem;
}
.prompt-pre {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.75rem 0.85rem; margin: 0; max-height: 60vh; overflow: auto;
  font-family: var(--mono); font-size: var(--fs-sm); line-height: 1.5; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
}
/* Code toggle for the Sample Prompt panel (Thoroughbred / Greyhound / Harness) */
.prompt-codes .pill {
  padding: 0.3rem 0.7rem; border-radius: 999px; cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text-dim);
  font-size: var(--fs-sm); font-weight: 600;
}
.prompt-codes .pill:hover { color: var(--text); border-color: var(--accent-light); }
.prompt-codes .pill-on {
  background: rgba(var(--accent-rgb), 0.15); border-color: var(--accent-light); color: var(--text);
}

/* Backtest table */
.bt-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); margin-top: 0.5rem; }
.bt-table th, .bt-table td {
  padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--border); text-align: left;
}
.bt-table th { color: var(--text-dim); font-weight: 600; font-size: var(--fs-sm); }
.bt-table .bt-num { text-align: right; font-family: var(--mono); }
.bt-table tr.bt-best td { color: var(--accent-light); font-weight: 700; }

/* ── Component library: shared data table / buttons / badges (UI.*) ─────────── */
/* Horizontal-scroll wrapper so wide tables scroll instead of overflowing the
   page on mobile. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-wrap .data-table { min-width: 480px; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
.data-table th { text-align: left; color: var(--text-dim); font-weight: 600; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.03em; padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--border); }
.data-table td { padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
.data-table tbody tr:hover { background: rgba(var(--accent-rgb), 0.05); }
/* tabular-nums is what actually keeps a numeric column aligned — the FACE does not, and on the
   marketing pages --mono is aliased back to the body font (see the funnel's one-face rule). */
.data-table .dt-num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.data-table .dt-muted { color: var(--text-muted); }
/* #830 Drop a low-value column below 640px. Added to the shared table rules rather than to one
   page because the need is general: a data table wide enough to be useful on a desktop is usually
   wider than a phone, and horizontally scrolling past the column a reader came for is worse than
   not showing them the track name. Applies to the header cell and the body cell together, so a
   hidden column cannot leave a dangling <th>. Everything hidden here stays in the CSV export. */
@media (max-width: 640px) {
  .data-table th.dt-hide-sm,
  .data-table td.dt-hide-sm { display: none; }
}
/* The inverse: a sub-line that appears ONLY where a `dt-hide-sm` column has been hidden, so the fact
   it carried is still on screen rather than merely still in the CSV. Exactly one representation is
   visible at any width — the sub-line below 640px, the dedicated column above it — so nothing is
   ever duplicated. Used by the track record to keep the meeting and race visible on a phone without
   spending a column on them (#830 hid those columns because a 9-column table pushes P/L, the figure
   a reader came for, off the right edge). */
.dt-sub { display: none; }
@media (max-width: 640px) {
  .dt-sub { display: block; font-size: var(--fs-xs); color: var(--text-muted); font-weight: 400; margin-top: 1px; }
}
.data-table .dt-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }
/* Winning rows, findable at a glance. A settled ledger is read by scanning for the wins, and with
   every row in one ink the reader has to parse a P/L column figure by figure to find them.
   Shared here rather than on the track-record page because every settled/result table has the
   same job. The tint is on the CELLS, not the row, so it survives `tbody tr:hover` (which paints
   the tr) — the hover shift stays visible on top of it. The left edge is an inset box-shadow so it
   costs no layout width and cannot shift a column.
   Colour is never the only signal: the row is paired with a "1st" badge in the Finish column.
   Both hues come from the theme's --win token, which is amber in dark and indigo in light (light
   mode reserves red for live alerts and never uses green for a result — see :root[data-theme]). */
.data-table tr.dt-win td { background: var(--win-bg); }
.data-table tr.dt-win td:first-child { box-shadow: inset 3px 0 0 var(--win); }
.data-table .dt-pos { color: var(--win); font-weight: 600; }

/* a11y (#168): fill is #6a5ad4 so white text clears WCAG AA (5.22:1; var(--accent)
   #7c6cf0 was only 3.99:1). Shared 44px min hit-area on standalone buttons below. */
/* #416 .btn-primary was `background: #6a5ad4` — a LITERAL. The main button was the same purple
   in every theme, including both palette trials, which is a large part of why they read as
   "barely any change". It now follows the tokens like everything else.
   --on-accent (not #fff) because a light accent needs DARK text: white on the mint is 1.62:1. */
.btn-primary { background: var(--accent-btn); color: var(--on-accent); border: none; padding: 0.35rem 0.9rem; font-weight: 600; font-size: var(--fs-body); border-radius: 7px; cursor: pointer; transition: background 0.15s, filter 0.15s; }
.btn-primary:hover { filter: brightness(0.92); }
/* One accessible hit-area for the standalone button family (compact .btn-sm opts down). */
.btn-primary, .btn-secondary, .btn-outline, .btn-danger, .hero-cta {
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
}
.btn-sm { min-height: 34px; }
.btn-block { width: 100%; }

/* Segmented control (UI.segmented) — code tabs for consolidated sections (#164). */
.segmented { display: flex; gap: 4px; padding: 4px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.segmented::-webkit-scrollbar { display: none; }
.seg-btn {
  flex: 1 0 auto; min-height: 40px; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.4rem 0.9rem; border: none; border-radius: 7px; background: transparent; color: var(--text-muted);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; white-space: nowrap; transition: background 0.15s, color 0.15s;
}
.seg-btn:hover { color: var(--text); background: var(--bg-hover); }
/* Active tab is tinted in the RACE-TYPE colour + outlined, matching the All-Races filter
   chips (.rf-chip.active) so race-type selection looks like one system everywhere. */
.seg-btn.active { background: color-mix(in srgb, var(--rt-c, var(--accent)) 20%, transparent); color: var(--text); box-shadow: inset 0 0 0 1.5px var(--rt-c, var(--accent)); }
.seg-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--rt-c, currentColor); }
.bb-dot-horse { --rt-c: var(--type-horse); }
.bb-dot-grey { --rt-c: var(--type-grey); }
.bb-dot-harness { --rt-c: var(--type-harness); }
.bb-tabs { margin-bottom: 0.8rem; }
/* #441 Best Bets "quality races" strike filter toggle */
.bb-strike { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--fs-sm); color: var(--text-mid); cursor: pointer; margin: 0 0 0.7rem; user-select: none; }
.bb-strike input { cursor: pointer; }
.bb-strike-cnt { color: var(--text-muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
/* #432 Best Bets user filters (min price / min prob / hide low-conf / upcoming) */
.bb-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem; margin: 0 0 0.7rem; font-size: var(--fs-sm); color: var(--text-mid); }
.bb-f { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; }
.bb-f input[type="number"] { width: 58px; }
/* #NNN state / track / going selects (dedicated Best Bets page) */
.bb-geo { display: flex; flex-wrap: wrap; gap: 0.7rem; margin: 0 0 0.7rem; font-size: var(--fs-sm); color: var(--text-mid); }
.bb-geo:empty { display: none; }
.bb-geo select { max-width: 170px; }
/* #468 clear-all-filters button — quiet, sits inline at the end of the filter bar */
.bb-clear { align-self: center; padding: 0.2rem 0.6rem; border: 1px solid var(--border); background: transparent; color: var(--text-mid); border-radius: var(--radius); cursor: pointer; font: inherit; font-size: var(--fs-xs); }
.bb-clear:hover { border-color: var(--accent); color: var(--accent); }
.bb-datenote { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 0.5rem; font-variant-numeric: tabular-nums; }
/* #NNN AI-vs-Basic flow toggle */
.bb-flow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0 0 0.7rem; font-size: var(--fs-sm); }
.bb-flow:empty { display: none; }
.bb-flowlabel { color: var(--text-mid); margin-right: 0.15rem; }
.bb-flowbtn { padding: 0.28rem 0.75rem; border: 1px solid var(--border); background: transparent; color: var(--text-mid); border-radius: var(--radius); cursor: pointer; font: inherit; line-height: 1.2; }
.bb-flowbtn.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.bb-flowbtn.locked { opacity: 0.65; }
.bb-flowbtn:hover:not(.active) { border-color: var(--accent); color: var(--accent); }
/* #1194 ask 2 — on the RACE page the same buttons are a tablist, and must look like one. Scoped to
   [role=tablist] so best-bets, which renders the identical class as a free-standing filter group,
   is untouched. The tabs sit ON the rule that the prediction card hangs from: the active tab breaks
   the rule and drops its own bottom border, which is the one visual cue that says "the thing below
   belongs to me" — a detached pill cannot say that, which is why the strip read as a filter.

   The UNDERLINE idiom, not a filled pill: the selected tab is marked by a rule along the edge it
   shares with the panel, which is what makes the two read as one object. A filled pill (the
   best-bets treatment, kept there) says "this filter is on" — a different claim. Background stays
   transparent so nothing has to match the card's gradient across two themes. */
.bb-flowtoggle[role="tablist"] { display: flex; gap: 0.25rem; margin: 0 auto; max-width: 1380px;
  padding: 0 0.75rem; border-bottom: 1px solid var(--border); }
.bb-flowtoggle[role="tablist"] .bb-flowbtn { border: 0; border-bottom: 3px solid transparent;
  border-radius: 0; background: transparent; color: var(--text-mid); margin-bottom: -1px;
  padding: 0.4rem 0.9rem; }
.bb-flowtoggle[role="tablist"] .bb-flowbtn[aria-selected="true"] { background: transparent;
  border-bottom-color: var(--accent); color: var(--text); font-weight: 700; }
.bb-flowtoggle[role="tablist"] .bb-flowbtn:hover:not([aria-selected="true"]) {
  color: var(--accent); border-bottom-color: color-mix(in srgb, var(--accent) 40%, transparent); }
/* The card hangs directly off the tab rule — a 1rem gap would break the join that carries the
   whole affordance. */
.bb-flowtoggle[role="tablist"] + .top-pick { margin-top: 0; border-top-left-radius: 0;
  border-top-right-radius: 0; }
/* Keyboard nav is the point of the roving tabindex — the focus ring must be unmistakable. */
.bb-flowtoggle[role="tablist"] .bb-flowbtn:focus-visible { outline: 2px solid var(--accent);
  outline-offset: -2px; }
/* #434 free tier: hide the Pro-only controls + show an upsell */
[data-tier="free"] .bb-strike, [data-tier="free"] .bb-filters,
[data-tier="free"] .bb-geo, [data-tier="free"] #bbShow { display: none; } /* #468 filters are Pro */
.bb-upsell { display: block; font-size: var(--fs-sm); color: var(--accent); text-decoration: none; padding: 0.5rem 0.65rem; border: 1px solid var(--border); background: var(--bg-hover); margin-bottom: 0.6rem; font-weight: 600; }
.bb-upsell:hover { border-color: var(--accent); }

/* Universal search box (UI.searchBox) — top nav + search page, one consistent look. */
.searchbox { position: relative; }
.sb-field { display: flex; align-items: center; gap: 6px; background: var(--bg-input); border: 1px solid var(--border); border-radius: 8px; padding: 0 10px; height: 38px; }
.sb-field:focus-within { border-color: var(--accent); }
.sb-ico { width: 16px; height: 16px; color: var(--text-dim); flex-shrink: 0; }
.sb-input { flex: 1; min-width: 0; background: transparent; border: none; outline: none; color: var(--text); font: inherit; font-size: var(--fs-sm); }
.sb-input::placeholder { color: var(--text-dim); }
.sb-input::-webkit-search-cancel-button { -webkit-appearance: none; }
.sb-results {
  position: absolute; top: calc(100% + 5px); left: 0; right: 0; z-index: 400;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 4px;
  box-shadow: 0 14px 36px -12px rgba(0,0,0,0.78); max-height: 62vh; overflow-y: auto;
}
.sb-results[hidden] { display: none; }
.sb-item { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 9px; padding: 8px; border-radius: 7px; text-decoration: none; color: var(--text); }
.sb-item:hover, .sb-item.active { background: var(--bg-hover); }
.sb-type { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 2px 7px; border-radius: 5px; white-space: nowrap; min-width: 62px; text-align: center; }
.sb-type-rt { background: color-mix(in srgb, var(--rt-c) 20%, transparent); color: var(--rt-c); }
.sb-type-plain { background: var(--bg-light); color: var(--text-muted); }
.sb-label { font-weight: 600; font-size: var(--fs-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-sub { font-size: var(--fs-2xs); color: var(--text-dim); white-space: nowrap; }
.searchbox-compact .sb-field { height: 34px; }
@media (max-width: 560px) { .sb-item .sb-sub { display: none; } }

/* #171 Desktop persistent top-nav — the primary MENU items as a horizontal bar in the
   header top row, ≥900px only. Mobile keeps the drawer + bottom bar (below). The header
   hamburger stays on desktop for the full menu / betslip / account. */
.sh-nav { display: none; }
@media (min-width: 1100px) {
  .sh-nav { display: flex; align-items: center; gap: 1px; flex: 1 1 auto; min-width: 0; margin: 0 0.35rem; }
  .sh-nav .dn-item { position: relative; flex-shrink: 0; }
  .sh-nav .dn-link { display: inline-flex; align-items: center; gap: 3px; height: 34px; padding: 0 0.42rem; border-radius: 7px; color: #cfd2df; font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; text-decoration: none; cursor: pointer; }
  .sh-nav a.dn-link:hover, .sh-nav .dn-has-sub:hover > .dn-link, .sh-nav .dn-has-sub:focus-within > .dn-link { background: rgba(var(--overlay-rgb), 0.09); color: #fff; }
  .sh-nav .dn-item.active > .dn-link, .sh-nav a.dn-item.active { color: var(--accent-light); }
  .sh-nav .dn-caret { font-size: 0.65em; opacity: 0.7; }
  .sh-nav .dn-sub { display: none; position: absolute; top: 100%; left: 0; margin-top: 3px; min-width: 190px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 6px; box-shadow: 0 10px 30px -12px rgba(0,0,0,0.7); z-index: 260; }
  .sh-nav .dn-has-sub:hover > .dn-sub, .sh-nav .dn-has-sub:focus-within > .dn-sub { display: block; }
  .sh-nav .dn-sub-item { display: block; padding: 0.4rem 0.6rem; border-radius: 6px; color: var(--text-mid); font-size: var(--fs-sm); text-decoration: none; white-space: nowrap; }
  .sh-nav .dn-sub-item:hover { background: var(--bg-hover); color: var(--text); }
  .sh-nav .dn-heading { padding: 0.35rem 0.6rem 0.15rem; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); }
  /* The persistent nav carries wayfinding now — trim the hamburger to an icon. */
  .site-header .ntb-label { display: none; }
}

/* #167 Mobile bottom tab bar — core tasks, thumb-reachable. Desktop keeps the drawer. */
.bottom-nav { display: none; }
@media (max-width: 1099px) {
  .bottom-nav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
    background: var(--bg-card); border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .bn-tab {
    flex: 1; min-height: 54px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    padding: 6px 2px; color: var(--text-dim); text-decoration: none; font-size: var(--fs-2xs); font-weight: 600;
  }
  .bn-tab .bn-lbl { white-space: nowrap; } /* 5 tabs (#186): keep "Predictions" on one line */
  .bn-tab .bn-ico { line-height: 0; }
  .bn-tab .bn-ico svg { width: 23px; height: 23px; display: block; }
  .bn-tab.active { color: var(--accent-light); }
  .bn-tab:active { background: var(--bg-hover); }
  /* Keep page content clear of the fixed bar. */
  body { padding-bottom: calc(54px + env(safe-area-inset-bottom, 0px)); }
}

/* ── Sportsbet-style LEFT sidebar nav ─────────────────────────────────────────
   ≥1100px: the menu drawer is PINNED open as a fixed left sidebar and the page
   content is shifted right to sit beside it (no scrim / toggle). Below 1100px it
   stays a slide-out drawer opened by the bottom-bar ☰. (Placed after the --rail /
   body-padding rules so this wins for wide viewports.) */
@media (min-width: 1100px) {
  .top-nav { transform: translateX(0); box-shadow: none; }
  body { padding-left: var(--rail); }
    /* Full-bleed pages: see the geometry-gated rule below. Deliberately NOT exempted here — from
       1100 to 1579px there is not enough gutter for the fixed menu to overlay harmlessly. */
  .nav-scrim { display: none; }
  /* The header already carries the logo + My Account, so drop the sidebar's copies. */
  .top-nav .tn-brand, .top-nav .tn-user-area { display: none; }
}
  /* #870 Full-bleed pages (Prime Vectors) centre on the WHOLE viewport rather than on the space
     beside the pinned menu: dropping the sidebar padding lets the column's `margin:auto` measure
     against the full page width, and the fixed menu simply overlays the left gutter.

     THAT ONLY HOLDS IF THE GUTTER IS WIDER THAN THE MENU. The exemption used to apply from 1100px
     up, while the geometry needs:

         gutter = (viewport - content) / 2  >=  rail
         (viewport - 1100) / 2 >= 240   ->   viewport >= 1580px

     Between 1100 and 1579 the gutter is NARROWER than the 240px rail, so the menu covered the
     start of every line: on a 1436px window the gutter is 168px and ~72px of text sat under the
     menu, clipping "Unlike" to "Jnlike". Gated to the width where the assumption is actually true;
     below it the page falls back to the ordinary beside-the-menu layout every other page uses. */
  @media (min-width: 1580px) {
    body.pv-fullbleed { padding-left: 0; }
  }
.btn-ico { display: inline-flex; align-items: center; line-height: 0; }
/* Link-buttons (UI.button with href) render as <a> — kill the underline, keep the box. */
a.btn-primary, a.btn-secondary, a.btn-outline, a.btn-danger { text-decoration: none; }
.btn-danger { background: transparent; color: var(--danger); border: 1px solid rgba(239, 83, 80, 0.4); padding: 0.3rem 0.75rem; font-weight: 600; font-size: var(--fs-sm); border-radius: 7px; cursor: pointer; }
.btn-danger:hover { background: rgba(239, 83, 80, 0.1); }
.btn-sm { font-size: var(--fs-sm) !important; padding: 0.25rem 0.6rem !important; }

.role-badge { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; padding: 0.12rem 0.5rem; border-radius: 4px; background: var(--bg); border: 1px solid var(--border); color: var(--text-mid); }
.role-badge.role-admin { background: rgba(var(--accent-rgb), 0.16); color: var(--accent-light); border-color: rgba(var(--accent-rgb), 0.4); }
.role-badge.role-analyst { background: rgba(var(--ew-rgb), 0.14); color: var(--ew); border-color: rgba(var(--ew-rgb), 0.4); }
.role-badge.role-viewer, .role-badge.role-user { background: var(--bg); color: var(--text-muted); }
.status-pill { font-size: var(--fs-sm); font-weight: 600; }
.status-pill.status-active { color: var(--ew); }
.status-pill.status-inactive { color: var(--text-dim); }

.horse-results { display: flex; flex-direction: column; gap: 0.3rem; max-height: 70vh; overflow-y: auto; }
.horse-result {
  display: flex; flex-direction: column; gap: 0.15rem; text-align: left; cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.7rem; color: inherit;
}
.horse-result:hover { border-color: var(--accent); }
.horse-result.active { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.1); }
.hr-name { font-weight: 600; font-size: var(--fs-h4); }
.hr-stats { color: var(--text-muted); font-size: var(--fs-sm); }
.horse-profile { min-height: 100px; }
.hp-head h2 { margin: 0 0 0.3rem; }
.hp-bio { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin-bottom: 0.4rem; font-size: var(--fs-body); }
.hp-bio-item { color: var(--text); }
.hp-bio-k { color: var(--text-muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.03em; margin-right: 0.2rem; }
.hp-summary { color: var(--text-muted); font-size: var(--fs-body); margin-bottom: 0.5rem; }
.hp-section { margin: 1.1rem 0 0.5rem; font-size: var(--fs-h4); }
.hp-results { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
.hp-results th { text-align: left; color: var(--text-muted); font-weight: 600; padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--border); }
.hp-results td { padding: 0.35rem 0.5rem; border-bottom: 1px solid var(--border); }
.fin-win { color: var(--ew); font-weight: 700; }
.fin-place { color: var(--accent-light); font-weight: 600; }
.fin-other { color: var(--text-dim); }
.tip-hit { color: var(--ew); font-weight: 700; }
.tip-miss { color: var(--result-miss); }
.tip-pending { color: var(--text-dim); }
.hp-racelink { color: var(--accent-light); text-decoration: none; }
.hp-racelink:hover { text-decoration: underline; }
.hp-tipcount { font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted); margin-left: 0.4rem; }
.runner-horse-link { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--text-dim); }
.runner-horse-link:hover { color: var(--accent-light); border-bottom-color: var(--accent); }
.note-add { display: flex; gap: 0.5rem; margin-bottom: 0.6rem; }
.note-add textarea { flex: 1; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; color: var(--text); padding: 0.5rem; resize: vertical; font: inherit; }
.note-add button {
  align-self: flex-start;
  background: var(--accent-btn); color: var(--on-accent); border: none;
  padding: 0.5rem 1.1rem; font-weight: 600; font-size: var(--fs-body);
  border-radius: 8px; cursor: pointer; transition: background 0.15s; white-space: nowrap;
}
.note-add button:hover { background: #5a4ac4; }
.note-add button:disabled { background: var(--border); color: var(--text-dim); cursor: not-allowed; }
.notes-list { display: flex; flex-direction: column; gap: 0.5rem; }
.note-item { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 0.55rem 0.7rem; }
.note-text { font-size: var(--fs-h4); white-space: pre-wrap; }
.note-meta { color: var(--text-muted); font-size: var(--fs-xs); margin-top: 0.3rem; }
.note-del { background: none; border: none; color: #ef5350; cursor: pointer; font-size: var(--fs-xs); margin-left: 0.4rem; }
@media (max-width: 720px) {
  .horse-layout { grid-template-columns: 1fr; }
  /* Single column on mobile: let the results list flow with the page instead of
     scrolling inside a capped box (which made the page feel unscrollable). */
  .horse-results { max-height: none; overflow: visible; }
}

/* Home page sections — match the standard .home-section rhythm used elsewhere */
.landing-section { margin-bottom: 1rem; }

/* Best Bets of the Day */
#bestBetsSection { margin-bottom: 1rem; }
/* The header adopts the Prime Vectors wordmark treatment (same display face, uppercase, tracked,
   larger size + the brand's heading ink) so Best Bets and Prime Vectors read as one family. */
#bestBetsSection .section-title {
  font-family: var(--font-head); font-weight: 700; letter-spacing: 0.06em;
  font-size: 1.5rem; line-height: 1.2; text-transform: uppercase; color: var(--text);
}
.bb-subtitle { color: var(--text-muted); font-size: var(--fs-sm); }
.bb-bettype {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.04em;
  border-radius: 4px; padding: 0.08rem 0.32rem;
}
.bb-bt-win { background: rgba(var(--accent-rgb), 0.16); color: var(--accent-light); border: 1px solid rgba(var(--accent-rgb), 0.35); }
.bb-bt-ew  { background: rgba(var(--win-rgb), 0.16); color: var(--win); border: 1px solid rgba(var(--win-rgb), 0.35); }

/* Two rows (Win / Each-way), each with compact cards 4 across */
.bb-rows { display: flex; flex-direction: column; gap: 0.7rem; }
.bb-row-label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 0.25rem;
}
/* minmax(0,1fr) (not bare 1fr = minmax(min-content,1fr)) so a wide card — a long horse name or
   race line — can't force its track past its 1/4 share and push the whole grid over the section
   boundary (which was spilling the tiles off the page + adding a horizontal scrollbar). The card
   gets min-width:0 to match, so it shrinks to its track and its inner text ellipsizes instead. */
.best-bets { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.6rem; }
.bb-card {
  min-width: 0;
  display: flex; flex-direction: column; gap: 0.22rem;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 0.6rem 0.7rem; text-decoration: none; color: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.bb-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.bb-top { display: flex; align-items: center; gap: 0.35rem; }
.bb-top .bb-prob { margin-left: auto; }
/* #93 low-confidence flag on a best bet */
.bb-lowconf { font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--win); border: 1px solid var(--win); border-radius: 999px; padding: 0.05rem 0.4rem; margin-left: 0.4rem; white-space: nowrap; }
.bb-rank {
  width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(var(--accent-rgb), 0.15); color: var(--accent-light); font-weight: 700; font-size: var(--fs-xs);
}
.bb-tag {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.04em;
  background: rgba(var(--ew-rgb), 0.16); color: var(--ew);
  border: 1px solid rgba(var(--ew-rgb), 0.35); border-radius: 4px; padding: 0.05rem 0.3rem;
}
.bb-prob { font-family: var(--mono); font-weight: 800; font-size: var(--fs-h3); color: var(--text); line-height: 1; }
.bb-horse { font-weight: 700; font-size: var(--fs-h4); line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bb-race { color: var(--text-muted); font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bb-status {
  margin-top: 0.2rem; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em;
  text-align: center; border-radius: 4px; padding: 0.08rem 0.25rem;
}
.bb-status-win   { background: rgba(var(--result-win-rgb), 0.16); color: var(--result-win); border: 1px solid rgba(var(--result-win-rgb), 0.4); }
.bb-status-place { background: rgba(var(--result-place-rgb), 0.18); color: var(--result-place); border: 1px solid rgba(var(--result-place-rgb), 0.4); }
.bb-status-miss  { background: rgba(var(--result-miss-rgb), 0.14); color: var(--result-miss); border: 1px solid rgba(var(--result-miss-rgb), 0.3); }
.bb-status-awaiting, .bb-status-upcoming { background: rgba(var(--overlay-rgb), 0.05); color: var(--text-dim); border: 1px solid var(--border); }
/* Dim resulted cards slightly so upcoming tips stand out */
.bb-card.bb-win, .bb-card.bb-place, .bb-card.bb-miss { opacity: 0.85; }
/* Mobile-first: one card per row on phones/tablet-portrait — two 1fr tracks blow out to the
   card's min-content (long race line) and overflow narrow screens, so stack instead. */
@media (max-width: 760px) { .best-bets { grid-template-columns: 1fr; } }

/* Compact best bets (day page): tight single-line clickable rows */
.bbc-rows { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem 1.25rem; }
.bbc-group .bb-row-label { margin-bottom: 0.35rem; }
.bbc-list { display: flex; flex-direction: column; gap: 0.3rem; }
.bbc-row {
  display: grid; grid-template-columns: 34px 1fr auto auto; align-items: center;
  gap: 0.5rem; padding: 0.4rem 0.55rem; border: 1px solid var(--border);
  border-radius: 7px; background: var(--bg-card); text-decoration: none; color: var(--text);
}
.bbc-row:hover { border-color: var(--accent); background: var(--bg-hover); }
.bbc-bt { grid-column: 1; grid-row: 1 / span 2; align-self: center; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.03em; text-align: center; padding: 0.12rem 0; border-radius: 4px; }
.bbc-horse { font-weight: 700; font-size: var(--fs-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bbc-race { grid-column: 2; font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bbc-prob { grid-column: 3; grid-row: 1 / span 2; align-self: center; font-weight: 800; font-size: var(--fs-h4); color: var(--accent-light); }
.bbc-status { grid-column: 4; grid-row: 1 / span 2; align-self: center; font-size: var(--fs-2xs); font-weight: 700; padding: 0.12rem 0.4rem; border-radius: 4px; white-space: nowrap; }
.bbc-row.bb-win, .bbc-row.bb-place, .bbc-row.bb-miss { opacity: 0.85; }
@media (max-width: 760px) { .bbc-rows { grid-template-columns: 1fr; } }

/* Race page loading animation (spinner + shimmer skeleton) */
.race-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  padding: 2.5rem 1rem 1.5rem;
  color: var(--text-muted);
  font-size: var(--fs-h4);
}
.race-loading .loader {
  width: 38px; height: 38px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
.skeleton-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.skeleton-card {
  height: 62px;
  border-radius: 10px;
  background: var(--bg-card);
  position: relative;
  overflow: hidden;
}
.skeleton-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(var(--overlay-rgb), 0.06) 50%, transparent 100%);
  transform: translateX(-100%);
  animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer { 100% { transform: translateX(100%); } }

/* #301 Skeleton placeholders (reuse the shimmer) — reserve async sections' height, no CLS. */
.skel { position: relative; overflow: hidden; background: var(--bg-card); border-radius: 8px; display: inline-block; }
.skel::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent 0%, rgba(var(--overlay-rgb), 0.06) 50%, transparent 100%); transform: translateX(-100%); animation: shimmer 1.4s ease-in-out infinite; }
.skel-pills { display: flex; gap: 0.4rem; overflow: hidden; }
.skel-pills .skel-pill { width: 46px; height: 46px; border-radius: 10px; flex: 0 0 auto; }
.skel-hero { display: flex; flex-direction: column; gap: 0.6rem; min-height: 110px; padding: 0.3rem 0; }
.skel-hero .skel-line { height: 16px; border-radius: 6px; display: block; width: 100%; }
.skel-hero .skel-line-lg { height: 24px; width: 62%; }
.skel-hero-grid { display: flex; gap: 0.9rem; }
.skel-hero-grid .skel-cell { width: 64px; height: 34px; border-radius: 8px; }
.skel-tip { display: flex; flex-direction: column; gap: 0.5rem; min-height: 96px; padding: 0.6rem; }
.skel-tip .skel-line { height: 16px; border-radius: 6px; display: block; width: 100%; }
.skel-tip .skel-line-lg { height: 26px; width: 55%; }
.skel-tip .skel-line-sm { height: 12px; width: 28%; }

/* #301 Race-page condensed sticky header — the compact one-line core details, hidden until
   the hero condenses on scroll (mobile). Toggled on the #raceHero container. */
.hero-condensed { display: none; gap: 0.5rem; flex-wrap: wrap; align-items: center; font-weight: 700; font-size: var(--fs-sm); }
#raceHero.condensed .hero-condensed { display: flex; }
#raceHero.condensed .hero-top,
#raceHero.condensed .hero-name,
#raceHero.condensed .race-info-grid,
#raceHero.condensed .hero-tags,
#raceHero.condensed .race-cov-note { display: none; }

/* #301 / #341: pin the race-nav pills + race-details card under the site header while
   scrolling; the hero condenses (above) once scrolled past. #301 shipped this for mobile;
   #341 extends it to DESKTOP too (the header height --header-h is the same sticky anchor on
   both — the right rail already sticks at that offset), so desktop gets the same pinned
   race navigation + minimised hero card. */
.meet-nav-row { position: sticky; top: var(--header-h); z-index: 30; background: var(--bg); padding: 0.35rem 0 0.25rem; }
#raceHero { position: sticky; top: calc(var(--header-h) + var(--race-pills-h, 58px)); z-index: 29; background: var(--bg); border-bottom: 1px solid var(--border); }
#raceHero.condensed { padding: 0.3rem 0.1rem; }
#raceHero.condensed .race-hero { padding-top: 0.15rem; padding-bottom: 0.15rem; }

.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--text-muted);
  font-size: var(--fs-h4);
}

.error-msg {
  display: none;
  max-width: 1380px;
  margin: 1rem auto;
  padding: 0.75rem 1rem;
  background: rgba(var(--danger-rgb), 0.08);
  border: 1px solid rgba(var(--danger-rgb), 0.2);
  border-radius: var(--radius);
  color: var(--danger);
  font-size: var(--fs-body);
}

/* Page Layout with Sidebar */

.page-layout {
  display: flex;
  max-width: 1380px;
  margin: 0 auto;
  min-height: calc(100vh - 80px);
}

.nav-sidebar {
  width: 190px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  padding: 0.75rem 0;
  position: sticky;
  top: 64px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
}

.ns-section {
  padding: 0.25rem 0 0.6rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.4rem;
}

.ns-section:last-child { border-bottom: none; }

.ns-heading {
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0.25rem 0.75rem 0.35rem;
}

.ns-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.4rem 0.75rem;
  font-size: var(--fs-sm);
  color: var(--text-mid);
  transition: background 0.12s;
  border-left: 3px solid transparent;
}

.ns-link:hover { background: var(--bg-hover); color: var(--text); }
.ns-link.active { background: var(--bg-hover); border-left-color: var(--accent); color: var(--text); font-weight: 600; }

.ns-count {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  background: var(--bg);
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
}

.ns-accuracy {
  padding: 0.25rem 0.75rem;
}

.ns-acc-loading, .ns-acc-empty {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.acc-stat {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.2rem 0;
}

.acc-label {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.acc-value {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-mid);
}

.acc-value.acc-good { color: var(--green); }
.acc-value.acc-low { color: var(--text-muted); }

.acc-divider {
  height: 1px;
  background: var(--border);
  margin: 0.3rem 0;
}

.page-main {
  flex: 1;
  min-width: 0;
  padding: 0.75rem var(--page-pad-x) 2rem;
}

/* Landing Page */

.landing {
  max-width: 1380px;
  margin: 0 auto;
  padding: 0 1rem 2rem;
}

.landing-hero {
  text-align: center;
  padding: 2.5rem 1.5rem 2rem;
  background:
    radial-gradient(120% 140% at 50% -20%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 60%),
    var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 16px;
  margin-bottom: 1rem;
}
/* #166 slim hero for the task-first (logged-in) home — a compact brand strip above
   the picks, not a full marketing splash. */
.landing-hero-slim { padding: 1.4rem 1.5rem 1.5rem; }
.landing-hero-slim h2 { font-size: var(--fs-h2); margin-bottom: 0.9rem; }
.landing-hero-slim .hero-eyebrow { margin-bottom: 0.7rem; }
.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: var(--fs-xs); font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent-light); background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px; padding: 0.2rem 0.7rem; margin-bottom: 0.9rem;
}

.landing-hero h2 {
  font-size: var(--fs-h1);
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--text);
  margin-bottom: 0.4rem;
}

.landing-hero p {
  font-size: var(--fs-h4);
  color: var(--text-muted);
  max-width: 560px;
  margin: 0 auto;
  line-height: 1.5;
}

/* ── Vector data-terminal hero (home) ───────────────────────────────────────────
   Overrides the soft centred marketing hero with a flat, sharp, left-aligned analytical
   block: no radial glow, square corners, a monospace status badge (flat solid dot, no
   emoji/icon), and a geometric uppercase headline. Scoped under .vector-hero so it wins
   over the base .landing-hero rules; colour comes from tokens only. */
.vector-hero {
  text-align: left;
  background: var(--bg-card);           /* drop the radial marketing glow — flat panel */
}
.vector-hero .hero-eyebrow.vh-badge {
  font-family: var(--font-code);        /* real monospace — terminal voice */
  letter-spacing: 0.12em; flex-wrap: wrap; max-width: 100%;
}
/* Sharp corners are the VECTOR (editorial) identity only — other themes keep the hero's
   rounded card, pill badge and rounded CTA. The rest of the terminal treatment (flat panel,
   monospace badge, flat dot, uppercase copy) stays in every theme. */
:root[data-theme="light"] .vector-hero,
:root[data-theme="light"] .vector-hero .hero-eyebrow.vh-badge,
:root[data-theme="light"] .vector-hero .hero-cta {
  border-radius: 0;
}
/* Flat, solid status dot via pure CSS — isolated from any icon/emoji pattern. */
.vector-hero .vh-badge .status-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--green); flex: 0 0 auto;
}
.vector-hero .vh-title {
  font-family: var(--font-head);        /* geometric sans display face */
  text-transform: uppercase; letter-spacing: 0.02em; font-weight: 800;
  line-height: 1.05;
}
.vector-hero .vh-body {
  font-size: var(--fs-body); color: var(--text-muted); line-height: 1.6;
  max-width: 62ch; margin: 0.2rem 0 0;
}
.vector-hero .hero-ctas { justify-content: flex-start; }
.vector-hero .hero-cta {
  text-transform: uppercase; letter-spacing: 0.04em;
}

/* ═══ Public marketing pages (.mk-*) — pre-login funnel, Vector data-terminal look ═══
   Colour + type come from theme tokens only (pages force the editorial theme in <head>).
   Sharp geometry, high-contrast, mobile-first. Shared by Home/Pricing/About/Blog. */

/* ONE FACE ON THE FUNNEL. The marketing pages force `data-theme="light"`, and that theme
   re-points --mono at a real monospace for the race-detail LEDGER (see the editorial block
   above) — a deliberate choice for a dense financial grid. It was never meant for the funnel,
   but --mono is inherited, so every shared component that uses it followed: the track-record
   stat tiles, its "Coming soon" placeholder, the settled-ledger figures and the comparison
   table's headers all rendered in a face nothing else on the page used. Four typefaces on a
   sales page reads as unfinished, not as designed.
   Re-aliasing --mono to --font here (the #148 rule: one font throughout) restores it for the
   funnel ONLY — the app keeps its ledger face because the app has no [data-mk-nav] header.
   Number alignment does not depend on the face: --mono's promise is tabular figures, which
   `font-variant-numeric: tabular-nums` keeps (.mono users, and .dt-num, carry it). */
body:has(header[data-mk-nav]) { --mono: var(--font); }

/* One prose column, every marketing surface. The width was set per element and drifted: the
   hero lead ran 62ch at 1.12rem while section copy ran 60ch at 1.02rem — the same "60-odd
   characters" on paper, ~80px apart on screen, so the left-aligned text edge stepped in and out
   down the page. A rem measure is the same column whatever size the type is set at. */
:root { --mk-measure: 42rem; }
.mk-page { max-width: 1180px; margin: 0 auto; padding: 0 1.25rem; }
.mk-section { padding: 3.5rem 0; border-top: 1px solid var(--border); }
.mk-eyebrow { text-transform: uppercase; letter-spacing: 0.16em; font-size: 0.72rem; font-weight: 700; color: var(--accent); margin-bottom: 0.8rem; }
.mk-h2 { font-family: var(--font-head); font-weight: 800; text-transform: uppercase; letter-spacing: 0.01em; font-size: clamp(1.6rem, 4vw, 2.3rem); line-height: 1.08; color: var(--text); margin: 0 0 0.6rem; }
.mk-lead { font-size: 1.02rem; color: var(--text-muted); line-height: 1.6; max-width: var(--mk-measure); margin: 0; }
/* Marketing BODY copy — several paragraphs that have to read as a passage, which .mk-lead does
   not: it is a single standfirst, so it carries `margin: 0` and muted ink. Stacked, those ran
   together into one grey slab with no gap between paragraphs and every page repeating
   `style="max-width:60ch;margin-left:0"` inline to undo the lead's centring. Darker ink, a
   looser line, and real space between paragraphs — one class, no inline overrides. */
.mk-prose { font-size: 1.02rem; color: var(--text-mid); line-height: 1.7; max-width: var(--mk-measure); margin: 0 0 1.1rem; }
.mk-prose:last-child { margin-bottom: 0; }
.mk-prose em { color: var(--text); font-style: italic; }
/* The qualifier under a claim (the "not a promise of returns" line). Same column and alignment as
   the prose above it — it used to be centred at a different width, which is what made the text
   edge step in and out — but set quieter, so it reads as a footnote rather than as body copy. */
.mk-prose-note { font-size: 0.9rem; color: var(--text-muted); margin-top: 1.4rem; }
/* Nav */
/* #457 the global `header { display: none }` (hides the empty app-shell placeholder that nav.js
   replaces) also caught the marketing header, which DOES hold the nav — so force it visible. */
/* Marketing pages have their OWN sticky header + no app sidebar, so strip the app-shell body
   offsets that otherwise leave a grey gap above the nav (padding-top for the app's FIXED header)
   and push the whole page off full width (padding-left for the desktop sidebar). */
body:has(header[data-mk-nav]) { padding-top: 0; }
@media (min-width: 1100px) { body:has(header[data-mk-nav]) { padding-left: 0; } }
header[data-mk-nav] { display: block; position: sticky; top: 0; z-index: 40; background: var(--bg-card); border-bottom: 1px solid var(--border); }
/* #237 — the skip link. Off-screen until focused (never `display:none` — that would remove it
   from the Tab order along with the view), then pinned above the sticky header (z-index 40 above)
   so a sighted keyboard user actually sees where focus landed. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 50;
  background: var(--accent-btn); color: #fff; padding: 0.6rem 1rem;
  font-weight: 700; text-decoration: none; border-radius: 0 0 6px 0;
}
.skip-link:focus { left: 0; }
.mk-nav { max-width: 1180px; margin: 0 auto; padding: 0.7rem 1.25rem; display: flex; align-items: center; gap: 1rem; }
.mk-brand { display: inline-flex; align-items: center; gap: 0.5rem; font-family: var(--font-head); font-weight: 800; letter-spacing: 0.08em; font-size: 1.15rem; color: var(--text); text-decoration: none; }
.mk-brand-logo { height: 26px; width: auto; flex: 0 0 auto; }
.mk-brand-lg .mk-foot-logo, .mk-brand.mk-brand-lg { gap: 0.6rem; }
.mk-brand-odds { color: var(--accent); }
.mk-brand-lg { font-size: 1.5rem; }
.mk-nav-links { margin-left: auto; display: flex; align-items: center; gap: 1.3rem; }
.mk-nav-link { color: var(--text-mid); text-decoration: none; font-weight: 600; font-size: 0.9rem; }
.mk-nav-link:hover, .mk-nav-link[aria-current="page"] { color: var(--accent); }
.mk-cta { background: var(--accent-btn); color: var(--on-accent); text-decoration: none; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.82rem; padding: 0.55rem 1rem; border: 1px solid var(--accent-btn); }
.mk-cta:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.mk-cta-lg { font-size: 0.95rem; padding: 0.8rem 1.5rem; display: inline-block; }
.mk-cta-ghost { background: transparent; color: var(--accent); border: 1px solid var(--border); }
/* on hover the ghost CTA inherits .mk-cta:hover's accent fill — flip the text to on-accent so it stays legible. */
.mk-cta-ghost:hover { color: var(--on-accent); border-color: var(--accent-hover); }
.mk-nav-toggle { display: none; margin-left: auto; flex-direction: column; gap: 4px; background: none; border: 0; padding: 0.4rem; cursor: pointer; min-height: 44px; min-width: 44px; align-items: center; justify-content: center; }
.mk-nav-toggle span { width: 22px; height: 2px; background: var(--text); display: block; }
/* Hero */
.mk-hero { padding: 4rem 0 3rem; }
.mk-hero .mk-h1 { font-family: var(--font-head); font-weight: 800; text-transform: uppercase; letter-spacing: 0.01em; font-size: clamp(2.3rem, 6.5vw, 3.6rem); line-height: 1.02; color: var(--text); margin: 0 0 1rem; }
.mk-hero .mk-lead { font-size: 1.12rem; font-weight: 500; }
.mk-hero-ctas { display: flex; gap: 0.8rem; flex-wrap: wrap; margin-top: 1.6rem; }
/* Hero image — the on-brand race shot under the CTAs. Responsive, never scrolls the body. */
.mk-hero-media { margin-top: 2.4rem; }
/* Square corners to match the marketing cards (.mk-tier/.mk-stat have no radius). */
.mk-hero-media img { display: block; width: 100%; max-width: 1100px; height: auto; border-radius: 0; border: 1px solid var(--border); box-shadow: 0 18px 50px -24px rgba(0, 0, 0, 0.5); }
/* The badge lost its status dot: it was a live-indicator glyph on a static label ("Who we are",
   "The blog", "404"), so it signalled nothing and read as decoration. The face moves to
   --font-head with it — a monospace pill above a geometric headline was the funnel's fourth
   typeface (see the one-face rule at the top of this block). */
.mk-badge-live { display: inline-flex; align-items: center; gap: 0.4rem; font-family: var(--font-head); text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.72rem; font-weight: 700; color: var(--text-mid); border: 1px solid var(--border); padding: 0.25rem 0.6rem; margin-bottom: 1rem; }
/* Feature + step grids */
.mk-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); margin-top: 2rem; }
.mk-cell { background: var(--bg-card); padding: 1.5rem; }
.mk-cell-k { font-family: var(--font-code); font-size: 0.72rem; letter-spacing: 0.1em; color: var(--accent); text-transform: uppercase; }
.mk-cell-t { font-weight: 800; font-size: 1.05rem; color: var(--text); margin: 0.5rem 0 0.4rem; }
.mk-cell-d { font-size: 0.9rem; color: var(--text-muted); line-height: 1.55; }
.mk-steps { counter-reset: mk; }
.mk-step-k { font-family: var(--font-head); font-weight: 800; font-size: 1.4rem; color: var(--accent); }
/* Stats strip */
.mk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); }
.mk-stat { background: var(--bg-card); padding: 1.4rem 1.25rem; }
.mk-stat-n { font-family: var(--font-head); font-weight: 800; font-size: 1.9rem; color: var(--text); font-variant-numeric: tabular-nums; }
.mk-stat-l { font-size: 0.8rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 0.2rem; }
/* FAQ */
.mk-faq details { border-top: 1px solid var(--border); padding: 1rem 0; }
.mk-faq summary { font-weight: 700; color: var(--text); cursor: pointer; list-style: none; display: flex; justify-content: space-between; gap: 1rem; }
.mk-faq summary::after { content: "+"; color: var(--accent); font-weight: 700; }
.mk-faq details[open] summary::after { content: "−"; }
.mk-faq p { color: var(--text-muted); line-height: 1.6; margin: 0.7rem 0 0; }
/* #453 Legal / prose pages — left-aligned, readable measure (Terms / Privacy / Responsible Gambling). */
.mk-legal { max-width: var(--mk-measure); margin: 0 auto; text-align: left; }
.mk-legal h2 { font-size: var(--fs-h3); color: var(--text); margin: 2rem 0 0.6rem; }
.mk-legal h2:first-child { margin-top: 0; }
.mk-legal p, .mk-legal li { color: var(--text-mid); line-height: 1.7; }
.mk-legal p { margin: 0.7rem 0; }
.mk-legal ul { margin: 0.7rem 0; padding-left: 1.2rem; }
.mk-legal li { margin: 0.4rem 0; }
/* ⚠ #237 — underlined, not colour alone. A link inline in a paragraph of running prose (the global
   `a { text-decoration: none }` above) read identically to the surrounding text for anyone who
   can't tell it apart by colour — axe's link-in-text-block check on terms/privacy/RG. */
.mk-legal a { color: var(--accent); text-decoration: underline; }
.mk-legal-meta { color: var(--text-dim); font-size: var(--fs-sm); margin-top: 2rem; }
/* #454 blog — list cards + article */
.mk-blog-card { text-decoration: none; display: block; transition: border-color 0.15s; }
.mk-blog-card:hover { border-color: var(--accent); }
.mk-blog-more { color: var(--accent); font-weight: 600; font-size: var(--fs-sm); display: inline-block; margin-top: 0.7rem; }
.mk-blog-article { max-width: var(--mk-measure); margin: 0 auto; text-align: left; }
.mk-blog-back { color: var(--accent); text-decoration: none; font-size: var(--fs-sm); display: inline-block; margin-bottom: 1rem; }
.mk-blog-back:hover { text-decoration: underline; }
.mk-blog-meta { color: var(--text-dim); font-size: var(--fs-sm); margin: 0 0 1.5rem; }
.mk-blog-body h2 { margin-top: 1.9rem; }
/* CTA band */
.mk-cta-band { background: var(--bg-hover); border: 1px solid var(--border); padding: 2.5rem 1.5rem; text-align: center; margin: 3rem 0; }
/* Footer */
footer[data-mk-footer] { background: var(--bg-card); border-top: 1px solid var(--border); margin-top: 2rem; }
.mk-foot { max-width: 1180px; margin: 0 auto; padding: 2.5rem 1.25rem 1.5rem; }
.mk-foot-cols { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 2rem; }
.mk-foot-tag { color: var(--text-muted); font-size: 0.88rem; line-height: 1.55; max-width: 34ch; margin: 0.6rem 0 0; }
.mk-foot-h { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-mid); font-weight: 700; margin-bottom: 0.7rem; }
.mk-foot-col a { display: block; color: var(--text-muted); text-decoration: none; font-size: 0.9rem; padding: 0.2rem 0; }
.mk-foot-col a:hover { color: var(--accent); }
/* #450 auth-gated footer link, greyed + non-navigable for logged-out visitors (universal footer). */
.mk-foot-link--disabled { display: block; color: var(--text-dim); font-size: 0.9rem; padding: 0.2rem 0; opacity: 0.5; cursor: not-allowed; user-select: none; }
.mk-foot-bar { border-top: 1px solid var(--border); margin-top: 2rem; padding-top: 1.2rem; display: flex; align-items: baseline; gap: 0.8rem; flex-wrap: wrap; font-size: 0.78rem; color: var(--text-muted); }
.mk-18 { font-weight: 800; color: var(--text); border: 1px solid var(--border); padding: 0.05rem 0.35rem; }
.mk-foot-rg { flex: 1; min-width: 260px; line-height: 1.5; }
.mk-foot-cr { color: var(--text-dim); }
@media (max-width: 860px) {
  .mk-foot-cols { grid-template-columns: 1fr 1fr; gap: 1.5rem; }
}
/* Pricing */
/* #840 Mobile-first, and card-COUNT agnostic. Was a hardcoded `repeat(3, 1fr)`, which silently
   broke the moment Vector Focus made it four: three across and a fourth orphaned on its own row.
   The page is data-driven from PUBLIC_TIERS now, so the grid must not encode how many tiers there
   are - auto-fit means adding or masking a tier is a server change, never a CSS one. */
.mk-tiers { display: grid; grid-template-columns: 1fr; gap: 1rem; margin-top: 1.5rem; align-items: start; }
@media (min-width: 641px)  { .mk-tiers { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .mk-tiers { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); } }
.mk-tier { border: 1px solid var(--border); background: var(--bg-card); padding: 1.5rem 1.4rem; display: flex; flex-direction: column; gap: 0.5rem; position: relative; }
.mk-tier-featured { border-color: var(--accent); }
.mk-tier-badge { position: absolute; top: -0.7rem; left: 1.4rem; background: var(--accent-btn); color: var(--on-accent); font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 700; padding: 0.2rem 0.55rem; }
.mk-tier-k { font-family: var(--font-code); text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.78rem; color: var(--accent); }
.mk-tier-price { font-family: var(--font-head); font-weight: 800; font-size: 2.1rem; color: var(--text); }
.mk-tier-price span { font-family: var(--font); font-weight: 500; font-size: 0.9rem; color: var(--text-muted); }
.mk-tier-d { color: var(--text-muted); font-size: 0.9rem; }
.mk-tier-list { list-style: none; padding: 0; margin: 0.4rem 0 1rem; display: flex; flex-direction: column; gap: 0.4rem; }
.mk-tier-list li { font-size: 0.88rem; color: var(--text-mid); padding-left: 1.1rem; position: relative; }
.mk-tier-list li::before { content: "→"; position: absolute; left: 0; color: var(--accent); }
.mk-tier .mk-cta { margin-top: auto; text-align: center; }
/* #734 Lifecycle-email previews in the component showcase. The frame is deliberately given a plain
   white backdrop and a fixed height regardless of theme: an email does not inherit the app's theme,
   so previewing it inside a dark shell would misrepresent what lands in an inbox. */
.cl-mail { border: 1px solid var(--border); background: var(--bg-card); margin: 0 0 1.6rem; }
.cl-mail-head { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: space-between; align-items: center; padding: .7rem .9rem; border-bottom: 1px solid var(--border); font-size: .9rem; color: var(--text); }
.cl-mail-tp { font-family: var(--font-code); font-size: .72rem; letter-spacing: .08em; color: var(--text-muted); border: 1px solid var(--border); padding: .1rem .35rem; }
.cl-mail-tag { font-size: .7rem; letter-spacing: .05em; text-transform: uppercase; padding: .15rem .45rem; }
.cl-mail-mkt { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.cl-mail-svc { background: color-mix(in srgb, var(--text-muted) 16%, transparent); color: var(--text-muted); }
.cl-mail-open { font-size: .8rem; color: var(--accent); text-decoration: none; margin-left: .4rem; }
.cl-mail-subject { padding: .5rem .9rem; font-size: .84rem; color: var(--text-muted); border-bottom: 1px solid var(--border); font-family: var(--font-code); }
.cl-mail-frame { display: block; width: 100%; height: 34rem; border: 0; background: #fff; }
@media (max-width: 640px) { .cl-mail-frame { height: 28rem; } }

/* #706 value anchor — the "vs a $150+ syndicate" line under a tier price. Muted by default so it
   reads as context, not a second price; `strong` carries the saving. */
.mk-tier-value { font-size: 0.8rem; color: var(--text-muted); margin: -0.15rem 0 0.2rem; line-height: 1.4; }
.mk-tier-value strong { color: var(--ok, var(--accent)); font-weight: 700; }
/* #706 trust strip — "how we make money" claims. A responsive auto-fit grid so it needs no
   breakpoint of its own: one column on a phone, up to three on desktop. */
.mk-trust { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; margin-top: 1.4rem; }
.mk-trust-item { border: 1px solid var(--border); background: var(--bg-card); padding: 1.1rem 1.2rem; display: flex; flex-direction: column; gap: 0.35rem; }
.mk-trust-k { font-weight: 700; color: var(--text); font-size: 0.95rem; display: flex; align-items: center; gap: 0.5rem; }
.mk-trust-k::before { content: "✓"; color: var(--ok, var(--accent)); font-weight: 800; }
.mk-trust-d { color: var(--text-muted); font-size: 0.86rem; line-height: 1.55; }
.mk-cmp-wrap { overflow-x: auto; margin-top: 1.5rem; }
.mk-cmp { width: 100%; border-collapse: collapse; font-size: 0.9rem; min-width: 520px; }
.mk-cmp th, .mk-cmp td { text-align: left; padding: 0.65rem 0.8rem; border-bottom: 1px solid var(--border); }
.mk-cmp thead th { font-family: var(--font-head); text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.72rem; color: var(--text-muted); }
.mk-cmp td:not(:first-child), .mk-cmp th:not(:first-child) { text-align: center; }
.mk-cmp tbody td:first-child { color: var(--text); font-weight: 500; }
/* #840 the old max-width:720px single-column override is gone - 1fr is now the DEFAULT (mobile-first). */
@media (max-width: 640px) {
  .mk-nav-toggle { display: flex; }
  .mk-nav-links { display: none; position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; align-items: stretch; gap: 0; background: var(--bg-card); border-bottom: 1px solid var(--border); padding: 0.5rem 1.25rem 1rem; }
  .mk-nav-links.mk-open { display: flex; }
  .mk-nav-link { padding: 0.7rem 0; min-height: 44px; display: flex; align-items: center; }
  .mk-cta { text-align: center; min-height: 44px; display: flex; align-items: center; justify-content: center; }
  .mk-foot-cols { grid-template-columns: 1fr; }
}

.hero-stats {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.75rem;
  font-size: var(--fs-sm);
}

.hs-item { color: var(--text-mid); }
.hs-item.hs-good { color: var(--green); font-weight: 600; }
.hs-dot { color: var(--text-dim); }

/* International (non-Australian) race treatment — used site-wide */
.intl-badge {
  display: inline-block;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em;
  background: rgba(var(--ew-rgb), 0.16); color: var(--ew);
  border: 1px solid rgba(var(--ew-rgb), 0.4); border-radius: 4px;
  padding: 0.05rem 0.3rem; white-space: nowrap; vertical-align: middle;
}
/* #1509 — in light theme `--ew` is #d31f1e, only 4.05:1 against this tint at this chip's ~12.6px
   bold rendering (needs 4.5:1, not the 3:1 large-text floor). Dark theme's --ew (#2dd4bf, teal)
   already clears AA here, so this is scoped to light only. Same hue (HSL H/S held, L lowered). */
:root[data-theme="light"] .intl-badge { color: #bf1c1b; }
.intl { border-color: var(--ew); }
/* Trial / jump-out badge — same shape as the intl badge, amber to stand apart. */
.trial-badge {
  display: inline-block;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em;
  background: rgba(var(--win-rgb), 0.16); color: var(--win);
  border: 1px solid rgba(var(--win-rgb), 0.4); border-radius: 4px;
  padding: 0.05rem 0.3rem; white-space: nowrap; vertical-align: middle;
}

/* Hero benefit chips */
.hero-benefits {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}
.hb-chip {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-mid);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg));
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
}

/* Hero CTA row — primary + ghost secondary. */
.hero-ctas { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.25rem; }
.hero-cta-ghost { background: transparent; color: var(--accent-light); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.hero-cta-ghost:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* Landing — mobile space savers: hide the hero benefit chips, show only the
   scorecard rings (no per-type breakdown/header), and show only the how-it-works
   headings (icon + title), hiding their descriptions. */
@media (max-width: 640px) {
  .hero-benefits { display: none; }
  .perf-card .ps-head, .perf-card .ps-types { display: none; }
  .perf-card .ps-gauges { padding-top: 0; }
  .hiw-card .hiw-d, .hiw-card .hiw-link { display: none; }
}

/* Hero call-to-action */
.hero-cta {
  display: inline-block;
  margin-top: 1.1rem;
  background: var(--accent-btn);
  color: var(--on-accent);
  font-weight: 700;
  font-size: var(--fs-h4);
  padding: 0.6rem 1.3rem;
  border-radius: 9px;
  text-decoration: none;
  transition: background 0.15s, transform 0.1s;
}
.hero-cta:hover { background: #5a4ac4; }
.hero-cta:active { transform: translateY(1px); }

/* Next-to-jump strip */
.next-races {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 0.6rem;
}
.nr-card {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem 0.75rem;
  text-decoration: none;
  transition: border-color 0.12s, background 0.12s;
}
.nr-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.nr-countdown {
  font-family: var(--mono);
  font-size: var(--fs-h3);
  font-weight: 800;
  color: var(--accent-light);
}
.nr-course { font-size: var(--fs-body); font-weight: 600; color: var(--text); }
.nr-rno { color: var(--text-muted); font-weight: 600; }
.nr-time { font-size: var(--fs-xs); color: var(--text-dim); display: flex; align-items: center; gap: 0.35rem; }
.nr-ai {
  background: rgba(var(--accent-rgb), 0.18); color: var(--accent-light);
  font-weight: 700; font-size: var(--fs-2xs); padding: 0.05rem 0.3rem; border-radius: 4px;
}
.nr-code { margin-right: 0.15rem; }

/* Code tabs + today's meetings board (home) */
.code-tabs { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.9rem; border-bottom: 1px solid var(--border); }
.code-tab {
  background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
  padding: 0.5rem 0.9rem; font-family: var(--font); font-size: var(--fs-h4); font-weight: 700;
  color: var(--text-dim); cursor: pointer;
}
.code-tab:hover { color: var(--text); }
.code-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.code-tab-soon { opacity: 0.5; cursor: default; }
/* .board-meet / .board-region removed with loadMeetingsBoard — the bespoke third meetings
   renderer that had no callers. UI.raceMatrix owns this surface. */

/* Week Matrix */

.week-matrix {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.wm-day {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.wm-day-today {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.wm-day-today .wm-day-header {
  background: rgba(var(--accent-rgb), 0.12);
  border-bottom-color: rgba(var(--accent-rgb), 0.25);
}

.wm-day-today .wm-day-label {
  color: var(--accent-light);
}

.wm-day-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  background: rgba(var(--accent-rgb), 0.04);
  border-bottom: 1px solid var(--border);
}

.wm-day-label {
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--text);
}

.wm-day-date {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.wm-meets {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  padding: 0.5rem 0.75rem;
}

.wm-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.6rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  transition: border-color 0.12s, background 0.12s;
}

.wm-chip:hover { border-color: var(--accent); background: var(--bg-hover); }
.wm-chip.wm-abn { opacity: 0.4; }
.wm-chip.wm-done { border-color: var(--green); }

.wm-course {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}

.wm-info {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.wm-badges {
  display: flex;
  gap: 0.2rem;
}

.wm-loading, .wm-empty {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  padding: 0.2rem 0;
}

/* Landing Cards */

.landing-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.lc-card {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.85rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.12s, background 0.12s;
}

.lc-card:hover { border-color: var(--accent); background: var(--bg-hover); }

.lc-icon { font-size: var(--fs-h1); }

.lc-text {
  display: flex;
  flex-direction: column;
}

.lc-text strong { font-size: var(--fs-body); color: var(--text); }
.lc-text span { font-size: var(--fs-xs); color: var(--text-dim); }

/* Homepage Layout */

.home-section {
  margin-bottom: 1rem;
}

.home-section-primary {
  margin-bottom: 1.25rem;
}

.section-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.toggle-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}
.toggle-label input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 2.2rem;
  height: 1.2rem;
  background: var(--border);
  border-radius: 1rem;
  position: relative;
  cursor: pointer;
  transition: background 0.2s;
  flex-shrink: 0;
}
.toggle-label input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 0.15rem;
  left: 0.15rem;
  width: 0.9rem;
  height: 0.9rem;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s;
}
.toggle-label input[type="checkbox"]:checked {
  background: var(--accent);
}
.toggle-label input[type="checkbox"]:checked::after {
  transform: translateX(1rem);
}

.section-title {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.section-date {
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

/* Page masthead — the shared "data peak" hero (from the Prime Vectors page) reused across the
   feature pages: Prime Vectors, Best Bets, Daily Digest, Alerts. An uppercase eyebrow, a large
   display title, a lede and a muted body, so every page opens with the same voice + type scale.
   Rendered by UI.pageHero({ eyebrow, title, lede, body }); themed via tokens (light + dark). */
.page-hero { padding: 0.5rem 0 1.4rem; border-bottom: 1px solid var(--border); margin-bottom: 1.5rem; }
.ph-eyebrow { text-transform: uppercase; letter-spacing: 0.16em; font-size: 0.72rem; font-weight: 700; color: var(--accent); margin-bottom: 0.7rem; }
.ph-title { font-family: var(--font-head); font-weight: 700; letter-spacing: 0.01em; font-size: clamp(2.3rem, 7vw, 3.4rem); line-height: 1.03; margin: 0 0 0.95rem; color: var(--text); }
.ph-lede { font-size: 1.08rem; font-weight: 600; line-height: 1.5; color: var(--text); margin: 0 0 0.75rem; max-width: 62ch; }
.ph-body { font-size: 0.95rem; line-height: 1.68; color: var(--text-muted); margin: 0; max-width: 66ch; }

/* Prominent day/date heading on the race-day page */
.day-heading { display: flex; flex-direction: column; gap: 0.1rem; }
.day-page-head { margin: 0.25rem 0 1rem; }
.day-heading .day-title { font-size: var(--fs-h1); font-weight: 800; line-height: 1.1; }
.day-heading .day-date { font-size: var(--fs-h3); font-weight: 600; color: var(--text); }
@media (max-width: 760px) {
  .day-heading .day-title { font-size: var(--fs-h2); }
  .day-heading .day-date { font-size: var(--fs-h4); }
}

/* Toggle switch */
.toggle-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
}
/* #389 "Top pick" price filter — align its font/size with the race toggles (--fs-sm),
   not the larger default .jh-chip body size, so the filter row reads as one control set. */
.rt-userfilters { align-items: center; }
.rt-price-group { display: inline-flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; font-size: var(--fs-sm); }
.rt-price-group .rf-label { font-size: var(--fs-sm); }
.rt-price-group .rf-chip { font-size: var(--fs-sm); padding: 0.2rem 0.45rem; }
/* Stacked toggles: sit underneath each other, flush to the right of the header,
   with the switch to the right of its label text for a neat aligned column. */
.toggle-stack {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem 1.1rem;
  margin-left: auto;
}
.toggle-stack .toggle-track { order: 1; }
.toggle-label input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.toggle-track {
  position: relative;
  width: 32px;
  height: 18px;
  background: var(--border);
  border-radius: 9px;
  transition: background 0.2s;
  flex-shrink: 0;
}
.toggle-label input:checked + .toggle-track {
  background: var(--accent);
}
.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s;
}
.toggle-label input:checked + .toggle-track .toggle-thumb {
  transform: translateX(14px);
}
.toggle-text {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  white-space: nowrap;
}

.empty-state-sm {
  font-size: var(--fs-body);
  color: var(--text-dim);
  padding: 0.6rem 0;
}

/* Status badges (shared) */

.status-badge {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  padding: 0.08rem 0.35rem;
  border-radius: 3px;
  letter-spacing: 0.3px;
}

.sb-abn { color: var(--danger); background: rgba(var(--danger-rgb), 0.1); }
.sb-delayed { color: #f59e0b; background: rgba(245,158,11,0.12); }
.sb-abn-partial { color: var(--win); background: rgba(var(--win-rgb), 0.1); }
.sb-result { color: var(--green); background: rgba(var(--green-rgb), 0.1); }
.sb-started { color: var(--win); background: rgba(var(--win-rgb), 0.1); }
.sb-pred { color: var(--accent-light); background: rgba(var(--accent-rgb), 0.1); }

/* Compact meet row (yesterday/tomorrow) */

.meets-compact {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Upcoming / Next to Jump */

.upcoming-row {
  display: grid;
  /* minmax(0,1fr) lets the three tracks shrink below their content's intrinsic
     width so the cards resize dynamically to fit the cell (the nowrap detail line
     ellipsises) instead of overflowing it. Stacks to one column at the mobile
     breakpoint below. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
}

.upcoming-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  transition: background 0.12s, border-color 0.12s;
  text-decoration: none;
}

.upcoming-card:hover { background: var(--bg-hover); border-color: var(--accent); }

.upcoming-card.upcoming-imminent { border-color: var(--win); }

.uc-countdown {
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--accent-light);
  min-width: 44px;
  text-align: center;
  flex-shrink: 0;
}

.uc-countdown.uc-imminent { color: var(--win); }

.uc-body { flex: 1; min-width: 0; }

.uc-course {
  font-size: var(--fs-h4);
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.uc-detail {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 0.15rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.uc-badges { flex-shrink: 0; }

.upcoming-empty {
  color: var(--text-muted);
  font-size: var(--fs-body);
  padding: 0.5rem 0;
}

@media (max-width: 640px) {
  .upcoming-row { grid-template-columns: 1fr; }
}

.compact-meet {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: var(--bg-card);
  transition: background 0.12s;
}

.compact-meet:hover { background: var(--bg-hover); }
.compact-meet.cm-abandoned { opacity: 0.4; }

.cm-course {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text);
}

.cm-info {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-right: auto;
}

/* Full meet group (today) */

.meet-group {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 0.6rem;
}

.meet-group.mg-abandoned { opacity: 0.45; }

.mg-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0.85rem;
  background: rgba(var(--accent-rgb), 0.04);
  border-bottom: 1px solid var(--border);
  transition: background 0.12s;
}

.mg-header:hover { background: rgba(var(--accent-rgb), 0.08); }

.mg-title { display: flex; align-items: baseline; gap: 0.5rem; }

.mg-course {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.mg-meta {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.mg-badges {
  display: flex;
  gap: 0.3rem;
}

.mg-races {
  display: flex;
  flex-direction: column;
}

.race-nav-pill {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.8rem 1rem;
  border-top: 1px solid var(--border);
  transition: background 0.12s;
}

.race-nav-pill:first-child { border-top: none; }
.race-nav-pill:hover { background: var(--bg-hover); }
.race-nav-pill.rnp-abn { opacity: 0.4; }
.race-nav-pill.rnp-resulted { border-color: var(--green); }

.rnp-num {
  font-family: var(--mono);
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--accent-light);
  min-width: 36px;
}

.rnp-time {
  font-family: var(--mono);
  font-size: var(--fs-body);
  color: var(--text-dim);
  min-width: 68px;
}

.rnp-name {
  font-size: var(--fs-h4);
  color: var(--text);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rnp-meta {
  font-size: var(--fs-body);
  color: var(--text-dim);
  white-space: nowrap;
}

.rnp-status {
  display: flex;
  gap: 0.25rem;
  min-width: 32px;
  justify-content: flex-end;
}

/* Archive link card */

.archive-link-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s, background 0.15s;
}

.archive-link-card:hover { border-color: var(--accent); background: var(--bg-hover); }

.alc-icon { font-size: var(--fs-h2); }

.alc-text {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.alc-text strong { font-size: var(--fs-h4); color: var(--text); }
.alc-text span { font-size: var(--fs-xs); color: var(--text-dim); }
.alc-arrow { font-size: var(--fs-h3); color: var(--text-dim); }

/* Race Page Layout */

.race-layout {
  display: flex;
  max-width: 1380px; /* match the greyhound/harness page width (.page-main) so all three align */
  margin: 0 auto;
  min-height: calc(100vh - 80px);
}

.sidebar {
  width: 200px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  padding: 0.75rem 0;
  overflow-y: auto;
  position: sticky;
  top: 64px;
  max-height: calc(100vh - 48px);
}

.sidebar-heading {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0.25rem 0.75rem 0.5rem;
}

.sidebar-meets {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.sidebar-meet {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  transition: background 0.12s;
  border-left: 3px solid transparent;
}

.sidebar-meet:hover { background: var(--bg-hover); }
.sidebar-meet.active { background: var(--bg-hover); border-left-color: var(--accent); }

.sm-name {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sm-info {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  white-space: nowrap;
}

.sm-abandoned {
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--danger);
  background: rgba(var(--danger-rgb), 0.1);
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
}

/* Quaddie Section */

.quaddie-section {
  border-top: 1px solid var(--border);
  margin-top: 0.5rem;
  padding-top: 0.25rem;
}

.qd-leg {
  margin-bottom: 0.5rem;
}

.qd-leg-header {
  display: flex;                        /* so the leg verdict can sit hard right (margin-left:auto) */
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--accent-light);
  padding: 0.2rem 0.75rem;
}

.qd-picks {
  display: flex;
  flex-direction: column;
}

.qd-pick {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  font-size: var(--fs-xs);
  /* #1207 SOFTENED, NOT RESTRUCTURED. The first attempt here replaced the outline with a 3px left
     accent — and STYLE-01 (#862) rejected it, correctly: that idiom was stripped from 56 rules, and
     the ruling for REAL STATE (which win/each-way is) is a FULL border in the same colour. So the
     shape stays and only the weight changes: 2px to 1px with the semantic tint behind it, the same
     treatment the selected tab chip already uses. Four legs no longer draw sixteen heavy boxes, and
     the selection type still reads at a glance without inventing a second visual language.
     (Naming that selector literally here broke CM-01: it scans from the FIRST occurrence of a
     family name, so a mention in a comment above the real rule redirects the check to this block.) */
  border: 1px solid transparent;
  border-radius: 4px;
  margin-bottom: 2px;
}

/* The win/each-way tint is gone. In a four-leg ticket it painted every row by BET TYPE — a fact
   the reader is not scanning for here — and spent the only colour vocabulary the modal had, so
   there was nothing left to say the thing they ARE scanning for: did the leg come in. Colour in
   this modal now means exactly one thing, the result. Rows are neutral until then. */
.qd-pick { border-color: var(--border); }
.qd-pick.qd-top { font-weight: 700; }
.qd-pick.qd-pick-won { border-color: var(--win); background: var(--win-bg); font-weight: 700; }
/* The leg verdict, in the header beside the race number. Text as well as colour (#169) — "Leg
   won" / "Leg lost" reads without either hue. A lost leg is dimmed rather than reddened: it is
   settled information, not an error. */
.qd-leg-res { margin-left: auto; font-family: var(--font); font-size: var(--fs-2xs); font-weight: 700; text-transform: none; }
.qd-leg-won { color: var(--win); }
.qd-leg-lost { color: var(--text-muted); }
.qd-leg-is-lost .qd-picks { opacity: 0.55; }

.qd-pick-num {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--accent-light);
  min-width: 18px;
}

.qd-pick-name {
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* #1207 the per-leg width, in words, beside the race number — "standout" or "3 to cover". The
   per-line confidence letter and raw score this replaces are gone: neither carried a unit, both
   were per-runner where the decision is per-leg, and the score was a model total rescaled by a
   factor the reader could not see. */
/* #1207 The honesty line under the coverage figure. Deliberately not styled as an alert: it is a
   standing property of the number, not a warning about this particular ticket. */
.qd-caveat {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
}

/* The one-line "how this is built" note. Right-aligned and set small so it reads as a footnote to
   the figures above it rather than as a section, which is what the three-paragraph callout it
   replaces had become — it filled the modal above the fold and pushed the ticket below it. */
.qd-note {
  text-align: right;
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  line-height: 1.45;
  margin: 0.35rem 0 0.75rem;
}

.qd-leg-shape {
  font-family: var(--font);
  font-size: var(--fs-2xs);
  font-weight: 400;
  color: var(--text-dim);
  text-transform: none;
  margin-left: 0.4rem;
}

/* Meet Nav Row */

.meet-nav-row {
  display: flex;
  align-items: center;
  max-width: 1380px;
  margin: 0 auto 0.8rem;
  gap: 0.5rem;
}

.meet-nav-row .meet-nav { flex: 1; }

.quaddie-btn {
  flex-shrink: 0;
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--accent-light);
  border: 1px solid rgba(var(--accent-rgb), 0.25);
  padding: 0.35rem 0.75rem;
  border-radius: 6px;
  font-size: var(--fs-sm);
  font-weight: 700;
  font-family: var(--font);
  cursor: pointer;
  transition: all 0.12s;
  margin-right: 0.75rem;
}

.quaddie-btn:hover { background: rgba(var(--accent-rgb), 0.2); border-color: var(--accent); }

/* Modal */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 300;
  padding: 1rem;
}

.modal-content {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 100%;
  max-width: 380px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
}

.modal-header h3 {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.modal-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: var(--fs-h1);
  cursor: pointer;
  padding: 0 0.25rem;
  line-height: 1;
  transition: color 0.12s;
}

.modal-close:hover { color: var(--text); }

.modal-body {
  padding: 0.5rem 0.75rem 0.75rem;
}

.race-main {
  flex: 1;
  min-width: 0;
}

/* Abandoned Banner */

.abandoned-banner {
  max-width: 1380px;
  margin: 0.75rem auto;
  padding: 0.6rem 1rem;
  background: rgba(var(--danger-rgb), 0.1);
  border: 1px solid rgba(var(--danger-rgb), 0.25);
  border-radius: var(--radius);
  color: var(--danger);
  font-weight: 700;
  font-size: var(--fs-h4);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.resulted-banner {
  max-width: 1380px;
  margin: 0.75rem auto;
  padding: 0.75rem 1rem;
  background: linear-gradient(rgba(var(--green-rgb), 0.1), rgba(var(--green-rgb), 0.1)), var(--bg);
  border: 1px solid rgba(var(--green-rgb), 0.2);
  border-radius: var(--radius);
  text-align: center;
}

.rb-header {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--green);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 0.35rem;
}

.rb-positions {
  font-size: var(--fs-h4);
  color: var(--text);
  line-height: 1.6;
}

.rb-pos strong { color: var(--win); }

/* Meet Nav (race page) */

.meet-nav {
  max-width: 1380px;
  margin: 0 auto;
  padding: 0.5rem 0.75rem;
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.meet-nav::-webkit-scrollbar { display: none; }
.meet-nav:empty { display: none; }

.meet-nav-pill {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.05rem;
  padding: 0.3rem 0.4rem;
  border-radius: 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  /* Fixed width so every pill is identical across races AND race types — the time
     string (mono, up to "12:29pm") no longer drives per-pill width. */
  width: 3.6rem;
  box-sizing: border-box;
  flex-shrink: 0;
  transition: all 0.12s;
}
.meet-nav-pill .mnp-time { max-width: 100%; overflow: hidden; }

.meet-nav-pill:hover { border-color: var(--accent); background: var(--bg-hover); }
.meet-nav-pill.active { background: var(--accent-btn); border-color: var(--accent-btn); }
.meet-nav-pill.active .mnp-num { color: #fff; }
.meet-nav-pill.active .mnp-time { color: rgba(var(--overlay-rgb), 0.65); }
.meet-nav-pill.mnp-abn { opacity: 0.4; }

.mnp-num {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--accent-light);
}

.mnp-time {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
}

.mnp-indicators {
  display: flex;
  gap: 2px;
}

.mnp-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.dot-abn { background: var(--danger); }
.dot-result { background: var(--green); }
.dot-started { background: var(--win); }
.dot-pred { background: var(--accent-light); }

/* Race Hero */

.race-hero {
  max-width: 1380px;
  margin: 0.75rem auto;
  padding: 1.1rem 1.25rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.hero-top {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.25rem;
  flex-wrap: wrap;
}

.hero-course {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--accent-light);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* Race-type pill in the hero header (dot + label), colour-coded per race type. */
.hero-type {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--type-color, var(--accent)) 14%, transparent);
  color: var(--type-color, var(--accent-light));
}
.hero-type.rt-horse   { --type-color: var(--type-horse, var(--accent)); }
.hero-type.rt-grey    { --type-color: var(--type-grey, var(--green)); }
.hero-type.rt-harness { --type-color: var(--type-harness, var(--win)); }

.hero-divider { color: var(--text-dim); font-size: var(--fs-sm); }

.hero-state { font-size: var(--fs-sm); color: var(--text-muted); }
.hero-race-num { font-size: var(--fs-sm); color: var(--text-muted); }
.hero-date { font-size: var(--fs-sm); color: var(--text-muted); }

.hero-status {
  font-size: var(--fs-xs);
  color: var(--green);
  background: rgba(var(--green-rgb), 0.1);
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  margin-left: auto;
}

.hero-name {
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.5rem;
}

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.hero-chip {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.2rem 0.55rem;
  font-size: var(--fs-sm);
  color: var(--text-mid);
}

.chip-time { color: var(--accent-light); border-color: rgba(var(--accent-rgb), 0.25); }

/* Labelled race-info grid */
.race-info-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin: 0.6rem 0 0.2rem;
}
.ri-item { display: flex; flex-direction: column; gap: 0.1rem; }
.ri-label {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ri-val { font-size: var(--fs-h4); font-weight: 600; color: var(--text); }
/* Track value renders as a coloured pill via the going-* classes */
.ri-val.going-good, .ri-val.going-soft, .ri-val.going-heavy, .ri-val.going-synth {
  display: inline-block;
  padding: 0.05rem 0.45rem;
  border-radius: 5px;
  font-size: var(--fs-body);
  width: fit-content;
}

/* Track condition (going) colour coding */
.chip-going { font-weight: 600; }
.going-good  { color: #4caf7d; border-color: rgba(76,175,125,0.4);  background: rgba(76,175,125,0.08); }
.going-soft  { color: #e0a23c; border-color: rgba(224,162,60,0.4);  background: rgba(224,162,60,0.08); }
.going-heavy { color: #e57373; border-color: rgba(229,115,115,0.4); background: rgba(229,115,115,0.08); }
.going-synth { color: var(--ew); border-color: rgba(var(--ew-rgb), 0.4);  background: rgba(var(--ew-rgb), 0.08); }

/* (Removed dead .hero-bias — superseded by the .track-bias callout, now UI.trackBias.) */
/* Greyhounds page (meetings reuse the horse .meet-group layout) */
.gh-back { display: inline-block; font-size: var(--fs-body); color: var(--accent-light, #a78bfa); text-decoration: none; margin-bottom: 0.6rem; }
.gh-back:hover { text-decoration: underline; }
/* Greyhound race hero reuses the horse .race-hero / .hero-* / .race-info-grid styles. */
.gh-weights { font-size: var(--fs-xs); color: var(--text-dim, #64748b); margin-top: 0.35rem; }
.gh-ai-tag { font-size: var(--fs-2xs); font-weight: 800; padding: 0.1rem 0.35rem; border-radius: 4px; vertical-align: middle;
  background: rgba(var(--ew-rgb), 0.16); color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.45); }
/* Greyhound race result reuses the horse .results-card / .rc-* styles. */
.gh-scratched { margin-top: 0.8rem; font-size: var(--fs-sm); color: var(--text-muted); }
.ws-code-heading { font-size: var(--fs-h3); font-weight: 800; margin: 1.5rem 0 0.4rem;
  padding-bottom: 0.4rem; border-bottom: 1px solid var(--border, rgba(var(--overlay-rgb), 0.1)); }
.ws-code-heading:first-child { margin-top: 0; }

/* Shell-level watch/listen row (UI.racePage) sits directly under the hero for every
   race type. Collapses fully when the race has no replay/call yet. */
.race-watch-row { margin: 0.5rem 0 0.25rem; }
.race-watch-row:empty { display: none; margin: 0; }
.race-watch-row .watch-links { margin: 0; }
.watch-links { display: flex; gap: 0.5rem; margin: 0.5rem 0 0.25rem; flex-wrap: wrap; }
.watch-link {
  display: inline-flex; align-items: center; gap: 0.3rem; font-size: var(--fs-sm); font-weight: 700;
  padding: 0.3rem 0.7rem; border-radius: 6px; text-decoration: none;
  background: rgba(var(--ew-rgb), 0.14); color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.4);
}
.watch-link:hover { background: rgba(var(--ew-rgb), 0.22); }
.watch-link.watch-audio { background: rgba(148,163,184,0.14); color: #94a3b8; border-color: rgba(148,163,184,0.4); }
button.watch-link { cursor: pointer; font: inherit; }
.modal-content.replay-modal { max-width: 880px; width: 92vw; }
.modal-content.replay-modal .modal-body { padding-top: 0.5rem; }

/* UI.modal (#187) — shared content dialog. Size variants + a body-scroll-lock while
   open, a soft fade-in, and a bottom-sheet layout on phones. Reuses .modal-*. */
body.modal-open { overflow: hidden; }
.ui-modal { animation: ui-modal-fade 0.15s ease; }
.ui-modal-content { display: flex; flex-direction: column; max-height: 88vh; }
.ui-modal-content .modal-body { overflow-y: auto; }
.ui-modal-sm { max-width: 380px; }
.ui-modal-md { max-width: 560px; }
.ui-modal-lg { max-width: 820px; }

/* #630 Shared tier-upgrade lock — the inner body used by BOTH the dismissable modal (a locked
   item click) and the full-page lock (a below-tier page load via #629 guardPage). Themed with
   CSS vars; reuses .btn-primary / .btn-outline so there's no one-off button styling. */
.upgrade-lock { text-align: center; padding: 0.4rem 0.2rem; }
.upl-head { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
.upl-ico { width: 34px; height: 34px; color: var(--accent); opacity: 0.9; }
.upl-title { font-size: 1.02rem; font-weight: 700; color: var(--text); margin: 0; line-height: 1.3; }
.upl-tiers { display: inline-flex; align-items: center; gap: 0.45rem; margin: 0.55rem 0 0.2rem; font-size: 0.78rem; }
.upl-cur { color: var(--text-dim); }
.upl-arrow { color: var(--text-dim); }
.upl-req { font-weight: 700; color: var(--accent); }
.upl-price { color: var(--text-dim); font-weight: 500; }
.upl-body { font-size: 0.86rem; color: var(--text-dim); margin: 0.5rem auto 0.9rem; max-width: 30ch; line-height: 1.45; }
.upl-cta { display: flex; flex-direction: column; gap: 0.5rem; align-items: stretch; }
.upl-cta .btn-primary, .upl-cta .btn-outline { width: 100%; text-align: center; }

/* Full-page lock overlay (#629): a fixed sheet that IS the page for a below-tier user. The server
   never sends the gated data, so nothing sensitive sits underneath — this is just the face. */
.tier-lock-page { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center;
  justify-content: center; padding: 1.5rem; background: var(--bg); overflow-y: auto; }
.tier-lock-card { width: 100%; max-width: 400px; background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 14px; padding: 1.6rem 1.4rem; box-shadow: 0 10px 40px rgba(var(--overlay-rgb), 0.18); }
body.tier-locked { overflow: hidden; }
.tier-lock-card .upl-title { font-size: 1.15rem; }
@media (max-width: 640px) { .tier-lock-page { padding: 1rem; align-items: flex-start; padding-top: 3rem; } }
@keyframes ui-modal-fade { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 640px) {
  /* Bottom-sheet: dock to the bottom edge, full width, rounded top, slide up. */
  .ui-modal { align-items: flex-end; padding: 0; }
  .ui-modal .ui-modal-content { width: 100%; max-width: none; max-height: 85vh; border-radius: 14px 14px 0 0; animation: ui-modal-slide 0.2s ease; }
}
@keyframes ui-modal-slide { from { transform: translateY(100%); } to { transform: translateY(0); } }

/* Track bias as its own section below the prediction (same width as the rest). */
/* #877 UI.procStep — a numbered step in an operator process.
   The number column is what makes the ORDER readable at a glance; without it a panel of six buttons
   presents every action as equally available and equally safe, including the destructive ones. */
.proc-step {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  margin: 0.75rem 0;
  background: var(--bg-card);
}
.proc-step-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.proc-step-n {
  flex: 0 0 auto;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
  background: var(--accent);
  color: var(--bg);
}
.proc-step-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 650;
}
.proc-step-tag {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.proc-step-why {
  margin: 0.4rem 0 0;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-muted);
}
/* Stated beside the control, never in a paragraph the operator has already scrolled past.
   Deliberately NOT a left-accent bar: #862 removed those as decoration and STYLE-01 caps their
   count, so this reads as a panel via tint + a full border instead. */
.proc-step-consequence {
  margin: 0.45rem 0 0;
  font-size: 0.82rem;
  line-height: 1.5;
  padding: 0.45rem 0.6rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.proc-step-controls {
  margin-top: 0.7rem;
  display: flex;
  gap: 0.6rem;
  align-items: center;
  flex-wrap: wrap;
}
/* A step that destroys or publishes something must not look like the ones that only preview. */
.proc-step-danger {
  border-color: var(--danger, #c0392b);
}
.proc-step-danger .proc-step-n {
  background: var(--danger, #c0392b);
  color: #fff;
}
.proc-step-danger .proc-step-consequence {
  border-color: var(--danger, #c0392b);
}
.proc-step-done .proc-step-n {
  background: var(--success, #2e7d32);
  color: #fff;
}
.proc-step-tag-done {
  border-color: var(--success, #2e7d32);
  color: var(--success, #2e7d32);
}
@media (max-width: 640px) {
  .proc-step { padding: 0.7rem 0.75rem; }
  .proc-step-controls > * { flex: 1 1 auto; }
}

.track-bias {
  max-width: 1380px;
  margin: 0.75rem auto;
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.6rem 0.75rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.hb-tag {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--accent-light);
  background: rgba(var(--accent-rgb), 0.12);
  padding: 0.18rem 0.4rem;
  border-radius: 4px;
  flex-shrink: 0;
  margin-top: 0.1rem;
}
.hb-body { font-size: var(--fs-sm); color: var(--text-mid); line-height: 1.5; flex: 1 1 auto; min-width: 0; }
/* Subject words (Pace, Winning barriers, In-form today, the barrier verdict) bold + white. */
.hb-body strong { color: var(--text); font-weight: 700; }
.hb-line strong { text-transform: capitalize; }
/* #624 AI-schedule banner — message + dismiss on one row (layout only; colours from tokens). */
/* #725 An admin previewing a tier gets a visible marker on every locked menu item, so a preview
   is never mistaken for the customer's real entitlement while debugging a report. */
/* Marked with an outline + tint rather than a ::after label: SP1-01 forbids any `.tn-locked::after`
   rule, because that is how a duplicate emoji padlock crept in once before. The words live in the
   item's title attribute (set in applyMenuLocks) and on the banner below. */
.tn-preview-tier a.tn-locked { outline: 1px dashed var(--accent); outline-offset: -2px;
  background: color-mix(in srgb, var(--accent) 8%, transparent); }
.tn-preview-banner { display: block; padding: 0.3rem 0.6rem; font-size: 0.7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: #fff; background: var(--accent); }
/* #738 The prediction alert is square, like every other panel on the site. It was inheriting a
   rounded radius while the surfaces around it are 0px, so it read as a foreign element. */
.ai-sched, .ai-sched-x, #predAiBanner > * { border-radius: 0 !important; }
.ai-sched { display: flex; align-items: flex-start; gap: var(--sp-2, 0.5rem); }
.ai-sched-msg { flex: 1 1 auto; min-width: 0; }
.ai-sched-x { flex: 0 0 auto; background: none; border: 0; color: var(--text-dim, var(--text-mid)); font-size: 1.25rem; line-height: 1; cursor: pointer; padding: 0 0.15rem; border-radius: var(--radius-sm, 4px); }
.ai-sched-x:hover { color: var(--text); }
/* #550 trial daily-limit upsell — two-CTA row inside the callout (layout only; colours from tokens). */
.trial-upsell { margin: var(--sp-3, 0.75rem) 0; }
.trial-upsell .tu-msg { margin: 0 0 var(--sp-2, 0.5rem); }
.trial-upsell-cta { display: flex; gap: var(--sp-2, 0.5rem); flex-wrap: wrap; }
.hb-dim { color: var(--text-dim); }
/* Consistent line sizing across the main line and the sub-lines. */
.hb-sub { font-size: var(--fs-sm); color: var(--text-mid); margin-top: 0.2rem; }

.hero-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.4rem;
}

.hero-tag {
  font-size: var(--fs-xs);
  color: var(--accent-light);
  background: rgba(var(--accent-rgb), 0.08);
  border-radius: 4px;
  padding: 0.1rem 0.4rem;
}

.prize-section {
  margin-top: 0.6rem;
  padding: 0.5rem 0.65rem;
  background: rgba(var(--win-rgb), 0.04);
  border: 1px solid rgba(var(--win-rgb), 0.12);
  border-radius: 8px;
}

.prize-total { font-weight: 700; font-size: var(--fs-body); color: var(--win); }

.prize-breakdown {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.25rem;
}

.prize-item { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-muted); }
.prize-pos { color: var(--text-dim); margin-right: 0.1rem; }

/* Scratching Alert */

/* The scratchings alert renders a shared .alert-card (ac-scratching) inside this
   mount, so the container is just a wrapper — the card provides all the styling. */
.scratching-alert { margin: 0.6rem 0; }


/* Draw the eye to the main predict button when a scratching invalidated the
   stored prediction, so "regenerate" is obvious wherever the user looks. */
#predictBtn.needs-regen {
  background: var(--danger);
  animation: regenPulse 1.6s ease-in-out infinite;
}

#predictBtn.needs-regen:hover { background: var(--danger); filter: brightness(1.1); }

@keyframes regenPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 55%, transparent); }
  50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--danger) 0%, transparent); }
}

/* Race Actions */

.race-actions {
  max-width: 1380px;
  margin: 0.75rem auto;
  padding: 0 1rem;
  display: flex;
  gap: 0.6rem;
  align-items: center;
}

.race-actions select {
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 0.45rem 0.65rem;
  border-radius: 8px;
  font-size: var(--fs-body);
  font-family: var(--font);
}

.race-actions select:focus { outline: none; border-color: var(--accent); }

#predictBtn {
  background: var(--accent-btn);
  color: var(--on-accent);
  border: none;
  padding: 0.5rem 1.2rem;
  font-weight: 600;
  font-size: var(--fs-body);
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  transition: background 0.15s;
}

#predictBtn:hover { background: #5a4ac4; }
#predictBtn:disabled { background: var(--border); cursor: not-allowed; color: var(--text-dim); }

.btn-outline {
  background: transparent;
  color: var(--accent-light);
  border: 1px solid var(--border);
  padding: 0.5rem 1rem;
  font-weight: 600;
  font-size: var(--fs-body);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
.btn-outline:disabled { color: var(--text-dim); cursor: not-allowed; }

.rd-bar-track {
  width: 100%;
  height: 6px;
  background: var(--bg);
  border-radius: 3px;
  overflow: hidden;
}
.rd-bar-fill {
  width: 0%;
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  transition: width 0.4s ease;
}
.rd-bar-error { background: var(--danger, #e57373) !important; }
.rd-status {
  margin-top: 0.4rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

#recalcBtn {
  background: transparent;
  color: var(--accent-light);
  border: 1px solid var(--border);
  padding: 0.5rem 1rem;
  font-weight: 600;
  font-size: var(--fs-body);
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  transition: all 0.15s;
}

#recalcBtn:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.08); }
#recalcBtn:disabled { opacity: 0.4; cursor: not-allowed; }

.spinner {
  display: none;
  width: 14px; height: 14px;
  border: 2px solid rgba(var(--overlay-rgb), 0.25);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.5s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* #919 Handoff overlay (UI.handoffOverlay) — shown while the app is handing off to a hosted third
   party (Stripe Checkout / Billing Portal). It BLOCKS on purpose: the page is about to be replaced,
   so there is nothing useful left to do here, and blocking is what makes a second tap — and the
   duplicate Stripe session behind it — impossible.

   Centred and fixed, so it is visible wherever the user tapped from. The defect being fixed was
   feedback rendered at the bottom of a long panel, below the fold on a phone. */
.handoff-overlay {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center; padding: 1.5rem;
  background: rgba(8, 10, 16, .72); backdrop-filter: blur(3px);
}
.handoff-card {
  max-width: 360px; width: 100%; text-align: center;
  background: var(--bg-card); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.5rem 1.4rem; box-shadow: 0 20px 60px rgba(0, 0, 0, .5);
}
.handoff-spin {
  width: 28px; height: 28px; margin: 0 auto .8rem;
  border: 3px solid rgba(var(--overlay-rgb), .25);
  border-top-color: var(--accent);
  border-radius: 50%; animation: spin .6s linear infinite;
}
.handoff-title { font-size: var(--fs-md); font-weight: 700; }
.handoff-msg   { color: var(--text-muted); font-size: var(--fs-sm); margin-top: .35rem; line-height: 1.5; }
/* The reassurance sits apart from the status, because it answers a different question — and it is
   the question that decides whether a first payment happens at all. */
.handoff-note  { color: var(--text-muted); font-size: var(--fs-xs); margin-top: .8rem;
                 padding-top: .7rem; border-top: 1px solid var(--border); line-height: 1.5; }
.handoff-err-msg { color: var(--danger); font-size: var(--fs-sm); margin: 0 0 .9rem; line-height: 1.5; }

/* Respect a reduced-motion preference: the spinner is decoration, the text carries the meaning. */
@media (prefers-reduced-motion: reduce) { .handoff-spin { animation: none; } }

/* Top Pick Banner */

.top-pick {
  display: none;
  max-width: 1380px;
  margin: 1rem auto;
  padding: 1rem 1.25rem;
  background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.16), rgba(176,164,255,0.07)), var(--bg);
  border: 1px solid rgba(var(--accent-rgb), 0.2);
  border-radius: var(--radius);
  text-align: center;
}

.top-pick-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--accent-light);
  margin-bottom: 0.2rem;
}

.top-pick-name { font-size: var(--fs-h1); font-weight: 700; color: var(--win); }
.top-pick-ew { color: var(--ew); font-size: var(--fs-body); margin-top: 0.25rem; }

/* #1006 The split prediction cards. One card per KIND of claim — win probability vs market edge —
   each keyed on the semantic token the product already uses for it (--win / --ew), so the colour
   pair is the design system's own on every theme. State is carried the way #862 settled it: a FULL
   border in the state's colour plus a faint tint — never a left-accent bar (STYLE-01 ratchets that
   idiom out). The tint stays faint so the runner names, not the panels, are what the eye lands on.
   Labels name each card, so colour is never the only differentiator (#169). */
/* #1203 the group is a LAYOUT container, not a panel. It used to paint an accent gradient and a
   border around two already-tinted cards, so the card read as three nested coloured boxes. The
   slots carry the semantic colour (--win / --ew); the group carries only spacing. */
/* ⚠ SPECIFICITY: the light layer sets `:root[data-theme="light"] .top-pick { background: … }` at
   (0,3,0), so a plain `.top-pick.tp-split` at (0,2,0) LOSES and the olive panel survives — which is
   exactly how it rendered on staging after the first attempt. Matching the theme layer's own shape
   is the only thing that beats it until #1198 flattens those selectors. */
:root .top-pick.tp-split,
:root[data-theme="light"] .top-pick.tp-split {
  background: none; border: 0; padding: 0; margin: 0.75rem auto; border-radius: 0; }
.tp-split { padding: 0.65rem 0.75rem; text-align: left; }
.tp-card { border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.6rem 0.9rem; background: var(--bg-card); }
.tp-card + .tp-card { margin-top: 0.5rem; }
.tp-card-win { border-color: color-mix(in srgb, var(--win) 45%, var(--border));
  background: color-mix(in srgb, var(--win) 7%, var(--bg-card)); }
.tp-card-ew { border-color: color-mix(in srgb, var(--ew) 45%, var(--border));
  background: color-mix(in srgb, var(--ew) 6%, var(--bg-card)); }
.tp-split .top-pick-ew { margin-top: 0; font-size: var(--fs-h3); font-weight: 700; }
.tp-label-ew { color: var(--ew); }
/* #1194 `.top-pick-summary` is gone — the summary now renders as `.tp-reasons` INSIDE the win
   card, so its two layout rules and its colour rule went with it rather than lingering as dead CSS. */
.tp-split .top-pick-timestamp { padding: 0 0.25rem; }
/* #908 The percentage beside each pick — win% on the top pick, place% on the each-way. Muted and a
   size down: it qualifies the name, it does not compete with it. Tabular figures so the digits do
   not shift as the number changes on a live re-blend. */
/* #1194 ask 6 — was --text-muted, which measures 2.63:1 on the win card and 2.70:1 on the each-way
   card in the LIGHT theme (--text-muted is #8b97a8 on a near-white panel). That is below the 4.5:1
   floor for text this size, on the figure that qualifies the selection. --text-mid clears it in both
   themes: 8.57 / 11.86 on the win card, 8.88 / 12.18 on the each-way card. */
.tp-pct { color: var(--text-mid); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
/* The measured edge, when there IS one. Deliberately distinct from .tp-pct: a probability and a
   claim about value are different kinds of statement and must not read as one figure.
   #1194 ask 6 — --accent on the each-way card measures 3.83:1 in DARK (the one place this token is
   painted on a tinted panel rather than the page). --accent-light is 6.98 dark / 9.93 light. */
.tp-edge { color: var(--accent-light); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
/* ── #1203 the two slots, and the runner leading each ────────────────────────────────────────────
   Mobile stacks because there is no room not to. Above 900px they sit SIDE BY SIDE: the win bet and
   the each-way are peers — two selections from one prediction — and a vertical stack reads as a
   ranking, with the each-way looking like a footnote to the pick above it. Side by side also stops
   the reasoning bullets pushing the each-way below the fold, which is what happens now that BOTH
   slots carry a why-block and an about-block. */
.tp-slots { display: flex; flex-direction: column; gap: 0.5rem; }
@media (min-width: 900px) {
  /* auto-fit, NOT `1fr 1fr`: with only one slot (the each-way vacated, or none qualified) a fixed
     two-column grid leaves half the card as empty tinted space — which is exactly how it rendered
     on staging. auto-fit collapses the empty track instead. */
  .tp-slots { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
    gap: 0.75rem; align-items: start; }
  .tp-slots .tp-card + .tp-card { margin-top: 0; }   /* the grid gap owns the spacing */
}
/* The selection is the claim; the role and the probability qualify it. */
.tp-pick-row { display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem 0.75rem; flex-wrap: wrap; }
.tp-pick-name { display: flex; align-items: center; gap: 0.45rem;
  font-size: var(--fs-h2); font-weight: 700; line-height: 1.2; min-width: 0; }
/* The saddlecloth number relied on vertical-align:middle, which aligns to the parent BASELINE plus
   half an x-height — visibly low against a 1.25rem name. As a flex sibling it is genuinely centred
   at any size, with no magic offset to re-tune when the type scale moves. */
.tp-pick-name .tp-num { flex: none; font-size: 0.8em; min-width: 1.75em; height: 1.75em;
  padding: 0 0.35em; border-radius: var(--radius-sm, 6px); vertical-align: baseline; }
.tp-pick-role { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-mid); margin-bottom: 0.25rem; }
.tp-edge-row { margin-top: 0.15rem; }

/* #1194 ask 4 — the selection reasoning, inside the top-pick card as dot points. Indented to the
   text column rather than the bullet column so the runner name still starts the card's left edge:
   the name is the claim, the bullets support it. Marker is the win token, tying the list to the
   card it explains without inventing a colour. */
.tp-reasons { margin-top: 0.5rem; }
.tp-reasons-head { font-size: var(--fs-xs); font-weight: 700; color: var(--text-mid);
  margin-bottom: 0.25rem; }
.tp-reasons-list { margin: 0; padding-left: 1.1rem; color: var(--text-mid);
  font-size: var(--fs-sm); line-height: 1.5; }
.tp-reasons-list li { margin: 0.15rem 0; }
.tp-reasons-list li::marker { color: var(--win); }
/* #270 saddlecloth number chip before the pick name (AI + basic tips) */
.tp-num { display: inline-flex; align-items: center; justify-content: center; min-width: 1.5em; height: 1.5em; padding: 0 0.3em;
  font-size: 0.62em; font-weight: 800; color: var(--text); background: var(--bg); border: 1px solid var(--border);
  border-radius: 5px; vertical-align: middle; font-variant-numeric: tabular-nums; }
/* #272 odds-shift sparkline (open→race-time fixed-win price). Green = steamed (shortened),
   red = drifted, muted = flat. Odds axis inverted so a steaming line rises. */
.spark { vertical-align: middle; overflow: visible; }
.spark.spark-in { color: var(--green); }
.spark.spark-out { color: var(--danger, #ef4444); }
.spark.spark-flat { color: var(--text-muted); }
.spark .spark-cur { fill: currentColor; }
.spark .spark-open { fill: var(--text-muted); }
.odds-spark { display: inline-flex; align-items: center; gap: 0.3rem; }
.odds-spark .os-move { font-size: var(--fs-2xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.odds-spark.os-in .os-move { color: var(--green); }
.odds-spark.os-out .os-move { color: var(--danger, #ef4444); }
.odds-spark .os-text { font-size: var(--fs-2xs); color: var(--text-muted); }
/* #273 muted "no price history captured yet" placeholder (poller often off) — subtle so it
   never competes with the prediction; it just fills the gap where a sparkline can't yet show. */
.odds-spark.os-empty { color: var(--text-dim); font-size: var(--fs-xs); opacity: 0.65; }

/* #277 per-runner "Odds movement" accordion — a fuller price chart + open/now/low/high labels,
   or a muted "no data yet" message. Mobile-first: the SVG scales to the card width. */
.odds-chart { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.2rem 0.1rem; }
.odds-chart-svg { width: 100%; }
.odds-chart-svg .spark { width: 100%; height: auto; max-height: 80px; display: block; }
.odds-chart.os-in .spark { color: var(--green); }
.odds-chart.os-out .spark { color: var(--danger, #ef4444); }
/* #295 dual fixed-vs-Betfair overlay: fixed = solid accent, Betfair = dashed teal. */
.odds-chart-svg .odds-dual { width: 100%; height: auto; max-height: 80px; display: block; }
.odds-dual .ocl-fixed, .odds-dual .ocl-fixed-dot { color: var(--accent, #7c6cff); }
.odds-dual .ocl-bf, .odds-dual .ocl-bf-dot { color: var(--teal, var(--ew)); }
.odds-chart-legend { display: flex; gap: 0.9rem; font-size: var(--fs-2xs); }
.odds-chart-legend .ocl-key { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.odds-chart-legend .ocl-key::before { content: ""; width: 14px; height: 0; border-top-width: 2px; border-top-style: solid; }
.odds-chart-legend .ocl-key-fixed::before { border-top-color: var(--accent, #7c6cff); }
.odds-chart-legend .ocl-key-bf::before { border-top-color: var(--teal, var(--ew)); border-top-style: dashed; }
/* #319 per-source legend: an inline colour swatch instead of the ::before line rule */
.odds-chart-legend .ocl-key .ocl-swatch { display: inline-block; width: 12px; height: 2px; border-radius: 1px; }
.odds-chart-legend .ocl-key:has(.ocl-swatch)::before { display: none; }
.odds-chart-legend { flex-wrap: wrap; row-gap: 0.25rem; }
.odds-chart-bf-labels { opacity: 0.9; }
.odds-chart-labels { display: flex; flex-wrap: wrap; gap: 0.4rem 0.9rem; }
.odds-chart-labels .occ { display: flex; flex-direction: column; line-height: 1.2; }
.odds-chart-labels .occ-lab { font-size: var(--fs-2xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.odds-chart-labels .occ-val { font-size: var(--fs-sm); font-weight: 700; font-variant-numeric: tabular-nums; }
.odds-chart.os-in .occ-move .occ-val { color: var(--green); }
.odds-chart.os-out .occ-move .occ-val { color: var(--danger, #ef4444); }
.odds-chart-empty { display: flex; flex-direction: column; gap: 0.15rem; color: var(--text-dim); font-size: var(--fs-sm); padding: 0.35rem 0.1rem; }
.odds-chart-empty .oce-sub { font-size: var(--fs-2xs); color: var(--text-muted); }
/* #296 single captured price (no movement yet) — show the price, not a "no data" message. */
.odds-chart-single { gap: 0.25rem; }
.odds-chart-single .oce-sub { font-size: var(--fs-2xs); color: var(--text-muted); }

.top-pick-timestamp {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 0.4rem;
}

/* Runner Rows */

.runners-container {
  max-width: 1380px;
  margin: 0 auto;
  padding: 0.5rem 0 2rem; /* no side padding — cards align with the other 1380px sections */
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* #133 runner card is a vertical stack: compact row + full-width accordion below. */
.runner-row {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.4rem 0.35rem;   /* #133/#140 tighter edge padding to reclaim horizontal space */
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.runner-compact {
  display: grid;
  /* #310 dropped the standalone rank column — the model rank was a bare, unlabelled number
     that competed with the saddlecloth number; it now shows once as the labelled "VO #n" chip. */
  grid-template-columns: 70px 1fr 190px;
  gap: 0.4rem;
  align-items: start;
}
/* #133 barrier inline next to the runner name */
.runner-barrier { color: var(--text-muted); font-weight: 600; font-size: 0.85em; }
/* #134 always-visible main info (jockey/trainer/weight/gear/going per code).
   #141 One inline label:value pattern at ALL widths (was a desktop grid) — same
   compact flow as mobile, so the card reads identically everywhere. */
.runner-main-info { display: flex; flex-direction: column; gap: 0.05rem; margin-top: 0.25rem; }
.runner-main-info .info-cell { display: flex; flex-direction: row; align-items: baseline; gap: 0.3rem; background: none; border: none; border-radius: 0; padding: 0; }
.runner-main-info .info-label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); }
.runner-main-info .info-value { font-size: var(--fs-sm); color: var(--text-mid); }
/* #133 per-runner pace bar (back → lead) */
.pace-bar { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.25rem; }
.pace-bar-label { font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted); white-space: nowrap; min-width: 72px; }
.pace-bar-end { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); }
.pace-bar-track { position: relative; flex: 1; height: 6px; border-radius: 3px; background: linear-gradient(90deg, rgba(148,163,184,0.18), rgba(var(--ew-rgb), 0.28)); }
.pace-bar-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 3px; background: linear-gradient(90deg, rgba(var(--ew-rgb), 0.35), var(--accent)); }
.pace-bar-dot { position: absolute; top: 50%; width: 8px; height: 8px; border-radius: 50%; background: var(--accent-light); border: 1px solid var(--bg); transform: translate(-50%, -50%); }
/* #141 TAB signal chips (fast-form lean / tcdw / price move) in the pace-flags row */
.tab-chip { display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; gap: 0.2rem; font-weight: 700; border-radius: 5px; border: 1px solid var(--border); background: var(--bg-card); color: var(--text-mid); text-transform: uppercase; letter-spacing: 0.02em; white-space: nowrap; }
.tab-chip.tc-lead { color: var(--accent-light); border-color: var(--accent); background: rgba(var(--accent-rgb), 0.12); }
.tab-chip.tc-top { color: var(--win); border-color: var(--win); background: var(--win-bg); }
.tab-chip.tc-tcdw { font-family: var(--mono); color: var(--ew); border-color: var(--ew); background: var(--ew-bg); }
.tab-chip.tc-firm { color: var(--green); border-color: var(--green); }
.tab-chip.tc-drift { color: var(--text-muted); }
/* #135 win% / place% prediction boxes (styled like the mobile guide) */
.pred-boxes { display: flex; gap: 0.4rem; }
.pred-box { display: flex; flex-direction: column; align-items: center; min-width: 52px; padding: 0.3rem 0.5rem; border-radius: 8px; background: var(--bg-card); border: 1px solid var(--border); }
.pred-box .pb-lab { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); }
.pred-box .pb-val { font-size: var(--fs-h3); font-weight: 700; color: var(--text); }
/* #135 inline rank shown on mobile only (desktop uses the rank column) */
/* #137 TAB Win + Place odds stacked (replaces the Fair/TAB lines) */
.tab-odds { display: flex; flex-direction: column; gap: 0.1rem; align-items: flex-end; }
.tab-odds-row { display: flex; gap: 0.4rem; align-items: baseline; }
.tab-odds .to-lab { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); min-width: 30px; }
/* #521 the odds value uses the app font (not the monospace face) — tabular-nums (above) already
   aligns the digits, so the mono family only made the odds button read in a different font. */
.tab-odds .to-val { font-size: var(--fs-h4); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
/* #137 speed / drift / value flags in a full-width row below the pace bar */
.runner-paceflags { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin-top: 0.25rem; } /* #249 one centred line — chips must NOT set their own margin-top */
.runner-paceflags:empty { display: none; }
/* #133 always-visible actions row: bordered betslip section + follow button */
.runner-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.runner-betslip-section { border: 1px solid var(--border); border-radius: 8px; padding: 0.25rem 0.4rem; background: var(--bg-card); display: inline-flex; align-items: center; }
.runner-betslip-section:empty { display: none; }
/* #133/#139 one full-width "More" accordion — the summary is a clear button, and the
   OPEN state gets a filled background + accent border so it's obvious it's expanded and
   how to close it (tap the highlighted header again). */
.ui-accordion { margin-top: 0.35rem; }
.ui-accordion-sum { cursor: pointer; list-style: none; padding: 0.45rem 0.7rem; font-size: var(--fs-sm); font-weight: 700; color: var(--text-mid); user-select: none; display: flex; align-items: center; gap: 0.4rem; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; }
.ui-accordion-sum::-webkit-details-marker { display: none; }
.ui-accordion-sum::before { content: "▸"; font-size: var(--fs-xs); transition: transform 0.15s; }
.ui-accordion[open] > .ui-accordion-sum::before { transform: rotate(90deg); }
.ui-accordion-sum:hover { color: var(--text); border-color: var(--accent); }
.ui-accordion[open] { background: var(--bg-card); border: 1px solid var(--accent); border-radius: 10px; padding: 0.3rem; }
.ui-accordion[open] > .ui-accordion-sum { background: var(--bg-hover); border-color: var(--accent); color: var(--text); margin-bottom: 0.3rem; }
.ui-accordion[open] > .ui-accordion-sum::after { content: "tap to close"; margin-left: auto; font-size: var(--fs-2xs); font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
.ui-accordion-body { padding: 0.25rem 0.4rem 0.4rem; }

.runner-row.sel-win { border-color: var(--win); background: linear-gradient(var(--win-bg), var(--win-bg)), var(--bg); }
.runner-row.sel-ew { border-color: var(--ew); background: linear-gradient(var(--ew-bg), var(--ew-bg)), var(--bg); }
.runner-row.sel-avoid { border-color: var(--avoid); background: linear-gradient(var(--avoid-bg), var(--avoid-bg)), var(--bg); }
/* Scratched runner card — greyed out but still legible, struck-through name.
   Shared across all race types via UI.scratchedCard / UI.scratchedSection. */
.runner-row.scratched {
  opacity: 0.7;
  filter: grayscale(1);
  background: color-mix(in srgb, var(--text-dim) 8%, transparent);
}
.runner-row.scratched .runner-horse {
  text-decoration: line-through;
  color: var(--text-dim);
}
.runner-row.scratched .runner-connections { color: var(--text-dim); }

.scratched-header {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0.75rem 0 0.2rem;
  border-top: 1px solid var(--border);
  margin-top: 0.4rem;
}

.runner-num-silk {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.runner-num {
  font-family: var(--mono);
  font-size: var(--fs-h1);
  line-height: 1;
  font-weight: 800;
  color: var(--accent-light);
  text-align: center;
  background: rgba(var(--accent-rgb), 0.12);
  border-radius: 7px;
  padding: 0.25rem 0 0.3rem;
  min-width: 2.4rem;
}
/* #310 tiny "NO" caption inside the saddlecloth box so it unmistakably reads as the runner
   number (the one that matters), not one of the other numbers on the card. */
.runner-num-lab {
  display: block;
  font-family: var(--font, inherit);
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1;
  color: var(--text-dim);
  margin-bottom: 0.15rem;
}

.silk-img {
  width: 62px;
  height: auto;
  border-radius: 8px;
  box-sizing: border-box;
  padding: 3px;   /* #141 tighter padding on a white plate so the silk reads clearly */
  margin: 0.25rem 0.35rem;   /* #141 outside gap so neighbouring text doesn't touch the plate */
  background: #fff;
  border: 1px solid var(--border, rgba(var(--overlay-rgb), 0.1));
  object-fit: contain;
}

.runner-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.runner-horse-line {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.runner-horse { font-weight: 700; font-size: var(--fs-h4); color: var(--text); }

.runner-form {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-light);
  letter-spacing: 1px;
  background: rgba(var(--accent-rgb), 0.06);
  padding: 0.05rem 0.35rem;
  border-radius: 3px;
}

.data-flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--chip-h);
  height: var(--chip-h); /* #234 aligned */
  font-size: var(--fs-2xs);
  font-weight: 700;
  border-radius: 3px;
  margin-left: 0.25rem;
  cursor: help;
  vertical-align: middle;
}
.flag-enrich  { background: rgba(var(--ew-rgb), 0.16);  color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.35); }
.flag-profile { background: rgba(var(--win-rgb), 0.16);  color: var(--win); border: 1px solid rgba(var(--win-rgb), 0.35); }
.flag-debut   { width: auto; padding: 0 0.3rem; background: rgba(var(--accent-rgb), 0.16); color: var(--accent-light); border: 1px solid rgba(var(--accent-rgb), 0.35); }
/* #NNN TAB tip badge — a runner TAB's tipster selected. */
.flag-tabtip  { width: auto; padding: 0 0.35rem; gap: 0.2rem; background: rgba(var(--green-rgb, 52,211,153), 0.16); color: var(--green, #34d399); border: 1px solid rgba(var(--green-rgb, 52,211,153), 0.35); font-weight: 700; }

.runner-connections {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.65rem;
}

.runner-silks {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 0.2rem;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.conn-item strong { color: var(--text-mid); font-weight: 600; }
.claim { color: var(--ew); font-size: var(--fs-xs); }

.runner-info-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.info-cell {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.15rem 0.45rem;
  display: flex;
  gap: 0.3rem;
  align-items: baseline;
}

.info-label {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

.info-value {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-mid);
}

.runner-breeding-line {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.runner-breeding-line span { color: var(--text-muted); }

.runner-owner {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  line-height: 1.3;
}

.runner-last-raced {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.runner-stats-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.stat-cell {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.15rem 0.45rem;
  display: flex;
  gap: 0.3rem;
  align-items: baseline;
}

.stat-label {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

.stat-value {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.runner-comment {
  font-size: var(--fs-sm);
  color: var(--ew);
  font-style: italic;
  line-height: 1.35;
}

/* Racing.com enrichment block */
.enrichment-data {
  margin-top: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.rc-comment {
  font-size: var(--fs-sm);
  color: var(--text-mid);
  font-style: italic;
  line-height: 1.4;
}
.rc-gear {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.fais-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}
.fais-tag {
  font-size: var(--fs-xs);
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.fais-pos {
  background: rgba(100, 200, 120, 0.15);
  color: #6dc87a;
  border: 1px solid rgba(100, 200, 120, 0.25);
}
.fais-neg {
  background: rgba(220, 100, 100, 0.12);
  color: #dc8080;
  border: 1px solid rgba(220, 100, 100, 0.2);
}
.rc-stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.rc-stat-item {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
}
.rc-label {
  color: var(--text-dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.rc-time-fast { color: #4caf50; font-weight: 600; }
.rc-time-slow { color: #e57373; }
.rc-pips {
  font-size: var(--fs-2xs);
  letter-spacing: 0.05em;
  color: var(--accent);
}

/* Run history table */
.run-history {
  margin-top: 0.6rem;
}
.rh-title {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 0.3rem;
}
.rh-scroll {
  overflow-x: auto;
}
.rh-table {
  border-collapse: collapse;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  white-space: nowrap;
  width: 100%;
}
.rh-table th {
  color: var(--text-dim);
  font-weight: 600;
  text-transform: uppercase;
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  padding: 0.15rem 0.4rem 0.25rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.rh-table td {
  padding: 0.2rem 0.4rem;
  border-bottom: 1px solid rgba(var(--overlay-rgb), 0.04);
}
.rh-table .rh-finish {
  font-weight: 700;
  color: var(--text-mid);
}

/* #417 The icon set that replaced the emoji.
   Sized in `em` and coloured by `currentColor`, so an icon matches whatever text it sits
   beside and recolours with its container — which is the entire point: the emoji it replaced
   could do neither. `vertical-align: -0.14em` sits it on the text's optical centre rather
   than its baseline, where an SVG otherwise rides high. */
.vo-ico {
  width: 1.05em;
  height: 1.05em;
  vertical-align: -0.14em;
  flex: none;
}
/* Inside a chip/badge the glyph reads better a touch smaller than its label. */
.badge .vo-ico, .ac-badge .vo-ico { width: 0.95em; height: 0.95em; }
/* #526 the plan-lock padlock on gated items is a flat theme red (was a colourful 🔒 emoji);
   the SVG strokes with currentColor, so colouring the wrapper colours the icon. */
.lock-ico, .locked-field .vo-ico { color: var(--danger); }

.runner-reason {
  font-size: var(--fs-sm);
  color: var(--win);
  font-style: italic;
}

/* #404 Entry vs jump — the price we called it at, next to the price it went off at.
   Mobile-first: a wrapping flex row, so at ≤640px the legs stack onto a second line rather
   than forcing the (already dense, per #279/#301) card to scroll sideways. */
.entry-jump {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-top: 4px;
  font-size: var(--fs-xs);
}
.entry-jump-leg { display: inline-flex; align-items: baseline; gap: 4px; }
.entry-jump-lab { color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }
.entry-jump-val { font-weight: 600; font-variant-numeric: tabular-nums; }
.entry-jump-at { color: var(--text-dim); }
/* Both directions are styled with the same weight and prominence — deliberately. A CLV
   display that shouts the wins and mumbles the losses is an ad, not a track record. */
.clv-delta { font-weight: 600; font-variant-numeric: tabular-nums; }
.clv-up { color: var(--win); }
.clv-down { color: var(--danger); }

/* Right column: odds + prediction */

.runner-right {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: flex-end;
}

.odds-section {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  text-align: right;
}

.odds-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.4rem;
  align-items: baseline;
  font-family: var(--mono);
  font-size: var(--fs-xs);
}

.bookie-name {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.2px;
}

.odds-win { font-weight: 700; color: var(--win); }
.odds-place { color: var(--text-muted); }

.sp-row {
  font-family: var(--mono);
  font-size: var(--fs-h4);
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 0.1rem;
}
.bf-row {
  font-family: var(--mono);
  font-size: var(--fs-h4);
  font-weight: 700;
  color: #f5a623;
  margin-top: 0.1rem;
}
.bf-place { color: #d08a1e; font-weight: 600; margin-left: 0.25rem; }
.tote-px { color: var(--text-dim); font-weight: 500; margin-left: 0.25rem; }
/* Tote-to-Fixed Friction Index flags */
.friction-badge {
  display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; font-weight: 800; letter-spacing: 0.02em; border-radius: 5px; cursor: help;
}
.friction-badge.fr-overlay { background: rgba(var(--ew-rgb), 0.16); color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.45); }
.friction-badge.fr-trap    { background: rgba(239,83,80,0.16); color: #ef5350; border: 1px solid rgba(239,83,80,0.45); }
/* #1509 — light-theme contrast retune (guide.html badge/chip family, card #1509). Each override
   below holds the same hue/saturation as its base colour and only lowers lightness, computed per
   pair against ITS OWN composited background to clear axe's 4.5:1 (this chip family renders at
   ~12.6px bold, so the small-text threshold applies, not the 3:1 large-text one). Scoped to light
   theme only — dark theme uses different token values (e.g. --ew is teal, not red) that already
   pass, and this is a contrast fix, not a repalette. */
:root[data-theme="light"] .friction-badge.fr-overlay { color: #bf1c1b; }
:root[data-theme="light"] .friction-badge.fr-trap { color: #ce1613; }
.drift-flag {
  display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; font-weight: 800; letter-spacing: 0.02em; border-radius: 5px; cursor: help;
  background: rgba(245,158,11,0.16); color: #f59e0b; border: 1px solid rgba(245,158,11,0.45);
}
:root[data-theme="light"] .drift-flag { color: #966106; } /* #1509 — see comment above */
.place-value-flag {
  display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; font-weight: 800; letter-spacing: 0.02em; border-radius: 5px; cursor: help;
  background: rgba(var(--green-rgb), 0.16); color: var(--green); border: 1px solid rgba(var(--green-rgb), 0.45);
}
.early-speed-chip {
  display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; font-weight: 700; letter-spacing: 0.02em; border-radius: 5px; cursor: help;
  background: rgba(148,163,184,0.14); color: #94a3b8; border: 1px solid rgba(148,163,184,0.35);
}
:root[data-theme="light"] .early-speed-chip { color: #596b86; } /* #1509 — see fr-overlay comment above */
.delayed-tag {
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.03em; padding: 0.08rem 0.35rem;
  border-radius: 4px; cursor: help; vertical-align: middle;
  background: rgba(245,158,11,0.16); color: #f59e0b; border: 1px solid rgba(245,158,11,0.45);
}
/* #1007 The live-update row INSIDE the Top Pick card — replaces the old standalone note box,
   which sat below a stale-looking pick and read as contradicting it. Pill + row use tokens only
   (the old block carried literal blues/ambers and a var(--muted) that resolves nowhere). */
.tp-upd-pill { display: inline-block; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.04em;
  padding: 0.08rem 0.4rem; border-radius: 999px; margin-left: 0.45rem; vertical-align: middle;
  background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent-light);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.tp-upd-pill.tp-upd-scr { background: color-mix(in srgb, var(--brand-betfair) 14%, transparent);
  color: var(--brand-betfair); border-color: color-mix(in srgb, var(--brand-betfair) 45%, transparent); }
/* #1194 ask 5 — the change-note now sits OUTSIDE the cards (see the reversal note on #1007 in
   ui.js topPick). Outside it is on the page background, not a tinted panel, so it needs its own
   inset to line up with the cards' text column and a rule to tie it back to the group it annotates.
   A FULL border in the state's colour plus a faint tint, per the #862 settlement — NOT a left-accent
   bar, which STYLE-01 ratchets out and which this rule reached for first. */
.tp-updated { margin-top: 0.5rem; padding: 0.45rem 0.9rem; font-size: var(--fs-sm); line-height: 1.4;
  border: 1px solid color-mix(in srgb, var(--win) 30%, var(--border)); border-radius: var(--radius);
  background: color-mix(in srgb, var(--win) 4%, transparent); }
/* #1194 ask 6 — both were --text-muted: 2.63:1 in the light theme. --text-mid is 8.57 / 11.86. */
.tp-upd-from { color: var(--text-mid); }
.tp-upd-stamp { font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap; } /* #527 */
.tp-upd-why { margin-top: 0.15rem; color: var(--text-mid); font-size: var(--fs-sm); }

.was-top {
  display: inline-block; margin-left: 0.35rem; font-size: var(--fs-2xs); font-weight: 800;
  letter-spacing: 0.02em; padding: 0.1rem 0.35rem; border-radius: 4px; cursor: help;
  background: rgba(148,163,184,0.18); color: #94a3b8; border: 1px solid rgba(148,163,184,0.4);
}
:root[data-theme="light"] .was-top { color: #596b86; } /* #1509 — see fr-overlay comment above */
.reranked-dot {
  display: inline-block; margin-left: 0.3rem; font-size: var(--fs-2xs); font-weight: 800;
  letter-spacing: 0.03em; padding: 0.08rem 0.3rem; border-radius: 4px; cursor: help;
  background: rgba(96,165,250,0.14); color: #60a5fa; border: 1px solid rgba(96,165,250,0.4);
}
:root[data-theme="light"] .reranked-dot { color: #0767de; } /* #1509 — see fr-overlay comment above */

/* Race-level value opportunity alert */
.race-alert {
  max-width: 1380px; margin: 0 auto 1rem; padding: 0.75rem 1rem; border-radius: 10px;
  background: rgba(var(--ew-rgb), 0.10); border: 1px solid rgba(var(--ew-rgb), 0.45);
}
.race-alert .ra-head { font-weight: 800; color: var(--ew); font-size: var(--fs-h4); margin-bottom: 0.35rem; }
.race-alert .ra-sub { font-weight: 500; color: var(--text-dim); font-size: var(--fs-xs); letter-spacing: 0.02em; }
.race-alert .ra-item { font-size: var(--fs-body); color: var(--text); padding: 0.15rem 0; }
.race-alert .ra-vs { color: var(--text-dim); font-size: var(--fs-sm); }
.race-alert .ra-ratio { color: var(--ew); font-weight: 800; margin-left: 0.25rem; }
.race-alert .ra-tag { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.02em; padding: 0.1rem 0.35rem; border-radius: 4px; margin-right: 0.35rem; white-space: nowrap; }
.race-alert .rt-value   { background: rgba(var(--win-rgb), 0.16); color: var(--win); border: 1px solid rgba(var(--win-rgb), 0.45); }
.race-alert .rt-overlay { background: rgba(var(--ew-rgb), 0.16); color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.45); }

/* Alerts page */
.alert-card {
  display: block; text-decoration: none; color: var(--text);
  border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.9rem;
  margin-bottom: 0.6rem; background: var(--bg-card);
}
.alert-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.alert-card.ac-value   { border-color: var(--win); }
.alert-card.ac-overlay { border-color: var(--ew); }
.alert-card.ac-trap    { border-color: #ef5350; }
.alert-card.ac-rerank  { border-color: #60a5fa; }
.alert-card.ac-placevalue { border-color: var(--green); }
.ac-badge.ab-placevalue { background: rgba(var(--green-rgb), 0.16); color: var(--green); border: 1px solid rgba(var(--green-rgb), 0.45); }
/* Prediction updated (e.g. the basic flow refreshed from live data) — old → new pick + reason. */
.alert-card.ac-update { border-color: var(--accent); }
.ac-badge.ab-update { background: rgba(var(--accent-rgb), 0.16); color: var(--accent-light); border: 1px solid rgba(var(--accent-rgb), 0.45); }
/* Scratching — top-priority, critical red with a slightly heavier accent. */
.alert-card.ac-scratching { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 6%, var(--bg)); }
.ac-badge.ab-scratching { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent); }
:root[data-theme="light"] .ac-badge.ab-scratching { color: #bf1c1b; } /* #1509 — see fr-overlay comment above */
/* Blackbook — a followed runner is racing (amber ⭐), same card language as the rest. */
.alert-card.ac-blackbook { border-color: #fbbf24; }
.ac-badge.ab-blackbook { background: rgba(251,191,36,0.16); color: #fbbf24; border: 1px solid rgba(251,191,36,0.45); }
:root[data-theme="light"] .ac-badge.ab-blackbook { color: #8e6703; } /* #1509 — see fr-overlay comment above */
/* Guide / key page */
.guide-intro { color: var(--text); line-height: 1.6; margin: 0 0 0.5rem; }
.guide-foot { color: var(--muted, #94a3b8); font-size: var(--fs-body); line-height: 1.6; margin-top: 1.5rem; }
/* #1509 — normal-weight 15.3px still needs 4.5:1 (it's below the 18px/24px large-text floor).
   Scoped to light theme only; see fr-overlay comment above. */
:root[data-theme="light"] .guide-foot { color: #5c6f8a; }
.guide-h {
  font-size: var(--fs-h3); font-weight: 800; margin: 1.6rem 0 0.7rem; padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--border, rgba(var(--overlay-rgb), 0.1));
}
.guide-list { display: grid; gap: 0.6rem; }
.guide-item {
  display: grid; grid-template-columns: 190px 1fr; gap: 0.9rem; align-items: start;
  padding: 0.6rem 0.7rem; border: 1px solid var(--border, rgba(var(--overlay-rgb), 0.08));
  border-radius: 8px; background: var(--bg-card, rgba(var(--overlay-rgb), 0.02));
}
.guide-badge { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.guide-text { line-height: 1.5; font-size: var(--fs-h4); }
.guide-text strong:first-child { display: block; margin-bottom: 0.15rem; }
.guide-sub { margin: 0.4rem 0 0.4rem; padding-left: 1.1rem; display: grid; gap: 0.25rem; }
.guide-sub li { line-height: 1.45; }
.guide-sub li strong { color: var(--accent-light, #a78bfa); }
.guide-field {
  font-family: var(--mono, monospace); font-size: var(--fs-sm); font-weight: 700;
  padding: 0.12rem 0.45rem; border-radius: 5px; white-space: nowrap;
  background: rgba(var(--accent-rgb), 0.14); color: var(--accent-light, #a78bfa);
  border: 1px solid rgba(var(--accent-rgb), 0.35);
}
@media (max-width: 640px) {
  .guide-item { grid-template-columns: 1fr; gap: 0.4rem; }
}
/* Collapsible alert groups */
.alert-group { margin-bottom: 1rem; }
.alert-group-header {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  background: var(--bg-hover, rgba(var(--overlay-rgb), 0.04)); border: 1px solid var(--border, rgba(var(--overlay-rgb), 0.1));
  border-radius: 8px; padding: 0.55rem 0.8rem; cursor: pointer; color: inherit;
  font: inherit; text-align: left;
}
.alert-group-header:hover { border-color: var(--accent); }
.alert-group .agh-chevron { transition: transform 0.15s ease; font-size: var(--fs-xs); opacity: 0.8; }
.alert-group.collapsed .agh-chevron { transform: rotate(-90deg); }
.alert-group .agh-label { font-weight: 800; }
.alert-group .agh-count {
  background: rgba(var(--overlay-rgb), 0.1); border-radius: 999px; padding: 0.05rem 0.5rem;
  font-size: var(--fs-xs); font-weight: 800;
}
.alert-group .agh-hint { color: var(--muted, #94a3b8); font-size: var(--fs-xs); margin-left: auto; }
.alert-group-body { display: grid; gap: 0.5rem; margin-top: 0.5rem; }
.alert-group.collapsed .alert-group-body { display: none; }
.ac-badge.ab-value { background: rgba(var(--win-rgb), 0.16); color: var(--win); border: 1px solid rgba(var(--win-rgb), 0.45); }
.ac-badge.ab-rerank { background: rgba(96,165,250,0.16); color: #60a5fa; border: 1px solid rgba(96,165,250,0.45); }
:root[data-theme="light"] .ac-badge.ab-rerank { color: #0767de; } /* #1509 — see fr-overlay comment above */
.ac-from { color: var(--muted, #94a3b8); }
.alert-card .ac-line { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.5rem; }
/* Width-flexible: cards fill their container, so they reflow gracefully in the
   narrow sticky rail as well as at full page width. */
.alert-card { min-width: 0; }
.alert-card .ac-horse, .alert-card .ac-detail { overflow-wrap: anywhere; }
.ac-badge { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.02em; padding: 0.12rem 0.4rem; border-radius: 5px; white-space: nowrap; }
.ac-badge.ab-overlay { background: rgba(var(--ew-rgb), 0.16); color: var(--ew); border: 1px solid rgba(var(--ew-rgb), 0.45); }
:root[data-theme="light"] .ac-badge.ab-overlay { color: #bf1c1b; } /* #1509 — see fr-overlay comment above */
.ac-badge.ab-trap    { background: rgba(239,83,80,0.16); color: #ef5350; border: 1px solid rgba(239,83,80,0.45); }
:root[data-theme="light"] .ac-badge.ab-trap { color: #ce1613; } /* #1509 — see fr-overlay comment above */
.ac-horse { font-weight: 700; font-size: var(--fs-h4); }
.ac-detail { font-size: var(--fs-body); margin-top: 0.2rem; }
.ac-detail .ac-vs { color: var(--text-dim); font-size: var(--fs-sm); }
.alert-card .ac-race { font-size: var(--fs-sm); color: var(--text-dim); margin-top: 0.15rem; }
.fair-row {
  font-family: var(--mono);
  font-size: var(--fs-h4);
  font-weight: 600;
  color: var(--accent-light);
  margin-top: 0.1rem;
}
.fair-row.fair-value { color: var(--ew); font-weight: 700; }
.value-tag {
  font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.03em;
  background: rgba(var(--ew-rgb), 0.16); color: var(--ew);
  border: 1px solid rgba(var(--ew-rgb), 0.4); border-radius: 4px;
  padding: 0.05rem 0.3rem; margin-left: 0.25rem; vertical-align: middle;
}
:root[data-theme="light"] .value-tag { color: #bf1c1b; } /* #1509 — see fr-overlay comment above */

.runner-sort-bar {
  display: flex; align-items: center; gap: 0.5rem;
  margin: 0.25rem 0 0.6rem;
}
.runner-sort-bar .rs-label { font-size: var(--fs-sm); color: var(--text-muted); font-weight: 600; }
.runner-sort-select {
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
  border-radius: 8px; padding: 0.4rem 0.6rem; font-size: var(--fs-body); cursor: pointer;
}
.runner-sort-select:focus { outline: none; border-color: var(--accent); }
/* #237 — see .horse-search-input above for why: the plain :focus rule gave keyboard and mouse
   focus the identical (thin border-colour) treatment, with no ring for a Tab press to show. */
.runner-sort-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.prediction-section {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.2rem;
  padding-top: 0.4rem;
  border-top: 1px solid var(--border);
}

.badge {
  display: inline-flex; align-items: center; height: var(--chip-h); box-sizing: border-box; /* #234 aligned */
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 0 0.5rem;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.badge.sel-win { background: var(--win); color: var(--on-status, var(--bg-card)); }
.badge.sel-ew { background: var(--ew); color: var(--on-status, var(--bg-card)); }
.badge.sel-place { background: var(--green); color: var(--on-status, var(--bg-card)); }
.badge.sel-avoid { background: var(--avoid); color: var(--text); }
.badge.sel-scratched { background: var(--danger); color: #fff; }
.badge.badge-neutral { background: rgba(157,161,184,0.14); color: var(--text-muted); border: 1px solid var(--border); }
/* #860 The three-state set. `unknown` must NOT read as either good or bad — it is deliberately the
   dimmest and the only dashed one, so an unmeasured value looks unmeasured at a glance rather than
   borrowing the authority of a measured one. */
.badge.badge-ok { background: rgba(var(--result-win-rgb, 74,222,128), 0.16); color: var(--result-win, var(--green)); border: 1px solid rgba(var(--result-win-rgb, 74,222,128), 0.45); }
.badge.badge-warn { background: rgba(255,196,61,0.16); color: #ffcf5c; border: 1px solid rgba(255,196,61,0.45); }
.badge.badge-unknown { background: transparent; color: var(--text-dim, var(--text-muted)); border: 1px dashed var(--border); }
/* #286 "Top rated" — the VO Speed top tier, surfaced as a clear gold badge on the runner card,
   shown left-aligned just above the Add-to-Betslip actions. */
.badge.badge-toprated { background: linear-gradient(180deg, rgba(255,196,61,0.22), rgba(255,196,61,0.12)); color: #ffcf5c; border: 1px solid rgba(255,196,61,0.5); font-weight: 700; }
.runner-above-actions { display: flex; justify-content: flex-start; margin: 0.35rem 0 0.15rem; }

.runner-score-num {
  font-family: var(--mono);
  font-size: var(--fs-h1);
  font-weight: 700;
  color: var(--text);
}

.score-max {
  font-size: inherit;
  color: inherit;
  font-weight: inherit;
  margin-left: 0.2rem;
}

.runner-place-prob {
  font-family: var(--mono);
  font-size: var(--fs-h1);
  font-weight: 700;
  color: var(--text);
  margin-top: 0.15rem;
}

.runner-pfai-rank {
  margin-top: 0.3rem;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent-light);
  background: rgba(var(--accent-rgb), 0.12);
  border-radius: 6px;
  padding: 0.15rem 0.4rem;
  display: inline-block;
}
.runner-pfai-rank strong { font-size: var(--fs-body); font-weight: 800; }
/* #304/#305 Runner-card odds sources — a price BUTTON that opens the comparison modal */
.odds-src-btn { display: inline-flex; align-items: center; gap: 0.35rem; margin-top: 0.35rem; padding: 0.2rem 0.55rem;
  border: 0.5px solid var(--border-strong, var(--border)); border-radius: 8px; background: transparent; color: var(--text);
  cursor: pointer; font: inherit; }
.odds-src-btn:hover { background: rgba(var(--overlay-rgb), 0.06); border-color: var(--accent-light); }
.odds-src-btn .osb-price { font-size: var(--fs-body); font-weight: 800; font-variant-numeric: tabular-nums; }
.odds-src-btn .osb-count { font-size: var(--fs-2xs); color: var(--text-muted); background: rgba(var(--overlay-rgb), 0.08); border-radius: 999px; padding: 0.05rem 0.4rem; }
.odds-src-btn .osb-caret { font-style: normal; color: var(--text-muted); font-size: 0.9em; }
/* #306 the MAIN odds block doubles as the compare-sources trigger — a light box border + caret
   signal it's tappable. Keeps the right-aligned Win/Place layout. */
.tab-odds.odds-btn { position: relative; flex-direction: row; align-items: center; gap: 0.5rem;
  padding: 0.25rem 0.5rem; border: 0.5px solid var(--border-strong, var(--border)); border-radius: 8px;
  background: transparent; cursor: pointer; transition: border-color 0.12s, background 0.12s; }
.tab-odds.odds-btn > .tab-odds-row { flex-direction: column; align-items: flex-end; gap: 0; }
.tab-odds.odds-btn:hover, .tab-odds.odds-btn:focus-visible { background: rgba(var(--overlay-rgb), 0.06); border-color: var(--accent-light); outline: none; }
.tab-odds.odds-btn:focus-visible { box-shadow: 0 0 0 2px var(--accent-light); }
.tab-odds.odds-btn .odds-btn-caret { color: var(--text-muted); font-size: 0.95em; line-height: 1; }
.odds-sources-modal .os-asof { font-size: var(--fs-2xs); color: var(--text-muted); margin-top: 0.7rem; line-height: 1.35; }
.os-value { font-size: var(--fs-2xs); font-weight: 700; padding: 0.1rem 0.4rem; border-radius: 999px; background: rgba(74,222,128,0.16); color: var(--green); margin-left: 0.35rem; }
/* modal body (rendered inside the shared responsive UI.modal)

   The per-bookmaker rows (.os-row / .os-w / .os-p / .os-bet / .os-hero) are no longer rendered by
   `oddsSourcesModalBody` — the body is now two figures and a shelf of operator logos. Their rules
   are kept below because `.os-row` is a generic enough name that another surface may still reach
   for it; nothing in the modal emits them.

   The figures: the mean of the fixed books next to the exchange price. Laid out as equal columns
   so neither reads as the headline — they are two different markets, not a winner and a loser. */
.odds-sources-modal .os-figs { display: flex; gap: 0.5rem; margin-bottom: 0.6rem; }
.odds-sources-modal .os-fig { flex: 1 1 0; min-width: 0; padding: 0.55rem 0.7rem; border-radius: 10px; background: rgba(var(--overlay-rgb), 0.06); }
.odds-sources-modal .os-fig-lab { display: block; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.odds-sources-modal .os-fig-val { display: block; font-size: var(--fs-h3); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text); line-height: 1.15; }
.odds-sources-modal .os-fig-sub { display: block; font-size: var(--fs-2xs); color: var(--text-muted); }
@media (max-width: 480px) { .odds-sources-modal .os-figs { flex-wrap: wrap; } .odds-sources-modal .os-fig { flex: 1 1 45%; } }

/* The operator shelf — every configured bookmaker, logo only. FOUR ACROSS, so the roster sits as
   two rows of four as it grows to eight; a fixed column count (not auto-fill) is what guarantees
   that shape rather than leaving it to the container width. Two across on a phone.
   NO BORDER AND NO TILE: a bordered box reads as a card that ought to contain something, and these
   contain nothing but the mark. The logo alone reads as a button, which is what it is. */
.odds-sources-modal .os-books { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.9rem 0.6rem; margin: 0.9rem 0 0.4rem; }
.odds-sources-modal .os-book { display: flex; align-items: center; justify-content: center; min-height: 2.6rem; padding: 0.15rem; border: 0; background: none; text-decoration: none; opacity: 0.9; transition: opacity 0.12s, transform 0.12s; }
.odds-sources-modal .os-book:hover, .odds-sources-modal .os-book:focus-visible { opacity: 1; transform: translateY(-1px); outline: none; }
/* Keyboard focus still has to be visible with the border gone — the ring replaces it. */
.odds-sources-modal .os-book:focus-visible { box-shadow: 0 0 0 2px var(--accent); border-radius: 6px; }
/* An operator we cannot deeplink to is not a door; it is still on the shelf, just quieter. */
.odds-sources-modal .os-book-flat { opacity: 0.55; }
/* This shelf IS the navigation, so the mark carries it — roughly double the inline card badge. */
.odds-sources-modal .os-book .bk-logo { height: 2.2rem; max-width: 100%; }
.odds-sources-modal .os-book .bk-badge { font-size: var(--fs-sm); }
@media (max-width: 420px) { .odds-sources-modal .os-books { grid-template-columns: repeat(2, 1fr); } }

.odds-sources-modal .os-rows { display: flex; flex-direction: column; gap: 0.15rem; }
.odds-sources-modal .os-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.4rem; border-radius: 8px; }
/* #990 The best row must be identifiable WITHOUT colour (§6.4 / #169 — colour always paired with a
   text state). The green wash stays as the fast cue for readers who can see it; the UI.badge "BEST"
   in the row carries the meaning in words, and the inset rule gives a shape cue that survives both
   greyscale and a colour-vision deficiency. Marking is by PRICE, so a tie marks every row holding it. */
.odds-sources-modal .os-row.os-best { background: rgba(74,222,128,0.10); box-shadow: inset 3px 0 0 var(--green, #4ade80); }
.odds-sources-modal .os-row.os-best .os-w { text-decoration: underline; text-underline-offset: 3px; }
/* The badge sits between the price and the place sub-line; it must never push the row to wrap on a
   narrow phone, which is why it shrinks rather than the price column (AC: ≤640px, no truncation). */
.odds-sources-modal .os-row .badge { flex: 0 1 auto; min-width: 0; font-size: var(--fs-2xs); letter-spacing: 0.04em; }
@media (max-width: 640px) { .odds-sources-modal .os-row { gap: 0.4rem; } }
.odds-sources-modal .os-w { font-weight: 800; font-variant-numeric: tabular-nums; min-width: 3.4rem; }
.odds-sources-modal .os-p { color: var(--text-muted); font-size: var(--fs-xs); flex: 1; }
.odds-sources-modal .os-bet { font-size: var(--fs-xs); font-weight: 700; padding: 0.25rem 0.7rem; border-radius: 6px; background: var(--accent, #7c6cff); color: #fff; text-decoration: none; display: inline-flex; align-items: center; gap: 0.2rem; }
.odds-sources-modal .os-ext { font-style: normal; }
.odds-sources-modal .os-modal-value { font-size: var(--fs-sm); color: var(--green); margin-bottom: 0.5rem; }
/* #331 best-price hero — line-shopping headline */
.odds-sources-modal .os-hero { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0.6rem; margin-bottom: 0.5rem; border-radius: 10px; background: rgba(var(--overlay-rgb), 0.06); }
.odds-sources-modal .os-hero-lab { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.odds-sources-modal .os-hero-price { font-size: var(--fs-h3); font-weight: 800; font-variant-numeric: tabular-nums; }
.odds-sources-modal .os-hero-src { margin-left: auto; }
.odds-sources-modal .os-rg { font-size: var(--fs-2xs); color: var(--text-dim); margin-top: 0.7rem; text-align: center; }
.bk-badge { font-size: var(--fs-2xs); font-weight: 700; padding: 0.1rem 0.45rem; border-radius: 999px; white-space: nowrap; display: inline-flex; align-items: center; gap: 0.3rem; }
/* #644 bookmaker LOGO: the operator's official mark from /img/books/{slug}.svg. When the asset is
   present the text label is hidden; when it's absent (img onerror removes it) the coloured text
   badge shows — so a missing logo degrades cleanly, and we never embed a trademarked mark in code. */
.bk-logo { height: 1.05rem; width: auto; max-width: 3.2rem; display: block; border-radius: 3px; }
.bk-badge:has(img.bk-logo) { background: transparent !important; padding: 0; }
.bk-badge:has(img.bk-logo) .bk-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.bk-badge.bk-accent { background: rgba(96,165,250,0.16); color: #60a5fa; }
.bk-badge.bk-danger { background: rgba(239,68,68,0.16); color: var(--danger); }
.bk-badge.bk-success { background: rgba(74,222,128,0.16); color: #4ade80; }
.bk-badge.bk-warning { background: rgba(245,158,11,0.16); color: #f59e0b; }
.bk-badge.bk-neutral { background: rgba(148,163,184,0.16); color: var(--text-muted); }
.runner-pfai-rank.vo-rank-prov { color: var(--text-muted); background: rgba(148,163,184,0.12); } /* #297 provisional (pre-prediction) */
.runner-pfai-rank .vo-rank-prov-mark { font-size: var(--fs-2xs); opacity: 0.7; margin-left: 0.15rem; text-transform: uppercase; }
/* #298 Predicted finishing order panel */
.predicted-order .po-podium { display: flex; flex-direction: column; gap: 0.3rem; }
.predicted-order .po-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.5rem; border-radius: 8px; background: rgba(var(--overlay-rgb), 0.03); }
.predicted-order .po-1 { background: rgba(var(--accent-rgb), 0.14); }
/* #528 bring the finish-order rows onto the standard type scale (was inheriting the large body size) */
.predicted-order .po-pos { font-size: var(--fs-sm); font-weight: 800; min-width: 2.2rem; color: var(--accent-light); }
.predicted-order .po-num { font-size: var(--fs-xs); font-weight: 800; background: rgba(0,0,0,0.35); color: #fff; border-radius: 5px; padding: 0.05rem 0.35rem; }
.predicted-order .po-name { font-size: var(--fs-sm); font-weight: 700; flex: 1; }
.predicted-order .po-pcts { display: flex; gap: 0.6rem; font-variant-numeric: tabular-nums; }
.predicted-order .po-pct { font-size: var(--fs-sm); color: var(--text-muted); }
.predicted-order .po-note { font-size: var(--fs-2xs); color: var(--text-dim); margin: 0.4rem 0 0.6rem; }
.predicted-order .po-sub { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin-bottom: 0.3rem; }
.predicted-order /* #419 the podium demotion flag + the stated history empty — tokens only (TC-07) */
.po-flag { font-size: var(--fs-2xs); font-weight: 800; letter-spacing: 0.04em; color: var(--avoid);
  border: 1px solid rgba(var(--avoid-rgb), 0.5); background: var(--avoid-bg); border-radius: 4px;
  padding: 0 0.3rem; margin-left: 0.4rem; flex-shrink: 0; cursor: help; }
.rs-empty { color: var(--text-muted); font-size: var(--fs-sm); padding: 0.5rem 0; font-style: italic; }

.po-value-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0.5rem; flex-wrap: wrap; }
.predicted-order .po-vs { font-size: var(--fs-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.predicted-order .po-empty, .po-prompt { font-size: var(--fs-sm); color: var(--text-dim); padding: 0.4rem 0.1rem; }
.po-prompt { padding: 0.6rem 0.75rem; background: rgba(var(--overlay-rgb), 0.03); border-radius: 8px; margin: 0.5rem 0; }

.confidence-tag {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.conf-high { color: var(--green); }
.conf-medium { color: var(--win); }
.conf-low { color: var(--danger); }

.score-bar-wrap {
  width: 100%;
  background: var(--bg);
  border-radius: 3px;
  height: 3px;
  overflow: hidden;
}

.score-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  width: 0;
  transition: width 0.7s ease-out;
}

.score-breakdown {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 0.35rem;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
}

/* Results Section */

.results-section {
  max-width: 1380px;
  margin: 1rem auto;
  padding: 0 1rem;
}

#checkResultsBtn {
  background: transparent;
  color: var(--ew);
  border: 1px solid rgba(var(--ew-rgb), 0.3);
  padding: 0.45rem 1rem;
  border-radius: 8px;
  cursor: pointer;
  font-size: var(--fs-body);
  font-weight: 600;
  font-family: var(--font);
  transition: all 0.15s;
}

#checkResultsBtn:hover { border-color: var(--ew); background: rgba(var(--ew-rgb), 0.06); }
#checkResultsBtn:disabled { opacity: 0.4; cursor: not-allowed; }

.results-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  margin-top: 0.6rem;
}

.rc-header {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 0.5rem;
}

.rc-pending {
  color: var(--text-muted);
  font-size: var(--fs-body);
}

.rc-positions {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-bottom: 0.65rem;
  padding-bottom: 0.65rem;
  border-bottom: 1px solid var(--border);
}

.rc-pos {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.rc-pos-num {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-dim);
  min-width: 32px;
}

.rc-pos-horse {
  font-weight: 600;
  font-size: var(--fs-h4);
  color: var(--text);
}

.rc-pos-margin {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.rc-score-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.3rem;
}

.rc-label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  min-width: 80px;
}

.rc-value {
  font-weight: 600;
  font-size: var(--fs-body);
  color: var(--text);
}

.rc-badge {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.rc-correct { background: var(--result-win); color: #08130d; }   /* #NEW result palette */
.rc-placed { background: var(--result-place); color: #2a1c05; }
.rc-miss { background: var(--result-miss); color: #fff; }

.rc-accuracy {
  margin-top: 0.65rem;
  padding-top: 0.65rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.rc-acc-label {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  width: 100%;
  margin-bottom: 0.1rem;
}

.rc-acc-stat {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-mid);
  background: var(--bg);
  border: 1px solid var(--border);
  padding: 0.15rem 0.45rem;
  border-radius: 5px;
}

/* Stream Panel */

.stream-panel {
  display: none;
  max-width: 1380px;
  margin: 0.75rem auto;
  padding: 0 1rem;
}

.stream-panel pre {
  background: var(--bg);
  padding: 0.75rem;
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--ew);
  white-space: pre-wrap;
  max-height: 350px;
  overflow-y: auto;
}

#streamBtn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--accent-light);
  padding: 0.35rem 0.85rem;
  border-radius: 8px;
  cursor: pointer;
  font-size: var(--fs-sm);
  margin-bottom: 0.6rem;
}

#streamBtn:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.08); }

/* Usage Footer */

.usage-footer {
  display: none;
  max-width: 1380px;
  margin: 0.75rem auto 2rem;
  padding: 0.4rem 1rem;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-align: center;
  border-top: 1px solid var(--border);
}

/* ── Staking Panel ─────────────────────────────────────────────────────────── */

.staking-panel {
  max-width: 1380px;
  margin: 1.5rem auto;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.25rem 1.5rem;
}

.staking-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 1rem;
}

.staking-title {
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--text);
  margin: 0;
}

.staking-controls {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  font-size: var(--fs-body);
  color: var(--text-dim);
}

.staking-controls input,
.staking-controls select {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  padding: 0.25rem 0.5rem;
  font-size: var(--fs-body);
}

/* Secondary = a NEUTRAL surface button, clearly subordinate to the accent-filled
   primary (they used to be identical). Hierarchy: primary (accent) > secondary
   (neutral) > outline (ghost) > danger. */
.btn-secondary {
  background: var(--bg-light);
  color: var(--text);
  border: 1px solid var(--border-light);
  padding: 0.35rem 0.9rem;
  font-weight: 600;
  font-size: var(--fs-body);
  border-radius: 7px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn-secondary:hover { background: #464d61; border-color: var(--accent); color: #fff; }
.btn-secondary:active { background: #2f3546; }

/* Universal disabled — clearly non-interactive across every button variant (was only
   defined for a few). Greyed + not-allowed, with hover feedback suppressed. */
.btn-primary:disabled, .btn-secondary:disabled, .btn-outline:disabled, .btn-danger:disabled, .hero-cta:disabled,
.btn-primary[disabled], .btn-secondary[disabled], .btn-outline[disabled], .btn-danger[disabled] {
  opacity: 0.45; cursor: not-allowed; box-shadow: none; filter: grayscale(0.3);
}
.btn-primary:disabled:hover, .hero-cta:disabled:hover { background: var(--accent-btn); }
.btn-secondary:disabled:hover { background: var(--bg-light); border-color: var(--border-light); color: var(--text); }
.btn-outline:disabled:hover { background: transparent; border-color: var(--border); color: var(--accent-light); }
.btn-danger:disabled:hover { background: transparent; }

.staking-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}

.staking-table th {
  text-align: left;
  padding: 0.4rem 0.6rem;
  color: var(--text-dim);
  font-weight: 500;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.staking-table td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border-light, rgba(var(--overlay-rgb), 0.05));
  color: var(--text);
}

.staking-table tr:last-child td { border-bottom: none; }

.st-name { font-weight: 600; }
.st-toppick { color: var(--accent); }
.st-dim { color: var(--text-dim); }
.st-odds { font-weight: 500; }
.tp-badge {
  font-size: var(--fs-xs);
  font-weight: 700;
  background: var(--accent-btn);
  color: var(--on-accent);
  padding: 0.1rem 0.35rem;
  border-radius: 3px;
  vertical-align: middle;
  margin-left: 0.3rem;
}

.edge-pos { color: #4caf7d; font-weight: 600; }
.edge-neg { color: var(--text-dim); }

.st-stake { font-family: var(--mono); font-size: var(--fs-body); }

.bet-btn {
  font-size: var(--fs-xs);
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  margin-left: 0.4rem;
}
.bet-btn:hover { background: var(--accent-btn); color: var(--on-accent); }
.bet-tracked { border-color: #4caf7d; color: #4caf7d; }

.odds-cell {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  flex-wrap: wrap;
}
.odds-input {
  width: 56px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
  padding: 0.2rem 0.35rem;
  font-size: var(--fs-body);
  font-family: var(--mono);
}
.odds-edge { flex-basis: 100%; font-size: var(--fs-xs); margin-top: 0.1rem; }

.stake-cell {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  flex-wrap: wrap;
}
.stake-dollar { color: var(--text-dim); font-size: var(--fs-body); }
.stake-input {
  width: 56px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
  padding: 0.2rem 0.35rem;
  font-size: var(--fs-body);
  font-family: var(--mono);
}
.stake-suggest {
  flex-basis: 100%;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 0.1rem;
}
.stake-diff {
  font-size: var(--fs-xs);
  color: #e0a23c;
  font-family: var(--mono);
  margin-left: 0.25rem;
}

.staking-note {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin: 0.75rem 0 0;
}

.cal-banner {
  font-size: var(--fs-sm);
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  margin-bottom: 0.75rem;
  border: 1px solid transparent;
}
.cal-good    { background: rgba(76,175,125,0.1);  border-color: rgba(76,175,125,0.3);  color: #4caf7d; }
.cal-weak    { background: rgba(229,115,115,0.1); border-color: rgba(229,115,115,0.3); color: #e57373; }
.cal-neutral { background: rgba(var(--overlay-rgb), 0.04); border-color: var(--border); color: var(--text-dim); }

.prob-raw { color: var(--text-dim); font-size: var(--fs-sm); }
.prob-cal { color: var(--text); font-weight: 600; }
.cal-method { display: block; margin-top: 0.3rem; font-size: var(--fs-xs); color: var(--text-dim); }

.staking-loading, .staking-empty, .staking-err {
  padding: 1rem;
  color: var(--text-dim);
  font-size: var(--fs-body);
}
.staking-err { color: #e57373; }

/* ── Staking panel — mobile (stacked cards) ──────────────────────────────────
   The 6-column editable table is unusable on a phone, so below 760px each
   runner becomes a card: thead is hidden and every cell becomes a labelled
   row (label left via data-label, value/input right). */
@media (max-width: 760px) {
  .staking-panel { padding: 1rem; margin: 1rem auto; }

  /* Controls: each on its own full-width line with the field pushed right. */
  .staking-controls { flex-direction: column; align-items: stretch; gap: 0.5rem; width: 100%; }
  .staking-controls label {
    display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; width: 100%;
  }
  .staking-controls input,
  .staking-controls select { flex: 0 0 auto; min-width: 0; }
  .staking-controls #bankrollInput { width: 110px !important; }
  .staking-controls select { max-width: 60%; }
  #calcStakingBtn { width: 100%; padding: 0.55rem; }

  .staking-table thead { display: none; }
  .staking-table, .staking-table tbody, .staking-table tr, .staking-table td { display: block; width: 100%; }
  .staking-table tr {
    border: 1px solid var(--border); border-radius: 9px; padding: 0.3rem 0.7rem;
    margin-bottom: 0.7rem; background: var(--bg);
  }
  .staking-table td {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    padding: 0.45rem 0; border-bottom: 1px solid var(--border-light, rgba(var(--overlay-rgb), 0.06));
  }
  .staking-table tr td:last-child { border-bottom: none; }
  /* Label from data-label sits on the left of each row. */
  .staking-table td::before {
    content: attr(data-label); color: var(--text-dim); font-size: var(--fs-sm);
    font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; flex: 0 0 auto;
  }
  /* Horse name = card title: full-width, larger, no label. */
  .staking-table td.st-name {
    justify-content: flex-start; font-size: var(--fs-h3); padding-top: 0.55rem;
    border-bottom: 1px solid var(--border);
  }
  .staking-table td.st-name::before { content: none; }

  /* Bigger tap targets for the editable fields on the right. */
  .odds-cell, .stake-cell { justify-content: flex-end; flex-wrap: wrap; }
  .odds-input, .stake-input { width: 72px; padding: 0.4rem 0.45rem; font-size: var(--fs-h4); }
  .bet-btn { padding: 0.4rem 0.8rem; font-size: var(--fs-sm); margin-left: 0.5rem; }
  .odds-edge, .stake-suggest { flex-basis: 100%; text-align: right; }
}

/* ── P&L Tracker ───────────────────────────────────────────────────────────── */

.pnl-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}

.pnl-table th {
  text-align: left;
  padding: 0.35rem 0.6rem;
  color: var(--text-dim);
  font-weight: 500;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.pnl-table td {
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--border-light, rgba(var(--overlay-rgb), 0.05));
  color: var(--text);
}

.pnl-table tr:last-child td { border-bottom: none; }
.pnl-table tr:hover td { background: rgba(var(--overlay-rgb), 0.02); }

.pnl-horse { font-weight: 600; }
.pnl-mono  { font-family: var(--mono); font-weight: 600; }

.pnl-pos     { color: #4caf7d; }
.pnl-neg     { color: #e57373; }
.pnl-pending { color: var(--text-dim); }

.sc-bad { color: #e57373; }

/* Admin sub-navigation */
.admin-subnav {
  display: flex;
  gap: 0.4rem;
  margin: 0 0 1.5rem;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  align-items: center;
}
/* Group heading in the grouped admin sub-nav (#50) */
.admin-subnav-group {
  padding: 0 0.5rem 0 0.9rem;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.admin-subnav-group:first-child { padding-left: 0; }
.admin-subnav a {
  padding: 0.55rem 1rem;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.admin-subnav a:hover { color: var(--text); }
.admin-subnav a.active { color: var(--accent); border-bottom-color: var(--accent); }

/* Accuracy-over-time chart */
.acc-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 0.5rem;
  font-size: var(--fs-xs);
  color: var(--text-dim);
}
.acc-key { display: inline-flex; align-items: center; gap: 0.35rem; }
.ak-line { display: inline-block; width: 16px; height: 3px; border-radius: 2px; }
.ak-win     { background: var(--accent); }
.ak-place   { background: var(--ew); }
.ak-rollwin { background: var(--win); }

/* Bankroll page */
.bankroll-setup {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.1rem 1.25rem;
}
.bankroll-field { display: flex; flex-direction: column; gap: 0.3rem; font-size: var(--fs-sm); color: var(--text-dim); }
.bankroll-input-wrap { font-size: var(--fs-h3); color: var(--text); font-weight: 600; }
.bankroll-input-wrap input {
  width: 120px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); padding: 0.35rem 0.5rem; font-size: var(--fs-h3); font-weight: 600;
}
.bankroll-current { margin-left: auto; text-align: right; }
.bankroll-current-row { display: flex; align-items: baseline; gap: 0.6rem; justify-content: flex-end; }
.bc-label { font-size: var(--fs-sm); color: var(--text-dim); }
.bc-value { font-size: var(--fs-h1); font-weight: 700; font-family: var(--mono); }
.bankroll-current-sub { font-size: var(--fs-xs); color: var(--text-dim); margin-top: 0.2rem; }

/* Responsive */

/* Stats Page */

.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.5rem;
}

.stat-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
  text-align: center;
}

.sc-value {
  font-family: var(--mono);
  font-size: var(--fs-h2);
  font-weight: 700;
  color: var(--text);
}

.sc-value.sc-good { color: var(--green); }

.sc-label {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 0.2rem;
}

.sc-sub {
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  margin-top: 0.15rem;
  opacity: 0.7;
}

/* A fixed N-column stat grid (UI.statCards opts.cols). The default .stat-cards is
   auto-fill/minmax(140px,1fr), which is right when the number of cards varies — but with exactly
   three it packs 4-up on a wide screen, and on a phone it hits the repeat(2,1fr) override further
   down and breaks 2 + 1 with an orphan. A set of cards that means something AS A SET (one per
   model) has to keep its shape. */
.stat-cards--3 { grid-template-columns: repeat(3, 1fr); }

/* `sub` is the figure's scope (its n); `note` is what the thing IS. This one wraps, unlike the
   other two, because it is a sentence — hence the line-height and the left alignment. */
.sc-note {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 0.45rem;
  line-height: 1.4;
  text-align: left;
}

/* Not-yet-published: a card that holds its place in the row without pretending to carry a figure.
   Dashed and unfilled, so it reads as deliberately empty rather than as data that failed to load —
   a solid card with a blank value is indistinguishable from a fetch that half-worked. */
.stat-card--soon {
  border-style: dashed;
  background: transparent;
}
.stat-card--soon .sc-label { opacity: 0.75; }
.sc-soon {
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-dim);
}

/* Top-pick outcome breakdown bar */
.outcome-bar {
  display: flex; height: 26px; width: 100%;
  border-radius: 7px; overflow: hidden; margin-top: 0.5rem;
  background: var(--bg-card); border: 1px solid var(--border);
}
.outcome-bar .ob-seg {
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 700; color: #0b0d14; white-space: nowrap;
}
.ob-won { background: var(--result-win); }
.ob-placed { background: var(--result-place); }
.ob-unplaced { background: #5a3138; color: #e8c4c4; }
.outcome-legend {
  display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 0.5rem;
  font-size: var(--fs-sm); color: var(--text-muted);
}
.outcome-legend .ob-key {
  display: inline-block; width: 0.8rem; height: 0.8rem; border-radius: 3px;
  vertical-align: middle; margin-right: 0.3rem;
}

.factor-bars {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-top: 0.5rem;
}

.factor-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.fr-label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  min-width: 60px;
  text-transform: capitalize;
}

.fr-bar-wrap {
  flex: 1;
  height: 6px;
  background: var(--bg);
  border-radius: 3px;
  overflow: hidden;
}

.fr-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
}

.fr-value {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-mid);
  min-width: 28px;
  text-align: right;
}

.weight-grid {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.weight-item {
  display: grid;
  grid-template-columns: 110px 1fr 42px auto;
  align-items: center;
  gap: 0.5rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.4rem 0.6rem;
}

.wi-delta {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  white-space: nowrap;
  padding: 0.08rem 0.4rem;
  border-radius: 999px;
  cursor: help;
}
.wi-delta .wd-arrow { opacity: 0.6; margin: 0 0.1rem; }
.wi-delta.wd-up   { color: var(--green); background: rgba(var(--green-rgb), 0.12); }
.wi-delta.wd-down { color: var(--danger); background: rgba(var(--danger-rgb), 0.12); }

.stat-updated {
  font-size: var(--fs-sm);
  color: var(--text-muted, #94a3b8);
  margin: 0.1rem 0 0.7rem;
}

.wi-label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-transform: capitalize;
}

.wi-value {
  font-family: var(--mono);
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--text-mid);
}

.wi-value.wi-high { color: var(--green); }
.wi-value.wi-low { color: var(--danger); }

.wi-bar-wrap {
  height: 5px;
  background: var(--bg-hover);
  border-radius: 3px;
  overflow: hidden;
}
.wi-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  transition: width 0.4s ease;
}

.section-explainer {
  font-size: var(--fs-body);
  color: var(--text-mid);
  line-height: 1.7;
  margin-bottom: 0.75rem;
  margin-top: 0.25rem;
  padding: 0.75rem 1rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0 var(--radius) var(--radius) 0;
}
/* #530 account tier options — the four tiers + what each unlocks, current one highlighted */
.tier-options { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.5rem 0 0.75rem; }
.tier-opt { border: 1px solid var(--border); border-radius: var(--radius); padding: 0.6rem 0.8rem; background: var(--bg); }
.tier-opt-current { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.06); }
.tier-opt .to-head { display: flex; align-items: center; gap: 0.5rem; justify-content: space-between; }
.tier-opt .to-name { font-weight: 700; color: var(--text); }
.tier-opt .to-badge { font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--accent); background: rgba(var(--accent-rgb), 0.12); border-radius: 999px; padding: 0.05rem 0.5rem; white-space: nowrap; }
/* an "Included" marker on a tier below the user's current one — muted, not the accent 'Your tier' pill. */
.tier-opt .to-badge-inc { color: var(--text-muted); background: transparent; border: 1px solid var(--border); }
.tier-opt .to-perks { font-size: var(--fs-xs); color: var(--text-mid); margin-top: 0.2rem; line-height: 1.5; }
.tier-opt .to-perks-none { color: var(--text-dim); font-style: italic; }
.link-accent { color: var(--accent); font-weight: 600; text-decoration: none; }
.link-accent:hover { text-decoration: underline; }
/* #522 predictions "Load more" — reveals the older backlog past the first page */
.preds-load-more { display: block; margin: 1rem auto 0.5rem; }
/* A lead-in heading on its own line is OPT-IN via .se-lead (add it to a strong that is
   genuinely a mini-heading). Previously any strong:first-child blocked — which misfired on
   copy that merely opened with an inline bold word (CSS :first-child ignores the leading
   text node), forcing an unwanted line break mid-sentence. */
.section-explainer > strong.se-lead {
  display: block;
  color: var(--text);
  font-weight: 600;
  font-size: var(--fs-body);
  margin-bottom: 0.35rem;
}
.section-explainer strong {
  display: inline;
  color: var(--text);
  font-weight: 600;
}

/* Predictions Page */

.pred-group {
  margin-bottom: 1.5rem;
}

.pred-meet {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 0.6rem;
}

.pm-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.85rem;
  background: rgba(var(--accent-rgb), 0.04);
  border-bottom: 1px solid var(--border);
}

.pm-course {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.pm-date {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.pm-count {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-left: auto;
}

.pred-meet .pred-list {
  gap: 0;
}

.pred-meet .pred-card {
  border: none;
  border-radius: 0;
  border-top: 1px solid var(--border);
  background: transparent;
}

.pred-meet .pred-card:first-child { border-top: none; }
.pred-meet .pred-card:hover { background: var(--bg-hover); }

.pc-race-name {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pred-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pred-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  transition: border-color 0.12s, background 0.12s;
}

.pred-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.pred-card.pred-scratched { border-color: var(--danger); }

/* At-a-glance prediction outcome */
.pred-card.pred-won { border-color: var(--result-win); }
.pred-card.pred-place { border-color: #c9971f; }
.pred-card.pred-miss { border-color: var(--result-miss); }

.pc-outcome {
  margin-left: auto;
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  white-space: nowrap;
  letter-spacing: 0.3px;
}
.po-win { background: rgba(46, 158, 91, 0.18); color: #46c884; }
.po-place { background: rgba(201, 151, 31, 0.18); color: #e0b34a; }
.po-miss { background: rgba(var(--result-miss-rgb), 0.16); color: var(--result-miss); }
.po-pending { background: var(--bg-hover); color: var(--text-dim); }

.pc-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.pc-course {
  font-weight: 700;
  font-size: var(--fs-body);
  color: var(--text);
}

.pc-race {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--accent-light);
}

.pc-time {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-left: auto;
}

.pc-name {
  font-size: var(--fs-sm);
  color: var(--text-mid);
}

.pc-pick {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.15rem;
}

.pc-pick-label {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.pc-pick-value {
  font-weight: 700;
  font-size: var(--fs-body);
  color: var(--win);
}

.pc-ew {
  font-size: var(--fs-xs);
  color: var(--ew);
  margin-left: 0.5rem;
}

/* #442 Win / Each-way split tiles + type filter (token-only colour). */
.pc-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem; margin-top: 0.35rem; }
.pc-tile { border: 1px solid var(--border); border-radius: var(--radius); padding: 0.4rem 0.55rem; background: var(--bg-hover); min-width: 0; }
.pc-tile-win { border-color: var(--win); }
.pc-tile-ew { border-color: var(--ew); }
.pc-tile-k { display: block; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.pc-tile-h { display: block; font-weight: 700; font-size: var(--fs-body); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Flex, not inline-block, so the placing badge can be pushed to the tile's RIGHT edge while the
   outcome text and price stay together on the left. `gap` replaces the old inline whitespace, which
   flex would otherwise collapse. */
.pc-tile-o { display: flex; align-items: center; gap: 0.3rem; font-size: var(--fs-2xs); font-weight: 700; margin-top: 0.1rem; }
.pc-tile-o.o-win { color: var(--result-win); }
.pc-tile-o.o-place { color: var(--result-place); }
.pc-tile-o.o-miss { color: var(--result-miss); } /* #NEW result palette */
/* #462 Betfair SP shown beside a settled pick — subtle, tabular. */
.pc-tile-bsp { font-size: var(--fs-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; margin-left: 0.15rem; }
/* Inside the tile the flex `gap` already spaces it — the inline margin would double up. */
.pc-tile-o .pc-tile-bsp { margin-left: 0; }
.pc-tile-o.o-pend { color: var(--result-upcoming); }
/* Type filter: Win → win tile full width; Each-way → ew tile full width (+ hide no-ew races). */
[data-pred-type="win"] .pc-tile-ew, [data-pred-type="eachway"] .pc-tile-win { display: none; }
[data-pred-type="win"] .pc-tiles, [data-pred-type="eachway"] .pc-tiles { grid-template-columns: 1fr; }
[data-pred-type="eachway"] .pc-no-ew { display: none; }
@media (max-width: 640px) { .pc-tiles { grid-template-columns: 1fr; } }

/* #NNN Prime (AI) / Core (basic) pick display in the predictions history. The page-level toggle
   (AI viewers only) picks which flow every card shows; each card labels its active flow. */
.pc-flow { margin-top: 0.35rem; }
.pc-flow-label { display: inline-block; font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 0.05rem 0.45rem; border-radius: 999px; }
.pc-flow-prime > .pc-flow-label { color: var(--accent); background: rgba(var(--accent-rgb), 0.14); }
.pc-flow-core > .pc-flow-label { color: var(--text-muted); background: var(--bg-hover); border: 1px solid var(--border); }
.pc-flow > .pc-tiles { margin-top: 0.25rem; }
.pc-flow-toggle { margin: 0 0 0.75rem; }

.pc-summary {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-style: italic;
}

.pc-meta {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

/* Archive View */

.archive-view {
  max-width: 1380px;
  margin: 0 auto;
  padding: 0.75rem 1rem 2rem;
}

.archive-section {
  margin-bottom: 1.5rem;
}

.archive-heading {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0.45rem 0;
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
  cursor: pointer;
  margin-bottom: 0.5rem;
  text-align: left;
}
.archive-heading:hover { color: var(--text); }
.ah-caret { transition: transform 0.15s; font-size: var(--fs-xs); color: var(--accent-light); }
.archive-section.collapsed .ah-caret { transform: rotate(-90deg); }
.ah-label { flex: 1; }
.ah-count { font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--text-dim); }
.archive-section.collapsed .archive-section-body { display: none; }

/* Inner cards mirror the predictions page (.pred-meet / .pm-header / .pred-card). */
.archive-day {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 0.6rem;
  overflow: hidden;
}

.archive-day-header {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
  padding: 0.55rem 0.85rem;
  background: rgba(var(--accent-rgb), 0.04);
  border-bottom: 1px solid var(--border);
}

.archive-meets {
  display: flex;
  flex-direction: column;
}

.archive-meet-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.85rem;
  border-top: 1px solid var(--border);
  transition: background 0.12s;
}

.archive-meet-link:first-child { border-top: none; }
.archive-meet-link:hover { background: var(--bg-hover); }

.aml-course {
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.aml-info {
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.aml-pred {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--accent-light);
  background: rgba(var(--accent-rgb), 0.1);
  padding: 0.08rem 0.35rem;
  border-radius: 3px;
  margin-left: auto;
}

@media (max-width: 800px) {
  .sidebar { display: none; }
  .race-layout { display: block; }
  .nav-sidebar { display: none; }
  .page-layout { display: block; }
}

@media (max-width: 640px) {
  body { font-size: 14px; }

  header { padding: 1rem 0.75rem; }
  header h1 { font-size: var(--fs-h2); }

  .page-main { padding: 0.5rem var(--page-pad-x-m) 1.5rem; } /* #235 wider usable card area */
  .landing { padding: 0 0.85rem 1.5rem; }
  .landing-hero { padding: 1.25rem 0 1rem; }
  .landing-hero h2 { font-size: var(--fs-h3); }
  .landing-cards { grid-template-columns: 1fr; }
  .wm-meets { gap: 0.25rem; }

  .race-nav-pill { flex-wrap: wrap; gap: 0.3rem; }
  .rnp-meta { display: none; }

  .meet-nav { padding: 0.4rem 0.5rem; }

  .race-hero { padding: 1rem 0.75rem 0.4rem; }
  .hero-name { font-size: var(--fs-h3); }

  .race-actions { padding: 0 0.75rem; flex-wrap: wrap; }

  .top-pick { margin: 0.75rem 0.5rem; padding: 0.85rem; }
  .top-pick-name { font-size: var(--fs-h2); }

  .runners-container { padding: 0.4rem 0.5rem 1.5rem; }

  /* #135 mobile card (guided by the design mock): silk | compact info | win/place %
     boxes in ONE row, then the pace bar + actions full-width below. Rank goes inline. */
  .runner-row { padding: 0.45rem 0.3rem; gap: 0.35rem; }
  .runner-compact {
    grid-template-columns: 50px 1fr auto; /* #141 wide enough for the silk plate + its outside gap (no overlap into the name) */
    gap: 0.4rem;
    align-items: start;
  }
  /* #235 trim double padding inside the runner-card expander on phones */
  .stat-grid { padding: 0.55rem 0.6rem; gap: 0.5rem 0.6rem; }
  .rc-comments { padding: 0.55rem 0.6rem; }
  /* #135/#141 main info: one label:value line per row at all widths (base rule);
     mobile only trims the value size + top gap a touch. */
  .runner-main-info { margin-top: 0.15rem; }
  .runner-main-info .info-value { font-size: var(--fs-sm); }
  .runner-form { font-size: var(--fs-xs); }
  .pace-bar-label { min-width: 62px; }

  /* Tight, left-aligned silk + number so the name/connections get the width back */
  .runner-num-silk { align-items: stretch; gap: 0.2rem; }
  .silk-img { width: 42px; padding: 2px; margin: 0.25rem 0.2rem; }
  .runner-num {
    font-size: var(--fs-h3); min-width: 0; width: 100%; padding: 0.2rem 0;
  }

  /* Tables: smaller text + tighter cells so they wrap to width instead of overflowing */
  .bt-table { font-size: var(--fs-sm); }
  .bt-table th, .bt-table td { padding: 0.4rem 0.45rem; }

  /* Tab bars wrap / shrink so 4–5 tabs fit a phone width */
  .admin-subnav { flex-wrap: wrap; }
  .admin-subnav a { padding: 0.5rem 0.7rem; font-size: var(--fs-sm); }
  .stat-tab { padding: 0.5rem 0.6rem; font-size: var(--fs-sm); }
  .stat-cards { grid-template-columns: repeat(2, 1fr); }
  /* One per row on a phone — NOT the 2-up above. Three model columns squeezed into a 375px screen
     leave ~180px each, which is enough for the percentage and nothing else: the model name wraps
     and the `note` sentence becomes a ladder of two-word lines. Stacked, each model keeps its name,
     figure, count and description on one readable card. The row is a comparison, and a comparison
     the reader has to reassemble from fragments is not one. */
  .stat-cards--3 { grid-template-columns: 1fr; }
  .stat-cards--3 .sc-note { text-align: center; }

  /* #135 win/place % boxes stay in the right column (not a full-width second row) */
  .runner-right {
    grid-column: auto;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.3rem;
    border-top: none;
    padding-top: 0;
    margin-top: 0;
  }
  .runner-right .odds-section { font-size: var(--fs-xs); text-align: right; color: var(--text-muted); }

  .prediction-section {
    align-items: flex-end;   /* #140 keep win / confidence / win% / place% right-aligned on mobile */
    border-top: none;
    padding-top: 0;
  }

  .runner-info-grid, .runner-stats-grid { gap: 0.2rem; }
  .runner-breeding-line { display: none; }
  .runner-owner { display: none; }
}

@media (max-width: 380px) {
  .race-actions select { font-size: var(--fs-sm); padding: 0.4rem 0.5rem; }
  #predictBtn, #recalcBtn { font-size: var(--fs-sm); padding: 0.45rem 0.85rem; }
  .hero-meta { gap: 0.25rem; }
  .hero-chip { font-size: var(--fs-xs); padding: 0.15rem 0.4rem; }
}

/* ── Performance scorecard (UI.performanceScorecard) ─────────────────────────
   High-level, graphical, responsive summary of tip performance (win + each-way).
   Rings scale via SVG viewBox; the gauge row and type grid wrap on small screens. */
.perf-card {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px;
  padding: 1.25rem 1.35rem; max-width: 100%;
}
.perf-card .ps-head { margin-bottom: 0.5rem; }
.perf-card .ps-title { font-size: var(--fs-h3); font-weight: 700; }
.perf-card .ps-sub { font-size: var(--fs-body); color: var(--text-muted); margin-top: 0.15rem; display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.ps-trend { font-weight: 700; }
.ps-trend.up { color: var(--ew); } .ps-trend.down { color: var(--danger); }

.ps-gauges { display: flex; flex-wrap: wrap; gap: 1rem 1.5rem; justify-content: center; padding: 0.75rem 0 0.25rem; }
.ps-gauge { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; flex: 1 1 120px; min-width: 96px; max-width: 160px; }
.ps-ring { width: 100%; height: auto; max-width: 150px; }
.ps-ring .ps-track { fill: none; stroke: var(--border); stroke-width: 12; }
.ps-ring .ps-arc { fill: none; stroke-width: 12; stroke-linecap: round; transform: rotate(-90deg); transform-origin: 60px 60px; transition: stroke-dashoffset 0.9s cubic-bezier(0.4,0,0.2,1); }
.ps-ring .ps-val { fill: var(--text); font-size: 30px; font-weight: 800; text-anchor: middle; dominant-baseline: central; }
.ps-ring .ps-pct { font-size: 15px; font-weight: 700; fill: var(--text-muted); }
.ps-glabel { font-size: var(--fs-sm); color: var(--text-mid); font-weight: 600; text-align: center; }

.ps-types { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.6rem 1rem; margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid var(--border); }
.ps-type-head { display: flex; justify-content: space-between; font-size: var(--fs-body); font-weight: 600; margin-bottom: 0.35rem; }
.ps-type-n { color: var(--text-muted); font-weight: 500; }
.ps-tb { display: flex; align-items: center; gap: 0.4rem; font-size: var(--fs-xs); margin-bottom: 0.2rem; }
.ps-tb-k { color: var(--text-muted); width: 26px; flex: none; }
.ps-tb-track { flex: 1; height: 6px; background: rgba(var(--overlay-rgb), 0.06); border-radius: 4px; overflow: hidden; }
.ps-tb-fill { height: 100%; border-radius: 4px; transition: width 0.9s cubic-bezier(0.4,0,0.2,1); }
.ps-tb-fill.win { background: var(--win); } .ps-tb-fill.plc { background: var(--accent-light); } .ps-tb-fill.ew { background: var(--ew); }
.ps-tb-v { width: 34px; flex: none; text-align: right; color: var(--text-mid); font-variant-numeric: tabular-nums; }

/* ── Tulloch's Tips — modern "elite pick" card (ties into the dark theme) ─────── */
.tulloch {
  position: relative;
  border-radius: var(--radius);
  padding: 0.8rem 1rem 0.85rem;
  color: var(--tulloch-ink);
  /* Bold, premium background — a deep violet gradient with an accent glow so the
     card leaps off the dark page while staying in the purple accent family. */
  background:
    radial-gradient(120% 130% at 100% 0%, rgba(var(--tulloch-rgb), 0.28), rgba(var(--tulloch-rgb), 0) 55%),
    linear-gradient(150deg, var(--tulloch-g1) 0%, var(--tulloch-g2) 42%, var(--tulloch-g3) 100%);
  border: 1px solid var(--tulloch-edge);
  box-shadow: 0 10px 34px rgba(var(--tulloch-rgb), 0.28), 0 1px 0 rgba(var(--overlay-rgb), 0.08) inset;
  overflow: hidden;
}
/* signature gradient accent bar along the top edge */
.tulloch::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--tulloch), var(--ew) 70%, var(--win));
}
/* On the Prime Vectors page the hero already carries the brand, so the tips card sits FLAT on the
   page — drop the violet gradient panel, its glow shadow and the accent bar (same flattening as the
   rail). Scoped to .pv-flat so the component keeps its premium look everywhere else it's reused.
   The theme-qualified selectors must out-specify the base AND the light-theme `.tulloch` panel rule
   (which paints the Twilight-Indigo "ground" that was showing as blue bars BETWEEN the tiles). */
.pv-flat .tulloch,
:root[data-theme="light"] .pv-flat .tulloch,
:root[data-theme="dark"] .pv-flat .tulloch {
  background: transparent; border: 0; box-shadow: none; padding: 0; overflow: visible;
}
.pv-flat .tulloch::before { display: none; }
/* pv-flat renders on the light page ground, so the NON-tile text (heading, sub, track record,
   links, empty state) must use dark theme ink — the custard --tulloch-ink is for text ON the dark
   maroon tiles (which keep it). Fixes the low-contrast Prime Vectors headings on light. */
.pv-flat .tt-title { color: var(--text); }
.pv-flat .tt-sub { color: var(--text-mid); border-bottom-color: var(--border); }
.pv-flat .tt-more { color: var(--accent); }
.pv-flat .tt-empty { color: var(--text-muted); }
.pv-flat .tt-record { border-top-color: var(--border); }
.pv-flat .tt-rec-h, .pv-flat .tt-rec-k, .pv-flat .tt-rec-sub { color: var(--text-muted); }
.pv-flat .tt-rec-v { color: var(--text); }
.pv-flat .tt-rec-cell + .tt-rec-cell { border-left-color: var(--border); }
.tt-head { display: flex; align-items: center; gap: 0.55rem; }
.tt-crest {
  color: var(--tulloch-ink); font-size: 2.2rem; line-height: 0.8;
  filter: drop-shadow(0 0 12px rgba(var(--tulloch-rgb), 0.6));
}
.tulloch--body .tt-crest { font-size: 2.1rem; }
.tt-title {
  /* Prime Vectors — a precise, technical wordmark (replaced the Great Vibes cursive, which
     read as elegant/script and clashed with the algorithmic identity). Themeable display face,
     tight uppercase tracking. */
  font-family: var(--font-head);
  font-weight: 700; letter-spacing: 0.06em; font-size: 1.25rem; line-height: 1.2;
  text-transform: uppercase; color: var(--tulloch-ink);
}
.tt-sub {
  font-size: var(--fs-sm); color: rgba(var(--tulloch-ink-rgb), 0.72); margin: 0.05rem 0 0.5rem;
  padding-bottom: 0.45rem; border-bottom: 1px solid rgba(var(--tulloch-ink-rgb), 0.14);
}
.tt-list { display: flex; flex-direction: column; gap: 0.4rem; }
/* #592 locked (non-AI tier) upsell state for the Prime Vectors selections */
.tt-locked { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center;
  padding: 1.2rem 1rem; border: 1px dashed var(--border); border-radius: 10px; background: rgba(var(--overlay-rgb), 0.04); }
.tt-locked-ico { font-size: 1.6rem; line-height: 1; }
.tt-locked-txt { font-size: var(--fs-sm); color: var(--text-mid); max-width: 440px; }
.tt-pick {
  display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center;
  gap: 0.7rem; text-decoration: none; color: inherit;
  padding: 0.45rem 0.55rem; border-radius: var(--tulloch-card-radius);
  border: 1px solid rgba(var(--tulloch-ink-rgb), 0.1); background: var(--tulloch-card);
  transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.tt-pick:hover { background: var(--tulloch-card-hover); border-color: var(--tulloch); transform: translateY(-1px); }
.tt-main { min-width: 0; }
/* #354 the runner name must fit — WRAP to two lines instead of truncating with an ellipsis,
   and size it down so it fits the compact card. */
.tt-horse {
  font-weight: 700; font-size: var(--fs-body); color: var(--tulloch-ink); line-height: 1.2;
  overflow-wrap: anywhere; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.tt-meta { font-size: var(--fs-xs); color: rgba(var(--tulloch-ink-rgb), 0.66); margin-top: 0.1rem; }
.tt-odds { font-variant-numeric: tabular-nums; white-space: nowrap; }
.tt-flags { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.3rem; }
.tt-flag {
  font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.01em;
  color: var(--tulloch-ink); background: rgba(var(--tulloch-ink-rgb), 0.12);
  border: 1px solid rgba(var(--tulloch-ink-rgb), 0.3); border-radius: 999px; padding: 0.05rem 0.4rem;
}
.tt-status { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; flex: none; text-align: right; }
.tt-empty { font-size: var(--fs-body); color: rgba(var(--tulloch-ink-rgb), 0.72); padding: 0.4rem 0; }
/* Track record — one compact row: a small caption + win/place cells side by side (#258) */
.tt-record {
  margin-top: 0.55rem; padding-top: 0.5rem; border-top: 1px solid rgba(var(--tulloch-ink-rgb), 0.14);
}
.tt-rec-h { display: block; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.12em; color: rgba(var(--tulloch-ink-rgb), 0.55); margin-bottom: 0.3rem; }
.tt-rec-cells { display: flex; gap: 0.9rem; }
.tt-rec-cell { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.05rem; }
.tt-rec-cell + .tt-rec-cell { padding-left: 0.9rem; border-left: 1px solid rgba(var(--tulloch-ink-rgb), 0.12); }
.tt-rec-k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.1em; color: rgba(var(--tulloch-ink-rgb), 0.6); }
.tt-rec-v { font-size: var(--fs-body); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.tt-rec-roi { font-size: var(--fs-xs); font-weight: 700; }
.tt-rec-cell.pos .tt-rec-v { color: var(--tulloch-pos); } .tt-rec-cell.neg .tt-rec-v { color: var(--tulloch-neg); }
.tt-rec-sub { font-size: var(--fs-2xs); color: rgba(var(--tulloch-ink-rgb), 0.55); }
.tt-more {
  display: inline-block; margin-top: 0.55rem; font-size: var(--fs-sm); font-weight: 700;
  color: var(--tulloch-ink); text-decoration: none; letter-spacing: 0.01em;
}
.tt-more:hover { color: var(--tulloch-ink); text-decoration: underline; }
/* Body variant: larger, two-up grid of picks when there's room */
/* #354 On desktop the card had no max-width, so on a wide page (tulloch.html / Models tab)
   the premium gradient card stretched edge-to-edge and the auto-fit pick grid spread each
   row's runner/status far apart. Contain + centre the card, cap the grid at two up, and
   align rows to the top so uneven picks line up. */
/* #354 compact body variant: contained + centred, smaller header + tighter pick grid. */
.tulloch--body { padding: 0.85rem 1rem 0.95rem; max-width: 720px; margin-inline: auto; }
.tulloch--body .tt-title { font-size: 1.5rem; }
.tulloch--body .tt-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  align-items: start; gap: 0.5rem;
}
/* smaller pick cards in the body grid — trim the padding + conviction block */
.tulloch--body .tt-pick { padding: 0.4rem 0.5rem; gap: 0.55rem; }
.tulloch--body .tt-horse { font-size: var(--fs-sm); }
/* #354 halve the badges so the runner name gets the width — smaller status frees the pick
   row's third column; tiny flags + scope badge take almost no space. */
.tulloch--body .tt-status { font-size: 0.55rem; letter-spacing: 0.02em; }
.tulloch--body .tt-flags { gap: 0.2rem; margin-top: 0.25rem; }
.tulloch--body .tt-flag { font-size: 0.55rem; padding: 0.02rem 0.3rem; letter-spacing: 0; }
.tulloch--body .tt-scope { font-size: 0.55rem; padding: 0.02rem 0.32rem; } /* smaller "Horses only" badge */
/* one lone tip shouldn't become a full-width bar — keep it to a card's width, left-aligned */
.tulloch--body .tt-list:has(> .tt-pick:only-child) { grid-template-columns: minmax(260px, 380px); justify-content: start; }
@media (max-width: 640px) { .tulloch--body .tt-list, .tulloch--body .tt-list:has(> .tt-pick:only-child) { grid-template-columns: 1fr; } }

/* Tulloch's Tips — admin config sliders */
.tt-cfg { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.2rem; margin-top: 0.6rem; }
.tt-cfg-group { border: 1px solid var(--border); border-radius: 8px; padding: 0.8rem 0.9rem; }
.tt-cfg-title { font-weight: 700; font-size: var(--fs-body); margin-bottom: 0.6rem; color: var(--text); }
.tt-cfg-row { display: grid; grid-template-columns: 9.5rem 1fr 2.4rem; align-items: center; gap: 0.5rem; margin-bottom: 0.55rem; }
.tt-cfg-k { font-size: var(--fs-body); color: var(--text); }
.tt-cfg-v { font-size: var(--fs-body); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; color: var(--accent-light); }
/* #801 The hint now carries a worked EXAMPLE for every slider, so it is a short paragraph rather
   than a phrase. Full-width, wrapping, with room to breathe — this is documentation at the point of
   use, and an admin who has to guess what a lever does is how a lever ends up mis-set. */
.tt-cfg-hint { grid-column: 1 / -1; font-size: var(--fs-xs); color: var(--text-muted);
  margin-top: 0.1rem; line-height: 1.45; max-width: 62ch; }
.tt-cfg-hint b { color: var(--text-mid); font-weight: 700; }
/* Narrow screens: the 9.5rem label column squeezes the slider to nothing, so stack instead. */
@media (max-width: 640px) {
  .tt-cfg-row { grid-template-columns: 1fr 2.4rem; }
  .tt-cfg-row input[type=range] { grid-column: 1 / -1; }
}
.tt-cfg-row input[type=range] { width: 100%; accent-color: var(--accent); }

/* Model profit/loss card */
.pl-rows { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 0.75rem; margin-top: 0.25rem; }
.pl-row { border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem; background: rgba(var(--overlay-rgb), 0.02); }
.pl-row-head { display: flex; justify-content: space-between; align-items: center; font-size: var(--fs-h4); font-weight: 700; margin-bottom: 0.55rem; }
.pl-row-n { color: var(--text-muted); font-weight: 500; font-size: var(--fs-sm); }
.pl-figs { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
.pl-fig { background: rgba(var(--overlay-rgb), 0.03); border-radius: 8px; padding: 0.5rem 0.6rem; }
.pl-fig-k { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 0.15rem; }
.pl-fig-v { font-size: var(--fs-h3); font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.pl-fig-roi { font-size: var(--fs-sm); font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 0.1rem; }
.pl-fig-v.pos, .pl-fig-roi.pos, .pl-overall.pos strong { color: var(--green); }
.pl-fig-v.neg, .pl-fig-roi.neg, .pl-overall.neg strong { color: var(--danger); }
.pl-overall { margin-top: 0.85rem; padding-top: 0.75rem; border-top: 1px solid var(--border); font-size: var(--fs-h4); }
.pl-note { margin-top: 0.6rem; font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.5; }
/* #444 BSP coverage chip beside the "at Betfair SP" label — subtle, tabular so the % lines up. */
.pl-cov { font-variant-numeric: tabular-nums; opacity: 0.9; }
/* #463 VectorOdds logo mark in the universal footer brand block, beside the wordmark. */
.mk-foot-brand { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; }
.mk-foot-logo { height: 32px; width: auto; max-width: 100%; flex: 0 0 auto; }

@media (max-width: 480px) {
  .perf-card { padding: 1rem; }
  .ps-gauges { gap: 0.75rem; }
  .ps-gauge { flex-basis: 90px; min-width: 84px; }
  .ps-ring .ps-val { font-size: 26px; }
  /* Weight bars: shrink the label so the bar keeps room on a phone. */
  .weight-item { grid-template-columns: 78px 1fr auto auto; gap: 0.35rem; padding: 0.4rem 0.5rem; }
  .pl-note { font-size: var(--fs-xs); }
}
  
/* Betslip Button */  
.betslip-btn { background: none; border: none; cursor: pointer; padding: 4px; color: #a0aec0; transition: all 0.2s ease; display: inline-flex; align-items: center; justify-content: center; }  
.betslip-btn:hover { color: #e2e8f0; transform: scale(1.1); }  
.betslip-btn.active { color: #fbbf24; }  
.betslip-btn.active .star-icon { fill: #fbbf24; }  
.star-icon { width: 18px; height: 18px; } 

/* ── Account / betslip components (#64/#65) ─────────────────────────────────
   Single-source classes for the My Account page + betslip rows, using theme
   variables (replacing the hardcoded-hex inline styles). account.js should
   adopt these when the #20 fix lands. Mobile: rows stack at 640px. */
/* Account / settings layout (#113/#142) — reusable page layout (UI.accountLayout):
   a horizontal tab bar across the top with full-width panels below (the same tab
   convention as the component showcase), so the content gets the whole page width
   instead of losing a fixed left rail. Same nav structure at every width. */
.acct-layout { display: block; }
/* Desktop: tabs wrap, no scroll (overflow-x:auto would force overflow-y:auto and
   show a stray vertical scrollbar). Mobile below switches to a horizontal scroll. */
.acct-nav { display: flex; flex-flow: row wrap; gap: 0.2rem; margin-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
.acct-nav-link { display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 1rem; color: var(--text-muted); text-decoration: none; font-size: var(--fs-h4); font-weight: 600; white-space: nowrap; border: none; border-bottom: 2px solid transparent; border-radius: 6px 6px 0 0; margin-bottom: -1px; transition: background .15s, color .15s, border-color .15s; }
.acct-nav-link:hover { background: var(--bg-hover, rgba(var(--overlay-rgb), 0.04)); color: var(--text, #fff); }
.acct-nav-link.active { color: var(--green); border-bottom-color: var(--green); background: transparent; }
.acct-nav-ico { font-size: var(--fs-h3); line-height: 1; }
.acct-main { min-width: 0; }
/* #739 The account sections sit ON the page like Best Bets — no white card, no rounded corners,
   no border. Each section now has its own masthead (UI.pageHero); wrapping that in a panel put a
   card inside a card, and the rounded box also broke the site's square geometry. Content inside
   (rows, callouts) keeps its own surfaces. */
.acct-panel { padding: 0; background: transparent; border-radius: 0; border: 0; }
.acct-panel[hidden] { display: none; }
@media (max-width: 720px) {
  .acct-nav { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; margin-bottom: 1rem; -webkit-overflow-scrolling: touch; }
  .acct-nav-link { padding: 0.5rem 0.75rem; font-size: var(--fs-h4); }
  .acct-panel { padding: 1.1rem; }
}
.betslip-item { display: flex; justify-content: space-between; align-items: center; padding: 0.75rem; background: var(--bg-hover); border-radius: 6px; border: 1px solid var(--border); margin-bottom: 0.5rem; }
.betslip-item .bs-details { display: flex; align-items: center; gap: 0.75rem; color: var(--text, #fff); text-decoration: none; flex: 1; min-width: 0; }
.betslip-item .bs-course { font-weight: 500; }
.betslip-item .bs-time { font-size: var(--fs-body); color: var(--text-muted); }
.btn-remove-betslip { background: transparent; border: 1px solid var(--danger, #ef4444); color: var(--danger, #ef4444); border-radius: 4px; padding: 0.4rem 0.75rem; cursor: pointer; margin-left: 1rem; min-height: 40px; }
.bs-section-label { margin-top: 1.5rem; margin-bottom: 0.75rem; font-size: var(--fs-h4); font-weight: bold; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; }
/* Touch target: the star is 18px visual but needs a ~40px hit area on mobile. */
.betslip-btn { min-width: 32px; min-height: 32px; }
@media (max-width: 640px) {
  .betslip-item { flex-wrap: wrap; gap: 0.5rem; }
  .btn-remove-betslip { margin-left: 0; width: 100%; }
  .betslip-btn { min-width: 40px; min-height: 40px; }
}

/* Segmented betslip control (#73 AC) */
/* The Win/E-W/Place pills ARE component-library badges (.badge.sel-* / .badge-neutral).
   The control is just a flex row; .bs-seg-btn is a button reset + tap target that
   defers ALL appearance to the badge classes. */
.betslip-runner-control {
  display: inline-flex;
  gap: 0.3rem;
  margin: 4px 0;
}
.bs-seg-btn {
  border: none;
  cursor: pointer;
  padding: 4px 9px;          /* a touch larger than a static badge, for tapability */
  min-height: 26px;
  transition: filter 0.15s ease;
}
.bs-seg-btn.badge-neutral:hover { color: var(--text); border-color: var(--text-dim); }
.bs-seg-btn.sel-win:hover, .bs-seg-btn.sel-ew:hover, .bs-seg-btn.sel-place:hover { filter: brightness(1.1); }


/* ── Virtual Betslip v2 (#74 rich cards + #75 sort/group) ──────────────────── */
.bs-loading { color: var(--text-muted); padding: 1rem 0; }
.bs-controls { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--border); }
.bs-ctl-group { display: inline-flex; align-items: center; gap: 0.4rem; }
.bs-ctl-lbl { font-size: var(--fs-sm); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
.bs-ctl-btn { background: var(--bg-hover); border: 1px solid var(--border); color: var(--text-muted); border-radius: 4px; padding: 0.3rem 0.6rem; font-size: var(--fs-sm); cursor: pointer; min-height: 32px; }
.bs-ctl-btn.active { background: var(--accent-btn); color: var(--on-accent); border-color: var(--accent-btn); }
.bs-ctl-toggle { font-size: var(--fs-body); color: var(--text-muted); cursor: pointer; gap: 0.3rem; }
.bs-group-head { margin: 1.25rem 0 0.6rem; font-size: var(--fs-body); font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; display: flex; align-items: center; gap: 0.5rem; }
.bs-card { background: var(--bg-hover); border: 1px solid var(--border); border-radius: 6px; margin-bottom: 0.6rem; overflow: hidden; }
.bs-card-head { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; background: rgba(0,0,0,0.15); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.bs-race-link { display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; color: var(--text, #fff); min-width: 0; }
.bs-track { font-weight: 600; font-size: var(--fs-h4); }
/* Share button (#49) — copy / native-share affordance. */
.share-btn { display: inline-flex; align-items: center; gap: 0.35rem; background: var(--bg-hover, rgba(var(--overlay-rgb), 0.05)); border: 1px solid var(--border); color: var(--text, #e8e8e8); border-radius: 999px; padding: 0.35rem 0.8rem; font-size: var(--fs-body); cursor: pointer; }
.share-btn:hover { border-color: var(--green); color: var(--green); }
/* Top bar: back-link on the left, share button on the right (in line), on every race type. */
.race-topbar { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; }
.race-topbar .share-btn { padding: 0.28rem 0.7rem; font-size: var(--fs-sm); }
/* Blackbook card links (#blackbook-links): bound-runner name -> profile, and a
   "racing today" link -> the race page. */
.bb-name-link { font-weight: 600; color: var(--text, #fff); text-decoration: none; }
.bb-name-link:hover { text-decoration: underline; color: var(--green); }
.bb-race-link { display: inline-block; margin-top: 0.3rem; font-size: var(--fs-body); color: var(--green); text-decoration: none; }
.bb-race-link:hover { text-decoration: underline; }
.bs-jump { font-size: var(--fs-body); color: var(--text-muted); white-space: nowrap; }
.bs-r-time { font-weight: 700; color: var(--accent); }
.bs-card-body { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.6rem 0.75rem; }
/* Betslip runner rows now render via the shared UI.runnerCard (see account.js),
   so the old bespoke silk/number/row styles were removed. Only the connection
   label bits remain betslip-specific (used in the runnerCard `connections` slot). */
.bs-conn { white-space: nowrap; }
.bs-lbl { color: var(--text-muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.4px; }

/* ── Bottom-sheet drawer (#99) ──────────────────────────────────────────────── */
.drawer-overlay { position: fixed; inset: 0; z-index: 500; background: rgba(0,0,0,0.5);
  display: flex; align-items: flex-end; justify-content: center; opacity: 0; transition: opacity 0.2s ease; }
.drawer-overlay.drawer-open { opacity: 1; }
.drawer-panel { width: 100%; max-width: 640px; max-height: 88vh; background: var(--bg, var(--bg-hover));
  border: 1px solid var(--border); border-bottom: 0; border-radius: 14px 14px 0 0;
  display: flex; flex-direction: column; transform: translateY(100%); transition: transform 0.25s ease; }
.drawer-overlay.drawer-open .drawer-panel { transform: translateY(0); }
.drawer-head { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg, var(--bg-hover)); border-radius: 14px 14px 0 0; }
.drawer-title { margin: 0; font-size: var(--fs-h4); }
.drawer-close { background: var(--bg-hover); border: 1px solid var(--border); color: var(--text);
  border-radius: 6px; padding: 0.4rem 0.7rem; cursor: pointer; min-height: 38px; font-weight: 600; }
.drawer-body { padding: 0.75rem 1rem 1.25rem; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.bs-drawer-chips { margin-bottom: 0.75rem; }
.stake-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.3rem; }
.stake-chip { cursor: pointer; padding: 6px 12px; min-height: 34px; }
/* Betslip drawer bet placement + P/L (#102) */
.bs-pnl-line { font-size: var(--fs-body); color: var(--text-muted); margin: 0 0 0.6rem; }
.bs-draw-item { margin-bottom: 0.5rem; }
.bs-place-row { display: flex; justify-content: flex-end; margin-top: -4px; }
.bs-place { min-height: 32px; }
.bs-place.bs-placed { color: var(--pos, var(--green)); border-color: var(--pos, var(--green)); }
.tn-betslip { position: relative; display: inline-flex; align-items: center; gap: 4px; background: transparent;
  border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 6px 8px; cursor: pointer; margin-left: auto; }
.tn-betslip .bs-star { fill: currentColor; }
.tn-betslip .betslip-badge:empty { display: none; }
.tn-betslip .betslip-badge { background: var(--accent); color: #fff; border-radius: 999px;
  font-size: var(--fs-xs); font-weight: 700; min-width: 16px; height: 16px; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center; }

/* ── Feedback form (#98) ────────────────────────────────────────────────────── */
.fb-return { margin-bottom: 0.75rem; }
.fb-form { display: flex; flex-direction: column; gap: 0.9rem; margin-top: 0.5rem; }
.fb-field { display: flex; flex-direction: column; gap: 0.35rem; }
.fb-lbl { font-size: var(--fs-body); font-weight: 600; color: var(--text); }
.fb-opt { color: var(--text-muted); font-weight: 400; }
.fb-input { width: 100%; box-sizing: border-box; background: var(--bg-hover); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; padding: 0.6rem 0.7rem; font-size: var(--fs-h4); font-family: inherit; }
.fb-textarea { resize: vertical; min-height: 110px; line-height: 1.45; }
.fb-input:focus { outline: none; border-color: var(--accent); }
/* #237 — see .horse-search-input above. */
.fb-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.fb-actions { display: flex; }
.fb-actions .btn-primary { width: 100%; min-height: 46px; }
.fb-status { margin-top: 0.9rem; }
/* Blackbook runner search typeahead (#107) */
.bb-typeahead { position: absolute; left: 0; right: 0; top: 100%; z-index: 20; margin-top: 2px;
  background: var(--bg, var(--bg-hover)); border: 1px solid var(--border); border-radius: 6px; max-height: 240px; overflow-y: auto; }
.bb-ta-row { padding: 0.5rem 0.7rem; cursor: pointer; font-size: var(--fs-h4); border-bottom: 1px solid var(--border); }
.bb-ta-row:last-child { border-bottom: 0; }
.bb-ta-row:hover { background: var(--bg-hover); }

/* Toast (#110) — transient bottom-right popups, stacked, fade in/out. */
.ui-toast-host { position: fixed; right: 16px; bottom: 16px; z-index: 600; display: flex; flex-direction: column; gap: 10px; max-width: 340px; pointer-events: none; }
.ui-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: 8px; background: var(--bg, var(--bg-hover));
  border: 1px solid var(--border); border-radius: 10px;
  padding: 0.7rem 0.8rem; box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  opacity: 0; transform: translateY(10px); transition: opacity 0.25s ease, transform 0.25s ease; }
.ui-toast.show { opacity: 1; transform: translateY(0); }
.ui-toast-body { flex: 1; min-width: 0; }
.ui-toast-title { font-weight: 700; font-size: var(--fs-h4); color: var(--text, #fff); }
.ui-toast-msg { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 2px; }
.ui-toast-cta { display: inline-block; margin-top: 7px; font-size: var(--fs-sm); font-weight: 600; color: var(--accent-light, #a78bfa);
  border: 1px solid var(--border); border-radius: 6px; padding: 4px 10px; text-decoration: none; }
.ui-toast-cta:hover { border-color: var(--accent); }
.ui-toast-x { background: transparent; border: 0; color: var(--text-muted); font-size: var(--fs-h3); line-height: 1; cursor: pointer; padding: 0 2px; }
@media (max-width: 640px) { .ui-toast-host { left: 12px; right: 12px; max-width: none; bottom: 12px; } }

/* Notification channel rows (#110) */
.notif-channel { border: 1px solid var(--border); border-radius: 8px; padding: 0.75rem 0.9rem; margin: 0.75rem 0; background: var(--bg-hover); }
.notif-channel-head { display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem; }
.notif-channel-name { font-weight: 600; color: var(--text); }
.notif-row { display: flex; align-items: center; gap: 0.5rem; font-size: var(--fs-h4); color: var(--text); margin: 0.4rem 0; }
.notif-soon { opacity: 0.55; }
.notif-soon .notif-channel-head { margin-bottom: 0; }
/* Keep the submit + status in view when the mobile keyboard opens on small screens. */
@media (max-width: 640px) { .fb-form { scroll-margin-bottom: 40vh; } .fb-textarea { min-height: 90px; } }
.btn-remove-betslip { align-self: center; }
@media (max-width: 640px) {
  .bs-card-body { flex-wrap: wrap; }
  .bs-runner { flex-basis: calc(100% - 46px); }
  .btn-remove-betslip { width: 100%; margin-top: 0.4rem; }
  .bs-runner-meta { gap: 0.3rem 0.7rem; }
}

/* ── "Add to betslip" affordance on the per-runner control (#87) ───────────── */
.betslip-add-wrap { display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 5px; }
.betslip-add-label { display: inline-flex; align-items: center; gap: 0.25rem; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-muted); white-space: nowrap; }
.betslip-add-label .bs-star { fill: none; stroke: var(--text-muted); stroke-width: 1.6; }
.betslip-add-wrap.on-slip .betslip-add-label { color: var(--win, #fbbf24); }
.betslip-add-wrap.on-slip .bs-star { fill: var(--win, #fbbf24); stroke: var(--win, #fbbf24); }
@media (max-width: 640px) { .betslip-add-wrap { flex-wrap: wrap; } }

/* ── Betslip page v2 (#89): race-grouped cards, runner rows, dated archive ──── */
.bs-race-runners { display: flex; flex-direction: column; }
.bs-runner-row { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.6rem 0.75rem; border-top: 1px solid var(--border); }
.bs-runner-row:first-child { border-top: none; }
.bs-archive { margin-top: 1.5rem; }
.bs-archive-head { color: var(--text-dim); }
.bs-archive-date { border: 1px solid var(--border); border-radius: 6px; margin-bottom: 0.5rem; background: var(--bg-card); overflow: hidden; }
.bs-archive-date > summary { cursor: pointer; padding: 0.6rem 0.8rem; display: flex; align-items: center; gap: 0.5rem; font-weight: 600; list-style: none; user-select: none; }
.bs-archive-date > summary::-webkit-details-marker { display: none; }
.bs-archive-date > summary::before { content: "▸"; color: var(--text-muted); transition: transform 0.15s ease; }
.bs-archive-date[open] > summary::before { transform: rotate(90deg); }
.bs-archive-date-lbl { flex: 1; }
.bs-archive-date .bs-card { margin: 0 0.6rem 0.6rem; }
.bs-controls { margin-bottom: 1rem; }
@media (max-width: 640px) { .bs-runner-row { flex-wrap: wrap; } .bs-runner-row .btn-remove-betslip { width: 100%; margin-top: 0.4rem; } }

/* Follow (blackbook) control on runner cards (#108) */
.follow-control { display: inline-flex; align-items: center; gap: 4px; background: transparent; border: 1px solid var(--border);
  color: var(--text-muted); border-radius: 6px; padding: 3px 8px; font-size: var(--fs-xs); cursor: pointer; margin-left: 6px; }
.follow-control:hover { border-color: var(--accent); color: var(--text); }
.follow-control .bs-star { fill: currentColor; }
.follow-control.fc-on { color: #fbbf24; border-color: #fbbf24; }

/* Shared admin tab bar — the underline tabs used by the Models/flows page AND the
   A/B test page, so admin tabs are one component (reuse, not per-page copies). */
.md-wrap { width: 100%; }
.md-tabs { display: flex; gap: 0.3rem; margin: 0.8rem 0 1.2rem; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.md-tab { background: none; border: none; border-bottom: 2px solid transparent; color: var(--text-muted); font-weight: 700; font-size: var(--fs-h4); padding: 0.55rem 1rem; cursor: pointer; font-family: inherit; }
.md-tab:hover { color: var(--text); }
.md-tab.active { color: var(--text); border-bottom-color: var(--accent); }
.md-actions { margin-left: auto; display: flex; gap: 0.5rem; align-items: center; }
/* Shared form input (was duplicated inline on admin pages). */
.form-input { background: var(--bg); border: 1px solid var(--border); color: var(--text); padding: 0.45rem 0.6rem; border-radius: 8px; font-size: var(--fs-body); }
.form-input:focus { outline: none; border-color: var(--accent); }
/* #237 — see .horse-search-input above. */
.form-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Per-code Best Bets section tags (#36). */
.bb-code-tag { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; vertical-align: middle; margin-left: 0.4rem; padding: 1px 6px; border-radius: 999px; background: rgba(var(--accent-rgb), 0.15); color: var(--accent-light); }
.bb-code-tag.bb-code-grey { background: rgba(var(--ew-rgb), 0.15); color: var(--ew); }
.bb-code-tag.bb-code-harness { background: rgba(224,162,60,0.15); color: #e0a23c; }

/* Projected speed / pace map (#56). */
/* #137 pace map now lives inside a collapsed accordion (one row per runner, each
   with the SAME pace-bar chart as the runner cards). The accordion supplies the
   frame, so the inner .pace-map has no border/background of its own. */
.pace-map { margin: 0; padding: 0.15rem 0.1rem; }
.pace-map .pm-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.4rem; }
.pace-map .pm-src { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 0 4px; }
.pace-map .pm-axis-hint { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); }
/* #518 ONE grid for all rows (rows are display:contents) so the two columns share a width across
   every row: the number+name column is the width of the LONGEST name (full names, never truncated)
   and — crucially — every pace bar starts at the SAME x, so the bars line up in a column. */
.pace-map .pm-rows { display: grid; grid-template-columns: max-content minmax(7rem, 1fr); column-gap: 0.5rem; row-gap: 0.3rem; align-items: center; }
.pace-map .pm-row { display: contents; }
.pace-map .pm-runner { display: inline-flex; align-items: baseline; gap: 0.35rem; }
/* #518 the run-style label is a FIXED width (fits the longest, "Backmarker") so the bar TRACK — the
   chart line — starts at the same x on every row. A per-row min-width let "Backmarker" push its
   line ~6px right of the rest. */
.pace-map .pm-bar .pace-bar-label { flex: 0 0 5rem; width: 5rem; min-width: 5rem; }
.pace-map .pm-num { font-family: var(--mono); font-weight: 700; font-size: var(--fs-sm); color: var(--accent-light); }
.pace-map .pm-name { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.pace-map .pm-bar { min-width: 0; }
.pace-map .pm-bar .pace-bar { margin-top: 0; }
/* #516 a runner the pace engine had no read for — listed (never dropped) but visibly muted. */
.pace-map .pm-row-nopace .pm-num, .pace-map .pm-row-nopace .pm-name { opacity: 0.55; }
.pace-map .pm-nopace { font-size: var(--fs-2xs); color: var(--text-dim); font-style: italic; }
@media (max-width: 640px) { .pace-map .pm-rows { grid-template-columns: max-content minmax(6rem, 1fr); } }

/* #136 expanded-accordion content: runner comments, dark stat grid, recent starts. */
.rc-comments { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem; margin: 0.4rem 0 0.6rem; }
.rc-comments-title { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); font-weight: 700; margin-bottom: 0.3rem; }
.rc-comments-body { font-size: var(--fs-body); color: var(--text-mid); line-height: 1.5; }
.stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem 0.8rem; background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 0.8rem 0.9rem; margin-bottom: 0.6rem; }
.stat-grid .sg-cell { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.stat-grid .sg-lab { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); }
.stat-grid .sg-val { font-size: var(--fs-h4); font-weight: 700; color: var(--text); overflow-wrap: anywhere; }
.recent-starts .rs-title { font-size: var(--fs-xs); font-weight: 700; color: var(--text); margin: 0.4rem 0 0.15rem; }
.recent-starts .rs-days { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 0.4rem; }
.recent-starts .ui-accordion { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 0.25rem; }
/* #529 compact single-line start rows at --fs-xs to match the runner-card density */
.recent-starts .ui-accordion-sum { padding: 0.3rem 0.55rem; font-size: var(--fs-xs); color: var(--text-mid); }
.recent-starts .ui-accordion .stat-grid { margin: 0 0.45rem 0.4rem; padding: 0.5rem 0.55rem; gap: 0.4rem 0.55rem; }
.recent-starts .stat-grid .sg-val { font-size: var(--fs-sm); } /* was --fs-h4 — too heavy for the nested rows */
.recent-starts .ui-accordion[open] > .ui-accordion-sum::after { content: none; } /* #139 keep nested run rows clean */
/* #222 recent-start summary: finish/field badge leads, then the race line */
.recent-starts .ui-accordion-sum { display: flex; align-items: baseline; gap: 0.5rem; }
.rs-fin { font-family: var(--mono); font-weight: 800; font-size: var(--fs-xs); min-width: 2.4em; flex: none; } /* #529 match compact row scale */
.rs-fin .rs-of { font-weight: 600; font-size: 0.82em; opacity: 0.7; }
.rs-line { color: var(--text-mid); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recent-starts .rs-nodetail { padding: 0.5rem 0.6rem; font-size: var(--fs-xs); color: var(--text-dim); }
/* #621 per-run running/steward comment — muted italic prose above the stat grid. */
.recent-starts .rs-comment { padding: 0.4rem 0.6rem 0.15rem; font-size: var(--fs-sm); font-style: italic; color: var(--text-dim); line-height: 1.45; }
@media (max-width: 640px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }

/* Sectional-time ranks on the runner card (#57). */
.rc-sectionals { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.35rem; }
.rc-sectionals .rc-src { font-size: var(--fs-2xs); background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 0 3px; color: var(--text-dim); margin-left: 2px; }
.sectional-chips { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.sectional-chip { display: inline-flex; align-items: baseline; gap: 3px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 1px 6px; font-size: var(--fs-xs); }
.sectional-chip .sc-lab { color: var(--text-muted); }
.sectional-chip .sc-rank { font-family: var(--mono); font-weight: 700; color: var(--accent-light); }
/* #218 interpreted closing-speed read — fast finisher (green) vs closed-hard-when-beaten (amber) */
.sectional-read { display: inline-flex; align-items: center; height: var(--chip-h); font-size: var(--chip-fs); padding: 0 var(--chip-px); box-sizing: border-box; gap: 0.3rem; font-weight: 700; border-radius: 6px; border: 1px solid transparent; }
.sectional-read.sr-fast { color: var(--green); background: rgba(var(--green-rgb), 0.12); border-color: rgba(var(--green-rgb), 0.3); }
.sectional-read.sr-badtrip { color: var(--win); background: rgba(var(--win-rgb), 0.12); border-color: rgba(var(--win-rgb), 0.3); }

/* #261 VO Speed tier badge — our blended speed rating (abstracted; sectional ranks never shown). */
/* #268 the VO Speed rating tier is now folded onto the VO Speed figure as a green suffix
   ("105 · Top-rated"); the old standalone .speed-badge is retired. */
.vo-speed-tier { color: var(--green); font-weight: 600; }

/* Value Scanner (#52) — filter chips + min-edge control row. */
.value-controls { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin: 0.4rem 0 1rem; }
.value-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.val-chip { cursor: pointer; }
.value-minedge { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--fs-body); color: var(--text-muted); }

/* Today digest (#63) — light layout helpers around the shared UI components. */
.digest-code-group { margin: 0.6rem 0 1rem; }
.digest-code-head { display: flex; align-items: center; gap: 0.4rem; font-weight: 700; font-size: var(--fs-h4); margin-bottom: 0.35rem; color: var(--text); }
.digest-pl-bycode { margin-top: 0.75rem; font-size: var(--fs-body); color: var(--text-mid); }
/* "Your bets today" — slim single-line rows (#63 follow-up). One bet per line: bet badge,
   runner (grows + ellipsises), race, price, jump time, live countdown / "Jumped". Replaces the
   per-code table so a full card of bets stays scannable. Squared per the light-theme rule. */
.digest-betlist { display: flex; flex-direction: column; }
.digest-betline {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.5rem 0.35rem; text-decoration: none; color: var(--text);
  border-bottom: 1px solid var(--border); font-size: var(--fs-body); line-height: 1.2;
}
.digest-betline:last-child { border-bottom: 0; }
.digest-betline:hover { background: var(--surface-2, rgba(0, 0, 0, 0.03)); }
.digest-betline .dbl-bet { flex: 0 0 auto; }
.digest-betline .dbl-runner { flex: 1 1 auto; min-width: 0; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.digest-betline .dbl-race { flex: 0 0 auto; color: var(--text-mid); white-space: nowrap; }
.digest-betline .dbl-odds { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 600; }
.digest-betline .dbl-time { flex: 0 0 auto; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.digest-betline .dbl-cd { flex: 0 0 auto; min-width: 2.8em; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-mid); font-weight: 600; }
.digest-betline .dbl-status { flex: 0 0 auto; min-width: 2.8em; text-align: right; color: var(--text-muted); font-weight: 600; }
.digest-betline.is-jumped { opacity: 0.72; }
/* #149 watch-replay link on runner-profile result rows */
.hp-replay { display: inline-block; margin-left: 0.4rem; padding: 0; background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--fs-xs); font-weight: 700; color: var(--ew); white-space: nowrap; }
.hp-replay:hover { text-decoration: underline; }

/* #151 AI paywall — slim "on Free plan" upgrade strip (fixed to the bottom). */
.ai-upgrade-strip { position: fixed; left: 0; right: 0; bottom: 0; z-index: 60; display: flex;
  align-items: center; justify-content: center; gap: 0.75rem; padding: 0.5rem 1rem;
  background: var(--card, #1b2233); border-top: 1px solid var(--border, #2c3446);
  color: var(--text, #e8edf6); font-size: var(--fs-sm); box-shadow: 0 -2px 10px rgba(0,0,0,0.25); }
.ai-upgrade-strip a { color: var(--ew, #f5c451); font-weight: 700; text-decoration: none; }
.ai-upgrade-strip a:hover { text-decoration: underline; }
.ai-upgrade-strip button { background: none; border: none; color: var(--text-dim, #9aa5b8);
  font-size: 1.1rem; line-height: 1; cursor: pointer; padding: 0 0.25rem; }

/* #200 Compact betslip rows (⭐ modal / drawer): one legible line per selection with
   race context (Track · R# · time) + live/snapshot odds. Mobile-first; scales up fine. */
.bs-rows { display: flex; flex-direction: column; gap: 0.5rem; }
.bs-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0.65rem;
  border: 1px solid var(--border); border-radius: 10px; background: var(--bg-card); }
/* Whole-row click-through to the race (mobile + desktop). */
a.bs-row-link { text-decoration: none; color: inherit; cursor: pointer;
  transition: border-color 0.12s, background 0.12s; }
a.bs-row-link:hover, a.bs-row-link:focus-visible { border-color: var(--accent);
  background: rgba(var(--accent-rgb), 0.08); outline: none; }
.bs-row-chev { flex: 0 0 auto; margin-left: 0.1rem; font-size: 1.15rem; line-height: 1;
  color: var(--text-dim); font-weight: 400; }
/* #736 betslipRow result + action slots. The row is a flex line, so both sit inline after the
   odds; the action is a real control, so it must not inherit the row-link's pointer semantics. */
/* #739 Compact add-form: inline fields that wrap, labels above in small caps, so the form is a
   quick action rather than a page-height stack. */
/* Grid, not flex: a wrapping flex row let the fields drift right when a line was short. A grid
   pins every field to the left edge at any width. Mobile first — one column, then two. */
.fb-form-compact { display: grid; grid-template-columns: 1fr; gap: 0.5rem; align-items: end;
  justify-items: stretch; text-align: left; }
@media (min-width: 641px) { .fb-form-compact { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.fb-form-compact .fb-field { margin: 0; min-width: 0; }
.fb-form-compact .fb-lbl { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted); display: block; margin-bottom: 0.15rem; }
.fb-form-compact .fb-input { padding: 0.35rem 0.5rem; font-size: 0.85rem; }
.fb-form-compact .fb-actions, .fb-form-compact button { justify-self: start; }
/* #739 hero action row — right-aligned under the masthead, above the controls. */
.bs-hero-actions { display: flex; justify-content: flex-end; margin: 0 0 0.4rem; }
/* #739 Tighten the gap between the sort/filter bar and the first row — the controls and the list
   are one unit, and the old gap read as a section break. */
.bs-controls { margin-bottom: 0.35rem; }
.bs-controls + .bs-row, .bs-controls + .bs-row-link { margin-top: 0; }
/* #739 Square corners everywhere on the row — the site's sharp geometry (0px), so a saved
   selection matches the rest of the app rather than being the one rounded panel. */
.bs-row, .bs-row-link, .bs-row-silk, .bs-row-x { border-radius: 0 !important; }
/* MOBILE FIRST. The base rules ARE the phone layout — the row starts compact and is progressively
   enhanced upward, rather than being a desktop row that gets things hidden from it.
   Silk is dynamic content: on a phone the width belongs to the name, odds and result, so it is
   absent by default and only appears once there is room for it. */
.bs-row-silk { display: none; }
@media (min-width: 641px) { .bs-row-silk { display: block; } }
/* Trainer + note lines under the runner name. Both omit entirely when absent — no empty labels. */
.bs-row-conn { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 0.1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bs-row-conn-lab { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.68rem; }
.bs-row-note { font-size: var(--fs-xs); color: var(--text-mid); margin-top: 0.15rem; font-style: italic; }
/* A small circular ✕ replaces the worded Remove/Unfollow buttons — one affordance, one shape,
   used by both the betslip and the blackbook. */
.bs-row-x { flex: 0 0 auto; width: 22px; height: 22px; margin-left: 0.5rem; padding: 0; line-height: 1;
  font-size: 1rem; cursor: pointer; background: var(--bg-light); color: var(--text-mid);
  border: 1px solid var(--border); border-radius: 50% !important; position: relative; z-index: 1; }
.bs-row-x:hover { background: var(--danger); color: #fff; border-color: var(--danger); }
.bs-row-result { display:flex; align-items:center; gap:0.3rem; margin-left:0.5rem; white-space:nowrap; }
.bs-row-action { margin-left:0.5rem; }
.bs-row-action .btn { position:relative; z-index:1; }
.bs-row-num { flex: 0 0 auto; min-width: 1.6rem; height: 1.6rem; display: flex; align-items: center;
  justify-content: center; font-weight: 700; font-size: 0.85rem; border-radius: 6px;
  background: rgba(var(--overlay-rgb), 0.06); color: var(--text); }
.bs-row-silk { flex: 0 0 auto; width: 22px; height: 22px; object-fit: contain; border-radius: 4px; }
.bs-row-main { flex: 1 1 auto; min-width: 0; }
/* The truncation belongs to the NAME, not the row. `.bs-row-name` is a flex container and the
   runner name used to be a bare text node — an anonymous flex item, which does not shrink and
   cannot take `text-overflow`. So a long name pushed the bet-type badge past the hidden edge and
   it was CLIPPED MID-WORD ("WIN" rendering as "WI"), which reads as a broken component rather than
   as a name that is too long. Now the name is a span that shrinks and ellipsises; the badge never
   moves. */
.bs-row-name { display: flex; align-items: center; gap: 0.4rem; font-weight: 600; font-size: 0.92rem;
  color: var(--text); min-width: 0; overflow: hidden; }
.bs-row-nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bs-row-name .badge { flex: 0 0 auto; }
.bs-row-ctx { margin-top: 0.15rem; font-size: 0.78rem; color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bs-row-odds { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end;
  gap: 0.1rem; text-align: right; }
.bs-od { display: inline-flex; align-items: baseline; gap: 0.3rem; font-variant-numeric: tabular-nums;
  font-weight: 700; font-size: 0.9rem; color: var(--text); }
.bs-od-place { font-weight: 600; font-size: 0.8rem; color: var(--text-muted); }
.bs-od-lab { font-weight: 600; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-dim); }
.bs-od-live { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--accent-light); border: 1px solid var(--accent); border-radius: 4px;
  padding: 0 0.2rem; margin-left: 0.15rem; }

/* #202 "Hide run races" toggle in the race-page nav row — compact, doesn't shrink,
   sits to the right of the scrollable pill strip (reuses the shared switch markup). */
.meet-nav-toggle { flex: 0 0 auto; display: inline-flex; align-items: center; }
.meet-nav-toggle:empty { display: none; }
.meet-nav-toggle .toggle-label { gap: 0.4rem; }
.toggle-sm .toggle-text { font-size: 0.78rem; color: var(--text-dim); white-space: nowrap; }
@media (max-width: 640px) { .toggle-sm .toggle-text { font-size: 0.72rem; } }

/* #203 redo 2: "Today's form" — ONE compact row per race type, AI + Basic as inline
   micro-bars (win% + a 4px fill) in the same row. ~150px tall on mobile; details in
   tooltips instead of extra rows. */
.tf-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px;
  padding: 0.7rem 0.9rem 0.6rem; max-width: 1380px; margin: 0 auto; }
.tf-head { margin-bottom: 0.4rem; }
.tf-title { font-weight: 700; font-size: 0.95rem; color: var(--text); }
.tf-sub { font-size: 0.72rem; color: var(--text-dim); }
.tf-rows { display: flex; flex-direction: column; }
.tf-row { display: grid; grid-template-columns: minmax(66px, 90px) 1fr 1fr 3.6em;
  gap: 0.6rem; align-items: center; padding: 0.28rem 0; }
.tf-row + .tf-row { border-top: 1px solid rgba(var(--overlay-rgb), 0.05); }
.tf-row-head { padding: 0 0 0.1rem; }
.tf-ch { font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.tf-row-overall { font-weight: 700; }
.tf-row-overall .tf-cv { font-size: 0.95rem; }
.tf-rlabel { display: flex; align-items: center; gap: 0.35rem; font-size: 0.8rem; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tf-cell { display: flex; align-items: center; gap: 0.45rem; min-width: 0; }
.tf-cell-empty { color: var(--text-dim); font-size: 0.8rem; }
/* #653b tf-cellbody / tf-cvwrap: on desktop these are transparent flex rows so the cell reads as
   [win% · bar · E/W] exactly as before; on mobile they become the vertical stack (see the 640px block). */
.tf-cellbody { display: flex; flex: 1 1 auto; align-items: center; gap: 0.45rem; min-width: 0; }
.tf-cvwrap { display: flex; flex: 1 1 auto; align-items: center; gap: 0.45rem; min-width: 0; }
.tf-cv { font-size: 0.86rem; font-weight: 800; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.tf-cv-ai { color: var(--accent-light); }
.tf-cv-basic { color: var(--win); }
.tf-mtrack { flex: 1 1 auto; height: 4px; border-radius: 3px; background: rgba(var(--overlay-rgb), 0.08); overflow: hidden; }
.tf-mfill { display: block; height: 100%; border-radius: 3px; transition: width 0.5s cubic-bezier(0.2,0.7,0.2,1); }
.tf-mfill-ai { background: linear-gradient(90deg, var(--accent), var(--accent-light)); }
.tf-mfill-basic { background: linear-gradient(90deg, var(--win), #ffe08a); }
/* #628 the XGBoost (Vector Apex, id `boost` — #1558 renamed the tier, not the model) column of
   Today's form — shown only for tiers with boost-signals. */
.tf-cv-xgb { color: var(--chart-3); }
.tf-mfill-xgb { background: linear-gradient(90deg, var(--chart-3), var(--chart-3)); }
.tf-3model .tf-row { grid-template-columns: minmax(52px, 74px) 1fr 1fr 1fr 3em; }
.tf-rn { font-size: 0.68rem; color: var(--text-dim); text-align: right; font-variant-numeric: tabular-nums; }
/* #525 each-way top-3 rate — a muted secondary stat beside the top-pick win% */
.tf-ew { flex: 0 0 auto; font-size: 0.66rem; color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .tf-mfill { transition: none; } }
.tf-cellname { display: none; } /* #653b desktop shows model names in the header row, not per-cell */
/* #653b Today's Form — 3-model (Core/Prime/Boost) MOBILE layout. Three model cells, each with
   win% + mini-bar + "E/W xx%" INLINE, overflow the 5-column grid on a narrow screen (one model's
   E/W collided with the next model's %, rendered as "E/W 55%41%"). Below the mobile breakpoint,
   stack the models vertically: a "N races" heading, then each model on its own row — the model
   NAME on the left, vertically centred against a two-line stack of [win% + bar chart] over
   [E/W xx%]. Unchanged in 2-model mode and on wider screens. */
@media (max-width: 640px) {
  /* Hide the header row on mobile — the per-cell model names replace it. Uses .tf-row.tf-row-head
     (higher specificity than the .tf-3model .tf-row flex rule below) so the hide wins regardless of
     source order — otherwise the header leaks back as a duplicate "Races / Core / Prime / Boost" stack. */
  .tf-3model .tf-row.tf-row-head { display: none; }
  .tf-3model .tf-row { display: flex; flex-direction: column; align-items: stretch; gap: 0.55rem; padding: 0.4rem 0; }
  .tf-3model .tf-rlabel { display: none; }           /* horse-only product → no code label */
  .tf-3model .tf-rn { order: -1; text-align: left; font-size: 0.7rem; }   /* "N races" heads the block */
  .tf-3model .tf-row:not(.tf-row-head) .tf-rn::after { content: " races"; color: var(--text-dim); font-weight: 400; }
  .tf-3model .tf-cell { display: flex; flex-direction: row; align-items: center; gap: 0.85rem; width: 100%; padding-left: 0.2rem; }
  .tf-3model .tf-cellname { display: inline-block; flex: 0 0 auto; min-width: 4.2em; font-weight: 700; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; }
  .tf-3model .tf-cellbody { display: flex; flex-direction: column; align-items: flex-start; gap: 0.28rem; flex: 1 1 auto; min-width: 0; }
  .tf-3model .tf-cvwrap { display: flex; flex-direction: row; align-items: center; gap: 0.6rem; width: 100%; }
  .tf-3model .tf-cv { flex: 0 0 auto; min-width: 2.6em; font-size: 1rem; }
  .tf-3model .tf-mtrack { display: block; flex: 1 1 auto; max-width: 55%; height: 5px; }   /* bar chart stays visible */
  .tf-3model .tf-ew { font-size: 0.66rem; }
  .tf-3model .tf-cell-empty { align-items: center; gap: 0.85rem; padding-left: 0.2rem; }

  /* #855 2-MODEL MODE: reclaim the same always-empty label column.
     `.tf-row` declares `minmax(66px, 90px) 1fr 1fr 3.6em`, and column 1 is the race-code label —
     which `_tfRow("", "", …)` has passed as EMPTY since the product went horse-only. On a 359px
     card that is 90px, a quarter of the width, holding nothing, and the win-rate bar it squeezes
     out measured 0px at 320px and 360px, 2px at 375px. The card still printed "38% · E/W 38%", so
     the graphic was silently absent rather than visibly broken.
     The TRACK has to go, not just the span: `display:none` removes the element from grid flow, but
     the template still declares four columns and the first keeps its 66-90px minimum.
     #653b fixed exactly this for `.tf-3model` and left the 2-model branch — the one every Core and
     Prime subscriber sees — untouched. Scoping a mobile fix to one branch should always prompt
     "and what does the other one do here?". */
  .tf-card:not(.tf-3model) .tf-row { grid-template-columns: 1fr 1fr 3.2em; }
  .tf-card:not(.tf-3model) .tf-rlabel { display: none; }
}

/* #60 small-sample de-emphasis in stat tables — a low-n strike isn't signal. */
.dt-dim { color: var(--text-dim); font-weight: 500; }
/* Signed value cells in data tables (e.g. ROI, P/L) — reuse the semantic colour vars. */
.dt-pos { color: var(--green); font-weight: 600; }
.dt-neg { color: var(--danger); font-weight: 600; }

/* ═══ Coverage matrix (#262 UI.coverageMatrix) — "which source has which data" grid ═══ */
.coverage-matrix .cov-rowlabel { font-weight: 600; white-space: nowrap; position: sticky; left: 0; background: var(--bg-card); }
.coverage-matrix .cov-cell { text-align: center; }
.cov-status { font-size: 1.05rem; cursor: help; }
.cov-full { color: var(--green); }
.cov-partial { color: var(--win); }
.cov-none { color: var(--text-dim); opacity: 0.6; }
.cov-legend { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.5rem; font-size: var(--fs-xs); color: var(--text-dim); }
.cov-legend .cov-status { font-size: 0.9rem; cursor: default; }
.cov-hint { opacity: 0.8; }

/* #332 Race speed map — projected run-style lanes */

/* #335 Closing-speed sprint bar */
.closing-speed-bar { display: inline-flex; align-items: flex-end; gap: 3px; height: 22px; vertical-align: middle; }
.closing-speed-bar .csb-seg { display: inline-flex; flex-direction: column; align-items: center; gap: 1px; }
.closing-speed-bar .csb-track { width: 7px; height: 16px; display: flex; align-items: flex-end; background: rgba(var(--overlay-rgb),0.08); border-radius: 2px; overflow: hidden; }
.closing-speed-bar .csb-fill { width: 100%; border-radius: 2px 2px 0 0; }
.closing-speed-bar .csb-lab { font-size: 0.5rem; color: var(--text-dim); line-height: 1; }

/* #336 Market-aware stance tag (honest framing) */
.mkt-stance { font-size: var(--fs-2xs); font-weight: 700; padding: 0.1rem 0.4rem; border-radius: 999px; white-space: nowrap; }
.mkt-stance.ms-value { color: var(--green,#22c55e); background: rgba(34,197,94,0.14); }
.mkt-stance.ms-inline { color: var(--text-muted); background: rgba(var(--overlay-rgb),0.08); }
.mkt-stance.ms-under { color: var(--text-dim); background: rgba(var(--overlay-rgb),0.06); }

/* #334 Value board — dedicated cross-race overlay list */
.vb-controls { display: flex; flex-direction: column; gap: .5rem; margin: .25rem 0 1rem; }
.vb-ctrl-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.vb-table td.vb-race a { font-weight: 600; }
.vb-table .vb-off { display: block; font-size: .8em; color: var(--text-muted); }
.vb-table td.vb-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vb-table td.vb-edge { text-align: right; white-space: nowrap; }
.vb-table td.vb-runner { min-width: 9rem; }
@media (max-width: 640px) {
  .vb-table td.vb-race a { font-size: .92em; }
  .vb-table .vb-off { font-size: .72em; }
}

/* #333 My betting (CLV journal) */
.mb-h3 { font-size: 1rem; margin: 1.25rem 0 .5rem; }
.mb-h3-sub { font-weight: 400; font-size: .8em; color: var(--text-muted); }
.mb-chart-wrap { margin: 1rem 0 .5rem; }
.mb-table td.mb-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mb-table .mb-when { display: block; font-size: .78em; color: var(--text-muted); }
.mb-table td.mb-runner { min-width: 9rem; }
.mb-pos { color: var(--green, #22c55e); font-weight: 600; }
.mb-neg { color: var(--red, #ef4444); font-weight: 600; }

/* #337 My runners feed */
.mr-feed { display: flex; flex-direction: column; gap: .5rem; }
.mr-item { display: flex; align-items: center; gap: .75rem; padding: .6rem .75rem; border: 1px solid var(--border, rgba(var(--overlay-rgb),0.12)); border-radius: 10px; text-decoration: none; color: inherit; background: var(--card-bg, rgba(var(--overlay-rgb),0.03)); }
.mr-item:hover { border-color: var(--accent, #6ea8fe); }
.mr-item .mr-when { min-width: 3.5rem; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }
.mr-item.mr-now .mr-when { color: var(--green, #22c55e); }
.mr-item.mr-soon .mr-when { color: var(--amber, var(--win)); }
.mr-item.mr-later .mr-when { color: var(--text-muted); }
.mr-main { display: flex; flex-direction: column; gap: .1rem; flex: 1; min-width: 0; }
.mr-runner { font-size: .98em; }
.mr-race { font-size: .82em; color: var(--text-muted); }
.mr-assess { font-size: .8em; color: var(--text-dim); }
.mr-following { margin-top: 1.25rem; }
.mr-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.mr-chip { font-size: .85em; padding: .2rem .5rem; border-radius: 999px; background: rgba(var(--overlay-rgb),0.08); }
.mr-chip-type { font-size: .8em; color: var(--text-muted); text-transform: capitalize; }

/* #309 Live going "as at" stamp on the race card Track cell */
.going-asof { font-size: .72em; color: var(--text-muted); font-weight: 400; white-space: nowrap; }
#goingDriftNote:not(:empty) { margin-top: .6rem; }
/* #838 Vector Focus unlock control. Mobile-first: the default is the STACKED layout a phone gets,
   and the row only becomes a row once there is width for it - the reverse would leave the button
   squeezed beside the copy on a 375px screen. */
#focusUnlockNote:not(:empty) { margin-top: .6rem; }
.focus-unlock {
  display: flex; flex-direction: column; align-items: stretch; gap: .55rem;
  padding: .7rem .8rem; border: 1px solid var(--border);
  border-radius: var(--radius-sm, 6px); background: var(--bg-card);
}
.focus-unlock .fu-copy { font-size: var(--fs-sm); line-height: 1.45; }
.focus-unlock .fu-bal { display: block; color: var(--text-muted); font-size: var(--fs-xs); margin-top: .15rem; }
.focus-unlock .fu-bonus { color: var(--accent); }
.focus-unlock .fu-low { color: var(--warning, var(--accent)); font-weight: 600; }
@media (min-width: 641px) {
  .focus-unlock { flex-direction: row; align-items: center; justify-content: space-between; gap: 1rem; }
  .focus-unlock .fu-copy { flex: 1 1 auto; }
}

/* #315 Exotics — "good strike" flag (≥55%) + no sample-size count in the cell */
.ex-good { color: var(--green, #22c55e); font-weight: 700; }
td.ex-good-cell { color: var(--green, #22c55e); }

/* #375 An each-way is ONE bet with TWO legs (#365), each closing against its own market — so a
   journal row can carry two lines per price cell, tagged W/P. Win and Place bets have a single
   leg and render as one line, so the tag is omitted rather than shown blank. */
.mb-leg { white-space: nowrap; }
.mb-leg + .mb-leg { margin-top: 0.15rem; }
.mb-leg-tag {
  display: inline-block; min-width: 1.1em; margin-right: 0.3em;
  font-size: 0.7em; font-weight: 600; opacity: 0.6; text-align: left;
}


/* ── Auth pages (#417) ───────────────────────────────────────────────────────
   login / register / forgot / reset used to be four standalone pages: no styles.css, no
   theme-boot, ~83 colour literals and an inline <style> block each. They were permanently
   dark whatever the toggle said — the FIRST screen a user ever sees ignored their theme.
   And because they never loaded this file, the mandatory 18+ footer they all render
   (.rg-footer/.rg-18/.rg-tagline, defined above) fell back to unstyled text.

   One block, four pages. Every colour is a token, so the auth screens follow the palette
   like everything else.
   ------------------------------------------------------------------------- */
.auth-body {
  /* the accent/green wash that gives the login its depth — as tints of the palette, so it
     re-tones per theme instead of staying a purple haze over a mint page */
  background:
    radial-gradient(120% 90% at 15% -10%, rgba(var(--accent-rgb), 0.22), transparent 55%),
    radial-gradient(120% 90% at 100% 110%, rgba(var(--green-rgb), 0.12), transparent 50%),
    var(--bg);
  color: var(--text);
  font-family: var(--font);
  min-height: 100vh; min-height: 100dvh;
  /* column so the (static) 18+ footer stacks BELOW the card, not beside it — the body is a
     flex parent and the footer is its second child, which read as a row before. */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 1.25rem;
}
.auth-box {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 1.75rem 1.5rem 1.5rem;
  width: 100%; max-width: 380px;
  margin: auto;                       /* centre the card in the free space above the footer */
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.auth-box.auth-wide { max-width: 460px; }
/* signature accent bar */
.auth-box::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--green), var(--accent));
}
.auth-body .rg-footer { margin-top: auto; flex-shrink: 0; width: 100%; border-top: none; }
.auth-box h1 { font-size: 1.15rem; text-align: center; margin-bottom: 1.35rem; font-weight: 700; }
.auth-box label { display: block; font-size: 0.78rem; color: var(--text-muted); margin-bottom: 0.3rem; font-weight: 500; }
.auth-box input {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 0.7rem 0.8rem;
  border-radius: 10px;
  font-size: 1rem;                    /* 16px: anything smaller makes iOS zoom on focus */
  font-family: var(--font);
  margin-bottom: 1rem;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.auth-box input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.22); }
.auth-btn {
  width: 100%;
  background: var(--accent-btn); color: var(--on-accent);
  border: none; padding: 0.75rem; border-radius: 10px;
  font-size: 0.95rem; font-weight: 700; font-family: var(--font);
  cursor: pointer; transition: filter 0.12s, transform 0.06s;
}
.auth-btn:hover { filter: brightness(1.08); }
.auth-btn:active { transform: translateY(1px); }
.auth-btn:disabled { opacity: 0.6; cursor: not-allowed; }
/* #551 Google Sign-In: an "or" divider + a neutral secondary button (reuses .auth-btn sizing) */
.auth-or { display: flex; align-items: center; gap: 0.6rem; margin: 1rem 0 0.75rem; color: var(--text-dim); font-size: 0.78rem; }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.auth-btn-google { display: flex; align-items: center; justify-content: center; text-decoration: none;
  background: var(--bg-card); color: var(--text); border: 1px solid var(--border); }
.auth-btn-google:hover { filter: none; border-color: var(--accent); }
.auth-error { color: var(--danger); font-size: 0.82rem; text-align: center; margin-top: 0.85rem; display: none; }
.auth-ok { color: var(--green); }
.auth-hint { color: var(--text-muted); font-size: 0.78rem; line-height: 1.5; margin-bottom: 1rem; }
.auth-link { color: var(--text-muted); font-size: 0.8rem; text-decoration: none; }
.auth-link:hover { color: var(--text-mid); }
.auth-foot { text-align: center; margin-top: 0.7rem; }
/* #307 self-registration CTA: an "or" divider + a full-width secondary (outline) button so
   the register path is an obvious mechanism, distinct from the primary Sign In. */
.reg-or { display: flex; align-items: center; gap: 0.75rem; margin: 1.25rem 0 0.9rem; }
.reg-or::before, .reg-or::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.reg-or span { color: var(--text-dim); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; }
.reg-btn {
  display: block; width: 100%; text-align: center; text-decoration: none;
  padding: 0.7rem; border-radius: 10px; font-size: 0.9rem; font-weight: 700;
  color: var(--green); background: transparent; border: 1px solid rgba(var(--green-rgb), 0.5);
  transition: background 0.12s, border-color 0.12s;
}
.reg-btn:hover { background: rgba(var(--green-rgb), 0.08); border-color: var(--green); }
.auth-note { font-size: 0.82rem; line-height: 1.5; padding: 0.6rem 0.7rem; border-radius: 8px; margin-top: 0.85rem; }
.auth-note.ok { color: var(--green); background: rgba(var(--green-rgb), 0.1); border: 1px solid rgba(var(--green-rgb), 0.35); }
.auth-note.err { color: var(--danger); background: rgba(var(--danger-rgb), 0.1); border: 1px solid rgba(var(--danger-rgb), 0.35); }
@media (min-width: 480px) { .auth-box { padding: 2.25rem 2rem 1.85rem; } }

/* register.html's extras — in the shared block, not a one-off on the page (#417).
   row2 is mobile-first: one column, two only when there is room for two. */
.auth-row2 { display: grid; grid-template-columns: 1fr; gap: 0 0.75rem; }
@media (min-width: 480px) { .auth-row2 { grid-template-columns: 1fr 1fr; } }
.auth-decl { display: flex; gap: 0.55rem; align-items: flex-start; margin: 0 0 1rem; }
.auth-decl input { width: 1.05rem; height: 1.05rem; margin-top: 0.15rem; accent-color: var(--accent); }
.auth-decl span { font-size: 0.8rem; color: var(--text-muted); line-height: 1.45; }
.auth-done { display: none; text-align: center; }
.auth-done .auth-big { font-size: 2rem; margin-bottom: 0.6rem; }
.auth-backlink { text-align: center; margin-top: 1rem; font-size: 0.8rem; }
.auth-backlink a { color: var(--accent); font-weight: 600; }

/* #631 Public performance page — Core/Prime/Boost columns, mobile-first (stack) → 3-up desktop. */
.perf-cols { display: grid; grid-template-columns: 1fr; gap: 1rem; margin-top: 0.5rem; }
@media (min-width: 780px) { .perf-cols { grid-template-columns: repeat(3, 1fr); align-items: start; } }
.perf-col { border: 1px solid var(--border); border-radius: var(--radius, 12px); padding: 1rem; background: var(--surface, var(--bg)); display: flex; flex-direction: column; gap: 0.75rem; }
.perf-col[data-ch="prime"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.perf-col-head { display: flex; align-items: baseline; justify-content: space-between; }
.perf-tier { margin: 0; font-size: 1.35rem; font-weight: 800; }
.perf-vectors { font-size: 0.82rem; color: var(--text-mid); background: var(--surface-2, rgba(127,127,127,0.08)); border-radius: var(--radius-sm, 8px); padding: 0.4rem 0.6rem; }
.perf-vectors .pv-tag { font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.72rem; color: var(--accent); margin-right: 0.35rem; }
.perf-vectors-soon, .perf-vectors-thin { opacity: 0.8; }
.perf-dim { color: var(--text-dim, var(--text-mid)); }
.perf-split { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.perf-split-cell { text-align: center; border: 1px solid var(--border); border-radius: var(--radius-sm, 8px); padding: 0.5rem; }
.perf-split-cell .ps-h { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-mid); }
.perf-split-cell .ps-roi { font-size: 1.15rem; font-weight: 800; }
.perf-split-cell .ps-roi.pos { color: var(--green, #34d399); }
.perf-split-cell .ps-roi.neg { color: var(--danger, #dc3232); }
.perf-split-cell .ps-sub { font-size: 0.7rem; color: var(--text-dim, var(--text-mid)); }
.perf-cov { font-size: 0.72rem; color: var(--text-dim, var(--text-mid)); }
.perf-incl { margin: 0 0 0.6rem; font-size: 0.85rem; color: var(--text-mid); }
.perf-cta { margin-top: auto; }
.perf-cta .btn { width: 100%; }
.perf-basis { font-size: 0.75rem; color: var(--text-dim, var(--text-mid)); margin-top: 1rem; line-height: 1.5; }
.perf-out.po-won { color: var(--green, #34d399); font-weight: 700; }
.perf-out.po-placed { color: var(--accent); font-weight: 600; }
.perf-out.po-miss { color: var(--text-dim, var(--text-mid)); }

/* #794 The filtered record — the page headline. Mobile-first: single column by default, and the
   stat cards only go side-by-side once there is room, so the two headline figures never squeeze
   into unreadable slivers on a phone (most of this page's traffic). */
.pv-hero { display: flex; flex-direction: column; gap: 0.75rem; padding: 0.9rem; margin-top: 0.5rem;
  background: var(--surface-2, rgba(127,127,127,0.06)); border-radius: var(--radius, 12px);
  border: 1px solid var(--border, rgba(127,127,127,0.18)); }
.pv-hero-thin { opacity: 0.95; }
.pv-hero-head { display: flex; align-items: baseline; gap: 0.4rem; flex-wrap: wrap; }
.pv-hero-head .pv-tag { font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em;
  font-size: 0.74rem; color: var(--accent); }
.pv-hero .stat-cards { gap: 0.6rem; }
.pv-filters { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.25rem;
  font-size: 0.85rem; color: var(--text-mid); }
@media (min-width: 560px) { .pv-filters { grid-template-columns: 1fr 1fr; column-gap: 1.2rem; } }
.pv-history { font-size: 0.85rem; }
/* Wide tables scroll inside their own container — the page body must never scroll sideways. */
.pv-history > div { overflow-x: auto; }

/* #794 The scope line under any published figure: n · window · price basis. Deliberately quiet —
   it qualifies the number without competing with it, but it is never optional. */
.figure-basis { font-size: 0.74rem; color: var(--text-dim, var(--text-mid)); line-height: 1.5; }
.fb-cov { font-size: 0.72rem; color: var(--text-dim, var(--text-mid)); line-height: 1.5; margin-top: 0.15rem; }

/* #839 Pricing fine print - the GST/currency statement under the lead. Muted and small, but
   never hidden: a price qualifier the reader has to hunt for is not a qualifier. */
.mk-fineprint { color: var(--text-muted); font-size: var(--fs-xs); margin: .35rem 0 0; }

/* #578 Operator identity block on the legal pages. Mobile-first: a definition list stacks by
   default (a label above its value reads fine on a phone), and only becomes a two-column grid
   when there is width for it. Legal detail must stay legible, so it does not shrink below the
   body size. */
.mk-operator { margin: 0.6rem 0 0; display: grid; grid-template-columns: 1fr; gap: 0.15rem 1rem; }
.mk-operator dt { color: var(--text-muted); font-size: var(--fs-sm); }
.mk-operator dd { margin: 0 0 0.5rem; color: var(--text); }
@media (min-width: 561px) {
  .mk-operator { grid-template-columns: max-content 1fr; align-items: baseline; }
  .mk-operator dd { margin-bottom: 0.2rem; }
}

/* #838b Vector Focus unlock meter on the account page. Mobile-first: stacks by default and stays
   stacked — this is a readout, not a row of controls, so there is nothing to reflow at width. */
.focus-meter {
  margin: 0 0 1rem; padding: .8rem .9rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm, 6px); background: var(--bg-card);
}
.focus-meter-low { border-color: var(--warning, var(--accent)); }
.focus-meter .fm-count { font-size: var(--fs-md); }
.focus-meter .fm-count strong { font-size: var(--fs-lg); }
.focus-meter .fm-bonus { color: var(--accent); font-size: var(--fs-sm); }
.focus-meter .fm-sub { color: var(--text-muted); font-size: var(--fs-xs); margin-top: .25rem; line-height: 1.5; }
.focus-meter .btn { margin-top: .6rem; }
/* #902 Races actually unlocked — the earned number, distinct from the quota lines above it, so it
   reads as what you HAVE rather than another way of saying what you have spent. */
.focus-meter .fm-unlocked { color: var(--text); }

/* #902 The spend-one-unlock confirmation body. The lead line is normal size and the supporting
   counts step down, because the decision is "is this race worth one?" and the breakdown is context
   for it — not four figures of equal weight. */
.focus-confirm p { margin: 0 0 .5rem; }
.focus-confirm p:last-child { margin-bottom: 0; }
.focus-confirm .fc-sub { color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.5; }

/* #847 Subscription tier cards. Three problems the old layout had, and the fixes:
   1. Track/Core/Focus rendered IDENTICAL feature lists, so the ladder had no visible delineation —
      `.to-summary` carries what the tier actually IS, above the flag list.
   2. Some cards had an action and some had none. Every card now renders `.to-act` in the same slot,
      at the same min-width, whether it is a button or a badge — a row where controls appear and
      disappear reads as broken, not as "that one is not for you".
   3. The buttons were labelled with sentences of different lengths, so they were different widths
      down the column. They carry the price now; the tier name is already the heading beside it. */
.tier-opt { border: 1px solid var(--border); border-radius: var(--radius-sm, 6px); padding: .85rem .9rem; margin-bottom: .6rem; background: var(--bg-card); }
.tier-opt-current { border-color: var(--accent); border-left-width: 3px; }
.to-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; }
.to-name { font-weight: 700; font-size: var(--fs-md); }
.to-act { min-width: 8.5rem; text-align: center; flex: 0 0 auto; }
.to-summary { color: var(--text); font-size: var(--fs-sm); line-height: 1.5; margin-top: .4rem; }
.to-perks { color: var(--text-muted); font-size: var(--fs-xs); line-height: 1.5; margin-top: .35rem; }
span.to-act { display: inline-block; padding: .3rem .55rem; border-radius: 4px; font-size: var(--fs-xs);
  background: var(--bg-input); color: var(--text-muted); border: 1px solid var(--border); }
/* Mobile-first: below 560px the action drops under the name at full width rather than being
   squeezed to a few characters beside a long tier name. */
@media (max-width: 560px) {
  .to-head { flex-direction: column; align-items: stretch; gap: .5rem; }
  .to-act { min-width: 0; width: 100%; }
}

/* #868 The model-surface strip (UI.modelNav). Reuses the segmented control's look, but its segments
   are ANCHORS — links work without JS, are bookmarkable and middle-clickable, and the same markup
   serves a page that switches in-place and one that navigates. Anchors need the text-decoration and
   colour reset that buttons get for free. */
.model-nav { flex-wrap: wrap; }
.model-nav .seg-btn { text-decoration: none; color: inherit; display: inline-flex; align-items: center; }
.model-nav .seg-btn:hover { text-decoration: none; }

/* #869 Placing badges on a prediction tile. Filled, using the semantic result palette so they follow
   the theme (the light theme deliberately re-maps success colours). Green = the bet did its job;
   orange = a place that did not pay a win bet. The ordinal is IN the badge, so the meaning survives
   without colour vision (#169: colour is always paired with a text state). */
.badge.res-win { background: rgba(var(--result-win-rgb), 0.18); color: var(--result-win); border: 1px solid rgba(var(--result-win-rgb), 0.45); }
.badge.res-place { background: rgba(var(--result-place-rgb), 0.18); color: var(--result-place); border: 1px solid rgba(var(--result-place-rgb), 0.45); }
/* Sized to sit INSIDE a tile: the base .badge is --fs-xs at --chip-h, which is a control-sized chip
   and swamps a --fs-2xs line. Height auto + tighter padding keeps it a label on the line, not a
   second control. */
/* Pushed to the RIGHT EDGE of the tile. `margin-left: auto` eats the free space in the flex row, so
   the badge lands hard right whatever the length of the outcome text or the price beside it — which
   is the point: the placing then sits in the same column down the whole list and can be read by
   scanning one edge, instead of drifting with "Won $5.73" vs "Placed 2nd $7.20".
   The right padding is the badge's own breathing room INSIDE the tile's 0.55rem padding, so it does
   not sit flush against the border. */
.pc-tile-o .badge.res-win, .pc-tile-o .badge.res-place {
  margin-left: auto; margin-right: 0.15rem; font-size: var(--fs-2xs); height: auto; line-height: 1.5; padding: 0 0.4rem;
}

/* #869 FONT AUDIT FIX — the price was outranking the result.
   `.pc-tile-o` (the outcome: "5th", "Won") sat at --fs-2xs while `.pc-tile-bsp` (the Betfair SP
   nested inside it) sat at --fs-xs, so the SECONDARY number rendered larger than the thing the row
   exists to report. Levelled to the same step; the price keeps its muted colour to stay secondary. */
.pc-tile-bsp { font-size: var(--fs-2xs); }

/* #890 Settled betslip rows as a list. The card shape is right for a LIVE bet — silk, trainer, a
   remove control — but once the race is run the question is a comparison across rows, which is a
   table.

   FOUR columns, deliberately. The first version had one per field and needed horizontal scrolling on
   a phone, which left each day's table at a different scroll offset — so the headers down the page
   disagreed and the whole thing read as broken. The secondary facts (bet type, meeting, race number)
   stack UNDER the runner instead, which fits the viewport. */
.bs-settled { width: 100%; font-size: var(--fs-xs); table-layout: fixed; }
.bs-settled th, .bs-settled td { padding: 0.45rem 0.4rem; vertical-align: middle; }
.bs-settled .num { text-align: right; font-variant-numeric: tabular-nums; }
/* The result cell carries a badge and, when a bet is unpriced on this basis, the REASON. Let it wrap
   instead of forcing the table wider than the screen — an unsettled row is exactly the one whose
   explanation matters most, so truncating it would hide the useful part. */
.bs-settled td:last-child { white-space: normal; line-height: 1.35; }
.bs-settled th.num { text-align: right; }
.bs-settled .bs-l-runner { font-weight: 700; line-height: 1.25; }
/* The qualifiers are a caption on the runner, not a second heading. */
.bs-settled .bs-l-sub {
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  line-height: 1.35;
  margin-top: 0.1rem;
}
/* Headers are the long strings ("Taken → close (Sportsbet)"), so let them wrap rather than force
   the table wider than the screen. */
.bs-settled th { white-space: normal; line-height: 1.2; font-size: var(--fs-2xs); }
@media (max-width: 480px) {
  .bs-settled { font-size: var(--fs-2xs); }
  .bs-settled th, .bs-settled td { padding: 0.4rem 0.25rem; }
}

/* ═══ Pipeline flow (#894 UI.pipelineFlow) — a funnel where each stage is a subset of the one above ═══
   Mobile-first: the stage stacks head/bar/note vertically and stays readable at 320px. No fixed
   widths — the bar is the only element that scales, and it scales by PASS-THROUGH not by count. */
.flow-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.65rem; }
.flow-stage { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.7rem 0.8rem; }
.flow-stage-worst { border-color: var(--danger); box-shadow: 0 0 0 1px var(--danger) inset; }
.flow-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.flow-label { font-weight: 600; }
.flow-arrow { color: var(--text-dim); }
.flow-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.flow-of { color: var(--text-muted); font-weight: 400; }
.flow-bar { position: relative; height: 1.15rem; background: var(--bg-subtle); border-radius: 999px; margin: 0.45rem 0 0.35rem; overflow: hidden; }
.flow-fill { display: block; height: 100%; border-radius: 999px; }
.flow-fill.flow-ok { background: var(--green); }
.flow-fill.flow-warn { background: var(--win); }
.flow-fill.flow-bad { background: var(--danger); }
.flow-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: flex-end;
            padding-right: 0.5rem; font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.flow-note { font-size: var(--fs-xs); color: var(--text-muted); }
.flow-service { font-size: var(--fs-xs); color: var(--text-dim); margin-top: 0.2rem; }
.flow-service code { background: var(--bg-subtle); padding: 0.05rem 0.3rem; border-radius: 3px; }
.flow-verdict { font-size: var(--fs-xs); color: var(--danger); font-weight: 600; margin-top: 0.35rem; }
.flow-endtoend { margin-top: 0.8rem; padding: 0.6rem 0.8rem; background: var(--bg-subtle);
                 border-radius: var(--radius); font-variant-numeric: tabular-nums; }

/* Health payload blocks (#900, now Diagnostics ?tab=health) — readable JSON that never forces the page to
   scroll sideways: the PRE scrolls inside its own box, which is the mobile-first rule for wide
   content. Monospace + tabular figures so a heap percentage lines up between refreshes. */
.health-json { margin: 0; padding: 0.6rem 0.75rem; background: var(--bg-subtle); border-radius: var(--radius);
               font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
               font-size: var(--fs-xs); line-height: 1.45; overflow-x: auto; white-space: pre; }

/* #923 Race-lifecycle timeline. The colour carries the v1→v2 state, and it is DERIVED server-side —
   a lifecycle diagram with hand-written statuses is the artefact this project keeps being misled by,
   so nothing here is styled from a constant the page owns. */
.lc-legend { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: 1rem; }
.lc-key { font-size: var(--fs-xs); padding: .15rem .5rem; border-radius: 999px; }
.lc-serving { font-size: var(--fs-xs); color: var(--text-muted); margin-left: auto; }
.lc-badge-v2    { background: rgba(45,212,191,.16); color: var(--ew); }
.lc-badge-dual  { background: rgba(var(--accent-rgb,106,90,212),.16); color: var(--accent); }
.lc-badge-v1    { background: rgba(var(--danger-rgb,211,31,30),.14); color: var(--danger); }
.lc-badge-empty { background: var(--bg-subtle); color: var(--text-muted); }

/* The rail runs through the dots, so the ORDER is readable as a sequence rather than a list. */
.lc-timeline { position: relative; padding-left: 1.4rem; }
.lc-timeline::before { content: ""; position: absolute; left: .34rem; top: .4rem; bottom: .4rem; width: 2px; background: var(--border); }
.lc-phase { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em;
            color: var(--text-muted); margin: 1rem 0 .4rem -1.4rem; }
.lc-row { position: relative; padding: .55rem 0 .7rem; }
.lc-dot { position: absolute; left: -1.24rem; top: .85rem; width: .62rem; height: .62rem;
          border-radius: 50%; background: var(--bg-card); border: 2px solid var(--border); }
.lc-v2 > .lc-dot   { border-color: var(--ew); }
.lc-dual > .lc-dot { border-color: var(--accent); }
.lc-v1 > .lc-dot   { border-color: var(--danger); }
.lc-head { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.lc-label { font-weight: 700; }
.lc-src { font-size: var(--fs-xs); color: var(--text-muted); }
.lc-status { font-size: var(--fs-xs); padding: .1rem .45rem; border-radius: 999px; margin-left: auto; }
.lc-detail { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.5; margin-top: .25rem; }
.lc-tables { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .45rem; }
.lc-tbl { font-family: ui-monospace, Consolas, monospace; font-size: var(--fs-xs);
          padding: .1rem .4rem; border-radius: var(--radius-sm, 5px); background: var(--bg-subtle); }
.lc-tbl b { font-variant-numeric: tabular-nums; }
.lc-canon { box-shadow: inset 2px 0 0 var(--ew); }
.lc-v2    { box-shadow: inset 2px 0 0 var(--accent); }
.lc-v1    { box-shadow: inset 2px 0 0 var(--danger); }
/* The join key is the field a box-and-arrow diagram always omits, and the one you debug from. */
.lc-join { font-size: var(--fs-xs); color: var(--text-muted); margin-top: .4rem; }
.lc-join code { font-size: var(--fs-xs); }
.lc-arch { color: var(--text-muted); opacity: .8; }

/* #1211 Track-alias admin: one course, however many names the providers give it. Plain rows —
   this is an operator tool, and the grouping is the information, not the chrome. */
.ta-group { border: 1px solid var(--border); border-radius: 6px; padding: 0.6rem 0.75rem; margin-bottom: 0.9rem; }
.ta-group-head { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-dim); margin-bottom: 0.4rem; }
.ta-row { display: flex; gap: 1rem; justify-content: space-between; align-items: flex-start;
          padding: 0.45rem 0; border-top: 1px solid var(--border); flex-wrap: wrap; }
.ta-row:first-of-type { border-top: 0; }
.ta-actions { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
