/* ==========================================================================
   theme.css — Volleyball Predictor design system
   Loaded automatically by Dash from /assets.

   "Operational instrument" direction — shift5.io-derived — see
   docs/design/redesign-spec.md. This REPLACES the prior cyan/indigo glass
   "sci-fi HUD" direction wholesale: near-black monochrome canvas, ONE hot
   coral accent used sparingly, one grotesk + one monospace, giant display
   type, hairline crosshair/corner-tick/instrument-readout motifs, near-
   silent chrome (no glow, no gradient blobs, no ambient loops).

   Palette (kept in sync with the _BG/_SURFACE_1/... constants in app.py and
   every dashboard/pages module — change both together):
     bg          #0e0e0e      surface-1   #161616
     surface-2   #1e1e1e      nav         #0a0a0a
     text        #f2f0ed      text-sec    #8c8c8c      text-tert  #5a5a5a
     accent      #ff5841      accent-press #e04530
     line        #262626      line-strong #383838
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');

:root {
  /* Backgrounds — monochrome near-black, 3 elevation tiers */
  --bg:              #0e0e0e;   /* canvas */
  --surface-1:       #161616;   /* standard card */
  --surface-2:       #1e1e1e;   /* raised / hovered / nested */
  --nav:             #0a0a0a;   /* sticky header, darker than canvas */

  /* Back-compat aliases (kept so any un-migrated selector still resolves) */
  --card:            var(--surface-1);

  /* Text — warm near-white → mid grey → dim grey */
  --text:            #f2f0ed;
  --text-secondary:  #8c8c8c;
  --text-tertiary:   #5a5a5a;
  --muted:           var(--text-secondary); /* back-compat alias */

  /* Accent — THE color. Coral-red. Sparingly: active state, primary CTA,
     the one hero number, live indicators, focus rings. Not for large fills. */
  --accent:          #ff5841;
  --accent-press:    #e04530;   /* pressed/hover-darken of accent */

  /* Hairlines / structure */
  --line:            #262626;
  --line-strong:     #383838;

  /* Semantic (volleyball W/L) — loss reads as the coral accent, win as a
     single restrained cool-mint used ONLY for win marks */
  --win:             #3fe0a0;
  --loss:            #ff5841;

  /* Elo tiers — a monochrome ramp topped by the coral accent (Elite) */
  --tier-elite:        #ff5841;
  --tier-contender:    #f2f0ed;
  --tier-competitive:  #b4b4b4;
  --tier-developing:   #8c8c8c;
  --tier-rebuilding:   #5a5a5a;

  /* Fonts — ONE grotesk everywhere, ONE mono for all technical/data voice */
  --font-body: 'Space Grotesk', 'Arial', sans-serif;
  --font-head: 'Space Grotesk', 'Arial', sans-serif;
  --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
  --ease-out:  cubic-bezier(0.075, 0.82, 0.165, 1);  /* shift5 signature ease-out */

  /* Type scale — giant display like shift5's ~200px hero; exactly ONE
     --fs-mega/--fs-display moment per view (the number the page is about) */
  --fs-mega:      clamp(3.5rem, 12vw, 11rem);
  --fs-display:   clamp(2.5rem, 6vw, 4.5rem);
  --fs-h1:        clamp(1.6rem, 4.5vw, 2.25rem);
  --fs-h2:        1.5rem;
  --fs-h3:        1.125rem;
  --fs-body:      1rem;
  --fs-body-sm:   0.875rem;
  --fs-label:     0.6875rem;   /* eyebrows, table headers, metadata tags */
  --fs-mono-data: 0.8125rem;   /* Elo/stat numbers, badges */

  /* Spacing scale (4px base unit) */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;
  --space-6: 48px;
  --space-7: 64px;
  --space-8: 96px;

  /* Radius — near-square cards read as "instrument panel" against the
     corner-tick motif; buttons/pills stay fully rounded */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-pill: 9999px;

  /* Layout */
  --content-max: 1900px;
}

/* ── Base ───────────────────────────────────────────────────────────────── */

html, body {
  background-color: var(--bg) !important;
  color: var(--text);
  font-family: var(--font-body) !important;
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  scroll-behavior: smooth;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-head) !important;
  letter-spacing: -0.01em;
  color: var(--text);
}

::selection {
  background: rgba(255, 88, 65, 0.3);
  color: var(--text);
}

/* Custom scrollbar — neutral, no accent glow */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 6px;
  border: 2px solid var(--bg);
}
::-webkit-scrollbar-thumb:hover { background: #454545; }

/* Initial "Loading..." splash → quiet reticle ring, no glow */
._dash-loading {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: transparent;
  font-size: 0;
}
._dash-loading::after {
  content: "";
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Components mid-callback fade slightly instead of janking */
*[data-dash-is-loading="true"] {
  opacity: 0.45;
  transition: opacity 0.3s ease;
}

/* ── Global overflow safety ─────────────────────────────────────────────── */
/* Media never exceeds its container; wide data surfaces scroll inside their
   own box instead of pushing the page wider than the viewport. NOTE: html/body
   deliberately keep default overflow-x so real overflow bugs stay visible. */
img, svg, video, canvas {
  max-width: 100%;
  height: auto;
}
.dash-table-container .dash-spreadsheet-container,
.dash-spreadsheet-inner {
  max-width: 100%;
  overflow-x: auto;
}
pre, code { overflow-x: auto; }

/* ── Responsive layout utilities ────────────────────────────────────────── */
/* Page modules previously set `gridTemplateColumns` in inline `style={}` dicts,
   which no media query can override — so those grids stayed multi-column at
   320px and pushed the page wider than the viewport. These two classes replace
   them so the breakpoints live here, in one place.
   `min-width: 0` on the tracks is load-bearing: a bare `1fr` is
   `minmax(auto, 1fr)` and refuses to shrink below its content. */

/* Sidebar + main split (ranked index list beside a chart, filter beside table). */
.grid-split {
  display: grid;
  grid-template-columns: minmax(260px, 360px) minmax(0, 1fr);
  gap: var(--space-4);
}
.grid-split > * { min-width: 0; }
/* Wider sidebar variant — match-row lists (Results, Past Results) need more
   room for a date + two nation names than a ranked index list does. */
.grid-split--wide { grid-template-columns: minmax(280px, 420px) minmax(0, 1fr); }
/* Head-to-head selector: dropdown / "VS" / dropdown. VS is auto-width so the
   two dropdowns split the row; stacks to one column on phones. */
.grid-vs {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: var(--space-4);
  align-items: end;
}
.grid-vs > * { min-width: 0; }
@media (max-width: 640px) { .grid-vs { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 900px) {
  /* Below tablet the sidebar column can no longer hold its 260px floor beside
     a chart — stack instead of overflowing. */
  .grid-split,
  .grid-split--wide { grid-template-columns: minmax(0, 1fr); }
}

/* Filter/control rows. auto-fit + `min(100%, Npx)` means the row reflows from
   N columns to 1 on its own, with no breakpoint to keep in sync. */
.grid-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: var(--space-4);
  align-items: end;
}
.grid-filters > * { min-width: 0; }

/* ── Page shell ─────────────────────────────────────────────────────────── */

.page-shell {
  max-width: var(--content-max);
  margin: 0 auto;
  padding-left: clamp(24px, 4vw, 72px);
  padding-right: clamp(24px, 4vw, 72px);
}


/* Every content block on every page is a `.section`. Symmetric vertical
   margins so each block sits clear of the one above AND below it (adjacent
   block margins collapse, so section-to-section stays one --space-7, and the
   first section sits --space-7 under the page title/subnav). */
.section {
  margin-top: var(--space-7);
  margin-bottom: var(--space-7);
}

/* ── Header / brand ─────────────────────────────────────────────────────── */

/* Solid nav-black fill so it stays visible while sticky-scrolling over
   page content; all navigation lives in the #nav-menu overlay. */
.app-header {
  position: sticky;
  top: 0;
  z-index: 200;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  padding: 14px 28px;
  background: var(--nav);
  border-bottom: none;
  color: var(--text);
}
@media (max-width: 767px) {
  .app-header { padding: 10px 20px; }
}

/* ── Search bar (header, center) — visual only, not wired up yet ────────── */
.header-search {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 10px;
}
.header-search-label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  white-space: nowrap;
}
.header-search-input {
  width: 220px;
  background: transparent;
  border: 1px solid var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--text-secondary);
  outline: none;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.header-search-input::placeholder {
  color: var(--text-secondary);
  opacity: 0.7;
}
.header-search-input:focus {
  color: var(--text);
  border-color: var(--accent);
  /* Bootstrap/UA focus rings are blue — force the coral accent so the search
     bar stays on-palette when focused. */
  outline: none;
  box-shadow: none;
}
@media (max-width: 900px) {
  .header-search { display: none; }
}

/* ── Search results page ───────────────────────────────────────────────── */

/* Deliberately not `.page-shell`: that reserves a 372px right gutter for the
   SECTIONS rail, which the search page hides. Same centering + side padding,
   plus the vertical rhythm peer pages set inline (space-6 top / space-8 bottom)
   so the title box clears the sticky header instead of butting against it. */
.search-root {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: var(--space-6) var(--space-4) var(--space-8);
}

.search-results {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.search-result-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--line);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.2s ease, background-color 0.2s ease;
}
.search-result-row:last-child { border-bottom: none; }
.search-result-row:hover {
    color: var(--text);
    background-color: var(--surface-2);
}
.search-result-row--top { color: var(--text); }
.search-result-row--top:hover .search-result-title { color: var(--accent); }

