@import url(https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&display=swap);
/* DM Mono — tabular monospace backup for the PnL/bonus value numbers (VALUE_FONT),
 * so digits stay aligned if Paralucent is unavailable. Imported here (not only in
 * index.html) so it also loads inside the Webflow code component. @import must come
 * before the @font-face rules below. */

/*
 * Paralucent — Topstep's licensed display typeface, served from their Webflow CDN
 * (CORS: access-control-allow-origin: *). Available weights: 300 / 500 / 600.
 * Used on this Topstep-owned project. If self-hosting later, swap these URLs.
 */
@font-face {
  font-family: 'Paralucent';
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url('https://cdn.prod.website-files.com/69e902b0a74d3d99a517f56d/69e907e2f623d1ef9903112b_Paralucent_Light.woff')
    format('woff');
}
@font-face {
  font-family: 'Paralucent';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('https://cdn.prod.website-files.com/69e902b0a74d3d99a517f56d/69e907e2281c7f8155047ad1_Paralucent_Medium.woff')
    format('woff');
}
@font-face {
  font-family: 'Paralucent';
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url('https://cdn.prod.website-files.com/69e902b0a74d3d99a517f56d/69e907e2b696aaf2db74eb5d_Paralucent_Demi_Bold.woff')
    format('woff');
}

/*
 * Topstep brand tokens — SINGLE SOURCE OF TRUTH.
 * Matched to topstep.com: pure black canvas, warm gold accent, white headings,
 * pill buttons, rounded near-black cards. Swap exact hex here if brand kit differs.
 */
:host {
  /* Surfaces — pure black canvas like topstep.com dark sections */
  --bg: #000000;
  --bg-2: #060606;
  --surface: rgba(255, 255, 255, 0.035);
  --surface-2: rgba(255, 255, 255, 0.07);
  --surface-border: rgba(255, 255, 255, 0.07);
  --surface-border-strong: rgba(255, 255, 255, 0.16);

  /* Brand — Topstep gold (richened + brightened for pop on black) */
  --accent: #dbb359;
  --accent-2: #efd9a8;
  --accent-deep: #9c7637;
  --accent-glow: rgba(219, 179, 89, 0.5);
  --accent-soft: rgba(219, 179, 89, 0.14);

  /* Semantic — bright gold for gains (on-brand), muted red for losses */
  --pos: #e9e7e2;
  --neg: #e8675b;

  /* Text — a touch brighter for contrast */
  --text: #f7f6f3;
  --text-dim: #a3a19b;
  --text-faint: #6e6c66;

  /* Type — Topstep: Work Sans (body) + Paralucent→Poppins (display) */
  --font-sans: 'Work Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Paralucent', 'Poppins', 'Work Sans', system-ui, sans-serif;
  --font-mono: 'Work Sans', system-ui, sans-serif;

  /* Shape — Topstep uses generous pill / rounded-card radii */
  --radius: 18px;
  --radius-sm: 12px;
  --radius-lg: 24px;
  --blur: 16px;

  /* Shadows */
  --shadow-card: 0 18px 50px -22px rgba(0, 0, 0, 0.9);
  --shadow-glow: 0 0 0 1px var(--accent-soft), 0 12px 40px -8px var(--accent-glow);

  /* Chart line "casing" — near-black matching the plot background, drawn under
     each bump line so crossings read clearly (invisible except where it
     occludes a lower line). Tunable if the plot backdrop shifts. */
  --chart-casing: #0a0b0d;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body,
#root {
  min-height: 100%;
}

body {
  font-family: var(--font-sans);
  color: var(--text);
  background: linear-gradient(180deg, #101218 0%, #08090c 42%, #000000 100%) fixed;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

::selection {
  background: var(--accent-soft);
  color: var(--text);
}

/* ---------- Hero zone + video background (full width, top of page only) ---------- */
.hero-zone {
  position: relative;
  padding-bottom: clamp(56px, 9vh, 140px);
}
.hero-zone > * {
  position: relative;
  z-index: 1;
}
.hero-video {
  position: absolute;
  top: 0;
  left: 50%;
  width: 100vw;
  height: 100%;
  transform: translateX(-50%);
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}
.hero-video video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-video__scrim {
  position: absolute;
  inset: 0;
  /* darken for legibility + fade into the page background at the bottom */
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.5) 0%,
    rgba(0, 0, 0, 0.35) 35%,
    rgba(6, 7, 11, 0.85) 80%,
    #000 100%
  );
}

/* ---------- Layout shell ---------- */
.app {
  position: relative;
  z-index: 1;
  max-width: 1600px;
  margin: 0 auto;
  padding: clamp(5px, 1vw, 14px) clamp(16px, 4vw, 40px) 96px;
}

/* ---------- Nav ---------- */
.nav {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 8px 4px 0;
}

.logo {
  height: 26px;
  width: auto;
  display: block;
}

.nav__links {
  display: flex;
  gap: 30px;
  list-style: none;
  margin: 0 auto;
}

.nav__links li {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 15px;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.18s;
}

.nav__links li:hover,
.nav__links li.is-active {
  color: var(--text);
}

.nav__right {
  display: flex;
  align-items: center;
  gap: 22px;
}

.nav__login {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 15px;
  color: var(--text-dim);
  cursor: pointer;
}
.nav__login:hover {
  color: var(--text);
}

/* ---------- Buttons (pill) ---------- */
.btn {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  padding: 13px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s, box-shadow 0.2s, background 0.2s, border-color 0.2s;
  white-space: nowrap;
}
.btn:hover {
  transform: translateY(-1px);
}

.btn--sm {
  padding: 9px 20px;
  font-size: 14px;
}

.btn--primary {
  background: #000;
  color: var(--text);
  border-color: var(--accent);
  padding: 11px 24px;
  font-size: 15px;
}
.btn--primary:hover {
  background: rgba(219, 179, 89, 0.12);
}

/* ---------- Hero row (header left, stats right; wraps when needed) ---------- */
.hero-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(32px, 5vw, 72px);
  padding-top: clamp(40px, 8vw, 96px);
}
.hero-row > .hero {
  flex: 1 1 460px;
}
.hero-row > .stats {
  flex: 1 1 440px;
}

/* ---------- Stats strip ---------- */
.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}

