/* Football Oracle Dashboard - dark command-center theme */

@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@600;700;800;900&family=Rajdhani:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  --bg: #05070a;
  --bg-elev: #0a0f16;
  --panel: #0d131c;
  --panel-2: #121a26;
  --border: #1c2733;
  --border-glow: rgba(56, 232, 225, 0.25);
  --text: #e8f0f5;
  --text-dim: #7f909e;
  --text-faint: #4a5866;

  --accent-cyan: #38e8e1;
  --accent-violet: #a06bff;
  --accent-magenta: #ff5fb0;
  --accent-gold: #ffb84d;

  --good: #2ee6a0;
  --bad: #ff5c72;
  --pending: #ffc857;
  --neutral: #6b7fa3;

  --grad-signature: linear-gradient(120deg, var(--accent-cyan), var(--accent-violet) 55%, var(--accent-magenta));

  --radius: 14px;
  --radius-sm: 9px;
  --shadow-glow: 0 0 0 1px rgba(56,232,225,0.08), 0 20px 60px -20px rgba(0,0,0,0.7);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ambient background: slow-moving grid + radial glow */
.bg-fx {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(1200px 600px at 15% -10%, rgba(56,232,225,0.10), transparent 60%),
    radial-gradient(900px 700px at 110% 20%, rgba(160,107,255,0.10), transparent 60%),
    radial-gradient(800px 500px at 50% 120%, rgba(255,95,176,0.06), transparent 60%);
}
.bg-fx::before {
  content: "";
  position: absolute; inset: -200px;
  background-image:
    linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.025) 1px, transparent 1px);
  background-size: 42px 42px;
  animation: driftGrid 60s linear infinite;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 20%, black 40%, transparent 90%);
}
@keyframes driftGrid { from { transform: translate(0,0); } to { transform: translate(42px, 42px); } }
@media (prefers-reduced-motion: reduce) { .bg-fx::before { animation: none; } }

h1, h2, h3, h4 {
  font-family: 'Orbitron', 'Rajdhani', system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin: 0;
  color: var(--text);
}
.mono, code { font-family: 'IBM Plex Mono', monospace; }

a { color: var(--accent-cyan); text-decoration: none; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #1c2733; border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: #2a3a4a; }

/* ---------------- Shell ---------------- */
.shell {
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
}
.rail-toggle { display: none; }
.rail-backdrop { display: none; }

@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }

  /* Off-canvas drawer instead of stacking the nav above the page
     content - switching pages used to mean scrolling all the way back
     up past every card on the page just to reach the next nav button.
     Deniz's ask, 2026-08-23. */
  .rail {
    position: fixed !important; top: 0; left: 0; height: 100vh !important;
    width: min(78vw, 300px);
    z-index: 60;
    transform: translateX(-100%);
    transition: transform .25s ease;
    box-shadow: 0 0 40px rgba(0,0,0,0.5);
  }
  .rail.open { transform: translateX(0); }

  .rail-toggle {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    position: fixed; top: 14px; left: 14px; z-index: 70;
    width: 42px; height: 42px; border-radius: var(--radius-sm);
    background: var(--panel-2); border: 1px solid var(--border);
    box-shadow: var(--shadow-glow);
  }
  .rail-toggle-bar { width: 18px; height: 2px; background: var(--text); border-radius: 2px; transition: transform .2s ease, opacity .2s ease; }
  .rail-toggle[aria-expanded="true"] .rail-toggle-bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .rail-toggle[aria-expanded="true"] .rail-toggle-bar:nth-child(2) { opacity: 0; }
  .rail-toggle[aria-expanded="true"] .rail-toggle-bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

  .rail-backdrop {
    position: fixed; inset: 0; z-index: 55;
    background: rgba(4,7,11,0.6); backdrop-filter: blur(2px);
    opacity: 0; pointer-events: none; transition: opacity .2s ease;
  }
  .rail-backdrop.open { opacity: 1; pointer-events: auto; }

  /* !important: the base `main { padding: 28px ... }` rule (below,
     outside this media query) has equal specificity and comes later in
     the file, so it wins the normal cascade on any viewport - found
     live 2026-08-23, the hamburger button sat directly on top of the
     page title because this padding-top was silently losing. */
  main { padding-top: 76px !important; }
}
@media (prefers-reduced-motion: reduce) {
  .rail, .rail-backdrop, .rail-toggle-bar { transition: none; }
}