.search-result-type-badge {
    flex-shrink: 0;
    padding: 2px var(--space-2);
    font-size: 0.6875rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    color: var(--text-tertiary);
    background: var(--bg);
}
.search-result-type-badge--nation   { border-color: var(--accent);         color: var(--accent); }
.search-result-type-badge--page     { border-color: var(--text-secondary); color: var(--text-secondary); }
.search-result-type-badge--player   { border-color: var(--text-secondary); color: var(--text-secondary); }
.search-result-type-badge--position { border-color: var(--text-secondary); color: var(--text-secondary); }
.search-result-type-badge--match    { border-color: var(--win);            color: var(--win); }

.search-result-title {
    flex-shrink: 0;
    color: var(--text);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 40%;
}

.search-result-meta {
    flex: 1 1 auto;
    color: var(--text-secondary);
    text-transform: none;
    letter-spacing: 0.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.search-result-arrow {
    flex-shrink: 0;
    margin-left: var(--space-2);
    color: var(--text-tertiary);
    font-size: 1rem;
    line-height: 1;
}
.search-result-row:hover .search-result-arrow { color: var(--accent); }

/* ── MENU trigger (header, right) ──────────────────────────────────────── */

.nav-menu-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  border: none;
  padding: 6px 2px;
  cursor: pointer;
  color: var(--text);
}
.nav-menu-btn .fs-label {
  color: var(--text);
  letter-spacing: 0.18em;
  transition: color 0.2s ease;
}
.nav-menu-btn:hover .fs-label {
  color: var(--accent);
}

/* Pure-CSS two-line "menu" glyph — no icon font, no third-party mark. */
.nav-menu-icon {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 20px;
}
.nav-menu-icon::before,
.nav-menu-icon::after {
  content: "";
  display: block;
  height: 1px;
  width: 100%;
  background: var(--text);
  transition: background 0.2s ease;
}
.nav-menu-btn:hover .nav-menu-icon::before,
.nav-menu-btn:hover .nav-menu-icon::after {
  background: var(--accent);
}

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  /* Auto-contrast: the transparent sticky header scrolls over sections whose
     background can equal the logo's own color (coral wordmark over the coral
     hero, light "HUB" over a light panel), making it vanish. `difference`
     blends the logo against whatever is behind it, so it always renders as the
     OPPOSITE color of the background it's currently over. Needs clean base
     colors below (white) for the inversion to read correctly. */
  mix-blend-mode: difference;
}

/* Brand mark: static instrument reticle (crosshair + coral center dot).
   Single one-shot fade/scale on load — NOT a looping/orbiting animation. */
.brand-mark {
  position: relative;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border: 1px solid var(--line-strong);
  background-color: transparent;
  background-image:
    linear-gradient(to right, transparent calc(50% - 0.5px), var(--line-strong) calc(50% - 0.5px), var(--line-strong) calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(to bottom, transparent calc(50% - 0.5px), var(--line-strong) calc(50% - 0.5px), var(--line-strong) calc(50% + 0.5px), transparent calc(50% + 0.5px));
  /* CSS bloom is NOT applied here — assets/anim.js drives the reticle
     bloom via anime.js so it composes cleanly with the rest of the boot-up
     choreography. Reduced-motion users get a static mark through the
     js-anim reset below. */
}
.brand-mark::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  transform: translate(-50%, -50%);
}
@keyframes brand-in {
  from { opacity: 0; transform: scale(0.85); }
  to   { opacity: 1; transform: scale(1); }
}
/* (brand-in is now dead — bootReticle() in assets/anim.js drives the bloom
   via anime.js. Kept only so any external reference still parses.) */

.brand-title {
  font-family: var(--font-head);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
/* White bases so the .brand `mix-blend-mode: difference` inverts cleanly to the
   true opposite of whatever background scrolls under the header. */
.brand-title .brand-league {
  color: #ffffff;
}
.brand-title .brand-rest {
  color: #ffffff;
  font-weight: 500;
  opacity: 0.82;
}

.brand-sub {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #ffffff;
  opacity: 0.55;
  margin-top: 2px;
}

/* ── League segmented switch (dead CSS if league switch is unmounted;
     harmless, kept for when NCAA is re-wired) ───────────────────────────── */

.league-switch {
  display: flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface-1);
}

.league-pill {
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-head);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 7px 18px;
  cursor: pointer;
  transition: color 0.25s ease, background 0.25s ease,
              border-color 0.25s ease;
}
.league-pill:hover { color: var(--text); }

.league-pill--active {
  color: var(--bg);
  background: var(--accent);
  border-color: var(--accent);
}
.league-pill--active:hover { color: var(--bg); }

/* ── Tabs ───────────────────────────────────────────────────────────────── */
/* dcc.Tabs#vnl-tabs stays mounted as the active-tab STATE (render_vnl_tab +
   the Home-jump callback both depend on it) but is visually retired — all
   navigation now happens through the #nav-menu overlay. */

.ld-tabs {
  display: none !important;
  position: sticky;
  top: 67px;                       /* sits just under the sticky header */
  z-index: 150;
  background: rgba(10, 10, 10, 0.9) !important;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line) !important;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
@media (max-width: 767px) {
  .ld-tabs { top: 56px; }
}

.ld-tab {
  position: relative;
  background: transparent !important;
  border: none !important;
  color: var(--text-secondary) !important;
  font-family: var(--font-head);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 14px 20px !important;
  white-space: nowrap;
  transition: color 0.2s ease, background 0.2s ease;
}
.ld-tab:hover {
  color: var(--text) !important;
  background: rgba(255, 88, 65, 0.06) !important;
}

.ld-tab--active {
  background: transparent !important;
  border: none !important;
  color: var(--accent) !important;
  font-weight: 700;
}
/* Solid coral underline, no glow — restrained per the new motion budget */
.ld-tab--active::after {
  content: "";
  position: absolute;
  left: 16%;
  right: 16%;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  animation: underline-in 0.3s var(--ease-out) both;
}
@keyframes underline-in {
  from { left: 45%; right: 45%; opacity: 0; }
  to   { left: 16%; right: 16%; opacity: 1; }
}

/* ── Page transitions ───────────────────────────────────────────────────── */

#tab-content > div,
#vnl-tab-content > div {
  animation: page-in 0.4s var(--ease-out) both;
}
@keyframes page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Chart entrance: fade the Plotly container in on first render. */
.js-plotly-plot {
  animation: chart-in 0.5s var(--ease-out) both;
}
@keyframes chart-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── Typography utilities ───────────────────────────────────────────────── */