.stat-card {
  padding: 22px 24px 20px;
  background: linear-gradient(160deg, #24262e, #15161b);
}

.stat-card__num {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(26px, 3.4vw, 40px);
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
.stat-card__num--green {
  color: #3ddc84;
}
/* Countdown is longer (8d 14:23:05) so it sits a touch smaller, with fixed
   digit widths so it doesn't jitter as it ticks. */
.stat-card__num--count {
  font-size: clamp(20px, 2.6vw, 30px);
  font-variant-numeric: tabular-nums;
}

.stat-card__num sup {
  font-size: 0.5em;
  vertical-align: super;
}

.stat-card__label {
  margin-top: 12px;
  font-size: 14px;
  color: var(--text-dim);
}

/* ---------- Hero ---------- */
.hero {
  text-align: left;
}

.hero__title {
  font-family: var(--font-display);
  margin: 22px 0 14px;
  font-size: clamp(38px, 7.5vw, 76px);
  font-weight: 500;
  line-height: 1.12;
  padding-bottom: 0.08em;
  letter-spacing: -0.02em;
  background: linear-gradient(180deg, #ffffff 35%, #d8d4cc 135%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__title .accent {
  background: linear-gradient(180deg, var(--accent-2), var(--accent-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__sub {
  max-width: 620px;
  margin: 0;
  color: var(--text);
  font-size: clamp(17px, 2.2vw, 20px);
  line-height: 1.6;
  text-wrap: balance;
}

/* ---------- Glass card ---------- */
.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--blur));
  box-shadow: var(--shadow-card);
}

.card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.18), transparent 40%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

.section {
  margin-top: clamp(72px, 9vw, 132px);
}
/* The race section follows the hero zone, which already has bottom padding. */
#charts {
  margin-top: clamp(20px, 2.5vw, 40px);
}

/* The Payouts section sits on a darker full-bleed band to offset it from the
   page, while its content stays aligned with the rest of the layout. */
#board {
  position: relative;
  margin-top: clamp(20px, 3vw, 44px);
  padding-top: clamp(28px, 4vw, 56px);
  padding-bottom: clamp(40px, 6vw, 80px);
}
#board::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  background: #030304;
  z-index: -2;
}

.section__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
  margin-bottom: clamp(28px, 3.5vw, 44px);
}

.section__title {
  font-family: var(--font-display);
  font-size: clamp(30px, 4.6vw, 46px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.015em;
}

.section__sub {
  max-width: 640px;
  color: var(--text);
  font-size: clamp(17px, 2vw, 20px);
  line-height: 1.55;
  text-wrap: balance;
}

/* ---------- Month selector ---------- */
.months {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-start;
  margin-top: clamp(26px, 3vw, 38px);
}

.month-chip {
  position: relative;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-dim);
  padding: 10px 18px;
  border: 1px solid var(--surface-border);
  border-radius: 999px;
  background: var(--surface);
  backdrop-filter: blur(var(--blur));
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, transform 0.15s;
  white-space: nowrap;
}

.month-chip:hover {
  color: var(--text);
  transform: translateY(-1px);
}

.month-chip.is-active {
  color: #1a1408;
  border-color: transparent;
}

.month-chip__bg {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  box-shadow: 0 8px 24px -6px var(--accent-glow);
  z-index: -1;
}

/* ---------- Bar chart race ---------- */
.race-card {
  padding: clamp(18px, 3vw, 28px);
  /* The two numeric columns pinned to the right, hoisted here so the row's `right`
     inset, the slot's width, the bar's length calc and the column headers all read
     from ONE number each. They were duplicated literals, which is how a header ends
     up a few px out from the column it heads. */
  --prize-slot: 124px;
  --pnl-slot: 108px;
}

.race-progress {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.race-track {
  position: relative;
  /* Rows joining or dropping off the board slide through the bottom edge, so the
     track has to clip. The padding widens the clip box far enough that the bars'
     outer glow still renders at the first/last row; the matching negative margin
     cancels the padding's effect on layout (rows are absolutely positioned to the
     padding box, so they shift with it and stay put on screen). */
  overflow: hidden;
  padding: 10px 0;
  margin: -10px 0;
}

.race-row {
  position: absolute;
  top: 0;
  left: 0;
  /* reserve a fixed gutter on the right for the (pinned) reward ladder */
  right: var(--prize-slot);
  height: 40px;
  display: grid;
  grid-template-columns: 52px 24px 150px 1fr;
  align-items: center;
  gap: 12px;
}

.race-rank {
  display: flex;
  align-items: center;
  /* Right-aligned so the ordinal sits against the column's trailing edge and the
     gap to the movement arrow matches the arrow→name gap (rather than the whole
     52px cell width reading as one big gutter after a left-aligned number). */
  justify-content: flex-end;
  font-family: var(--font-mono);
  font-size: 20px;
  font-weight: 500;
  /* Fallback only — BarChartRace sets this inline per row to the SAME colour as that
     row's bar (gold / silver / bronze / pack slate), so the two statements of rank
     can't disagree. */
  color: var(--text-dim);
}

/* Own grid column, between the rank number and the name. */
.race-move {
  font-size: 12px;
  line-height: 1;
  letter-spacing: -1px;
  text-align: center;
  justify-self: center;
}
/* Movement: green climbing, red dropping. (--pos is a near-white "gain" text
   tone, so use an explicit green here for a clear up/down read.) */
.race-move.is-up { color: #6bd08f; }
.race-move.is-down { color: var(--neg); }

.race-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.race-flag {
  margin-right: 8px;
  font-size: 24px;
  line-height: 1;
}

.race-bar-track {
  position: relative;
  /* Slimmer than the 40px row it sits in, leaving 7px of air above and below. The
     row is `align-items: center`, so this height alone sets how thick the bars read
     and they stay centred in their lane without any offset — and .race-bar is
     height:100% of this, with the PnL number riding the bar's own 50% line.
     Room to go slimmer still: .race-bar's 7px radius only rounds into a full pill
     at 14px or below, and the 40px row is what would need revisiting first. */
  height: 26px;
  /* --pnl-slot is inherited from .race-card: a fixed slot reserved at the bar's end
     for the PnL number. Bars scale to the track width MINUS that slot (see the
     inline calc in BarChartRace), so a bar's length stays exactly proportional to
     its PnL and a wide number never shortens its own bar. The bonus lives in the
     pinned ladder, not here. */
}

/* PnL number, sitting just past the bar's end (`left` set inline to the bar's
   width). It rides the bar tip within the reserved slot and never overlaps it. */
.race-bar-end {
  position: absolute;
  top: 50%;
  transform: translate(12px, -50%);
  white-space: nowrap;
  pointer-events: none;
}

/* Solid magnitude bar (width scales with standing) with a fixed-width mini
   trend line embedded at its left, clipped to the bar. */
/* Name overlaid inside the bar — phones only (desktop uses the name column).
   Clipped to the bar by .race-bar's overflow:hidden, so it never spills past the
   bar's right edge onto the dark track. */
.race-bar-name {
  display: none;
}

.race-bar {
  position: relative;
  height: 100%;
  /* width set inline: pct% of (track − --pnl-slot), so lengths stay proportional */
  min-width: 2px;
  border-radius: 7px;
  overflow: hidden;
  box-shadow:
    0 1px 6px -4px currentColor,
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.5);
}
/* Diagonal glossy sheen sweeping across the bar */
.race-bar::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 28%,
    rgba(255, 255, 255, 0.22) 47%,
    rgba(255, 255, 255, 0.05) 55%,
    transparent 70%
  );
}
/* Top-to-bottom sheen for depth */
.race-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), transparent 45%, rgba(0, 0, 0, 0.18));
}

/* Podium bars (top 3): keep the gloss and add a colored outer glow so the
   gold/silver/bronze winners clearly jump off the panel. */