.rail {
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  background: linear-gradient(180deg, var(--bg-elev), var(--bg));
  border-right: 1px solid var(--border);
  padding: 22px 14px 20px;
  display: flex; flex-direction: column; gap: 6px;
}
.brand {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 6px 20px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 14px;
}
.brand-mark {
  width: 38px; height: 38px; border-radius: 10px; flex: none;
  background: var(--grad-signature);
  display: flex; align-items: center; justify-content: center;
  font-family: 'Orbitron'; font-weight: 900; color: #05070a; font-size: 1.1rem;
  box-shadow: 0 0 24px rgba(56,232,225,0.35);
}
.brand-text b { font-family: 'Orbitron'; font-size: 0.95rem; letter-spacing: 0.04em; display: block; }
.brand-text span { font-size: 0.62rem; color: var(--text-dim); font-family: 'IBM Plex Mono'; letter-spacing: 0.05em; }

.nav-item {
  all: unset; cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: var(--radius-sm);
  font-family: 'Rajdhani'; font-weight: 600; font-size: 0.98rem; letter-spacing: 0.02em;
  color: var(--text-dim);
  transition: background .15s, color .15s;
}
.nav-item .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint); flex: none; transition: background .15s, box-shadow .15s; }
.nav-item:hover { background: var(--panel); color: var(--text); }
.nav-item.active { background: var(--panel-2); color: var(--text); box-shadow: inset 2px 0 0 var(--accent-cyan); }
.nav-item.active .dot { background: var(--accent-cyan); box-shadow: 0 0 8px var(--accent-cyan); }
.nav-badge {
  margin-left: auto; font-family: 'IBM Plex Mono'; font-size: 0.72rem; font-weight: 700;
  color: var(--bg); background: var(--accent-cyan); padding: 1px 7px; border-radius: 10px; line-height: 1.5;
}
/* 2026-09-06, Deniz's ask: landing on Overview should auto-check live
   results (same free FotMob/ESPN/Sofascore-only grading call every
   other page already triggers), showing a small spinner in each of
   Predictions/Parlays/Fixtures/Player Props' own nav-badge slot while
   it runs, in place of - not next to - wherever the number already
   sits. */
.nav-badge-spinner {
  width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--border);
  border-top-color: var(--accent-cyan); animation: spin .8s linear infinite;
}

.rail-foot { margin-top: auto; padding: 12px 8px 0; border-top: 1px solid var(--border); font-size: 0.68rem; color: var(--text-faint); font-family: 'IBM Plex Mono'; }
.pulse-dot { display:inline-block; width:7px; height:7px; border-radius:50%; background: var(--good); box-shadow: 0 0 8px var(--good); animation: pulseDot 2s ease-in-out infinite; margin-right:6px; }
@keyframes pulseDot { 0%,100%{opacity:1;} 50%{opacity:0.35;} }

/* min-width: 0 (2026-09-06, real mobile bug: the whole Fixtures page
   scrolled sideways) - main is a CSS grid item (.shell's second
   column) and grid items default to min-width:auto, meaning they
   refuse to shrink below their content's intrinsic min width. A row
   with several non-wrapping children (.fixture-round's Round/odds
   text + Prediction button) set that intrinsic width wider than a
   phone viewport, and without this, main (and the whole page) was
   forced to that width instead of the child content wrapping/shrinking
   inside it. */
main { position: relative; padding: 28px clamp(16px, 3vw, 40px) 80px; max-width: 1400px; min-width: 0; }

/* ---------------- Page header ---------------- */
.page-eyebrow {
  font-family: 'IBM Plex Mono'; font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent-cyan); display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
}
.page-title { font-size: clamp(1.6rem, 3vw, 2.2rem); margin-bottom: 6px; text-wrap: balance; }
.page-sub { color: var(--text-dim); font-size: 0.92rem; margin-bottom: 26px; max-width: 70ch; }

/* ---------------- Cards / grids ---------------- */
.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1000px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

.card {
  background: linear-gradient(180deg, var(--panel), var(--bg-elev));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: var(--shadow-glow);
  position: relative;
  overflow: hidden;
}
.card.glow::before {
  content: ""; position: absolute; inset: 0; border-radius: var(--radius); padding: 1px;
  background: var(--grad-signature);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: 0.35;
}