.fs-mega {
  font-family: var(--font-head);
  font-size: var(--fs-mega);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.03em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.fs-display {
  font-family: var(--font-head);
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: 1.0;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.fs-h1 {
  font-family: var(--font-head);
  font-size: var(--fs-h1);
  font-weight: 700;
  line-height: 1.1;
  color: var(--text);
}
.fs-h2 {
  font-family: var(--font-head);
  font-size: var(--fs-h2);
  font-weight: 600;
  line-height: 1.2;
  color: var(--text);
}
.fs-h3 {
  font-family: var(--font-head);
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
}
.fs-label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.fs-mono-data {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-data);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.text-secondary { color: var(--text-secondary) !important; }
.text-tertiary  { color: var(--text-tertiary) !important; }

/* ── Motifs: metadata tags, crosshair, dividers, hairline grid ──────────── */

/* Mono metadata-tag eyebrow: `[ VNL // 2026 ]`, `01 / 06`, `ELO ▸ 1730.2` —
   page authors supply the bracket/glyph characters as text content, this
   class supplies the instrument-readout look. */
.meta-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.meta-tag--accent { color: var(--accent); }

/* Page title box: coral instrument-panel label anchoring each VNL subpage's
   top-left. Dark text on the one large coral fill the design otherwise avoids. */
.page-title-box {
  display: inline-block;
  background: var(--accent);
  color: var(--bg);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: var(--fs-h2);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-4);
}

/* Crosshair / reticle accent — thin hairline cross, echoes the shift5
   loader reticle. Reusable near heroes and as an empty-state mark. */
.crosshair {
  position: relative;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
}
.crosshair::before,
.crosshair::after {
  content: "";
  position: absolute;
  background: var(--line-strong);
}
.crosshair::before { top: 50%; left: 0; right: 0; height: 1px; transform: translateY(-50%); }
.crosshair::after   { left: 50%; top: 0; bottom: 0; width: 1px; transform: translateX(-50%); }

/* One-shot hero "scan" line sweep — plays once on load, then static. */
.hero-scan {
  position: relative;
  overflow: hidden;
}
.hero-scan::after {
  content: "";
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: scan-once 1s var(--ease-out) 1 both;
}
@keyframes scan-once {
  from { left: -100%; }
  to   { left: 100%; }
}

/* Divider with tick: a hairline rule with a short coral tick at the left. */
.divider-tick {
  position: relative;
  height: 1px;
  background: var(--line);
  margin: var(--space-4) 0;
}
.divider-tick::before {
  content: "";
  position: absolute;
  left: 0;
  top: -1px;
  width: 32px;
  height: 3px;
  background: var(--accent);
}

/* Hairline grid — faint 1px background grid for hero/feature areas only.
   Desktop only (disabled <768px); much subtler than the old animated grid. */
.hairline-grid {
  position: relative;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 48px 48px, 48px 48px;
}
@media (max-width: 767px) {
  .hairline-grid { background-image: none; }
}

/* ── Cards — corner-tick "instrument frame" instead of a full border ────── */

.corner-ticks,
.card,
.feature-card,
.stat-tile {
  position: relative;
  background-color: var(--surface-1) !important;
  background-image:
    linear-gradient(to right, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to left, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to right, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to top, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to left, var(--line-strong) 1px, transparent 1px),
    linear-gradient(to top, var(--line-strong) 1px, transparent 1px);
  background-repeat: no-repeat;
  background-size: 10px 1px, 1px 10px, 10px 1px, 1px 10px, 10px 1px, 1px 10px, 10px 1px, 1px 10px;
  background-position: top left, top left, top right, top right,
                        bottom left, bottom left, bottom right, bottom right;
}

/* Standard card — grouped stats, filters, small charts */
.card {
  border-radius: var(--radius-sm) !important;
  padding: var(--space-4);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  transition: background-color 0.25s ease;
}
.card:hover {
  background-color: var(--surface-2) !important;
}

/* Feature card — the page's primary chart / hero content. Larger padding
   signals "this is the important thing." */
.feature-card {
  border-radius: var(--radius-sm) !important;
  padding: var(--space-6);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  transition: background-color 0.25s ease;
}
.feature-card:hover {
  background-color: var(--surface-2) !important;
}
@media (max-width: 1199px) {
  .feature-card { padding: var(--space-4); }
}

/* ── Stat tiles / KPIs ──────────────────────────────────────────────────── */

.stat-tile-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
}
@media (max-width: 1199px) {
  .stat-tile-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 767px) {
  .stat-tile-row { grid-template-columns: 1fr; }
}

.stat-tile {
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.stat-tile-label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.stat-tile-value {
  font-family: var(--font-mono);
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 500;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  transition: color 0.4s ease;
}

.stat-tile-delta {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-data);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: fit-content;
}
.stat-tile-delta--up   { color: var(--win); }
.stat-tile-delta--down { color: var(--loss); }

/* ── Mono index list (§C) — signature pattern for ranked/ordered data:
   Rankings, Position Rankings, Players. `NN. LABEL ···· value  •` ────────── */