.race-bar.is-medal {
  box-shadow:
    0 2px 11px -4px currentColor,
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.55);
}

/* New-leader "heat": the instant a trader overtakes into 1st place, the top bar
   flares with a warm amber bloom that peaks then settles (~1.5s), so a fresh leader
   is impossible to miss. The outer glow rides the bar; .race-bar-heat is an inner
   bloom clipped to the bar shape. */
.race-bar.is-hot {
  animation: race-heat-glow 2.6s cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
.race-bar-heat {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 1;
  background: radial-gradient(
    130% 150% at 80% 50%,
    rgba(255, 240, 210, 1) 0%,
    rgba(255, 190, 90, 0.95) 26%,
    rgba(255, 140, 40, 0.6) 52%,
    transparent 78%
  );
  mix-blend-mode: screen;
  animation: race-heat-sheen 2.6s cubic-bezier(0.16, 0.84, 0.44, 1) forwards;
}
@keyframes race-heat-glow {
  0% {
    box-shadow: 0 2px 11px -4px currentColor, inset 0 0 0 1.5px rgba(255, 255, 255, 0.55);
  }
  10% {
    box-shadow:
      0 0 56px 10px rgba(255, 170, 60, 1),
      0 0 22px 3px rgba(255, 220, 150, 0.95),
      0 2px 11px -4px currentColor,
      inset 0 0 0 1.5px rgba(255, 245, 220, 1);
  }
  45% {
    box-shadow:
      0 0 38px 4px rgba(255, 160, 55, 0.72),
      0 2px 11px -4px currentColor,
      inset 0 0 0 1.5px rgba(255, 255, 255, 0.72);
  }
  100% {
    box-shadow: 0 2px 11px -4px currentColor, inset 0 0 0 1.5px rgba(255, 255, 255, 0.55);
  }
}
@keyframes race-heat-sheen {
  0% {
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  50% {
    opacity: 0.72;
  }
  100% {
    opacity: 0;
  }
}

/* Pack bars (4th+): matte and dim — kill the colored glow and knock the white
   sheens/inner border right down so the slate never brightens into a silver look
   that competes with 2nd place. */
.race-bar.is-pack {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
.race-bar.is-pack::before {
  opacity: 0.22;
}
.race-bar.is-pack::after {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 42%, rgba(0, 0, 0, 0.28));
}

/* PnL number, riding the bar end (see .race-bar-end). */
.race-value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  color: var(--text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* No colour here on purpose: the ordinal INHERITS it from .race-rank, which
   BarChartRace sets inline to that row's medal. This span used to hardcode
   --accent-2, and because a rule on the element beats an inherited value, it
   silently overrode the inline colour and left every place pale gold. */

/* Fixed reward ladder: one slot per place, pinned to the right, never moves as
   rows re-order (so the bonuses don't ride the moving PnL numbers). */
.race-prize-slot {
  position: absolute;
  right: 0;
  width: var(--prize-slot);
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.race-prize {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
  /* One neutral white at every place. The bonus used to be gold on the podium and
     green below it, which made the ladder a second colour-coded rank signal
     competing with the medal on the bar and the ordinal — and a green figure beside
     an amber bar read as unrelated. Rank is stated by the ordinal and the bar; this
     column just states money. */
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The podium's bonus is heavier, not a different colour — the medal on the bar and
   the ordinal already say which place it is. */
.race-prize-slot.is-top .race-prize {
  font-weight: 800;
}

/* Column heads for the two pinned numeric columns, matching the pair RankChart
   draws over its ladder.
   Hung off the CARD, not the track: .race-track clips (overflow:hidden, so rows can
   slide through its bottom edge), which would swallow anything sitting above the
   first row. Their `top` therefore reproduces the card's own top padding —
   card-pad-y + the header band — and steps back up from the first row.
   The 30px step is what sets the gap DOWN to the first row (30 less the ~15px the
   label itself occupies). --chart-tabs-h carries the matching 8px so growing that
   gap didn't push the heads up into the tab row. */
.race-col-head {
  position: absolute;
  top: calc(
    var(--card-pad-y, clamp(16px, 2.4vw, 24px)) + var(--chart-head-h, 0px) - 30px
  );
  text-align: center;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Same grey as the streak board's column heads and RankChart's SVG ones. */
  color: #8a938f;
  pointer-events: none;
}
/* Over the slot reserved at the bar's right end, where the leader's PnL sits. */
.race-col-head--pnl {
  right: calc(var(--card-pad-x, clamp(8px, 1.4vw, 14px)) + var(--prize-slot));
  width: var(--pnl-slot);
}
/* Over the pinned bonus ladder. */
.race-col-head--bonus {
  right: var(--card-pad-x, clamp(8px, 1.4vw, 14px));
  width: var(--prize-slot);
}

/* Top 3 rows: same size as the rest, just bolder. */
.race-row.is-leader .race-rank,
.race-row.is-leader .race-value,
.race-row.is-leader .race-name {
  font-weight: 800;
}
.race-value.pos { color: var(--pos); }
.race-value.neg { color: var(--neg); }

/* Transport bar docked under the panel, like a video player's controls. */
/* Play button centered just below the panel's bottom border. */
.race-play-dock {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}

/* Transport row BELOW the panel, under the scrubber: empty (left) · play/replay
   (center) · date (right). A 3-column grid keeps the play button truly
   page-centered regardless of the side elements' widths — which is why the
   now-empty left cell stays. */
.transport {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(12px, 2.5vw, 24px);
  min-height: 56px;
  /* Tight gap up to the scrubber, which sits directly above; the replay button's
     breathing room comes from the stacked row-gap on mobile. */
  margin-top: 12px;
}
.transport__left {
  justify-self: start;
}
.transport__center {
  justify-self: center;
}
/* Month stepper stacked over the day readout — one time control, two grains. The
   column is right-aligned in the transport grid (justify-self), but the two
   children center against each other so the date sits under the month. */
.transport__right {
  justify-self: end;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.transport .race-play-dock {
  margin-top: 0;
}
.transport .day-display {
  position: static;
  align-items: center;
}

/* Season picker sitting over the day readout. Deliberately quiet — the month is
   context you change occasionally, the date below it is the live figure that
   moves with the playhead. */
.month-step {
  display: flex;
  align-items: center;
  gap: 8px;
}
.month-step__label {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.month-step__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0 0 2px;
  border: 1px solid var(--surface-border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.18s, border-color 0.18s, background 0.18s;
}
.month-step__arrow:hover:not(:disabled) {
  color: var(--text);
  border-color: var(--accent);
  background: rgba(219, 179, 89, 0.1);
}
/* Ends of the data don't wrap — jumping from the newest month to the oldest
   reads as a glitch, so the arrow just goes inert. */
.month-step__arrow:disabled {
  opacity: 0.28;
  cursor: default;
}

/* Play / replay button — sized to sit in the transport row's rhythm. */
.race-play {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 150px;
  gap: 9px;
  padding: 11px 24px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  color: var(--text);
  border: 1px solid var(--accent);
  cursor: pointer;
  background: #000;
  transition: transform 0.15s, background 0.2s;
}
/* Deliberately larger than the button's 15px label — the glyph is the thing you
   aim at, and it carries every transport state (play / pause / replay). */
.race-play__icon {
  font-size: 21px;
  line-height: 1;
}
.race-play:hover {
  transform: translateY(-2px) scale(1.04);
  background: rgba(219, 179, 89, 0.1);
}

/* Scrubber sits below the panel, hugging the chart it scrubs like a video
   player's seek bar. The top margin is the gap up to the panel's bottom edge —
   generous enough that it doesn't read as welded to the chart. */
.race-scrubber {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: clamp(18px, 1.8vw, 28px) 0 0;
  height: 18px;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
/* Neutral track: the played portion is a soft white up to the thumb (--seek
   percent, set inline from the playhead), the remainder a faint rail — gold is
   reserved for the data + the primary controls. */
.race-scrubber::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0.55) 0 var(--seek, 0%),
    var(--surface-border-strong) var(--seek, 0%)
  );
}
.race-scrubber::-moz-range-track {
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0.55) 0 var(--seek, 0%),
    var(--surface-border-strong) var(--seek, 0%)
  );
}
/* Neutral light handle, vertically centered on the 5px track */
.race-scrubber::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  margin-top: -6.5px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #f2f0ea;
  box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.6);
  cursor: grab;
}
.race-scrubber::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: #f2f0ea;
  box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.6);
  cursor: grab;
}