.stat-card { display: flex; flex-direction: column; gap: 4px; }
.stat-label { font-family: 'IBM Plex Mono'; font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }
.stat-value { font-family: 'Orbitron'; font-size: 2rem; font-weight: 800; line-height: 1.1; }
.stat-value.grad { background: var(--grad-signature); -webkit-background-clip: text; background-clip: text; color: transparent; }
.stat-sub { font-size: 0.82rem; color: var(--text-dim); font-family: 'IBM Plex Mono'; }
.stat-sub.good { color: var(--good); }
.stat-sub.bad { color: var(--bad); }
/* 2026-09-07, real bug (Deniz's screenshot): the US Book/Iddaa/
   Combined columns in the Player Props breakdown didn't line up -
   each was its own independent flex column stacking label/record/
   units top-down, so any difference in content height between columns
   (plain text label vs <img> logo, a record cell with vs without a
   percentage) threw off every row below it. A real grid instead - see
   propBookColumnsHtml() in app.js - lays out label/record/units as
   actual grid ROWS, so each row's height is set once by its tallest
   cell and every column lines up on every row regardless of content. */
.prop-grid { display: grid; column-gap: 24px; row-gap: 6px; font-size: 0.85rem; }
.prop-grid-cell { display: flex; align-items: center; gap: 5px; }

/* ---------------- Pills ---------------- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 99px; font-size: 0.7rem; font-weight: 700;
  font-family: 'IBM Plex Mono'; letter-spacing: 0.02em; white-space: nowrap;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; }
.pill-high { background: rgba(46,230,160,0.13); color: var(--good); } .pill-high::before { background: var(--good); }
.pill-med  { background: rgba(160,107,255,0.15); color: var(--accent-violet); } .pill-med::before { background: var(--accent-violet); }
.pill-low  { background: rgba(107,127,163,0.18); color: var(--neutral); } .pill-low::before { background: var(--neutral); }
.pill-max  { background: rgba(255,184,77,0.15); color: var(--accent-gold); } .pill-max::before { background: var(--accent-gold); }
.pill-hit  { background: rgba(46,230,160,0.13); color: var(--good); } .pill-hit::before { background: var(--good); }
.pill-miss { background: rgba(255,92,114,0.13); color: var(--bad); } .pill-miss::before { background: var(--bad); }
.pill-void { background: rgba(107,127,163,0.13); color: var(--neutral); } .pill-void::before { background: var(--neutral); }
.pill-pending { background: rgba(255,200,87,0.13); color: var(--pending); } .pill-pending::before { background: var(--pending); animation: pulseDot 1.6s ease-in-out infinite; }
.pill-value { background: rgba(255,95,176,0.14); color: var(--accent-magenta); } .pill-value::before { background: var(--accent-magenta); }
/* 2026-09-06, Deniz's ask: a "Total" summary card above the per-bucket
   Parlay breakdown - none of the existing pill colors were free
   (Safe/Best/Standard already claim violet/gold/green), so Total gets
   the site's own primary accent instead. */
.pill-cyan { background: rgba(56,232,225,0.14); color: var(--accent-cyan); } .pill-cyan::before { background: var(--accent-cyan); }

/* ---------------- Pick / prop rows ---------------- */
.row-list { display: flex; flex-direction: column; gap: 10px; }
.pick-row {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 16px; transition: border-color .15s, transform .15s;
}
.pick-row:hover { border-color: var(--border-glow); }
.pick-row-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.pick-match { font-family: 'Rajdhani'; font-weight: 700; font-size: 1.02rem; }
.pick-market { color: var(--text-dim); font-size: 0.82rem; margin-top: 2px; }
.pick-odds { font-family: 'IBM Plex Mono'; font-size: 0.9rem; color: var(--text); }
.pick-right { display: flex; align-items: center; gap: 8px; }
.pick-rationale {
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border);
  font-size: 0.85rem; color: var(--text-dim); line-height: 1.55; cursor: pointer;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pick-rationale.expanded { -webkit-line-clamp: unset; }