.mono-index-list {
  display: flex;
  flex-direction: column;
}
.mono-index-row {
  display: flex;
  align-items: baseline;
  min-height: 30px;
  padding: 4px 0;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--line);
}
.mono-index-row:last-child { border-bottom: none; }
.mono-index-row--top { color: var(--text); }
.mono-index-row .mono-index-num {
  flex-shrink: 0;
  color: var(--text-tertiary);
  margin-right: var(--space-2);
}
.mono-index-row--top .mono-index-num { color: var(--accent); }
.mono-index-row .mono-index-label {
  flex-shrink: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mono-index-row .mono-index-leader {
  flex: 1 1 auto;
  align-self: center;
  height: 0;
  min-width: 12px;
  margin: 0 8px;
  border-bottom: 1px dotted var(--line-strong);
}
.mono-index-row .mono-index-value {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.mono-index-row .mono-index-dot {
  flex-shrink: 0;
  margin-left: var(--space-2);
  color: var(--text-tertiary);
  font-size: 1rem;
  line-height: 1;
}
.mono-index-row--top .mono-index-dot { color: var(--accent); }

/* Rankings header — title block (left) + in-page SECTIONS nav box (right). */
.rankings-header {
  display: grid;
  grid-template-columns: 1fr minmax(260px, 340px);
  gap: var(--space-6);
  align-items: start;
}
@media (max-width: 900px) {
  .rankings-header { grid-template-columns: 1fr; }
}

/* ── Dropdowns (Dash 4 dcc.Dropdown — Radix-based) ──────────────────────── */
/* Minimal underline-style trigger: quiet form control, no bordered box. */

button.dash-dropdown {
  background-color: transparent !important;
  border: none !important;
  border-bottom: 1px solid var(--line) !important;
  border-radius: 0 !important;
  color: var(--text) !important;
  padding-left: 2px !important;
  transition: border-color 0.2s ease;
}
button.dash-dropdown:hover {
  border-bottom-color: var(--line-strong) !important;
}
button.dash-dropdown:focus-visible,
button.dash-dropdown[data-state="open"] {
  border-bottom-color: var(--accent) !important;
  outline: none;
}
.dash-dropdown-value,
.dash-dropdown-value-item { color: var(--text) !important; }
.dash-dropdown-placeholder { color: var(--text-secondary) !important; }
.dash-dropdown-trigger-icon { color: var(--accent); }

/* Open dropdown menu must paint ABOVE charts/cards. Dash portals the menu
   to <body>, so a high z-index on the content (and its Radix portal wrapper)
   keeps it on top of Plotly graphs everywhere. */
.dash-dropdown-content,
[data-radix-popper-content-wrapper] {
  z-index: 1000 !important;
}
/* When the menu renders inline (not portaled), its z-index is trapped inside
   the filters card's auto-level stacking context, so later-DOM stat tiles
   paint over it. Elevate the card that holds an OPEN dropdown so its whole
   subtree (incl. the menu) sits above following siblings. */
.card:has(button.dash-dropdown[data-state="open"]) {
  position: relative;
  z-index: 40;
}
.dash-dropdown-content {
  background: var(--surface-2) !important;
  border: 1px solid var(--line-strong) !important;
  border-radius: var(--radius-md) !important;
  color: var(--text) !important;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
  /* Fade only — no translateY. Radix positions the popper with its own
     transform; animating transform here fights it and makes the menu
     bounce up/down. Opacity-only keeps the menu locked in place. */
  animation: menu-in 0.15s var(--ease-out) both;
}
@keyframes menu-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* Children of the menu pull white fills from Dash design tokens — flatten
   them so the dark .dash-dropdown-content background shows through. */
.dash-dropdown-content .dash-dropdown-grid-container,
.dash-dropdown-search-container,
.dash-options-list {
  background: transparent !important;
}
.dash-dropdown-search {
  background: var(--surface-1) !important;
  border: 1px solid var(--line) !important;
  border-radius: var(--radius-sm) !important;
  color: var(--text) !important;
}
.dash-dropdown-search:focus {
  border-color: var(--accent) !important;
  outline: none;
}
.dash-dropdown-search-icon { color: var(--text-secondary); }

.dash-dropdown-option,
.dash-options-list-option {
  background: transparent !important;
  color: var(--text-secondary) !important;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.dash-dropdown-option:hover,
.dash-options-list-option:hover,
.dash-options-list-option.focused {
  background: rgba(255, 88, 65, 0.1) !important;
  color: var(--text) !important;
}
.dash-dropdown-option.selected,
.dash-options-list-option.selected {
  color: var(--accent) !important;
}
.dash-options-list-option-checkbox { accent-color: var(--accent); }

@media (max-width: 767px) {
  button.dash-dropdown { min-height: 44px; }
  .dash-options-list-option { min-height: 44px; display: flex; align-items: center; }
}

/* ── DataTables ─────────────────────────────────────────────────────────── */

.dash-table-container {
  border-radius: var(--radius-md);
  overflow: hidden;
}
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th {
  font-family: var(--font-body) !important;
  font-variant-numeric: tabular-nums;
  height: 44px;
  border-bottom: 1px solid var(--line) !important;
  transition: background-color 0.15s ease;
}
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th {
  font-family: var(--font-mono) !important;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 11px !important;
  background-color: var(--surface-2) !important;
}
/* Near-zero zebra striping */
.dash-spreadsheet-inner tr:nth-child(even) td {
  background-color: rgba(255, 255, 255, 0.015);
}
/* Row hover — subtle black -> dark gray, text stays readable */
.dash-spreadsheet-inner tr:hover td {
  background-color: #1b1b1b !important;
  color: var(--text) !important;
}
/* Override Dash's default white/light active+selected+focused cell highlight
   (it was rendering white-on-white and hiding the row) — keep it a subtle dark
   gray with readable text. */
.dash-spreadsheet-inner td.cell--selected,
.dash-spreadsheet-inner td.focused,
.dash-spreadsheet-inner td.cell--active,
.dash-spreadsheet-inner td.dash-cell.focused,
.dash-spreadsheet-inner .dash-cell.focused,
.dash-spreadsheet-inner tr:hover td.cell--selected {
  background-color: #1e1e1e !important;
  color: var(--text) !important;
  border: 1px solid var(--line-strong) !important;
}
.dash-table-container .previous-next-container .page-number,
.dash-table-container .previous-next-container button {
  color: var(--text-secondary) !important;
  background: transparent !important;
  border: none;
}
.dash-table-container .previous-next-container button:hover {
  color: var(--accent) !important;
}
/* Filter row inputs */
.dash-table-container .dash-filter input {
  color: var(--text) !important;
}

/* ── Buttons / badges / inputs ──────────────────────────────────────────── */

.btn {
  font-family: var(--font-head) !important;
  letter-spacing: 0.02em;
  border-radius: var(--radius-pill) !important;
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.btn:active { filter: brightness(0.96); }

/* Primary — filled coral CTA, dark text, no lift on hover */
.btn-primary,
.btn.btn-primary {
  background: var(--accent) !important;
  border: none !important;
  color: var(--bg) !important;
}
.btn-primary:hover,
.btn.btn-primary:hover {
  background: var(--accent-press) !important;
}

/* Secondary — ghost, 1px line-strong border, coral border on hover */
.btn-secondary,
.btn.btn-secondary {
  background: transparent !important;
  border: 1px solid var(--line-strong) !important;
  color: var(--text) !important;
}
.btn-secondary:hover,
.btn.btn-secondary:hover {
  border-color: var(--accent) !important;
  color: var(--text) !important;
}

.badge {
  font-family: var(--font-mono) !important;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  background: transparent;
  border: 1px solid currentColor;
  opacity: 1;
}
.badge.bg-success,
.badge[style*="3fe0a0"] { color: var(--win); background: rgba(63, 224, 160, 0.08) !important; }
.badge.bg-danger,
.badge[style*="ff5841"] { color: var(--loss); background: rgba(255, 88, 65, 0.08) !important; }

input[type="radio"], input[type="checkbox"] { accent-color: var(--accent); }

/* dcc.Slider */
.rc-slider-track { background-color: var(--accent) !important; }
.rc-slider-rail { background-color: var(--line) !important; }
.rc-slider-handle {
  border-color: var(--accent) !important;
  background-color: var(--bg) !important;
  box-shadow: none !important;
}
.rc-slider-dot { border-color: var(--line) !important; background: var(--surface-1) !important; }
.rc-slider-dot-active { border-color: var(--accent) !important; }
.rc-slider-mark-text { color: var(--text-secondary) !important; font-family: var(--font-mono); font-size: 10px; }

/* ── Home / landing page ("Volleyball Hub") ─────────────────────────────── */

/* Real SVG brand logo mark in the top-left header. Sized to sit on the
   wordmark baseline. */
.brand-logo {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  display: block;
}

.wordmark-figure {
  position: relative;
}
/* Full-viewport, full-bleed hero grid — coral / light / dark cells sit
   flush with zero gap (no gutters, no rounded corners, no margins) so the
   composition reads as one solid filled screen below the app chrome.
   Stacks to 1 column under 900px; .fs-mega inside clamps down on its own. */
.home-root {
  width: 100%;
}

.home-hero {
  display: grid;
  grid-template-columns: 1.6fr 0.9fr;
  min-height: calc(100vh - 130px);
}
@media (max-width: 900px) {
  .home-hero { grid-template-columns: 1fr; min-height: 0; }
}

.home-hero-cell {
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
}

/* Coral cell — the giant brand wordmark. Only chromatic fill on the page
   besides the light cell. */
.home-hero-coral {
  position: relative;
  background-color: var(--accent);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  color: var(--bg);
  justify-content: space-between;
}
.home-hero-coral .fs-mega,
.home-hero-coral .fs-h3 {
  color: inherit;
}
/* The VOLLEYBALL HUB wordmark must ALWAYS fully fit the coral cell — never
   clip on wide screens / browser fullscreen. Sized off the coral column width
   (smaller than the global --fs-mega, which is tuned for a full-width hero),
   and allowed to wrap. */
.home-hero-coral .fs-mega {
  font-size: clamp(2.25rem, 7.5vw, 6.5rem);
  line-height: 0.95;
  /* Break ONLY at the explicit <br> (VOLLEYBALL / HUB) — never mid-word. */
  white-space: nowrap;
  max-width: 100%;
}

/* Crosshair override for use on the coral hero cell (needs dark lines,
   not the default --line-strong, to stay visible on coral). */
.home-hero-crosshair {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  opacity: 0.5;
}
.home-hero-crosshair::before,
.home-hero-crosshair::after {
  background: var(--bg);
}

/* Tagline band — the supporting statement on its OWN full-width row below the
   hero (all screens), no longer a middle hero column: light grey fill, dark
   text. Lets the coral brand block dominate the hero row. */
.home-tagline-band {
  background-color: #c9c9c9;
  color: var(--bg);
  padding: var(--space-5) clamp(24px, 6vw, 96px);
}
.home-tagline-band .home-tagline-text {
  color: var(--bg);
  max-width: 900px;
  margin: 0;
}

/* Dark cell — COVERAGE eyebrow + mono league index + readout strip. */
.home-hero-dark {
  background-color: var(--surface-2);
  color: var(--text);
}

/* Decorative mono "readout" strip under the league index — purely
   stylistic instrument texture, not real telemetry. Sinks to the bottom
   of the dark cell. */
.home-readout-strip {
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  line-height: 1.9;
  color: var(--text-tertiary);
  opacity: 0.7;
}

/* Clickable league-index row (VNL) — jumps straight into the VNL app */
.home-index-row {
  cursor: pointer;
  transition: color 0.2s ease;
}
.home-index-row:hover { color: var(--text); }
.home-index-row:hover .mono-index-num { color: var(--accent); }

/* ── VNL league band — full-bleed section below the hero; the entry point
   into the VNL app. Written as a reusable template so future league bands
   (AVP, etc.) can be appended below it with the same shape. ────────────── */
.home-vnl-band {
  width: 100%;
  background-color: var(--bg);
  border-top: 1px solid var(--line);
  padding: var(--space-7) 0;
}
.home-vnl-band-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 clamp(24px, 6vw, 96px);
}

/* Live cards row — TOP RANKED / LATEST RESULT / NEXT UP */
.home-live-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}
@media (max-width: 900px) {
  .home-live-row { grid-template-columns: 1fr; }
}

/* legacy — replaced by .home-cta-slab but kept for safety */
/* Coral CTA panel — full-width, dark text, big arrow glyph */
.home-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  width: 100%;
  background: var(--accent);
  border: none;
  border-radius: var(--radius-sm);
  padding: var(--space-6);
  cursor: pointer;
  text-align: left;
  transition: background-color 0.2s ease;
}
.home-cta:hover { background: var(--accent-press); }
.home-cta-arrow {
  font-family: var(--font-head);
  font-size: clamp(2rem, 4vw, 3rem);
  line-height: 1;
  color: var(--bg);
  flex-shrink: 0;
}

/* ── Footer ─────────────────────────────────────────────────────────────── */

.app-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 22px 28px 26px;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.app-footer .pulse-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  animation: breathe 2.4s ease-in-out infinite;
}
@keyframes breathe {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}

/* ── Responsive: tablet landscape / portrait ────────────────────────────── */

@media (max-width: 991px) {
  .feature-card { padding: var(--space-4); }
}

/* ── Fullscreen nav menu overlay ──────────────────────────────────────────
   Hidden by default; app.py toggles the "nav-menu--open" modifier class.
   Pattern adapted to our own dark theme: monochrome canvas, coral accent on
   hover/active only, one grotesk + one mono, no third-party marks/copy. */