@media (max-width: 720px) {

  /* The 3-column transport can't hold on a phone — stack it centered:
     date, then the play button, then the toggle. */
  .transport {
    grid-template-columns: 1fr;
    justify-items: center;
    /* larger gap between the stacked date and the replay button below it */
    row-gap: 20px;
  }
  /* (The phone override for the view tabs lives with .chart-slot further down —
     a media query adds no specificity, so it has to come after the base rule.) */
  /* Stacked: date first, then the play button under it. Play rides in the CENTER
     cell, so that's the one that orders; the left cell is empty. */
  .transport__left {
    display: none;
  }
  .transport__center {
    justify-self: center;
    order: 2;
  }
  .transport__right {
    justify-self: center;
    order: 1;
  }
  /* Smaller play button on a phone. */
  .race-play {
    padding: 11px 22px;
    font-size: 15px;
    gap: 8px;
    min-width: 132px;
  }
  .race-play__icon {
    font-size: 19px;
  }

  /* Rank, then the arrow column, then the bar; a narrower gutter holds the ladder.
     Shorter height packs the rows tighter — kept in lockstep with ROW_H_MOBILE (the
     vertical stride set in BarChartRace) so rows neither overlap nor gap. */
  /* Narrower gutter and a smaller reserved PnL slot at the phone font size; the
     row inset, slot width, bar calc and column heads all follow from these. */
  .race-card {
    --prize-slot: 64px;
    --pnl-slot: 84px;
  }
  .race-row {
    grid-template-columns: 30px 16px 1fr;
    gap: 8px;
    height: 32px;
  }
  .race-prize-slot {
    height: 32px;
  }
  .race-rank {
    font-size: 15px;
  }
  .race-move {
    font-size: 10px;
  }
  .race-value {
    font-size: 15px;
  }
  .race-prize {
    font-size: 14px;
  }
  .race-flag {
    font-size: 16px;
    margin-right: 5px;
  }
  /* No room for a separate name column on a phone — render the flag + name INSIDE
     the bar instead (classic bar-race style). The row's name column is hidden and
     the in-bar name (clipped to the bar by overflow:hidden) takes over, so a long
     name is cut off at the bar's right edge rather than spilling onto the track. */
  .race-name {
    display: none;
  }
  .race-bar-name {
    position: absolute;
    left: 10px; /* a little padding inside the bar */
    top: 0;
    height: 100%;
    display: flex;
    align-items: center;
    z-index: 2;
    pointer-events: none;
    white-space: nowrap;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 13px;
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 0 3px rgba(0, 0, 0, 0.9);
  }
  .race-bar-track {
    height: 22px;
  }
  /* No header band on a phone (the view tabs are hidden, so --chart-head-h is 0),
     which leaves no room above the first row — the heads would sit outside the card. */
  .race-col-head {
    display: none;
  }
  /* Reserve a clear band below the last row so the centered day readout
     (an absolute overlay pinned to the panel bottom) doesn't collide with
     the 10th-place row now that the name column is hidden — and drop the
     readout itself lower so it sits comfortably inside that band. */
  /* Specificity bumped: the later `.chart-card { padding: ... }` shorthand
     (declared after this block) would otherwise reset padding-bottom, and the
     later `.day-display` base rule would reset `bottom`. */
  .chart-card.race-card {
    padding-bottom: 16px;
  }
  .chart-slot .day-display {
    bottom: 16px;
  }
  /* Phone: the chart isn't rendered (see RankChart) — the legend table is the
     whole panel, so it needs less bottom padding. */
  .chart-card {
    padding-bottom: 24px;
  }
}

.race-progress__total {
  color: var(--text-faint);
}

/* ---------- Chart Studio (tabs + shared slot) ---------- */
/* Rank / Bar / PnL view switch — underline tabs in the panel's top-left corner.
   Deliberately containerless so it sits quietly inside the chart it drives; the
   month picker above the panel keeps the month stepper (see .month-step). */
.chart-tabs {
  display: inline-flex;
  align-items: flex-end;
  /* Proportioned to the 15px tracked caps. */
  gap: clamp(20px, 2vw, 30px);
}

/* Prominent "scoreboard" readout of the day — docked inside the panel, just
   above the bottom border. */
.day-display {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 28px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 12px;
}

.day-display__date {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 22px;
  line-height: 1;
  color: var(--text);
  white-space: nowrap;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.85);
}

/* The chart panel is the "screen" — hosts the play overlay.
   isolate keeps those overlays contained so the play button sits cleanly above. */
.chart-slot {
  position: relative;
  isolation: isolate;
  /* The card's own padding, hoisted to variables so the absolutely-positioned tab
     bar can line up with the card's padding box instead of guessing at it. */
  --card-pad-y: clamp(16px, 2.4vw, 24px);
  --card-pad-x: clamp(8px, 1.4vw, 14px);
  /* The header band, added to .chart-card's top padding. The bar race starts its
     first row at the card's top edge, so without this the tabs would land on top of
     1st place.
     Titles are gone — headings live on the host page around the widget now — so
     --chart-title-h is 0 and the band is the tab row alone. Kept as a variable
     rather than deleted: it's what the tab row's `top` is offset by, and a panel
     that ever regains a title above the tabs only has to set it. */
  --chart-title-h: 0px;
  /* Tab row (~29px: 15px caps plus its underline padding) plus the gap down to the
     chart. Grown by 8px when the PnL/Bonus column heads moved into this band, so the
     extra room lands UNDER those heads rather than squeezing them against the tabs —
     .race-col-head's offset grew by the same 8px, which holds it still relative to
     the tab row above it. */
  --chart-tabs-h: 70px;
  --chart-head-h: calc(var(--chart-title-h) + var(--chart-tabs-h));
}