.pnl { font-family: 'IBM Plex Mono'; font-weight: 700; font-size: 0.85rem; }
.stake-tag {
  font-family: 'IBM Plex Mono'; font-size: 0.76rem; color: var(--text-dim);
  background: var(--panel-2); border: 1px solid var(--border); padding: 2px 8px; border-radius: 20px;
}
.elite-star {
  color: var(--accent-gold);
  font-size: 1.5rem;
  line-height: 1;
  text-shadow: 0 0 10px rgba(255,184,77,0.75), 0 0 2px rgba(255,184,77,0.9);
  cursor: default;
  margin-left: 6px;
  vertical-align: middle;
  display: inline-block;
}
/* Violet, not gold, 2026-08-24 - the star glyph itself stays gold (ties
   to the per-pick star convention) but the CHIP's own color needed to
   be clearly out of the amber family: --pending (Pending status) and
   --accent-gold sit close enough on the wheel that an active Pending
   pill and an active Top Picks toggle read as "the same color" at a
   glance, exactly when a reader most needs to tell them apart. */
/* Slightly bigger and bolder than the status pills, not dramatically
   so - 2026-08-24, Deniz's first pass (0.92rem/7px 16px) read as much
   bigger rather than "a bit bigger," dialed back. Still meant to read
   as "click me" via color/glow rather than a large size jump. */
.elite-star-toggle {
  cursor: pointer; user-select: none;
  border: 1.5px solid rgba(160,107,255,0.55);
  color: var(--accent-violet);
  background: rgba(160,107,255,0.08);
  font-size: 0.84rem;
  padding: 5px 12px;
  box-shadow: 0 0 8px rgba(160,107,255,0.15);
  transition: background .15s, border-color .15s, box-shadow .15s, transform .15s;
}
.elite-star-toggle::before { content: none; }
.elite-star-toggle .star-ic { font-size: 0.95rem; color: var(--accent-gold); text-shadow: 0 0 6px rgba(255,184,77,0.7); }
.elite-star-toggle:hover {
  border-color: var(--accent-violet); background: rgba(160,107,255,0.14);
  box-shadow: 0 0 16px rgba(160,107,255,0.3); transform: translateY(-1px);
}
.elite-star-toggle.active {
  background: rgba(160,107,255,0.2); border-color: var(--accent-violet);
  box-shadow: 0 0 20px rgba(160,107,255,0.4);
}

/* ---------------- Game cards (picks grouped by fixture) ---------------- */
.game-card {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 18px 20px; animation: cardIn .3s ease both;
}
.game-card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.game-card-title { font-family: 'Rajdhani'; font-weight: 700; font-size: 1.3rem; letter-spacing: 0.01em; }
.game-card-sub { color: var(--text-dim); font-size: 0.82rem; margin-top: 3px; }
.single-pick-result-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 3px 9px;
  border-radius: var(--radius-sm);
  text-transform: uppercase;
  white-space: nowrap;
}
.single-pick-result-badge.hit {
  background: rgba(76, 222, 128, 0.15);
  color: var(--good);
  border: 1.5px solid var(--good);
  box-shadow: 0 0 10px rgba(76, 222, 128, 0.2);
}
.single-pick-result-badge.miss {
  background: rgba(255, 90, 90, 0.15);
  color: var(--bad);
  border: 1.5px solid var(--bad);
  box-shadow: 0 0 10px rgba(255, 90, 90, 0.2);
}
.single-pick-result-badge.void {
  background: rgba(160, 160, 160, 0.12);
  color: var(--text-dim);
  border: 1.5px solid var(--text-dim);
}
.game-picks-list { display: flex; flex-direction: column; gap: 4px; }
.game-pick-row + .game-pick-row { border-top: 1px dashed var(--border); padding-top: 10px; margin-top: 6px; }
.game-pick-summary {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; cursor: pointer; user-select: none;
}
.rationale-toggle {
  all: unset; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; color: var(--text-dim); font-size: 0.7rem;
  transition: transform .18s ease, color .15s; flex-shrink: 0;
}
.game-pick-row.expanded .rationale-toggle { transform: rotate(90deg); color: var(--accent-cyan); }
.game-pick-selection { font-size: 0.92rem; flex: 1; min-width: 160px; }
.game-pick-selection .market-tag { color: var(--text-dim); margin-right: 4px; }
.game-pick-rationale {
  display: none; margin: 8px 0 4px 28px; padding: 10px 12px; background: var(--panel-2);
  border-radius: var(--radius-sm); border-left: 2px solid var(--accent-violet);
  font-size: 0.84rem; color: var(--text-dim); line-height: 1.55;
}
.game-pick-row.expanded .game-pick-rationale { display: block; }