/* Dim backdrop — the page shows through, darkened; the grey panel floats
   top-right and does NOT cover the whole screen. */
.nav-menu {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  padding: 20px;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s var(--ease-out), visibility 0s linear 0.3s;
}
.nav-menu--open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.3s var(--ease-out), visibility 0s linear 0s;
}

/* Grey panel — light grey card, dark text, top-right, fits its content. */
.nav-menu-panel {
  width: clamp(320px, 42vw, 600px);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: #c9c9c9;
  color: var(--bg);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-6) var(--space-6);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  transform: translateY(-10px);
  transition: transform 0.3s var(--ease-out);
}
.nav-menu--open .nav-menu-panel {
  transform: translateY(0);
}
@media (max-width: 767px) {
  .nav-menu { padding: 12px; }
  .nav-menu-panel { width: 100%; padding: var(--space-4); }
}

.nav-menu-close {
  display: block;
  margin-left: auto;
  background: transparent;
  border: none;
  color: var(--bg);
  letter-spacing: 0.18em;
  padding: 4px;
  cursor: pointer;
  transition: color 0.2s ease;
}
.nav-menu-close:hover { color: var(--accent); }

.nav-menu-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.nav-menu-item {
  font-family: var(--font-head);
  font-size: clamp(1.6rem, 4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.1;
  color: var(--bg);
  cursor: pointer;
  width: fit-content;
  transition: color 0.2s ease;
}
.nav-menu-item:hover,
.nav-menu-item:focus-visible {
  color: var(--accent);
}

.nav-menu-item sup {
  font-family: var(--font-mono);
  font-size: 0.35em;
  font-weight: 500;
  color: var(--text-tertiary);
  margin-left: 6px;
  vertical-align: super;
}
.nav-menu-item:hover sup,
.nav-menu-section-toggle:hover sup {
  color: var(--accent);
}

.nav-menu-section {
  display: flex;
  flex-direction: column;
}

.nav-menu-sublist {
  display: none;
  flex-direction: column;
  gap: 4px;
  margin-top: var(--space-3);
  padding-left: clamp(1rem, 3vw, 2rem);
}
.nav-menu-sublist--open {
  display: flex;
}

.nav-menu-subitem {
  font-family: var(--font-head);
  font-size: clamp(1rem, 2vw, 1.35rem);
  font-weight: 500;
  color: #333333;
  cursor: pointer;
  width: fit-content;
  transition: color 0.2s ease;
}
.nav-menu-subitem:hover,
.nav-menu-subitem:focus-visible {
  color: var(--accent);
}
/* The "↳ " glyph itself always stays dim, even on hover of the label. */
.nav-menu-caret {
  color: var(--text-tertiary);
}

/* ── Motion preferences ─────────────────────────────────────────────────── */

/* ── Scroll-reveal (shift5-style) — gated behind html.js-anim so no-JS or
   reduced-motion never hides content. One-shot: .is-inview stays. ───────── */
html.js-anim [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.55s var(--ease-out), transform 0.55s var(--ease-out);
  transition-delay: calc(var(--reveal-i, 0) * 70ms);
  will-change: opacity, transform;
}
html.js-anim [data-reveal].is-inview {
  opacity: 1;
  transform: none;
}

/* ── anime.js boot-up "is-staged" pre-reveal state for inner items ──────
   These hold the hidden state set by anim.js BEFORE the anime.js stagger
   fires so a row doesn't flash in for one frame then jump to opacity 0
   (FOUC). Reduced-motion reset below forces them back to visible. */
html.js-anim .mono-index-row.is-staged {
  opacity: 0;
  transform: translateY(10px);
  transition: none;
}
html.js-anim .page-title-box[data-title-done] {
  opacity: 0;
  transform: translateX(-16px);
  transition: none;
}
html.js-anim .page-title-box.is-inview,
html.js-anim .mono-index-row.is-inview {
  opacity: 1;
  transform: none;
}
/* Count-up elements: stay at their initial text content (already rendered
   server-side by Dash to the final value). anime.js overwrites textContent
   on the way up; reduced-motion users keep the final number, never see
   the flash. The coral end-flash is driven by anim.js adding .is-inview
   to colorize via the countup-flash rule below. */
html.js-anim [data-countup].is-inview {
  animation: countup-flash 0.6s var(--ease-out) both;
}
@keyframes countup-flash {
  0%   { color: var(--accent); }
  100% { color: var(--text); }
}

/* Page header + SECTIONS side nav are always-on VNL chrome — never let the
   scroll-reveal layer hide them (belt-and-suspenders with anim.js NO_REVEAL).
   These live only on VNL page layouts, so Home stays clean. */
html.js-anim .rankings-header,
html.js-anim .rankings-header[data-reveal],
html.js-anim .rankings-sections,
html.js-anim .rankings-sections[data-reveal] {
  opacity: 1 !important;
  transform: none !important;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html.js-anim [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  html.js-anim .mono-index-row,
  html.js-anim .page-title-box,
  html.js-anim .mono-index-row.is-staged,
  html.js-anim .page-title-box[data-title-done] {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ── Bracket page (VNL Men's Finals — connectors + flags) ────────────────── */
.bracket {
    display: flex;
    align-items: stretch;
    overflow-x: auto;
    padding-bottom: var(--space-3);
    --elbow: 22px;
}

/* ── Columns ─────────────────────────────────────────────────────────────── */
.bracket-col {
    display: flex;
    flex-direction: column;
    flex: 1 1 0;
    min-width: 210px;
    padding-right: calc(var(--elbow) * 2);
}
.bracket-col--final {
    padding-right: 0;
}
.bracket-col > .fs-label {
    margin-bottom: var(--space-4);
}
.bracket-col-body {
    flex: 1;
    display: flex;
    flex-direction: column;
}

/* ── Pairs: each holds two matches feeding one next-round slot ───────────── */
.bracket-pair {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    position: relative;
}

/* Horizontal stub out the right edge of each match (every column except the final one). */
.bracket-col:not(.bracket-col--final) .bracket-match {
    position: relative;
}
.bracket-col:not(.bracket-col--final) .bracket-match::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 100%;
    width: var(--elbow);
    height: 1px;
    background: var(--line-strong);
}

/* Vertical connector joining the pair's two matches (centers at 25% + 75% under space-around). */
.bracket-col:not(.bracket-col--final) .bracket-pair::after {
    content: "";
    position: absolute;
    left: calc(100% + var(--elbow));
    top: 25%;
    height: 50%;
    width: 1px;
    background: var(--line-strong);
}

/* Horizontal feed into the next round, at the pair midpoint. */
.bracket-col:not(.bracket-col--final) .bracket-pair::before {
    content: "";
    position: absolute;
    left: calc(100% + var(--elbow));
    top: 50%;
    width: var(--elbow);
    height: 1px;
    background: var(--line-strong);
    z-index: 0;
}

/* ── Final column: center FINAL vertically (aligns with single SF pair feed); bronze sits below. */
.bracket-col--final .bracket-col-body {
    justify-content: center;
    gap: var(--space-6);
}
.bracket-slot-label {
    font-family: var(--font-mono);
    font-size: .625rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-bottom: var(--space-2);
}

/* ── Card ────────────────────────────────────────────────────────────────── */
.bracket-match {
    background-color: var(--surface-1);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    transition: background-color .2s ease;
}
.bracket-match:hover {
    background-color: var(--surface-2);
}
.bracket-match--final {
    border-left: 2px solid var(--accent);
}
.bracket-match--bronze {
    opacity: .85;
}
.bracket-match-label {
    font-family: var(--font-mono);
    font-size: .625rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}
.bracket-match-meta {
    font-family: var(--font-mono);
    font-size: .6875rem;
    letter-spacing: .02em;
    color: var(--text-secondary);
    padding-top: var(--space-2);
    margin-top: var(--space-1);
    border-top: 1px solid var(--line);
}

/* ── Team row + flag ─────────────────────────────────────────────────────── */
.bracket-team {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    font-family: var(--font-mono);
    font-size: .8125rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text);
}
.bracket-team > * {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bracket-team > .bracket-flag {
    overflow: visible;
}
.bracket-team--tbd {
    color: var(--text-tertiary);
}
.bracket-flag {
    width: 22px;
    height: 16px;
    object-fit: cover;
    border-radius: 2px;
    flex-shrink: 0;
    box-shadow: 0 0 0 1px var(--line-strong);
}
.bracket-flag--tbd {
    display: inline-block;
    background: var(--line-strong);
}

/* ── Roster drawer: click a bracket team to see its full squad ──────────── */
.bracket-team--clickable {
    cursor: pointer;
    border-radius: var(--radius-sm);
    margin: -4px -6px;
    padding: 4px 6px;
    transition: background-color .15s ease, color .15s ease;
}
.bracket-team--clickable:hover {
    background-color: var(--surface-2);
    color: var(--accent);
}

.bracket-roster-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .55);
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease;
    z-index: 900;
}
.bracket-roster-backdrop--open {
    opacity: 1;
    pointer-events: auto;
}

.bracket-roster-drawer,
.bracket-player-drawer {
    --drawer-w: clamp(320px, 33.333vw, 520px);
    position: fixed;
    top: 0;
    height: 100vh;
    width: var(--drawer-w);
    background: var(--surface-1);
    transition: transform .3s ease;
    z-index: 901;
    overflow-y: auto;
    padding: var(--space-5);
}
.bracket-roster-drawer {
    left: 0;
    border-right: 1px solid var(--line-strong);
    box-shadow: 8px 0 32px rgba(0, 0, 0, .4);
    transform: translateX(-100%);
}
.bracket-roster-drawer--open {
    transform: translateX(0);
}

/* Sits immediately to the right of the roster drawer — its own slide-in,
   independent of whether the roster drawer is mid-transition. Closed state
   is -200% (not -100%): its resting `left` is already offset by one drawer
   width, so -100% (its own width) would only bring it back to x=0 — still
   fully on-screen. -200% pushes it the rest of the way off-screen, landing
   at the same hidden x-position as the roster drawer. */
.bracket-player-drawer {
    left: var(--drawer-w);
    border-right: 1px solid var(--line-strong);
    box-shadow: 8px 0 32px rgba(0, 0, 0, .4);
    transform: translateX(-200%);
    z-index: 902;
}
.bracket-player-drawer--open {
    transform: translateX(0);
}
.bracket-player-title {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.bracket-player-name {
    font-family: var(--font-mono);
    font-size: 1rem;
    letter-spacing: .02em;
    color: var(--text);
}
.bracket-player-title .bracket-team {
    font-size: .8125rem;
    color: var(--text-secondary);
}
.bracket-player-title .bracket-flag {
    width: 20px;
    height: 14px;
}
/* Drawer is narrow regardless of viewport width, so force the 2-tile layout
   rather than relying on .stat-tile-row's window-width media queries. A 3rd
   tile (position rank, when known) spans the full row on its own line
   rather than leaving an awkward half-empty gap next to it. */
.bracket-player-drawer .stat-tile-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-bottom: var(--space-4);
}
.bracket-player-drawer .stat-tile-row > :nth-child(3):last-child {
    grid-column: 1 / -1;
}
/* Without this, two stat tiles side by side in the already-narrow drawer
   overflow on 320–430px phones (tile content can't shrink further). */
@media (max-width: 480px) {
    .bracket-player-drawer .stat-tile-row {
        grid-template-columns: minmax(0, 1fr);
    }
}
.stat-tile-sublabel {
    font-family: var(--font-mono);
    font-size: var(--fs-label);
    color: var(--text-secondary);
    letter-spacing: 0.06em;
}
.bracket-jersey-block {
    display: flex;
    flex-direction: column;
    align-items: center;
}
.bracket-jersey-img {
    width: 100%;
    max-width: 320px;
    background: transparent;
}
.bracket-roster-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--line-strong);
}
.bracket-roster-header .bracket-team {
    font-size: 1rem;
}
.bracket-roster-header .bracket-flag {
    width: 30px;
    height: 22px;
}
.bracket-roster-close {
    background: none;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 1.25rem;
    line-height: 1;
    width: 32px;
    height: 32px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease;
}
.bracket-roster-close:hover {
    color: var(--accent);
    border-color: var(--accent);
}
.bracket-roster-groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.bracket-roster-group-label {
    color: var(--text-tertiary);
    margin-bottom: var(--space-2);
}
.bracket-roster-group-players {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.bracket-roster-player {
    font-family: var(--font-mono);
    font-size: .8125rem;
    letter-spacing: .02em;
    color: var(--text);
    padding: var(--space-2) var(--space-3);
    background: var(--surface-2);
    border-radius: var(--radius-sm);
}
.bracket-roster-player--clickable {
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}
.bracket-roster-player--clickable:hover {
    background-color: var(--line-strong);
    color: var(--accent);
}

/* ============================================== shift5.io parity pass — Aug 2026 ============================================== */
/* Source of truth: docs/design/shift5-parity.md §3.1–§3.5. Additive only — all existing rules preserved. */

/* ── §3.1 Hero typography promotion ─────────────────────────────────────── */
.home-hero-coral .fs-mega {
  /* Sized to the coral column (~64% viewport width, not full-bleed) so
     "VOLLEYBALL" always fits on one line without clipping against the
     System Status panel. This rule cascades after (and overrides the
     font-size from) the base .home-hero-coral .fs-mega rule above — the
     previous 11vw/9.25rem values here were still too large: at several
     viewport widths "VOLLEYBALL" (nowrap, single line) rendered wider than
     the coral column and bled into the System Status panel. Capped back
     down to the same safe ceiling as the base rule. */
  font-size: clamp(2.25rem, 7.5vw, 6.5rem);
  letter-spacing: -0.02em;
  line-height: 0.95;
}
.home-hero-coral .hero-eyebrow {
  font-family: var(--font-head);
  font-weight: 500;
  font-size: clamp(1.5rem, 2.4vw, 2.375rem);  /* ~38px @ 1440w */
  color: var(--bg);
  margin-bottom: var(--space-1);
  max-width: 480px;
}
/* The full sentence "All about volleyball is here." stays on one
   unbroken line, pushed down so it ENDS directly under the ball, touching
   it — tuned against .hub-mark-img's position within .wordmark-figure at
   the ~1440x1150 reference viewport. At the normal .hero-eyebrow size the
   line is wider than the gap between the cell's left padding and the
   ball, so it's scaled down (~0.8x) to fit while keeping the same left
   start as "Driven by data." below it. */
.hero-eyebrow-line1 {
  display: inline-block;
  white-space: pre;
  font-size: clamp(1.2rem, 1.92vw, 1.9rem);
  /* Was 282px, tuned to align under a hero ball image that was never
     added (assets/hero-volleyball.jpg doesn't exist) — with no image to
     align to it just pushed the whole hero eyebrow down, leaving a huge
     empty gap at the top of the coral cell. */
  margin-top: var(--space-3);
}
/* ── §3.2 System Status panel ──────────────────────────────────────────── */
.aside-stats {
  background: var(--surface-2);
  color: var(--text);
  padding: clamp(32px, 4vw, 64px) clamp(24px, 3vw, 48px);
  position: relative;
  display: flex;
  flex-direction: column;
}
.aside-stats-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--space-5);
}
.aside-stats-title {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: -0.24px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.aside-stats-blip {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
  animation: seq 1s steps(1) infinite;
}
@keyframes seq {
  0%   { opacity: 1; }
  25%  { opacity: 0; }
  to   { opacity: 0; }
}
.stats-list { flex: 1 1 auto; }
.social-asset {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}
.social-title {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: -0.24px;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: var(--space-3);
}
.social-list {
  display: flex;
  flex-direction: column;
}
.social-row {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: -0.02em;
  color: var(--text);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}
.social-row:last-child { border-bottom: none; }

/* ── §3.3 News strip + hairline reveal ────────────────────────────────── */
.home-news-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin-top: 0;
}
@media (max-width: 900px) { .home-news-strip { grid-template-columns: 1fr; } }
/* Two-up variant — the Home "Past seasons" recall row (VNL + AVP). */
.home-news-strip--two { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 900px) { .home-news-strip--two { grid-template-columns: 1fr; } }