/* The tab row IS the header band now. z-index clears both the outgoing and incoming
   panel mid-slide, and the .card::before sheen. Spans the full width and centers its
   inline-flex child, so the wrapper ignores pointer events and only the tabs
   themselves stay clickable. */
.chart-slot__tabs {
  position: absolute;
  top: calc(var(--card-pad-y) + var(--chart-title-h));
  left: 0;
  right: 0;
  z-index: 2;
  text-align: center;
  pointer-events: none;
}
.chart-slot__tabs .chart-tabs {
  pointer-events: auto;
}

/* A panel with a single view has no toggle, so it hands that half of the header
   band back to the chart. */
.chart-slot--notabs {
  --chart-tabs-h: 0px;
}

/* Phones render the bar race only (RankChart drops to a legend), so the switch has
   nothing to switch — hide it and hand the whole band back to the card. With the
   titles gone that leaves no header band at all here. Must sit AFTER the .chart-slot
   rule above: a media query carries no extra specificity, so source order is what
   decides --chart-tabs-h. */
@media (max-width: 720px) {
  .chart-slot__tabs {
    display: none;
  }
  .chart-slot {
    --chart-tabs-h: 0px;
  }
}

.chart-tab {
  position: relative;
  font-family: var(--font-display);
  /* Control scale, and uppercase to bind it to the kicker's treatment above —
     the header then descends 46 → 22 → 15 instead of bulging at the bottom. */
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-faint);
  /* Bottom padding is the gap the underline sits in — it anchors to the button's
     bottom edge, so this is what keeps the rule off the text. */
  padding: 2px 2px 9px;
  border: none;
  background: transparent;
  cursor: pointer;
  transition: color 0.2s;
  white-space: nowrap;
}
.chart-tab:hover {
  color: var(--text);
}
/* --accent-2 is the light end of the underline gradient below, so the active
   label and its rule read as one gold unit against two faint neighbours. */
.chart-tab.is-active {
  color: var(--accent-2);
}
/* The sliding rule. framer-motion moves this element between tabs via layoutId,
   so it animates position + width in one gesture instead of cross-fading. */
.chart-tab__underline {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  box-shadow: 0 2px 12px -2px var(--accent-glow);
}

/* ---------- Winning-streak panel ---------- */
/* Breathing room between the two stacked competition panels. */
.panel-gap {
  margin-top: clamp(56px, 7vw, 104px);
}

/* Screen-reader-only utility. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The top winning-streak lengths as a table, one row per length. ONE grid for the
   whole board (rows are display:contents) rather than a grid per row — that's what
   lets a column size to its content and still line up across every row. Centred at
   its natural width, so leftover panel width sits OUTSIDE the table instead of being
   absorbed by a flexible column and tearing a hole through the middle of a row. */
.streak-board {
  display: grid;
  /* Fallback only — StreakBoard replaces this inline with tracks measured
     against the widest value the whole MONTH will ever put in each column, so
     the table's width is constant and centring can't slide it mid-replay.
     Five tracks at most: place · streak · run · traders · bonus. The traders track
     only exists when the feed names people, so the inline template is built from
     however many cells the row actually renders. */
  grid-template-columns: 64px max-content max-content max-content max-content;
  --mark-w: 8px;
  --mark-gap: 5px;
  column-gap: clamp(24px, 3.2vw, 60px);
  row-gap: clamp(14px, 1.6vw, 20px);
  /* Baseline, not centre — every cell is one line now, and this keeps their
     baselines on one rule regardless of the mark run's height. */
  align-items: baseline;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
}
.streak-board__row {
  display: contents;
}

.streak-board__col-head {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Matches the SVG column headers on the PnL ladder (RankChart's Customized
     block), which can't use CSS vars and so hardcode this grey. */
  color: #8a938f;
}

/* The ranks a row holds — "1st", or a span like "2nd–4th" when traders are tied.
   Its track is measured in JS; the letter-spacing below is charged by hand there
   (PLACE_TRACKING_EM), since canvas measureText can't see it. */
.streak-board__place {
  font-family: var(--font-display);
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Colour comes inline from rankColor(), keyed on the ROW — the top three rows are
     always gold/silver/bronze from the same MEDAL palette the rank chart uses,
     whatever ranks a tie has pushed them onto. Empty rows are pack grey. */
}

.streak-board__cell {
  min-width: 0;
}

/* The one repeating shape: a bold value in text colour, its unit in the dim
   tone, both at ONE size. Emphasis is weight and colour, never size — three
   different type sizes across one row is what stopped this reading as a table. */
.streak-board__value {
  font-family: var(--font-display);
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 500;
  line-height: 1.15;
  color: var(--text-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.streak-board__value strong {
  font-weight: 700;
  color: var(--text);
}
.streak-board__value--empty {
  color: var(--text-faint);
}

/* One mark per qualifying day. Never wraps — the column is max-content, so a
   longer run widens the table rather than folding onto a second line. At the top
   of the field streak lengths cluster tightly, so this reads as "a run of
   consecutive days" more than as a length comparison. */
/* Width is reserved for the month's LONGEST run, so earning a mark fills the box
   rather than widening the column. Early in the month the box is mostly empty —
   that static gap is the price of the table not lurching sideways as runs grow.
   Both the width and the mark size come from StreakBoard (runMetrics), which
   thins the marks on a long month so the run can't grow without bound. */
.streak-board__run {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--mark-gap);
  flex-wrap: nowrap;
  /* Width comes from the grid track (RUN_COL in StreakBoard), not from content —
     marks fill it from the left and a shorter run just leaves the tail empty. */
  min-width: 0;
  /* Sit the squares ON the text baseline, like the bottom of a glyph.
     The marks are EMPTY spans, so this flex box has no real baseline and one is
     synthesised from its bottom margin edge — which is exactly what's wanted here:
     baseline alignment therefore rests the squares' bottoms on the row's baseline.

     That only works because the marks are small and square. At the old fixed 18px
     height the same rule pushed them well above the cap height, and the fix for
     that was the height, not the alignment — centring instead only brought them to
     the row's middle, still a couple of px high. Across every month length the
     square lands between 12.4px and 14.4px from the line-box top, i.e. always at or
     just below the 12.1px centre, never floating above it. */
  align-self: baseline;
}
.streak-board__day {
  display: block;
  /* Square: height tracks the same var as the width. StreakBoard solves both the
     size and the gap so `top` marks fill the run column exactly. */
  width: var(--mark-w, 8px);
  height: var(--mark-w, 8px);
  border-radius: 2px;
  flex: none;
  transform-origin: center;
}

/* Who holds the row's streak length. Never wraps: StreakBoard packs only as many
   chips as NAME_COL fits and turns the remainder into the "+N" button, so the row
   stays exactly one line tall whether one trader holds the length or five hundred.
   `nowrap` is the backstop that makes a packing bug visible as an overflow rather
   than silently doubling the row's height. */
.streak-board__names {
  display: flex;
  align-items: baseline;
  gap: 10px; /* CHIP_GAP in StreakBoard — keep the two in step */
  flex-wrap: nowrap;
  min-width: 0;
  overflow: hidden;
}
.streak-board__chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-display);
  /* One size below the row's figures: the names are the row's subject but the
     streak and the bonus are what it's ranked and paid on. */
  font-size: clamp(15px, 1.3vw, 18px);
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.streak-board__flag {
  line-height: 1;
  /* FLAG_W in StreakBoard budgets 22px for this — flag glyph plus the chip gap. */
  font-size: 15px;
}