/* Fixtures page (2026-09-04) */
.team-logo { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; border-radius: 3px; }
.fixture-row { display: flex; align-items: center; gap: 14px; padding: 10px 14px; }
/* 2026-09-05: rows are now wrapped in .fixture-row-wrap (each carries
   its own expandable prediction panel below it), so the divider moved
   from .fixture-row's own sibling selector to the wrapper's. */
.fixture-row-wrap + .fixture-row-wrap { border-top: 1px dashed var(--border); }
.fixture-row-clickable { cursor: pointer; transition: background .12s; }
.fixture-row-clickable:hover { background: var(--panel-2); }
.fixture-time { font-family: 'IBM Plex Mono'; font-size: 0.78rem; color: var(--text-dim); width: 64px; flex-shrink: 0; }
/* Live score + ML odds (2026-09-04) widened this column from a plain
   time label into a small stack (score + live-clock/FT pill), so it's
   min-width rather than the old fixed 64px width - lets it grow for a
   two-digit score without wrapping. */
.fixture-time-col { min-width: 64px; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 3px; }
.fixture-teams { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
/* 2026-09-04 v5 (Deniz: "It should be like this -- Lyon: 3 1.4 (-250)")
   - v3/v4 kept spreading name/score and odds across the row's full
   width with justify-content:space-between, which visually orphaned
   the odds far from the team they belonged to no matter how the
   name/score grouping itself was fixed. .fixture-team is now a single
   tight left-aligned run - logo, "Team:", score, odds - same "hug
   together, no stretch" idea finally applied to the WHOLE row instead
   of just half of it. */
.fixture-team { display: flex; align-items: center; gap: 8px; font-size: 0.92rem; min-width: 0; }
.fixture-team-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex-shrink: 1; }
.fixture-team-score { font-family: 'IBM Plex Mono'; font-weight: 700; font-size: 0.92rem; color: var(--text); flex-shrink: 0; }
.fixture-ml { flex-shrink: 0; font-size: 0.78rem; color: var(--accent-cyan); }
.fixture-round { font-family: 'IBM Plex Mono'; font-size: 0.72rem; color: var(--text-faint); flex-shrink: 0; text-align: right; }
.fixture-ml-book { font-size: 0.64rem; color: var(--text-faint); margin-top: 4px; white-space: nowrap; }

/* Mobile reflow (2026-09-06, real bug: the whole page scrolled
   sideways on a phone). Desktop's 3-column row (time | teams | round)
   has two non-wrapping columns (fixed-width time, right-aligned round
   info with a nowrap bookmaker line and a Prediction button) that
   together don't fit a phone's width next to team names+odds. Below
   the breakpoint, the row wraps to two stacked lines instead: time/
   score/clock alongside the teams on top, round/odds-book/Prediction
   button below, everything left-aligned and allowed to wrap normally. */
@media (max-width: 640px) {
  .fixture-row { flex-direction: column; align-items: stretch; row-gap: 8px; }
  .fixture-time-col { flex-direction: row; align-items: center; justify-content: flex-start; }
  .fixture-round { text-align: left; white-space: normal; }
  .fixture-ml-book { white-space: normal; }
  .pill-prediction { margin-top: 8px; }
}

/* Expandable prediction (2026-09-05, Deniz's ask: "make the row
   expandable and show our prediction when they expand it... a button
   like Prediction... that tells the user we have something for that
   game"). .pill-prediction is a real <button>, not a styled <span>
   like every other pill here - reset the native button chrome first so
   it still just reads as a pill. */