.news-item {
  position: relative;
  padding: var(--space-5) var(--space-4);
  background: var(--surface-1);
}
.news-item + .news-item { border-left: 0; }

/* The three animateable hairlines — these reveal via [data-line] handler in anim.js */
.news-item__lines {
  position: absolute; inset: 0; pointer-events: none;
}
.news-item__line {
  position: absolute;
  background: var(--line-strong);
}
.news-item__line[data-line="x"] {
  left: 0; right: 0; height: 1px;
  transform: scaleX(0); transform-origin: left center;
}
.news-item__line[data-line="x"]:nth-of-type(1) { top: 0; }
.news-item__line[data-line="x"]:nth-of-type(2) { bottom: 0; }
.news-item__line[data-line="y"] {
  top: 0; bottom: 0; right: 0; width: 1px;
  transform: scaleY(0); transform-origin: top center;
}
html.js-anim .is-inview .news-item__line { transform: scaleX(1) scaleY(1); transition: transform 480ms var(--ease-out); }
html.js-anim .is-inview .news-item__line[data-line="x"]:nth-of-type(2) { transition-delay: 80ms; }
html.js-anim .is-inview .news-item__line[data-line="y"] { transition-delay: 160ms; }

/* ── §3.4 CTA slab (coral, full-bleed) ────────────────────────────────── */
.home-cta-slab {
  position: relative;
  width: 100%;
  min-height: 360px;
  background: var(--accent);
  border: none; border-radius: 0;
  padding: var(--space-6);
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  display: block;
}
.home-cta-slab:hover { background: var(--accent-press); }
.cta-icon {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  font-family: var(--font-head);
  font-size: 80px;
  line-height: 1;
  color: var(--bg);
  user-select: none;
}
.cta-label {
  position: absolute;
  bottom: var(--space-5);
  left: var(--space-5);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.75rem, 2.6vw, 2.375rem);  /* ~38px @ 1440w */
  color: var(--bg);
  margin: 0;
  line-height: 1;
  letter-spacing: -0.02em;
}
@media (max-width: 768px) {
  .home-cta-slab { min-height: 220px; }
  .cta-icon { font-size: 48px; top: var(--space-4); right: var(--space-4); }
  .cta-label { bottom: var(--space-4); left: var(--space-4); }
}