/* Opens the full roster. A real button so it's tab-reachable and announces its
   state; styled as a quiet count rather than a control, since it sits inside a
   table row and shouldn't compete with the bonus figure. */
.streak-board__more {
  appearance: none;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--surface-border);
  border-radius: 999px;
  padding: 2px 9px;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.streak-board__more:hover,
.streak-board__more[aria-expanded='true'] {
  background: rgba(255, 255, 255, 0.12);
  color: var(--text);
}
.streak-board__more:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The expanded roster, spanning every column of the row it belongs to. `1 / -1`
   works because the row is display:contents, so this is a direct grid item.
   `align-self: start` is required: the grid aligns items to the text baseline, and
   without this the panel gets dragged onto the row's baseline. */
.streak-board__roster {
  grid-column: 1 / -1;
  align-self: start;
  overflow: hidden; /* framer animates height; this clips during the transition */
}
.streak-board__roster-head {
  margin: 4px 0 8px;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a938f;
}
/* Auto-fill columns so a 6-name tie doesn't stretch into a sparse full-width row,
   and a 500-name one scrolls instead of running off the panel. */
.streak-board__roster-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px 16px;
  max-height: 200px;
  overflow-y: auto;
  padding: 2px 2px 8px;
}

@media (max-width: 720px) {
  /* Still ONE grid, exactly like desktop — only the run column drops out. A grid
     per row was what broke the columns here: `auto` tracks size to their own row's
     content, so "1ST" and "5TH–6TH" started their neighbours at different offsets
     and the streak figures never lined up. One grid measures each track across
     every row, so "21 days" and "9 days" share a column.

     Three tracks for the three cells that stay on the top line — place · streak ·
     bonus. The count must match or rows wrap. Full width rather than fit-content,
     so the bonus can sit out on the right edge. */
  .streak-board {
    grid-template-columns: auto auto 1fr;
    width: 100%;
    column-gap: 14px;
    row-gap: 12px;
  }
  /* Bonus hugs the right edge. Targeted by class, NOT :last-child — the roster
     panel becomes the row's last child when it opens, and would inherit this. */
  .streak-board__cell--bonus {
    text-align: right;
  }
  /* Names get their own line beneath the figures: four cells will not fit a 360px
     screen, and a name is too long to share a track with the bonus. Spanning the
     full row also gives the packer the whole width to fill, so more names fit here
     than on desktop despite the narrower screen.
     StreakBoard renders this cell LAST on mobile — a `1 / -1` span placed mid-row
     pushes every cell after it onto the following line. */
  .streak-board__names {
    grid-column: 1 / -1;
    flex-wrap: wrap;
    margin: -2px 0 6px;
  }
  .streak-board__roster-list {
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    max-height: 220px;
  }
}

/* ---------- Chart ---------- */
.chart-card {
  /* Tighter horizontal margins so the bars use more of the width; trimmed bottom
     so the table doesn't leave a big gap below the last row. The top gains the
     header band (see .chart-slot) so content clears the title + view switch — the
     fallback 0px covers any .chart-card rendered outside a .chart-slot, such as
     HorseRace's loading panel. */
  padding: calc(var(--card-pad-y, clamp(16px, 2.4vw, 24px)) + var(--chart-head-h, 0px))
    var(--card-pad-x, clamp(8px, 1.4vw, 14px));
  padding-bottom: clamp(10px, 1.4vw, 16px);
  /* Never let the card exceed its container (the mobile legend's intrinsic
     width would otherwise push it past the viewport). */
  max-width: 100%;
  box-sizing: border-box;
  /* Warm-grey surface echoing the hero stat cards, but semi-transparent so the
     black page shows through (the inherited .card backdrop-blur frosts it). */
  background: linear-gradient(160deg, rgba(36, 38, 46, 0.22), rgba(21, 22, 27, 0.22));
  /* Drop the card outline + top-edge highlight: on this large, mostly-empty
     panel they read as stray faint horizontal lines at the top and bottom
     rather than a tidy card edge. The solid fill + shadow define it instead. */
  border: none;
}
.chart-card::before {
  content: none;
}

.chart-wrap {
  width: 100%;
  height: clamp(360px, 48vw, 500px);
}

/* ---------- Empty panel ---------- */
/* A month that's in the feed but carries no results yet — the opening days of a
   new month. min-height matches .chart-wrap so the panel keeps a chart's footprint
   and the page doesn't jump when the first day lands. */
.panel-empty {
  display: grid;
  place-items: center;
  min-height: clamp(360px, 48vw, 500px);
  text-align: center;
}

/* The streak ladder is a short table rather than a plot area, so its empty panel
   holds 60% of the chart's footprint — the full height leaves the message
   stranded in the middle of a mostly-blank card. Same clamp, scaled. */
.panel-empty--compact {
  min-height: clamp(216px, 28.8vw, 300px);
}

.panel-empty__inner {
  display: grid;
  justify-items: center;
  gap: 14px;
  /* Holds the line break off the edges on a wide panel — a single line of copy
     stretched across 1600px reads as a stray label rather than a message. */
  max-width: 36ch;
  padding: 0 20px;
}

/* Gold disc echoing the accent pills elsewhere, so an empty panel still looks
   like part of the board rather than a browser default. */
.panel-empty__mark {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow:
    0 0 0 1px rgba(219, 179, 89, 0.28),
    0 14px 36px -18px var(--accent-glow);
}

.panel-empty__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(17px, 1.6vw, 21px);
  letter-spacing: -0.01em;
  color: var(--text);
}

.panel-empty__hint {
  margin: 0;
  font-size: 14px;
  color: var(--text-dim);
}

@media (max-width: 720px) {
  /* The phone panels are much shorter than the desktop chart, so matching
     .chart-wrap here would leave a tall empty box. */
  .panel-empty {
    min-height: 260px;
  }
  .panel-empty--compact {
    min-height: 156px;
  }
  .panel-empty__mark {
    width: 46px;
    height: 46px;
  }
}

/* Mobile-only trader key for the trend/rank charts (inline end-labels don't
   fit on a phone). One trader per row, laid out on a shared 6-column grid so
   every column lines up vertically across rows (each item spreads its cells
   into the parent columns via display:contents).
   Columns: [bar] [rank] [flag] [name →] [value] [prize]. */