.pill-prediction {
  all: unset; display: inline-flex; align-items: center; gap: 5px; margin-top: 6px;
  font-family: 'IBM Plex Mono'; font-size: 0.68rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--accent-violet); background: rgba(167, 139, 250, 0.12);
  border: 1px solid rgba(167, 139, 250, 0.35); border-radius: 20px;
  padding: 3px 10px; cursor: pointer; transition: all .15s;
}
.pill-prediction:hover { background: rgba(167, 139, 250, 0.22); border-color: var(--accent-violet); }
.fixture-prediction-arrow { display: inline-block; font-size: 0.6rem; transition: transform .18s ease; }
.fixture-row-wrap.expanded .fixture-prediction-arrow { transform: rotate(90deg); }
.fixture-row-wrap.expanded .pill-prediction { background: rgba(167, 139, 250, 0.22); border-color: var(--accent-violet); }

.fixture-picks-detail {
  display: none; margin: 0 14px 14px 14px; padding: 12px 14px; background: var(--panel-2);
  border-radius: var(--radius-sm); border: 1px solid var(--border);
}
.fixture-row-wrap.expanded .fixture-picks-detail { display: block; }
.fixture-pick-row + .fixture-pick-row { border-top: 1px dashed var(--border); padding-top: 10px; margin-top: 10px; }
.fixture-pick-summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fixture-pick-selection { font-size: 0.92rem; flex: 1; min-width: 160px; }
.fixture-pick-rationale {
  margin: 8px 0 0 0; padding: 10px 12px; background: var(--panel);
  border-radius: var(--radius-sm); border-left: 2px solid var(--accent-violet);
  font-size: 0.84rem; color: var(--text-dim); line-height: 1.55;
}

/* "Run Analysis" (2026-09-08, Deniz's ask: "a button to run analysis
   on any game I want") - accent-cyan (not the Prediction pill's
   violet) so the two are visually distinct at a glance: cyan means
   "nothing here yet, you can start it", violet means "there's
   something to expand". */
.pill-run-analysis {
  all: unset; display: inline-flex; align-items: center; gap: 5px; margin-top: 6px;
  font-family: 'IBM Plex Mono'; font-size: 0.68rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--accent-cyan); background: rgba(56, 232, 225, 0.1);
  border: 1px solid rgba(56, 232, 225, 0.3); border-radius: 20px;
  padding: 3px 10px; cursor: pointer; transition: all .15s;
}
.pill-run-analysis:hover { background: rgba(56, 232, 225, 0.2); border-color: var(--accent-cyan); }
.pill-run-analysis:disabled { cursor: default; opacity: 0.85; }

/* Near-miss candidates (2026-09-08) - the "closest picks" reasoning
   Deniz asked for, kept visually distinct (muted, dashed left border
   instead of the real pick's solid violet one) from an actual pick so
   there's never a doubt about which is which at a glance. */
.fixture-near-misses { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border); }
.fixture-near-misses-label {
  font-family: 'IBM Plex Mono'; font-size: 0.72rem; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 8px;
}
.fixture-near-miss-row { opacity: 0.85; }
.fixture-near-miss-row .fixture-pick-rationale { border-left-style: dashed; }
.fixture-near-miss-reason {
  margin: 6px 0 0 0; font-size: 0.8rem; color: var(--text-faint); font-style: italic;
}
.date-tabs { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.date-tab {
  background: var(--panel); border: 1px solid var(--border); color: var(--text-dim);
  padding: 7px 16px; border-radius: 20px; font-size: 0.85rem; cursor: pointer; transition: all .15s;
}
.date-tab:hover { border-color: var(--border-strong); color: var(--text); }
.date-tab.active { background: var(--accent-cyan); border-color: var(--accent-cyan); color: #05070a; font-weight: 600; }
.actual-pill {
  display: inline-block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.76rem;
  color: var(--accent-cyan);
  background: rgba(0, 229, 255, 0.08);
  border: 1px solid rgba(0, 229, 255, 0.25);
  padding: 1px 6px;
  border-radius: 4px;
  margin-left: 6px;
  font-weight: 500;
  vertical-align: middle;
}
.pill-live {
  background: rgba(255, 68, 68, 0.15);
  color: #ff4444;
  border: 1px solid rgba(255, 68, 68, 0.4);
  animation: pulseLive 2s infinite ease-in-out;
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 700;
  letter-spacing: 0.04em;
}
@keyframes pulseLive {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.65; transform: scale(0.98); }
}
.live-corners-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: #ffb74d;
  background: rgba(255, 183, 77, 0.1);
  border: 1px solid rgba(255, 183, 77, 0.35);
  padding: 2px 7px;
  border-radius: 4px;
  font-weight: 600;
  vertical-align: middle;
}
.pnl.pos { color: var(--good); } .pnl.neg { color: var(--bad); } .pnl.zero { color: var(--text-dim); }