/* ── "Choose your path" sport-picker (home.py, sits under the CTA slab) ──── */
/* Home landing blocks (women's live section, past-seasons recall): full-width
   band with equal top/bottom padding so each sits clear of its neighbours. */
.home-section {
  width: 100%;
  padding: var(--space-7) var(--space-5);
  background: var(--bg);
}
@media (max-width: 640px) { .home-section { padding: var(--space-6) var(--space-3); } }

.home-path {
  width: 100%;
  padding: var(--space-7) var(--space-5);
  text-align: center;
  background: var(--bg);
}
.path-heading-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.path-heading {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  color: var(--text);
  margin: 0;
  letter-spacing: -0.01em;
}
/* Wide, short chevron pointing down — two coral bars meeting at a point,
   deliberately flat/wide rather than a tall arrow glyph. */
.path-arrow {
  width: 96px;
  height: 30px;
  position: relative;
}
.path-arrow::before,
.path-arrow::after {
  content: "";
  position: absolute;
  top: 0;
  width: 58%;
  height: 5px;
  border-radius: 3px;
  background: var(--accent);
}
.path-arrow::before { left: 0;  transform-origin: top left;  transform: rotate(24deg); }
.path-arrow::after   { right: 0; transform-origin: top right; transform: rotate(-24deg); }

.path-columns {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--line-strong);
}
@media (max-width: 900px) {
  .path-columns { grid-template-columns: 1fr; }
}

.path-card {
  padding: var(--space-6) var(--space-4);
  background: var(--surface-1);
  border-right: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
  transition: background-color .2s ease;
}
.path-columns .path-card:last-child { border-right: none; }
@media (max-width: 900px) {
  .path-columns .path-card { border-right: none; }
}

.path-card--live { cursor: pointer; }
.path-card--live:hover { background: var(--surface-2); }
.path-card--soon { opacity: .55; }

.path-card-label {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  color: var(--text);
  letter-spacing: -0.01em;
}
.path-card--live .path-card-label { color: var(--accent); }

.path-card-sublabel {
  font-family: var(--font-mono);
  font-size: .8125rem;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-top: var(--space-2);
}

.path-card-status {
  font-family: var(--font-mono);
  font-size: .625rem;
  letter-spacing: .14em;
  color: var(--text-tertiary);
  margin-top: var(--space-3);
}
.path-card--live .path-card-status { color: var(--accent); }

.path-card--wide {
  border-right: none;
}

/* Non-animated fallback: [data-mask="y"] elements show unfolded (no wipe)
   for users without JS motion or with reduced-motion preference. Currently
   unused (the home.py footer that consumed this was removed — empty-clip
   rendering bug, never revealed even on scroll), kept as it's referenced
   generically by anim.js's bootClipReveal for any future coral slab. */
html:not(.js-anim) [data-mask="y"] { clip-path: inset(0); }

/* Extended reduced-motion guard for clip-path wipes. */
@media (prefers-reduced-motion: reduce) {
  [data-mask="y"] { clip-path: inset(0) !important; transition: none !important; }
}

/* ── Official VNL Rankings tab (dashboard/pages/vnl/official_rankings.py) ──── */
/* Instrument-style standings table built as flex rows (custom HTML, not a   */
/* dash_table, because rows carry flag <img>s). Grid columns:                */
/*   POS · NATION · P · W · L · PTS · STATUS                                  */
.official-standings {
  display: flex;
  flex-direction: column;
}
.official-row {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 40px 40px 40px 56px 96px;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
}
.official-row--head {
  border-bottom: 1px solid var(--line-strong);
  color: var(--text-secondary);
}
.official-row--playoff {
  background: rgba(255, 88, 65, 0.05);
}
.official-row--leader {
  background: rgba(255, 88, 65, 0.10);
}
.official-pos {
  color: var(--text-secondary);
  letter-spacing: 0.06em;
}
.official-row--playoff .official-pos {
  color: var(--accent);
}
.official-nation {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.05em;
  color: var(--text);
}
.official-nation > * {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.official-nation > img {
  overflow: visible;
}
.official-stat {
  text-align: right;
  color: var(--text-secondary);
}
.official-stat--pts {
  color: var(--text);
  font-weight: 600;
}
.official-badge {
  justify-self: end;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.official-badge--playoff {
  color: var(--accent);
}
/* Without this, the 7 fixed tracks (44+40+40+40+56+96 + gaps) plus the nation
   name total ~524px and the standings table overflows every phone viewport
   (320/375/430px). STATUS is dropped because playoff position is already
   signalled by the .official-row--playoff tint + coral .official-pos. */
@media (max-width: 767px) {
  .official-row {
    grid-template-columns: 30px minmax(0, 1fr) 26px 26px 26px 40px;
    gap: var(--space-1);
    padding: 8px var(--space-1);
  }
  .official-nation {
    font-size: var(--fs-label);
    letter-spacing: 0.02em;
  }
  .official-badge {
    display: none;
  }
}

/* ── Past VNL Results tab (dashboard/pages/vnl/past_results.py) ─────────────── */
/* Year-by-year results grid, same flex-row-not-dash_table approach as        */
/* .official-standings above (rows carry flag <img>s). Grid columns:          */
/*   YEAR · GOLD · SILVER · BRONZE                                            */
.history-grid {
  display: flex;
  flex-direction: column;
}
.history-row {
  display: grid;
  grid-template-columns: 64px repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: var(--space-2);
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
}
.history-row--head {
  border-bottom: 1px solid var(--line-strong);
  color: var(--text-secondary);
}
.history-year {
  color: var(--text-secondary);
  font-family: var(--font-mono);
  letter-spacing: 0.06em;
}
.history-medal {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.05em;
}
.history-medal > * {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-medal > img {
  overflow: visible;
}
.history-medal--gold {
  color: var(--accent);
}
.history-medal--silver {
  color: var(--text);
}
.history-medal--bronze {
  color: var(--text-secondary);
}
/* Without this, the 64px year track plus three flag+nation medal cells
   exceed 320–430px phone viewports and the results grid overflows. */
@media (max-width: 767px) {
  .history-row {
    grid-template-columns: 46px repeat(3, minmax(0, 1fr));
    gap: var(--space-1);
    padding: 8px var(--space-1);
  }
  .history-medal {
    font-size: var(--fs-label);
    letter-spacing: 0.02em;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SEARCH + AI SUMMARY
   The header search bar's two-way chooser, the Search results page and the
   AI Summary chat. Same instrument-panel vocabulary as the rest of the app:
   near-square surfaces, hairline borders, mono for anything technical, coral
   reserved for the active/primary path only.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Header chooser ──────────────────────────────────────────────────────── */

.header-search-field {
  position: relative;
  display: flex;
}

.header-search-choice {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 120;
  display: none;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  background: var(--surface-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
}
.header-search-choice--open {
  display: flex;
  animation: search-choice-in 0.18s var(--ease-out) both;
}
@keyframes search-choice-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.header-search-choice > .fs-label {
  padding: 2px 8px 6px;
  letter-spacing: 0.18em;
}

.header-choice {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border-left: 2px solid transparent;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.header-choice:hover {
  background: var(--surface-2);
  border-left-color: var(--accent);
}
.header-choice-name {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--text);
}
.header-choice-hint {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
}
.header-choice--ai .header-choice-name { color: var(--accent); }

/* ── Search page ─────────────────────────────────────────────────────────── */

.site-search-box {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-5);
  /* The SECTIONS rail floats over the top-right of every VNL page, so the
     box has to stop short of it or the buttons end up underneath. */
  max-width: 820px;
}
.site-search-input {
  flex: 1 1 320px;
  min-width: 0;
  background: var(--surface-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--text);
  outline: none;
  transition: border-color 0.2s ease;
}
.site-search-input:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: none;
}
.site-search-input::placeholder { color: var(--text-tertiary); }

.site-search-go,
.site-search-ai {
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  color: var(--text);
  cursor: pointer;
  letter-spacing: 0.14em;
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}
.site-search-go:hover { border-color: var(--text); background: var(--surface-2); }
.site-search-ai { color: var(--accent); border-color: var(--accent); }
.site-search-ai:hover { background: var(--accent); color: var(--bg); }

.search-results {
  display: grid;
  gap: var(--space-2);
}
.search-result {
  padding: var(--space-3);
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-left: 2px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-left-color 0.2s ease, background 0.2s ease;
}
.search-result:hover {
  border-left-color: var(--accent);
  background: var(--surface-2);
}
.search-result-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}
.search-result-title {
  font-family: var(--font-head);
  font-size: var(--fs-h3);
  color: var(--text);
}
.search-result-text {
  color: var(--text-secondary);
  line-height: 1.55;
  margin: 0 0 var(--space-2);
}
.search-result-source {
  display: inline-block;
  color: var(--accent);
  letter-spacing: 0.14em;
  cursor: pointer;
}
.search-result-source:hover { text-decoration: underline; }

/* ── AI Summary page ─────────────────────────────────────────────────────── */

.ai-status {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.ai-status-dot--off {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--text-tertiary);
  display: inline-block;
}

.ai-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-4);
}

.ai-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  max-height: 62vh;
  overflow-y: auto;
  padding-right: var(--space-2);
}