.chart-legend {
  display: grid;
  /* place · flag · name(1fr) · PnL · bar · prize — matches the desktop label. */
  grid-template-columns: auto auto minmax(0, 1fr) auto auto auto;
  align-items: center;
  column-gap: 10px;
  row-gap: 12px;
  margin-bottom: 18px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
}
.chart-legend__item {
  /* A real (animatable) box that still borrows the parent's column tracks via
     subgrid, so every row's columns stay aligned while framer can slide the row
     to a new slot when the standings re-sort. */
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
}
.chart-legend__bar {
  position: relative;
  width: 36px;
  height: 12px;
  border-radius: 3px;
  background: rgba(196, 204, 216, 0.09); /* silver track */
  overflow: hidden;
}
.chart-legend__bar-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: rgba(196, 204, 216, 0.34); /* silver fill */
}
.chart-legend__place {
  justify-self: end;
  color: var(--accent-2);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.chart-legend__flag {
  line-height: 1;
}
.chart-legend__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-weight: 600;
}
.chart-legend__val {
  justify-self: end;
  color: var(--pos);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.chart-legend__prize {
  justify-self: end;
  color: var(--accent);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* End-of-line label dot leader — subtle dotted line from name to value. */
.chart-dotlead {
  stroke: rgba(255, 255, 255, 0.28);
  stroke-width: 1;
  stroke-dasharray: 1 4;
  stroke-linecap: round;
}

/* Instant (no native-title delay) country tooltip on the label flag. */
.chart-flag {
  cursor: default;
}
.chart-flag__tip {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s ease;
}
.chart-flag:hover .chart-flag__tip {
  opacity: 1;
}
.chart-flag__tip rect {
  fill: #1a1d24;
  stroke: rgba(255, 255, 255, 0.2);
}
.chart-flag__tip text {
  fill: #f7f6f3;
}

/* Bump-line casing: background-colored stroke drawn under each line so crossings
   read clearly. Its stroke/width tweens are declared on the curve below, for the
   same reason the line's are. */
.rank-casing {
  stroke: var(--chart-casing);
}

/* Bump-line glow tiers. Every line carries `.rank-line` with a 3-part
   drop-shadow that is transparent by default; the podium classes below swap in
   colored, blurred shadows. Because the filter always has the same 3 shadows,
   `transition: filter` interpolates blur + color, so the glow softly fades in
   and out as a line enters or leaves the top 3 — alongside the stroke color,
   which eases over the same duration. */
.rank-line {
  filter:
    drop-shadow(0 0 0 transparent)
    drop-shadow(0 0 0 transparent)
    drop-shadow(0 0 0 transparent);
  /* Only `filter` can be transitioned here: Recharts puts our className on the
     wrapping <g>, and the filter applies to that group. Everything else lives on
     the inner <path> — see the curve rule below. */
  transition: filter 0.6s ease;
}

/* Recharts renders each Line as <g class="recharts-line rank-line"> wrapping a
   <path class="recharts-line-curve">, and stroke/stroke-width/stroke-opacity are
   presentation attributes on that PATH. `transition` is not an inherited
   property, so a transition declared on the <g> never reaches them and they snap.
   Declaring it on the curve is what actually animates the color.

   `stroke` matches the group's 0.6s so a line entering or leaving the top 3
   changes color and gains its glow as one gesture. That's ~52% of a day-step at
   the current pace (~1140ms) — a visible sweep between medal colors that still
   resolves within the day it belongs to. The ceiling is a full day-step (~1.1s):
   past that a change would still be running when the next crossing starts, which
   is what made the original 0.45s-on-a-620ms-step feel detached. Opacity and
   width stay separate — they serve hover and the exit fade, not crossings. */
.rank-line .recharts-line-curve,
.rank-casing .recharts-line-curve {
  transition: stroke 0.6s ease, stroke-opacity 0.45s ease, stroke-width 0.45s ease;
}

/* #1 leader — brightest. Radii kept tight so the glow hugs the line rather than
   blooming out into the label column at the line's end. */
/* Each shadow is mixed toward transparent so the bloom sits lighter on the line
   than the medal color itself — tight radii AND reduced opacity. */
.leader-line {
  filter:
    drop-shadow(0 0 1.5px color-mix(in srgb, var(--leader-color) 70%, transparent))
    drop-shadow(0 0 3px color-mix(in srgb, var(--leader-color) 55%, transparent))
    drop-shadow(0 0 6px color-mix(in srgb, var(--leader-color) 40%, transparent));
}

/* #2 / #3 — colored glow in each trader's own hue, a touch softer than #1. */
.glow-2 {
  filter:
    drop-shadow(0 0 1.5px color-mix(in srgb, var(--glow-2) 65%, transparent))
    drop-shadow(0 0 2.5px color-mix(in srgb, var(--glow-2) 50%, transparent))
    drop-shadow(0 0 4.5px color-mix(in srgb, var(--glow-2) 35%, transparent));
}
.glow-3 {
  filter:
    drop-shadow(0 0 1.5px color-mix(in srgb, var(--glow-3) 65%, transparent))
    drop-shadow(0 0 2.5px color-mix(in srgb, var(--glow-3) 50%, transparent))
    drop-shadow(0 0 4.5px color-mix(in srgb, var(--glow-3) 35%, transparent));
}

@media (prefers-reduced-motion: reduce) {
  .rank-line { transition: none; }
  /* No pulsing flare: hold a gentle, steady warm bloom on the new leader instead. */
  .race-bar.is-hot { animation: none; }
  .race-bar-heat { animation: none; opacity: 0.5; }
}

/* The SVG root clips overflow by default, which shears the leader-line glow at
   the plot edge. Let it bleed into the card's padding instead of being cropped
   against the sides of the panel. */
.chart-wrap .recharts-surface {
  overflow: visible;
}

/* ---------- Custom tooltip ---------- */
.tooltip {
  min-width: 210px;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: rgba(10, 16, 17, 0.92);
  border: 1px solid var(--surface-border-strong);
  backdrop-filter: blur(var(--blur));
  box-shadow: var(--shadow-card);
}

.tooltip__date {
  font-size: 12px;
  color: var(--text-faint);
  margin-bottom: 8px;
  font-family: var(--font-mono);
}

.tooltip__row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 2px 0;
}

.tooltip__name {
  flex: 1;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tooltip__val {
  font-family: var(--font-mono);
  font-weight: 700;
}

.swatch {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  flex: none;
}

/* ---------- Podium ---------- */
.podium {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  align-items: end;
  margin-bottom: 22px;
}

.podium__col {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.podium__col.rank-1 { order: 2; transform: translateY(-26px); }
.podium__col.rank-2 { order: 1; }
.podium__col.rank-3 { order: 3; }

/* Rank component sits above each card */
.podium__header {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  margin-bottom: 14px;
}

.podium__rankbadge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.podium__item {
  position: relative;
  width: 100%;
  padding: 24px 20px 0;
  text-align: center;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--surface-border);
  overflow: hidden;
}

/* Keep all card content above the ambient glow so text stays crisp. */
.podium__item > * {
  position: relative;
}
.podium__glow {
  z-index: 0;
}

/* #1 card — the standout: bigger and gold-edged, kept clean. */
.podium__col.rank-1 .podium__item {
  padding: 30px 22px 0;
  border-color: rgba(232, 210, 168, 0.55);
  background: var(--surface);
  box-shadow:
    0 0 0 1px rgba(232, 210, 168, 0.3),
    0 22px 55px -18px rgba(0, 0, 0, 0.85);
}

.podium__glow {
  position: absolute;
  inset: -40% 0 auto 0;
  height: 50%;
  filter: blur(55px);
  opacity: 0.16;
  pointer-events: none;
}
.podium__col.rank-1 .podium__glow {
  opacity: 0.26;
}

/* Champion plaque (#1) — large, high-contrast, sits above the card */
.podium__champ {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #120c02;
  padding: 13px 30px;
  border-radius: 999px;
  background: linear-gradient(120deg, var(--accent-2), var(--accent));
  box-shadow: 0 12px 34px -8px var(--accent-glow);
}

.podium__champ-icon {
  font-size: 18px;
  line-height: 1;
  transform-origin: center;
  animation: crown-pulse 2.4s ease-in-out infinite;
}
@keyframes crown-pulse {
  0%, 100% { transform: translateY(-1px) scale(1); filter: drop-shadow(0 0 0 transparent); }
  50% { transform: translateY(-2px) scale(1.14); filter: drop-shadow(0 0 7px rgba(255, 240, 200, 0.85)); }
}
@media (prefers-reduced-motion: reduce) {
  .podium__champ-icon { animation: none; }
}

.podium__medal {
  font-size: 22px;
  line-height: 1;
}

.podium__avatar {
  width: 56px;
  height: 56px;
  margin: 12px auto 10px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 20px;
  color: #1a1408;
}
.podium__col.rank-1 .podium__avatar {
  width: 72px;
  height: 72px;
  font-size: 26px;
}

.podium__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
}
.podium__col.rank-1 .podium__name {
  font-size: 19px;
}

.podium__handle {
  font-size: 12.5px;
  color: var(--text-faint);
  margin-bottom: 16px;
}

/* Net PnL is the focal point of each card */
.podium__net {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.podium__net-val {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(30px, 4.4vw, 40px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
.podium__col.rank-1 .podium__net-val {
  font-size: clamp(40px, 5.6vw, 56px);
}

.podium__net-label {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Bonus stays prominent as a gold pill, but secondary to net PnL */
.podium__bonus {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}

.podium__bonus-pill {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 30px;
  color: var(--accent-2);
  padding: 8px 22px;
  border-radius: 999px;
  border: 1px solid rgba(219, 179, 89, 0.5);
  background: rgba(219, 179, 89, 0.12);
  font-variant-numeric: tabular-nums;
}
.podium__col.rank-1 .podium__bonus-pill {
  font-size: 40px;
  padding: 10px 26px;
}

.podium__bonus-label {
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* PnL curve bleeds to the bottom edge of the card */
.podium__spark {
  margin: 18px -20px 0;
  line-height: 0;
}
.podium__col.rank-1 .podium__spark {
  margin: 20px -22px 0;
}

/* ---------- Leaderboard rows ---------- */
.rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.row {
  display: grid;
  grid-template-columns: 44px 1fr 120px 84px 84px 96px;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--surface-border);
  transition: border-color 0.2s, background 0.2s, transform 0.15s;
}

.row:hover {
  border-color: var(--surface-border-strong);
  background: var(--surface-2);
  transform: translateX(2px);
}

/* Rows outside the paid positions are visually de-emphasized. */
.row.is-unpaid {
  opacity: 0.6;
}
.row.is-unpaid:hover {
  opacity: 1;
}

/* Prize column */
.row__prize {
  text-align: right;
}
.row__prize-pill {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  color: var(--accent-2);
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid rgba(219, 179, 89, 0.45);
  background: rgba(219, 179, 89, 0.1);
  font-variant-numeric: tabular-nums;
}
.row__prize-none {
  color: var(--text-faint);
}

.row__rank {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-faint);
  text-align: center;
}

.row__trader {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.row__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 14px;
  color: #1a1408;
  flex: none;
}

.row__names {
  min-width: 0;
}

.row__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row__handle {
  font-size: 12.5px;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row__pnl {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

.row__metric {
  text-align: right;
}

.row__metric-val {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 14px;
}

.row__metric-label {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.row__spark {
  grid-column: 1 / -1;
}

.pos { color: var(--pos); }
.neg { color: var(--neg); }

/* Tabular figures keep count-up + columns from jittering (Work Sans is proportional). */
.row__rank,
.row__metric-val,
.tooltip__val,
.tooltip__date {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

.col-head {
  display: grid;
  grid-template-columns: 44px 1fr 120px 84px 84px 96px;
  gap: 14px;
  padding: 0 18px 10px;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.col-head span:nth-child(3),
.col-head span:nth-child(4),
.col-head span:nth-child(5),
.col-head span:nth-child(6) { text-align: right; }

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .nav__links {
    display: none;
  }
  .stats {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 720px) {
  .row,
  .col-head {
    grid-template-columns: 34px 1fr 96px 84px;
  }
  .col-head span:nth-child(4),
  .col-head span:nth-child(5),
  .row__metric {
    display: none;
  }
  .podium {
    gap: 8px;
  }
  .podium__item { padding: 16px 10px 0; }
  .podium__col.rank-1 .podium__item { padding: 20px 10px 0; }
  .podium__handle { display: none; }
}

/* Narrow phones: the 3-across podium can't hold the PnL figures / bonus pills
   (~90px per card). Stack it into one readable column, champion first, and
   drop the font sizes on the stats strip so the big money figures and the
   countdown stop overflowing their 2-up cards. */
@media (max-width: 560px) {
  .podium {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .podium__col.rank-1 { order: 1; transform: none; }
  .podium__col.rank-2 { order: 2; }
  .podium__col.rank-3 { order: 3; }
  .podium__header { min-height: 0; margin-bottom: 10px; }
  .podium__item,
  .podium__col.rank-1 .podium__item { padding: 24px 20px 0; }
  .podium__handle { display: block; }

  .stat-card { padding: 18px 16px; }
  .stat-card__num { font-size: 24px; }
  .stat-card__num--count { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ===== HUD frame — self-contained; delete this block (and HudFrame.tsx) to remove ===== */
.hud {
  position: relative;
}
.hud__overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: var(--radius-lg);
  /* inner vignette + soft gold edge glow */
  box-shadow:
    inset 0 0 80px rgba(0, 0, 0, 0.55),
    inset 0 0 0 1px rgba(219, 179, 89, 0.14);
  overflow: hidden;
}
.hud__scan {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(255, 255, 255, 0.035) 0px,
    rgba(255, 255, 255, 0.035) 1px,
    transparent 1px,
    transparent 4px
  );
  opacity: 0.6;
  animation: hud-scan 7s linear infinite;
}
@keyframes hud-scan {
  from { background-position: 0 0; }
  to { background-position: 0 -200px; }
}
/* ===== end HUD frame ===== */