.day-units-badge {
  display: flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: var(--radius-sm);
  border: 1px solid currentColor; font-family: 'IBM Plex Mono';
}
.day-units-badge.pos { color: var(--good); background: rgba(76,222,128,0.08); }
.day-units-badge.neg { color: var(--bad); background: rgba(255,90,90,0.08); }
.day-units-badge.zero { color: var(--text-dim); background: var(--panel); }
.day-units-label { font-family: 'Rajdhani'; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.85; }
.day-units-value { font-weight: 700; font-size: 1.1rem; }

/* height comes from an inline aspect-ratio (set per-render in
   unitsChartSvg(), narrower on small screens) - not a fixed px height -
   see that function's own comment for why: preserveAspectRatio="none"
   plus a viewBox/CSS-box mismatch was stretching every element
   (text, dots, stroke widths) non-uniformly on any width narrower than
   the chart's native 900px, worst on mobile. */
.units-chart { width: 100%; display: block; overflow: visible; }
.chart-bar-pos { fill: var(--good); opacity: 0.85; }
.chart-bar-neg { fill: var(--bad); opacity: 0.85; }
.chart-bar-zero { fill: var(--text-dim); opacity: 0.5; }
.chart-bar-pos:hover, .chart-bar-neg:hover { opacity: 1; }
.chart-cum-line { stroke: var(--accent-violet); stroke-width: 2.5; filter: drop-shadow(0 0 4px rgba(160,107,255,0.5)); }
.chart-dot { fill: var(--panel); stroke: var(--accent-violet); stroke-width: 2; }
.chart-gridline { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-zero-line { stroke: var(--border-glow); stroke-width: 1.5; }
.chart-axis-label { fill: var(--text-dim); font-family: 'IBM Plex Mono'; font-size: 11px; }
.chart-legend { display: flex; gap: 18px; font-size: 0.78rem; color: var(--text-dim); margin-top: 8px; }
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend .swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* ---------------- Buttons / controls ---------------- */
.btn {
  all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  font-family: 'Rajdhani'; font-weight: 600; font-size: 0.85rem; color: var(--text-dim);
  transition: all .15s;
}
.btn:hover { color: var(--text); border-color: var(--border-glow); background: var(--panel); }
.btn.primary { background: var(--grad-signature); color: #05070a; border: none; font-weight: 700; }
.btn.primary:hover { filter: brightness(1.1); }

.select, select {
  background: var(--panel); border: 1px solid var(--border); color: var(--text);
  padding: 7px 10px; border-radius: var(--radius-sm); font-family: 'IBM Plex Mono'; font-size: 0.82rem;
}

.tabbar { display: flex; gap: 6px; margin-bottom: 20px; border-bottom: 1px solid var(--border); }
.tabbar button {
  all: unset; cursor: pointer; padding: 10px 16px; font-family: 'Rajdhani'; font-weight: 600; font-size: 0.9rem;
  color: var(--text-dim); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabbar button.active { color: var(--accent-cyan); border-bottom-color: var(--accent-cyan); }

/* ---------------- Loading / empty ---------------- */
.loading { display: flex; align-items: center; gap: 10px; color: var(--text-dim); font-family: 'IBM Plex Mono'; font-size: 0.85rem; padding: 30px 0; }
.spinner { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--border); border-top-color: var(--accent-cyan); animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.empty-note { color: var(--text-dim); font-size: 0.9rem; padding: 30px 0; text-align: center; font-family: 'IBM Plex Mono'; }

/* ---------------- Section header ---------------- */
.section-header { display: flex; align-items: center; justify-content: space-between; margin: 30px 0 14px; flex-wrap: wrap; gap: 10px; }
.section-header h3 { font-size: 1.05rem; }
.section-header .count { font-family: 'IBM Plex Mono'; font-size: 0.75rem; color: var(--text-dim); }

/* ---------------- Confidence bar viz ---------------- */
.conf-bar-track { height: 8px; border-radius: 99px; background: var(--panel-2); overflow: hidden; display: flex; border: 1px solid var(--border); }
.conf-bar-seg { height: 100%; }

/* ---------------- Admin ---------------- */
.status-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; margin-right: 8px; }
.status-dot.ok { background: var(--good); box-shadow: 0 0 8px var(--good); }
.status-dot.bad { background: var(--bad); box-shadow: 0 0 8px var(--bad); }
.status-dot.warn { background: var(--pending); box-shadow: 0 0 8px var(--pending); }
.status-dot.minor { background: var(--accent-cyan); box-shadow: 0 0 8px var(--accent-cyan); }
.status-dot.degraded { background: var(--pending); box-shadow: 0 0 8px var(--pending); }
.run-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 4px; border-bottom: 1px solid var(--border); font-size: 0.85rem; }
.run-row:last-child { border-bottom: none; }
.run-name { font-family: 'Rajdhani'; font-weight: 600; }
.run-time { color: var(--text-faint); font-family: 'IBM Plex Mono'; font-size: 0.72rem; }
.log-box {
  background: #020406; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px; font-family: 'IBM Plex Mono'; font-size: 0.72rem; color: #8fe6c9;
  max-height: 260px; overflow-y: auto; white-space: pre-wrap; line-height: 1.6;
}
.freshness-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.82rem; }
.freshness-row:last-child { border-bottom: none; }
.age-badge { font-family: 'IBM Plex Mono'; font-size: 0.72rem; padding: 2px 8px; border-radius: 6px; }
.age-fresh { background: rgba(46,230,160,0.13); color: var(--good); }
.age-stale { background: rgba(255,200,87,0.13); color: var(--pending); }