.ai-turn { display: flex; flex-direction: column; gap: var(--space-1); }
.ai-turn--user { align-items: flex-end; }
.ai-turn-who { letter-spacing: 0.18em; color: var(--text-tertiary); }

.ai-bubble {
  max-width: 74ch;
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-body-sm);
  line-height: 1.6;
}
.ai-bubble--user {
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  color: var(--text);
}
.ai-bubble--assistant {
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  color: var(--text);
}
.ai-bubble--refused { border-left-color: var(--text-tertiary); }
.ai-bubble p:last-child { margin-bottom: 0; }
.ai-bubble ul, .ai-bubble ol { margin: var(--space-2) 0; padding-left: 1.2em; }
.ai-bubble strong { color: var(--text); }
.ai-bubble code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  color: var(--accent);
}

.ai-sources {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}
.ai-source-chip {
  padding: 4px 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  letter-spacing: 0.1em;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.ai-source-chip:hover { color: var(--accent); border-color: var(--accent); }

.ai-empty { padding: var(--space-5) var(--space-2); }
.ai-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.ai-chip {
  padding: 10px 14px;
  background: var(--surface-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.ai-chip:hover {
  color: var(--text);
  border-color: var(--accent);
  background: var(--surface-2);
}

.ai-composer {
  display: flex;
  gap: var(--space-2);
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
}
.ai-input {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--text);
  outline: none;
  transition: border-color 0.2s ease;
}
.ai-input:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: none;
}
.ai-input::placeholder { color: var(--text-tertiary); }
.ai-send {
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 12px 22px;
  color: var(--bg);
  cursor: pointer;
  letter-spacing: 0.16em;
  transition: background 0.2s ease;
}
.ai-send:hover { background: var(--accent-press); border-color: var(--accent-press); }

@media (max-width: 640px) {
  .ai-bubble { max-width: 100%; }
  .ai-composer { flex-direction: column; }
  .ai-send { width: 100%; }
}

/* ── Dash 4 gotcha: dcc.Input is a wrapper, not an <input> ───────────────────
   `dcc.Input(className="x")` renders
       <div class="dash-input-container dash-input x">
         <input class="dash-input-element">
   so every rule above styles the *wrapper*. The real field keeps Dash's own
   font, colour and focus ring — which is why a styled search box still showed
   a stock focus outline. Flatten the inner element so it inherits the box we
   actually styled. (Same class of problem as the dcc.Dropdown note in
   CLAUDE.md — Dash 4 components are wrappers.) */

.header-search-input,
.site-search-input,
.ai-input {
  display: flex;
  align-items: stretch;
  /* Dash gives the container a fixed 34px height, which clips the field once
     it carries our own padding. Let the content size it. */
  height: auto;
  box-sizing: border-box;
}
.header-search-input .dash-input-element,
.site-search-input .dash-input-element,
.ai-input .dash-input-element {
  width: 100%;
  min-width: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  border: none;
  outline: none;
  box-shadow: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  /* Dash collapses the inner field to a few pixels inside a flex wrapper,
     which clips the glyphs. Pin it to its own line box instead. */
  flex: 1 1 auto;
  height: auto;
  line-height: 1.4;
  min-height: 1.4em;
}
.header-search-input .dash-input-element:focus,
.site-search-input .dash-input-element:focus,
.ai-input .dash-input-element:focus {
  outline: none;
  box-shadow: none;
  border: none;
}
.header-search-input .dash-input-element::placeholder,
.site-search-input .dash-input-element::placeholder,
.ai-input .dash-input-element::placeholder {
  color: var(--text-tertiary);
  opacity: 1;
}

/* The wrapper is the visible box, so it carries the focus treatment. */
.header-search-input:focus-within,
.site-search-input:focus-within,
.ai-input:focus-within {
  border-color: var(--accent);
  outline: none;
  box-shadow: none;
}

/* ── Archive chrome (dashboard/pages/_archive.py) ──────────────────────────── */
/* Shared by the VNL 2026 season archive and the AVP 2026 Manhattan Beach Open  */
/* archive. Deliberately quiet: a left coral rule + one coral badge, everything  */
/* else monochrome, so an archived page still reads as an instrument panel       */
/* rather than a warning box. No new hexes — all tokens from :root above.        */

.archive-banner {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  background: var(--surface-1);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}

.archive-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.archive-badge {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 3px 8px;
  white-space: nowrap;
}

.archive-season {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-data);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text);
}

.archive-status {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  color: var(--text-tertiary);
}
/* Separator dot between season and status — pure decoration, hidden from AT. */
.archive-status::before {
  content: "·";
  margin-right: var(--space-2);
  color: var(--line-strong);
}

.archive-summary {
  margin: var(--space-2) 0 0;
  max-width: 68ch;
  color: var(--text-secondary);
  line-height: 1.55;
}

.archive-results {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.archive-result-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}

.archive-result-label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  color: var(--text-tertiary);
}

.archive-result-value {
  font-weight: 600;
  color: var(--text);
}

.archive-result-value--top {
  color: var(--accent);
}

.archive-result-detail {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-data);
  color: var(--text-secondary);
}

.archive-note {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-label);
  color: var(--text-tertiary);
  max-width: 78ch;
}

/* Inline chip variant — for page headers that already sit under a banner. */
.archive-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  vertical-align: middle;
}

.archive-chip-badge {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 2px 6px;
}

/* LIVE variant — solid coral fill, dark text; used by the NCAAW season_chip()
   while the season is in play. Same footprint as the outlined ARCHIVE badge. */
.archive-chip-badge--live {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}

.archive-chip-season {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
}

/* Phones: the fact list stacks rather than wrapping mid-row, and the banner
   loses its generous side padding so it matches the card gutters. */
@media (max-width: 767px) {
  .archive-banner {
    padding: var(--space-3);
  }
  .archive-results {
    flex-direction: column;
    gap: var(--space-2);
  }
}

/* ── Bracket results (archived VNL 2026 Finals) ────────────────────────────── */
/* Added when the bracket switched from hardcoded TBD placeholders to real      */
/* played results. A winner row keeps full-strength text and gains a coral set  */
/* score; a loser row drops to secondary grey. Set scores are pushed right with */
/* margin-left:auto so they align down the column regardless of name length.    */

.bracket-team--winner {
  color: var(--text);
  font-weight: 600;
}

.bracket-team--loser {
  color: var(--text-tertiary);
}

.bracket-team--loser .bracket-flag {
  opacity: 0.45;
}

/* Set score ("3" / "0") trailing each played team row. */
.bracket-score {
  margin-left: auto;
  padding-left: var(--space-2);
  font-family: var(--font-mono);
  font-size: .8125rem;
  font-weight: 600;
  overflow: visible;
  flex-shrink: 0;
}

.bracket-team--winner .bracket-score {
  color: var(--accent);
}

.bracket-team--loser .bracket-score {
  color: var(--text-tertiary);
}

/* Champion callout under the final card. */
.bracket-champion {
  margin-top: var(--space-3);
  border: 1px solid var(--accent);
  padding: var(--space-3);
  text-align: center;
}

.bracket-champion-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  color: var(--text-tertiary);
  margin-bottom: var(--space-1);
}

.bracket-champion-name {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}