.overall-health-banner {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  padding: 12px 16px; margin: 12px 0 16px;
  border-radius: var(--radius-sm); border: 1px solid var(--border);
}
.overall-health-banner .status-dot { width: 11px; height: 11px; margin-right: 0; }
.overall-health-label { font-family: 'Orbitron'; font-weight: 700; font-size: 0.95rem; letter-spacing: 0.02em; }
.overall-health-banner.ok { background: rgba(46,230,160,0.09); border-color: rgba(46,230,160,0.3); }
.overall-health-banner.ok .overall-health-label { color: var(--good); }
.overall-health-banner.minor { background: rgba(56,232,225,0.09); border-color: rgba(56,232,225,0.3); }
.overall-health-banner.minor .overall-health-label { color: var(--accent-cyan); }
.overall-health-banner.degraded { background: rgba(255,200,87,0.09); border-color: rgba(255,200,87,0.3); }
.overall-health-banner.degraded .overall-health-label { color: var(--pending); }
.overall-health-banner.bad { background: rgba(255,92,114,0.09); border-color: rgba(255,92,114,0.3); }
.overall-health-banner.bad .overall-health-label { color: var(--bad); }

.card-group-label {
  font-family: 'IBM Plex Mono'; font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 6px;
}

.issue-group { border-bottom: 1px solid var(--border); }
.issue-group:last-child { border-bottom: none; }
.issue-group summary {
  list-style: none; cursor: pointer; padding: 10px 4px;
  display: flex; align-items: center; justify-content: space-between; font-size: 0.85rem;
}
.issue-group summary::-webkit-details-marker { display: none; }
.issue-group .chevron { display: inline-block; margin-left: 4px; color: var(--text-faint); transition: transform .15s ease; }
.issue-group[open] .chevron { transform: rotate(180deg); }
.issue-detail-row { padding: 8px 4px 8px 24px; border-top: 1px dashed var(--border); font-size: 0.78rem; }
.age-old { background: rgba(255,92,114,0.13); color: var(--bad); }

/* responsive tables/overflow */
.scroll-x { overflow-x: auto; }

/* ---------------- Entrance motion ---------------- */
@keyframes cardIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.card { animation: cardIn .45s cubic-bezier(.16,1,.3,1) both; }
.card:nth-of-type(1) { animation-delay: 0ms; }
.card:nth-of-type(2) { animation-delay: 50ms; }
.card:nth-of-type(3) { animation-delay: 100ms; }
.card:nth-of-type(4) { animation-delay: 150ms; }
.pick-row { animation: cardIn .3s ease both; }
@media (prefers-reduced-motion: reduce) { .card, .pick-row { animation: none; } }
