/* Consensus Edge — Slate redesign theme tokens
 * Source: docs/superpowers/specs/2026-05-09-slate-redesign-design.md §4
 */
:root {
  /* FIX 2026-07-12 (UI cleanup Batch 2): --slate-* tokens are now aliases of
     the canonical main.css palette — this block previously defined a parallel
     near-duplicate palette (e.g. --slate-green duplicated --accent's #10b981).
     Also: --slate-accent and --slate-text-1 were referenced throughout this
     file but NEVER defined — every var(--slate-accent, #1ddc7a) usage was
     silently rendering its fallback, a third shade of green. Both are now
     defined, so the fallbacks below are inert. */
  --slate-bg: var(--bg-base);
  --slate-bg-card: var(--bg-card);
  --slate-bg-hover: rgba(16,185,129,0.04);
  /* The wash that marks the card you clicked. Deliberately NEUTRAL: green now
     belongs to --slate-mkt-card.recommended and means "this is our pick", so a
     green tint on a card the user merely clicked would say the wrong thing.
     Replaced --slate-bg-selected (rgba(16,185,129,0.06)) on 2026-08-31. */
  --slate-bg-viewing: rgba(255,255,255,0.05);
  --slate-border: rgba(255,255,255,0.08);
  --slate-border-soft: rgba(255,255,255,0.04);
  /* 16% white. Completes the border ramp: -soft 4%, plain 8%, -strong 16%.
     Added 2026-09-01 for the takeover's back pill, which was invisible at 8%
     against the #111113 card it sits on. */
  --slate-border-strong: var(--border-strong);
  --slate-text: var(--text-primary);
  --slate-text-1: var(--text-primary);
  --slate-text-2: var(--text-muted);
  --slate-text-3: var(--text-dim);
  /* #52525b was 2.44:1 — WCAG 1.4.3 AA fail (FIX 2026-07-18 → #83838d, 5.0:1).
     FIX 2026-07-23: still read as dim gray; brightened to 6.9:1 with the rest
     of the muted ramp. Stays the lowest rung, below --text-dim. */
  --slate-text-4: #9c9ca6;
  --slate-green: var(--accent);
  --slate-green-bright: var(--accent-blue-light);
  --slate-blue: #60a5fa;
  --slate-amber: var(--accent-amber);
  --slate-red: var(--accent-red);
  --slate-accent: var(--accent);
  --slate-mono: var(--font-mono);
}

/* ===== Slate redesign — Tier 2 Filterable Board ===== */
.slate-section-h {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 0 0 12px;
}
.slate-section-title {
  font-size: 13px; color: var(--slate-text); text-transform: uppercase;
  letter-spacing: 0.18em; font-weight: 700;
}
.slate-section-meta { font-size: 12px; color: var(--slate-text-4); font-family: var(--slate-mono); }

.slate-row {
  display: grid;
  grid-template-columns: 56px 1fr 130px 96px 88px 80px 56px;
  gap: 12px; padding: 12px; align-items: center;
  border-bottom: 1px solid var(--slate-border-soft);
  cursor: pointer; transition: background 0.12s;
}
.slate-row:hover { background: var(--slate-bg-hover); }
.slate-row-sport {
  font-family: var(--slate-mono); font-size: 12px;
  color: var(--slate-text-3); font-weight: 700;
  padding: 4px 6px; background: rgba(255,255,255,0.04);
  border-radius: 4px; text-align: center; letter-spacing: 0.06em;
}
.slate-row-game { font-size: 14px; color: var(--slate-text); font-weight: 700; line-height: 1.3; }
.slate-row-pick { font-size: 13px; color: var(--slate-text); font-weight: 600; }

.slate-edge-chip {
  padding: 4px 8px; border-radius: 4px;
  font-family: var(--slate-mono); font-size: 12px;
  font-weight: 800; text-align: center;
}
.slate-edge-chip.pos {
  background: rgba(16,185,129,0.12);
  color: var(--slate-green);
  border: 1px solid rgba(16,185,129,0.3);
}
.slate-edge-chip.zero {
  color: var(--slate-text-4); font-weight: 600;
  border: 1px dashed var(--slate-border);
}
/* FIX 2026-07-20: a real edge on a pick the pipeline did NOT recommend is
   muted, not green. Green is reserved for BET/LEAN/LOCK — it means "we
   recommend this", not "this number is positive". */
.slate-edge-chip.muted {
  background: transparent;
  color: var(--slate-text-3);
  border: 1px solid var(--slate-border);
  font-weight: 700;
}
/* The pipeline's sizing grade, rendered INSIDE the EV chip (stacked above the
   number) so the verdict and the number are never separated — and so the row's
   child count is unchanged, which the positional phone selectors depend on. */
.slate-grade-chip {
  display: block;
  font-size: 12px; font-weight: 800;
  letter-spacing: 0.04em; line-height: 1.25;
  white-space: nowrap;
}
.slate-grade-chip.actionable { color: var(--slate-green); }
.slate-grade-chip.muted { color: var(--slate-text-3); }
.slate-row-add {
  background: transparent; color: var(--slate-text-3);
  border: 1px solid var(--slate-border);
  padding: 5px 0; border-radius: 4px;
  cursor: pointer; font-size: 15px; font-weight: 700;
  font-family: var(--slate-mono);
}
.slate-row-add:hover { border-color: var(--slate-green); color: var(--slate-green); }

/* ===== Slate redesign — Tier 3 Live Strip ===== */
.slate-live-strip { margin-top: 28px; }
.slate-live-row {
  display: grid;
  grid-template-columns: 56px 1fr 110px 90px 76px;
  gap: 12px; padding: 10px 12px; align-items: center;
  border-bottom: 1px solid var(--slate-border-soft);
  background: rgba(239,68,68,0.02);
}
.slate-live-status {
  font-family: var(--slate-mono); font-size: 12px;
  color: var(--slate-red); font-weight: 800;
  letter-spacing: 0.06em; margin-top: 2px;
}
.slate-live-score { font-family: var(--slate-mono); font-size: 12px; color: var(--slate-text-2); }
.slate-live-dot {
  display: inline-block; width: 6px; height: 6px;
  background: var(--slate-red); border-radius: 50%;
  animation: slate-pulse 2s infinite; margin-right: 6px;
  vertical-align: middle;
}
@keyframes slate-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* ===== Slate redesign — Drilldown takeover ===== */
.slate-drilldown[hidden] { display: none; }

/* Task 7 Step 1: freshness banner, first element of the takeover — a
   staleness disclosure that arrives after the numbers is an excuse, not a
   warning. Reuses .ob-freshness/.ob-freshness-text/.ob-stale/.ob-provisional
   from main.css (renderCardFreshness) so the card and takeover agree; this
   rule only relocates the rounded top corner here now that it renders first. */
.slate-takeover-freshness {
  padding: 10px 24px;
  background: var(--slate-bg-card);
  border-bottom: 1px solid var(--slate-border);
  border-radius: 12px 12px 0 0;
}
.slate-takeover-freshness .ob-stale { color: var(--slate-amber); }
.slate-takeover-freshness .ob-provisional { color: var(--slate-amber); }

.slate-takeover-h {
  padding: 16px 24px;
  border-bottom: 1px solid var(--slate-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  background: var(--slate-bg-card);
}

/* Task 7 Steps 2/3: one card per priced market under the header. `.dim` is a
   market buildDrilldown marked NOT a play (isPlay: tier > 0) — suppressed or
   below the sizing floor; its own `.slate-mkt-card-reason` states which,
   from the entry's own `reason` string (never synthesized here). `.selected`
   is the market currently driving the edge-math/line-shopping sections. */
.slate-mkt-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  padding: 16px 24px 0;
  background: var(--slate-bg-card);
}
.slate-mkt-card {
  background: var(--slate-bg);
  border: 1px solid var(--slate-border);
  border-radius: 8px;
  padding: 12px;
  text-align: left;
  cursor: pointer;
  font-family: var(--slate-mono);
  color: var(--slate-text);
}
.slate-mkt-card:hover { border-color: var(--slate-text-3); }
.slate-mkt-card.selected {
  background: var(--slate-bg-viewing);
}
/* The model's pick. Independent of .selected, so it stays put when the user
   clicks another market -- that separation IS the feature. Emitted only where
   buildDrilldown's `recommended` names the market, and that field is drawn
   only from markets where isPlay is true, so this class and .dim can never
   coexist. The honesty rule ("green never lands on a declined market") is
   therefore a property of the model, not of CSS specificity. Pinned by
   tests/js/game_drilldown.test.js and tests/test_signal_stack_css.py. */
.slate-mkt-card.recommended {
  border-color: var(--slate-green);
  box-shadow: 0 0 0 1px var(--slate-green);
}
.slate-mkt-card.dim { opacity: 0.55; }
.slate-mkt-card-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em;
  font-weight: 700; color: var(--slate-text-3);
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
}
.slate-mkt-card-badge {
  font-size: 12px; text-transform: none; letter-spacing: normal;
  font-weight: 700; color: var(--slate-text-2);
}
.slate-mkt-card-verdict {
  font-size: 13px; margin-top: 6px; color: var(--slate-text);
  line-height: 1.3;
}
.slate-mkt-card-row {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 8px; font-size: 13px;
}
.slate-mkt-card-ev { color: var(--slate-text); font-weight: 700; }
/* 2026-08-31 (Finding 1, final review): the base rule above used to be
   var(--slate-green), so EVERY market card's EV figure rendered green --
   not just the recommended one. .dim greyed the declined-market case, but on
   any game with 2+ plays the non-recommended, non-dim card still painted
   green. Green now belongs to .recommended alone.

   CORRECTED 2026-09-01. This comment previously claimed the .recommended
   rule is (0,3,1) and "clears" the .dim rule at (0,2,1). Both tuples were
   wrong and so was the conclusion. `.slate-mkt-card.recommended
   .slate-mkt-card-ev` and `.slate-mkt-card.dim .slate-mkt-card-ev` are each
   THREE chained/descendant class selectors with no type selector -- (0,3,0)
   apiece, an exact tie. The .dim rule is written after this one, so if both
   ever matched the same card, .dim would WIN, not lose.

   What actually keeps this correct is a data invariant, not the cascade:
   .recommended and .dim can never both be set. buildDrilldown draws
   `recommended` only from markets.filter(m => m.isPlay), and .dim is exactly
   !isPlay (game_drilldown.js). Pinned by the pairing test in
   tests/js/game_drilldown.test.js -- if that invariant ever breaks, this
   rule loses the tie silently and a declined market's EV turns green.

   Do NOT add -ev to the .selected brightening group below. That group is
   (0,3,0) too and is written later still, so it would beat BOTH of these on
   a selected card. Pinned by tests/test_signal_stack_css.py. */
.slate-mkt-card.recommended .slate-mkt-card-ev { color: var(--slate-green); }
.slate-mkt-card.dim .slate-mkt-card-ev { color: var(--slate-text-3); font-weight: 400; }
.slate-mkt-card-stake { color: var(--slate-text-2); }
.slate-mkt-card-reason {
  margin-top: 6px; font-size: 12px; color: var(--slate-text-4); font-style: italic;
}
/* 2026-08-31: selection used to be border-only, so a selected card's text
   was indistinguishable from an unselected one's -- and on a .dim card,
   whose 0.55 opacity fades the border too, selecting it barely registered.

   ORDER AND SPECIFICITY ARE LOAD-BEARING HERE, and both failure modes are
   invisible in review:
   1. `.selected.dim` (0,3,0) exists only to beat `.selected` (0,2,0) and
      keep the live-play green off a market buildDrilldown declined. At
      (0,3,0) it beats `.selected`'s (0,2,0) from ANYWHERE in the file --
      position doesn't matter, specificity alone decides it. That is exactly
      why deleting it as "redundant" is the danger: nothing else in the
      cascade stands in for it.
   2. The brightening group must NOT grow to cover `.slate-mkt-card-ev`.
      This group is (0,3,0) -- three chained/descendant classes, no type
      selector -- and `.slate-mkt-card.dim .slate-mkt-card-ev` is (0,3,0)
      too: an exact tie, decided by document order, and this group is later.
      (Both tuples read (0,2,1) here until 2026-09-01; the tie was right, the
      arithmetic was not.) So adding -ev here would brighten a declined
      market's EV to a live play's weight. The dim EV stays grey on a
      selected dim card for free, because `.selected.dim` matches `.dim`.
   Both pinned by tests/test_signal_stack_css.py. */
.slate-mkt-card.selected .slate-mkt-card-label,
.slate-mkt-card.selected .slate-mkt-card-badge,
.slate-mkt-card.selected .slate-mkt-card-verdict,
.slate-mkt-card.selected .slate-mkt-card-stake { color: var(--slate-text); }
.slate-mkt-card.selected.dim {
  opacity: 1;
  border-color: var(--slate-text-3);
  box-shadow: 0 0 0 1px var(--slate-text-3);
}
.slate-takeover-back {
  background: var(--bg-elevated);
  color: var(--slate-text-3);
  border: 1px solid var(--slate-border-strong);
  padding: 6px 11px;
  border-radius: 999px;
  font-size: 12px;
  cursor: pointer;
  font-family: var(--slate-mono);
}
/* Matches .slate-takeover-action:hover below -- the Share / Log bet / Add to
   parlay buttons in this same header. This rule used to paint --slate-green,
   which after 2026-08-31 is reserved for the model's recommendation; a
   navigation control turning green on hover was the last NAVIGATION control
   where it still meant "you are pointing at a button". (.slate-takeover-add
   below still paints #059669 on hover -- left alone deliberately, since its
   base state is already a green CTA and the hover isn't introducing green
   meaning, just continuing it.) */
.slate-takeover-back:hover { border-color: var(--slate-text-2); color: var(--slate-text); }

.slate-takeover-title-block { flex: 1; text-align: center; }
.slate-takeover-title { font-size: 18px; font-weight: 800; color: var(--slate-text); line-height: 1.2; }
.slate-takeover-title-ctx {
  font-size: 12px; color: var(--slate-text-3);
  font-family: var(--slate-mono); margin-top: 4px;
}

.slate-takeover-actions { display: flex; gap: 8px; }
.slate-takeover-action {
  background: transparent; border: 1px solid var(--slate-border);
  color: var(--slate-text-2); padding: 7px 13px; border-radius: 5px;
  font-size: 12px; cursor: pointer; font-weight: 700;
  font-family: var(--slate-mono);
}
.slate-takeover-action:hover { border-color: var(--slate-text-2); color: var(--slate-text); }
.slate-takeover-add {
  background: var(--slate-green); color: var(--slate-bg);
  border-color: var(--slate-green);
}
.slate-takeover-add:hover { background: #059669; border-color: #059669; }

/* Summary mosaic (4-cell, scaled larger than hero card) */
.slate-takeover-summary {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--slate-border-soft);
  border-bottom: 1px solid var(--slate-border);
}
.slate-ts-cell {
  background: var(--slate-bg);
  padding: 18px 12px;
  text-align: center;
  cursor: default;
}
.slate-ts-cell-l {
  font-size: 12px; color: var(--slate-text-3);
  text-transform: uppercase; letter-spacing: 0.14em; font-weight: 700;
}
.slate-ts-cell-v {
  font-family: var(--slate-mono);
  font-size: 28px; font-weight: 800;
  color: var(--slate-text);
  margin-top: 8px; line-height: 1;
}
.slate-ts-cell-v.accent { color: var(--slate-green); }
.slate-ts-cell-v.lock { color: var(--slate-blue); }
.slate-ts-cell-v.book {
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 16px;
}
.slate-ts-band {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--border);
}
.slate-ts-band-range {
  font-size: 13px;
  color: var(--text-dim);
  letter-spacing: 0.01em;
}
.slate-ts-band-mark {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 2px;
}
.slate-ts-band-mark b { color: var(--text-primary); }
.slate-ts-compelled {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-left: 3px solid var(--text-dim);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.5;
}
.slate-ts-compelled-k {
  font-weight: 700;
  color: var(--text-primary);
}
.slate-ts-cell-sub {
  font-size: 12px; color: var(--slate-text-4);
  margin-top: 6px; font-family: var(--slate-mono);
}

/* Narrative blocks */
.slate-takeover-body {
  padding: 24px;
  display: grid;
  /* FIX 2026-07-24: bare 1fr sizes the track to the widest child's
     min-content (grid items default min-width:auto) — one wide child
     (props rows, line-shopping table) stretched EVERY drilldown panel to
     ~469px at a 390px viewport, pushing the Signal Stack weight column
     off-screen. minmax(0, 1fr) pins the track to the container width so
     each panel's own overflow-x:auto container handles its excess. */
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
}
.slate-narr-block {
  background: var(--slate-bg-card);
  border: 1px solid var(--slate-border);
  border-radius: 10px;
  padding: 18px 20px;
}
/* 2026-09-07: ONE card for every analysis section. slate.js renders each
   section through CESectionCard.render (static/js/section_card.js); the
   five wrapper families (.slate-narr-block, .slate-sigstack, .slate-ctx,
   .gd-chalk, id-only hosts) and every green/grey/amber/blue left rule are
   gone from the drilldown. .slate-narr-block above is kept for any
   non-drilldown consumer only. The 2026-09-03 heading contract
   (.slate-narr-h / .slate-ctx-head / .gd-chalk-label) collapses to the one
   .slate-sec-h below. Guarded by tests/test_takeover_section_consistency.py.
   Spec: docs/superpowers/specs/2026-09-07-analysis-panel-uniform-sections-design.md */
.slate-sec {
  background: var(--slate-bg-card);
  border: 1px solid var(--slate-border);
  border-radius: 10px;
  padding: 18px 20px;
  min-width: 0;
}
.slate-sec-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px;
  margin-bottom: 12px;
}
.slate-sec-h {
  font-size: 12px; color: var(--slate-text-3);
  text-transform: uppercase; letter-spacing: 0.16em;
  font-weight: 700; margin: 0; line-height: 1.4;
}
.slate-sec-meta {
  margin-left: auto; font-family: var(--slate-mono);
  font-size: 12px; color: var(--slate-text-3);
}
.slate-sec-badge {
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
  padding: 1px 8px; border-radius: 10px; text-transform: uppercase;
}
.slate-sec-badge--amber { background: var(--accent-amber-bg); color: var(--accent-amber); }
.slate-sec-badge--blue { background: rgba(96,165,250,0.15); color: var(--slate-blue); }
/* The ⓘ. Same 17px dot as .info-btn (main.css) but the hit-area expansion
   is UNCONDITIONAL: this control sits in the body on every viewport, and a
   desktop trackpad user with a motor impairment needs the 44px reach too.
   Guarded by tests/test_slate_touch_targets.py. */
.slate-sec-info {
  position: relative; flex: 0 0 auto;
  width: 17px; height: 17px; border-radius: 50%;
  border: 1.5px solid var(--slate-green); background: transparent;
  color: var(--slate-green); font-size: 12px; font-weight: 700;
  font-family: var(--slate-mono); line-height: 1;
  cursor: pointer; display: inline-flex; align-items: center;
  justify-content: center; padding: 0; transition: background 0.15s, color 0.15s;
}
.slate-sec-info::after { content: ''; position: absolute; inset: -14px; }
.slate-sec-info:hover,
.slate-sec-info:focus-visible { background: var(--slate-green); color: var(--slate-bg); outline: none; }
.slate-sec-info:focus-visible { box-shadow: 0 0 0 2px var(--slate-bg), 0 0 0 4px var(--slate-green); }
.slate-sec-info[aria-expanded="true"] { background: var(--slate-green); color: var(--slate-bg); }
.slate-sec-def {
  font-size: 13px; line-height: 1.6; color: var(--slate-text-2);
  padding: 10px 12px; margin-bottom: 12px;
  background: rgba(255,255,255,0.03); border-radius: 6px;
}
.slate-sec-def p { margin: 0; }
.slate-sec-def p + p { margin-top: 6px; }
.slate-sec-def-why { color: var(--slate-text); }
.slate-sec-body { min-width: 0; }
/* Sub-labels inside a card (Injuries, Weather, Signal detail, AI modifier,
   the three favorites rows). One neutral style; green is for state only. */
.slate-sub-h {
  font-size: 12px; font-weight: 700; color: var(--slate-text-3);
  text-transform: uppercase; letter-spacing: 0.12em;
  margin: 12px 0 6px;
}
.slate-sub-h:first-child { margin-top: 0; }
.slate-sub-h .slate-ss-count { font-weight: 400; text-transform: none; letter-spacing: 0; margin-left: 6px; }
/* Disclosure summaries inside a card (NFL methods tree, NFL player props):
   were inline-styled; a class so the 44px touch floor can reach them. */
.slate-sec-disclose {
  cursor: pointer; color: var(--slate-green); font-weight: 600;
  min-height: 44px; display: flex; align-items: center;
}
/* Signal detail rows gain a magnitude cell once /signals-fired lands
   (static/js/signal_merge.js). Fourth column, mono, right-aligned. */
.slate-ss-sig { grid-template-columns: 1fr auto 52px auto; }
.slate-ss-sig-mag {
  font-family: var(--slate-mono); font-weight: 700; font-size: 12px;
  white-space: nowrap; text-align: right; min-width: 64px; color: var(--slate-text-3);
}
.slate-ss-sig-mag.good { color: var(--accent-green); }
.slate-ss-sig-mag.bad { color: var(--accent-red); }
.slate-ss-sig-name a { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--slate-text-3); }
.slate-ss-sig-name a:hover { color: var(--slate-text); border-bottom-style: solid; }
/* Group label above each run of cards: Verdict, Evidence, Market, Props.
   Hidden by slate.js _pruneEmptyGroups() when nothing follows it. */
.slate-sec-group {
  display: flex; align-items: center; gap: 12px;
  font-size: 12px; font-weight: 700; color: var(--slate-text-3);
  text-transform: uppercase; letter-spacing: 0.2em;
  margin-top: 8px;
}
.slate-sec-group:first-child { margin-top: 0; }
.slate-sec-group::after { content: ''; flex: 1; height: 1px; background: var(--slate-border); }
/* Async hosts render '<div id="..."></div>' until their fetch lands; an
   empty grid item still takes a gap, so hide it outright. Replaces the
   2026-09-04 `> *:not(:empty)` divider rule, which the bordered cards make
   redundant (a divider above a card double-borders). */
.slate-takeover-body > :empty { display: none; }
.slate-narr-body {
  color: var(--slate-text-2);
  font-size: 14px; line-height: 1.7;
}
.slate-narr-body p { margin: 0; }
.slate-narr-body p + p { margin-top: 10px; }
.slate-narr-body strong { color: var(--slate-text); }
.slate-narr-body code {
  background: rgba(255,255,255,0.06);
  padding: 1px 5px; border-radius: 3px;
  color: var(--slate-green);
  font-family: var(--slate-mono); font-size: 12px;
}
.slate-narr-math {
  font-family: var(--slate-mono);
  background: rgba(255,255,255,0.04);
  padding: 12px 14px; border-radius: 6px;
  font-size: 13px; line-height: 1.7;
  margin: 10px 0;
}
.slate-narr-math .accent { color: var(--slate-green); }

/* Line shopping table */
.slate-ls-table { width: 100%; border-collapse: collapse; }
.slate-ls-table th {
  font-family: var(--slate-mono);
  font-size: 12px; color: var(--slate-text-3);
  text-transform: uppercase; letter-spacing: 0.12em;
  font-weight: 700; padding: 8px 10px; text-align: left;
  border-bottom: 1px solid var(--slate-border);
}
.slate-ls-table td {
  padding: 10px; border-bottom: 1px solid var(--slate-border-soft);
  font-size: 13px; color: var(--slate-text);
}
.slate-ls-table tr.best { background: rgba(16,185,129,0.04); }
.slate-ls-table tr.best td { color: var(--slate-green); font-weight: 700; }
.slate-ls-price { font-family: var(--slate-mono); font-weight: 700; }

/* Reduced-juice EV display (NFL sprint W1 / spec M3 — 2026-05-10).
   "Juice" column shows implied-prob delta vs the sharpest book on the
   slate. Sharp users key on this number to choose where to bet.
   font-size was 0.92em, rendering 11.96px against a 13px <td> -- the other
   25 small-text classes in the drilldown are all pinned at exactly 12px.
   A relative unit here means a table restyle could move this number
   silently, so it is pinned instead. */
.slate-ls-juice {
  font-family: var(--slate-mono); font-size: 12px; font-weight: 600;
}
.slate-ls-juice--sharp {
  color: var(--slate-green); font-style: italic;
}
.slate-ls-juice--bad {
  /* Paying extra juice — warn-ish color but not alarming */
  color: var(--slate-warn, #c97a00);
}
.slate-ls-juice--good {
  /* Rare: book is sharper than the "sharpest" (outlier) */
  color: var(--slate-green);
}

/* Game props row (placeholder shape — Phase 2.5 fills the data) */
.slate-prop-row {
  display: grid;
  grid-template-columns: 1fr 90px 90px 80px 56px;
  gap: 10px; padding: 10px 12px; align-items: center;
  border-bottom: 1px solid var(--slate-border-soft);
}
.slate-prop-row:last-child { border-bottom: none; }
.slate-prop-name { font-size: 13px; color: var(--slate-text); font-weight: 600; }
.slate-prop-line {
  font-family: var(--slate-mono); font-size: 13px;
  color: var(--slate-text-2);
}

/* Phase 2.5 placeholder (shown for line-shopping and props blocks
   until enriched data is wired) */
.slate-coming-soon {
  padding: 32px 16px;
  text-align: center;
  color: var(--slate-text-3);
  font-size: 14px;
  font-family: var(--slate-mono);
  background: rgba(255,255,255,0.02);
  border: 1px dashed var(--slate-border);
  border-radius: 8px;
}
.slate-coming-soon strong { color: var(--slate-text-2); }

/* ===== Slate redesign — Parlay Cart (Phase 3) ===== */

/* The rail is a slide-in drawer at all viewport widths, hidden by default
   and toggled open via the top-nav "Parlay Builder" button.
   FIX 2026-05-09: was permanently visible at >=1024px and stole 352px of
   horizontal real estate from the slate; now only appears on demand. */
.parlay-rail {
  position: fixed;
  top: 72px;            /* below the top nav bar (~56px header + 16px gap) */
  right: 16px;
  width: 320px;
  max-height: calc(100vh - 88px);
  overflow-y: auto;
  background: var(--slate-bg-card);
  border: 1px solid var(--slate-border);
  border-radius: 12px;
  padding: 18px;
  font-family: 'IBM Plex Sans', sans-serif;
  z-index: 50;
  /* Closed state: slid off-screen to the right */
  transform: translateX(calc(100% + 24px));
  transition: transform 0.25s ease-out;
}
.parlay-rail.parlay-rail--open {
  transform: translateX(0);
}

.parlay-rail-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}
.parlay-rail-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-weight: 800;
  color: var(--slate-text);
}
.parlay-rail-count {
  font-family: var(--slate-mono);
  font-size: 12px;
  color: var(--slate-text-3);
  padding: 2px 7px;
  background: rgba(255,255,255,0.04);
  border-radius: 999px;
}

/* Empty state */
.parlay-empty {
  padding: 24px 12px;
  text-align: center;
  background: rgba(255,255,255,0.02);
  border: 1px dashed var(--slate-border);
  border-radius: 8px;
  font-size: 12px;
  color: var(--slate-text-3);
  line-height: 1.6;
}

/* Leg cards */
.parlay-leg {
  /* Declared, not inherited from whatever else claims this class name.
     main.css:857 used to style .parlay-leg unscoped for the floating parlay
     SLIP; `display` was the one property it declared and slate.css did not,
     so the slip's flex row laid out this card and pushed .parlay-leg-meta
     under the corner button. main.css is scoped to .parlay-slip now, and
     this line makes the cart's layout its own statement rather than a
     default that a future collision can take over again.
     Stacked: pick on its own line, .parlay-leg-meta below it. */
  display: block;
  background: var(--slate-bg);
  border: 1px solid var(--slate-border);
  border-radius: 7px;
  padding: 10px 11px;
  /* Gutter for the absolutely-positioned .parlay-leg-x below: 6px offset +
     28px button + 2px clearance. Without it the button sits on top of
     .parlay-leg-meta — measured at 1024px AND 1440px, an overlap of 13.8px
     on every leg with the button winning the hit test over the price, so a
     click beside the odds removed the leg. The phone block at the end of
     this file widens both to 44/46px. */
  padding-right: 36px;
  margin-bottom: 6px;
  position: relative;
}
.parlay-leg-x {
  position: absolute;
  top: 6px;
  right: 6px;
  /* A declared box, not a glyph-sized one: it was 18.8x15, which is both
     under the 24px AA target floor and impossible for the card to reserve a
     matching gutter against. 28px matches .slate-row-star, the desktop
     convention elsewhere on the board. */
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--slate-text-4);
  cursor: pointer;
  font-size: 15px;
  padding: 0;
  border-radius: 3px;
  line-height: 1;
  background: transparent;
  border: none;
}
.parlay-leg-x:hover { color: var(--slate-red); background: rgba(239,68,68,0.06); }
.parlay-leg-pick {
  font-size: 13px;
  font-weight: 700;
  color: var(--slate-text);
  padding-right: 18px;
  line-height: 1.3;
}
.parlay-leg-meta {
  font-family: var(--slate-mono);
  font-size: 12px;
  color: var(--slate-text-3);
  margin-top: 5px;
  display: flex;
  justify-content: space-between;
}
.parlay-leg-meta strong { color: var(--slate-text-2); }

/* Summary block */
.parlay-summary {
  background: rgba(16,185,129,0.06);
  border: 1px solid rgba(16,185,129,0.25);
  border-radius: 8px;
  padding: 11px 12px;
  margin: 12px 0 10px;
}
.parlay-summary-row {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--slate-text-2);
  margin-bottom: 5px;
}
.parlay-summary-row.big {
  font-size: 15px;
  font-weight: 800;
  color: var(--slate-text);
  margin-bottom: 0;
  padding-top: 7px;
  border-top: 1px solid rgba(16,185,129,0.18);
}
.parlay-summary-row strong { color: var(--slate-green); font-family: var(--slate-mono); }
.parlay-summary-row.neg strong { color: var(--slate-red); }
/* Grade-gated: a positive combined EV on a parlay with a non-recommended leg
   must NOT read green — dim it so it doesn't look like a model pick. */
.parlay-summary-row.muted strong { color: var(--slate-text-2); }
.parlay-summary-note {
  font-size: 12px;
  line-height: 1.35;
  color: var(--accent-amber, #e8a40f);
  margin-top: 6px;
}

/* Stake input */
.parlay-stake-input {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--slate-border);
  color: var(--slate-text);
  font-family: var(--slate-mono);
  font-size: 13px;
  font-weight: 700;
  padding: 3px 6px;
  border-radius: 4px;
  width: 70px;
  text-align: right;
}
.parlay-stake-input:focus {
  outline: none;
  border-color: var(--slate-green);
}

/* Build & Take CTA */
.parlay-cta {
  width: 100%;
  background: var(--slate-green);
  color: var(--slate-bg);
  border: none;
  padding: 11px;
  border-radius: 7px;
  font-weight: 800;
  font-size: 14px;
  cursor: pointer;
}
.parlay-cta:hover { background: #059669; }
.parlay-cta:disabled {
  background: rgba(255,255,255,0.06);
  color: var(--slate-text-3);
  cursor: not-allowed;
}

/* Helper text */
.parlay-helper {
  font-size: 12px;
  color: var(--slate-text-4);
  margin-top: 14px;
  line-height: 1.55;
  font-family: var(--slate-mono);
  text-align: center;
}

/* FIX 2026-05-09: removed the desktop right-padding reservation. The rail
   is now a slide-in drawer (closed by default) at all viewport widths,
   so .main-content reclaims the full screen width. */

/* Phase 4 — bottom-anchored drawer at <1024px, integrated with the
   existing bottom-nav Parlay tab. Hidden by default (translateY off-screen);
   .parlay-rail--open class slides it up. */
@media (max-width: 1023px) {
  .parlay-rail {
    /* Override Phase 3's fixed-position desktop rail */
    position: fixed;
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    max-width: 100vw;
    max-height: 75vh;
    border-radius: 16px 16px 0 0;
    border-bottom: none;
    transform: translateY(100%);
    transition: transform 0.25s ease-out;
    box-shadow: 0 -8px 24px rgba(0,0,0,0.4);
    /* Account for the bottom-nav height (~56px) so the drawer doesn't
       hide under it. The bottom-nav-padding env safe-area-inset is
       inherited from the V1 mobile foundations. */
    padding-bottom: calc(72px + env(safe-area-inset-bottom));
    z-index: 110;  /* above bottom-nav (main.css:73 sets nav z-index: 100) */
  }
  .parlay-rail.parlay-rail--open {
    transform: translateY(0);
  }
  /* Phase 4 does NOT add right-padding on .main-content for mobile —
     the drawer slides over content instead of pushing it. */
}

/* Backdrop when drawer is open — tap-to-close affordance */
.parlay-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease-out;
  z-index: 105;
}
/* Backdrop only goes opaque/active on mobile (modal drawer pattern).
   At desktop the drawer is non-modal — slate behind stays interactive. */
@media (max-width: 1023px) {
  .parlay-backdrop.parlay-backdrop--open {
    opacity: 1;
    pointer-events: auto;
  }
}

/* Cart count badge on the bottom-nav Parlay tab */
.bn-badge {
  position: absolute;
  top: 4px;
  right: calc(50% - 18px);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  background: var(--slate-green);
  color: var(--slate-bg);
  border-radius: 999px;
  font-family: var(--slate-mono);
  font-size: 12px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.bn-badge[hidden] { display: none; }

/* Ensure the bottom-nav button is the badge's positioning anchor.
   (V1 mobile work added .bottom-nav-item but may not have set
    position: relative; safe to set unconditionally — won't change
    layout, only enables abs-positioned children.) */
.bottom-nav-item { position: relative; }

/* ===== Slate redesign — Phase 4 Mobile Reflow ===== */

/* Tier 2 slate row — the SINGLE phone layout (<640px).
   FIX 2026-07-12 (Batch 2 follow-up): two overlapping phone blocks used to
   fight here — this one ("Phase 4": 3-col grid-template-areas card) and a
   later "Wave 3 #15" 4-col compact block (~L1131, now deleted). Both applied
   at phone widths: the later block overrode grid-template-columns (4 tracks)
   but NOT grid-template-areas (3 named columns), so the matchup cell rendered
   in a 70px track while the 131px 1fr track held only the pick, the 36px 4th
   track was orphaned, and the share/star chips auto-placed arbitrarily.
   Reproduced live on prod v415 (matchup w=70px) before this consolidation.
   Single layout now:
     matchup(1fr)  edge(84px)   star(44px)
     pick          price        add
   Hidden on phone (all carried by the tap-to-drilldown): column header,
   sport chip, confidence, multibook summary, share chip. */
@media (max-width: 639px) {
  .slate-row {
    grid-template-columns: 1fr 84px 44px;
    grid-template-areas:
      "matchup edge  star"
      "pick    price add";
    gap: 6px 8px;
    padding: 10px 8px;
  }
  .slate-row > .slate-row-sport { display: none; }  /* sport → drilldown */
  .slate-row > div:nth-child(2) { grid-area: matchup; }
  .slate-row > div:nth-child(3) { grid-area: pick; }
  .slate-row > div:nth-child(4) { grid-area: price; justify-self: end; text-align: right; }
  .slate-row > .slate-row-conf,
  .slate-row > div:has(.slate-row-conf) { display: none; }  /* conf → drilldown */
  .slate-row > .slate-edge-chip { grid-area: edge; justify-self: end; align-self: start; }
  .slate-row > .slate-row-share { display: none; }  /* drilldown has a Share action */
  /* WCAG 2.5.5 touch targets: the row's 3rd grid column is already 44px, so
     size the ☆/+ controls to fill it. FIX 2026-07-18: were 28px (star) / 36px
     (add) — Fitts's-law misfires one-handed, and unreliable for motor-impaired
     users (see accessibility-audit skill). */
  .slate-row > .slate-row-star  {
    grid-area: star; justify-self: end; align-self: start;
    min-width: 44px; min-height: 44px;
  }
  .slate-row > .slate-row-add   {
    grid-area: add; justify-self: end; align-self: end;
    min-width: 44px; min-height: 44px; font-size: 16px; padding: 0;
  }
  .slate-row > .slate-row-multibook { display: none; }  /* drilldown line-shopping */

  .slate-row-game { font-size: 14px; }
  .slate-row-time { font-size: 12px; }
  .slate-row-pick { font-size: 13px; }
  .slate-row-price { font-size: 14px; }
  .slate-row-pick-label { font-size: 12px; }
  .slate-row-book { font-size: 12px; }

  /* Drilldown prop rows: 5-col → 2-row card, same collapse as .slate-row
     above. FIX 2026-07-25: the desktop grid (1fr 90px 90px 80px 56px, gap
     10, padding 12) needs 380px of fixed width before the 1fr, and sits in
     a ~270px container inside the drilldown — so .slate-row-add rendered at
     x=467 against a 375px client width and pushed 107px of HORIZONTAL PAGE
     SCROLL. Pre-dates the shell-scroll fix (02ede1e); .main-content's old
     overflow:hidden had been MASKING it, which meant the price, EV chip and
     "+" add-to-parlay button were silently cut off and unreachable on
     phones — the same failure main.css:1718 documents for game cards.
     Guarded by tests/test_slate_prop_row_mobile.py. */
  .slate-prop-row {
    grid-template-columns: 1fr 84px 44px;
    grid-template-areas:
      "name edge  add"
      "line price add";
    gap: 4px 8px;
    padding: 10px 8px;
  }
  .slate-prop-row > .slate-prop-name { grid-area: name; }
  .slate-prop-row > .slate-prop-line { grid-area: line; }
  .slate-prop-row > .slate-ls-price  { grid-area: price; justify-self: end; }
  .slate-prop-row > .slate-edge-chip { grid-area: edge; justify-self: end; }
  /* WCAG 2.5.5 — the desktop column was 56px; keep a real target here. */
  .slate-prop-row > .slate-row-add {
    grid-area: add; justify-self: end; align-self: center;
    min-width: 44px; min-height: 44px; font-size: 16px; padding: 0;
  }
}

/* Tier 3 live row: 5-col → 2-row card on <640px */
@media (max-width: 639px) {
  .slate-live-row {
    grid-template-columns: 56px 1fr 76px;
    grid-template-areas:
      "sport matchup edge"
      "pick  score   .";
    row-gap: 8px;
    padding: 10px 10px;
  }
  .slate-live-row > .slate-row-sport  { grid-area: sport; }
  .slate-live-row > div:nth-child(2)  { grid-area: matchup; }
  .slate-live-row > .slate-row-pick   { grid-area: pick; font-size: 13px; }
  .slate-live-row > .slate-live-score { grid-area: score; font-size: 12px; }
  .slate-live-row > .slate-edge-chip  { grid-area: edge; justify-self: end; align-self: start; }
}

/* Drilldown takeover mobile reflow at <640px */
@media (max-width: 639px) {
  /* Header: at <=539px back and title share a row (see the max-width:539px
     block below) with actions stacked on full-width rows underneath; at
     540-639px back, title and actions each still get their own row. Title-
     block sizing varies by sub-breakpoint accordingly. */
  .slate-takeover-h {
    flex-wrap: wrap;
    padding: 12px 14px;
    gap: 10px;
  }
  /* FIX 2026-08-11 (Slate mobile review): both of these were SHRUNK here — to
     ~26px (12px font + 5px padding + 1px border) — and nothing bumped them
     back. .slate-takeover-back is the way OUT of the drilldown, the primary
     back-navigation on a phone. .slate-takeover-action only regained 44px in
     the max-width:539px block below, so 540-639px shipped the compact size.
     Keep the compact type (the header has to fit); buy the height with
     padding instead. Guarded by tests/test_slate_touch_targets.py. */
  .slate-takeover-back {
    flex: 0 0 auto; font-size: 12px; padding: 5px 9px;
    min-height: 44px; display: inline-flex; align-items: center;
  }
  /* FIX 2026-09-04: this carried `order: 3`, which sorted the title AFTER the
     back button and both actions (each `order: 0` by default) -- so tapping a
     game showed Log bet and Add to parlay before naming the game, 158px down.
     That contradicted this block's own opening comment, "stack title above
     actions": the intent was right and the implementation inverted it. DOM
     order governs now (back, title, actions); `flex: 1 1 100%` still gives the
     title its own full-width row. Guarded by
     tests/test_takeover_header_order.py. */
  .slate-takeover-title-block { flex: 1 1 100%; text-align: left; }
  .slate-takeover-title { font-size: 16px; }
  .slate-takeover-title-ctx { font-size: 12px; }
  .slate-takeover-actions { flex: 0 0 auto; gap: 6px; }
  .slate-takeover-action {
    font-size: 12px; padding: 5px 9px;
    min-height: 44px; display: inline-flex; align-items: center;
    justify-content: center;
  }

  /* Freshness banner + market cards: match the tightened side padding. */
  .slate-takeover-freshness { padding: 8px 14px; }
  .slate-mkt-cards {
    grid-template-columns: 1fr;
    padding: 12px 14px 0;
  }

  /* Summary mosaic: 4-cell → 2x2 grid */
  .slate-takeover-summary {
    grid-template-columns: repeat(2, 1fr);
    padding: 0 14px;
  }
  .slate-ts-cell { padding: 14px 8px; }
  .slate-ts-cell-v { font-size: 22px; margin-top: 6px; }
  .slate-ts-cell-v.book { font-size: 15px; }
  .slate-ts-cell-sub { font-size: 12px; }

  /* Body padding tighter */
  .slate-takeover-body {
    padding: 16px 14px;
    gap: 14px;
  }
  .slate-narr-block, .slate-sec { padding: 14px 14px; }
  /* FIX 2026-09-08: the Signal detail row is a 4-column grid on desktop
     (name | favored side | prob | magnitude). At 390px the three fixed
     columns left the name ~55px wide, so "Net Rating" and "Player Impact"
     broke into two lines (measured on the iPhone 14 Pro emulation). On a
     phone the name takes the full first line and the three numbers share
     the second. Scoped to the Signal detail list: the Confidence
     conviction rows reuse .slate-ss-sig with no magnitude cell and fit on
     one line as they are. Guarded by tests/test_signal_row_phone_layout.py. */
  .slate-ss-advanced .slate-ss-sig { grid-template-columns: 1fr auto auto; }
  .slate-ss-advanced .slate-ss-sig-name { grid-column: 1 / -1; }
  .slate-ss-advanced .slate-ss-sig-favor { text-align: left; }
  .slate-narr-h { font-size: 12px; }
  .slate-narr-body { font-size: 13px; line-height: 1.6; }
  .slate-narr-math { font-size: 12px; padding: 10px 12px; }

  /* Line-shopping table: horizontal scroll wrapper so all 4 columns
     stay readable without column collapse logic.
     FIX 2026-05-14 per reviewer audit: the prior CSS set thead/tbody/tr
     to display:table width:100%, which UNDID the overflow-x scroll
     because the inner table reset back to 100% width. Removed that
     override — table now inherits the parent's natural sizing and
     horizontally scrolls when columns exceed viewport. */
  .slate-narr-block .slate-ls-table,
  .slate-sec .slate-ls-table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
    min-width: 100%;
  }
}

/* NFL post-Q1 in-play update (Phase 6). 2026-09-07: the .slate-post-q1
   wrapper/header/ts rules went with the blue left rule; the block renders
   through CESectionCard now (LIVE badge + "updated" meta in the card head).
   The row-level rules below are unchanged. */
.slate-post-q1-probs {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  flex-wrap: wrap;
  font-size: 1.02rem;
  margin: 0.4rem 0;
}
.slate-post-q1-arrow {
  color: var(--slate-text-3);
  font-weight: bold;
}
.slate-post-q1-delta {
  font-weight: 700;
  padding: 0.15rem 0.55rem;
  border-radius: 10px;
  background: var(--bg-raised);
  font-size: 0.94rem;
}
.slate-post-q1-delta.good {
  background: var(--accent-green-bg);
  color: var(--accent-green);
}
.slate-post-q1-delta.bad {
  background: var(--accent-red-bg);
  color: var(--accent-red);
}
.slate-post-q1-state {
  font-family: -apple-system, monospace;
  font-size: 0.9rem;
  color: var(--slate-text-3);
  margin: 0.3rem 0;
}
.slate-post-q1-summary {
  font-size: 0.96rem;
  color: var(--slate-text-2);
  font-style: italic;
  margin-top: 0.3rem;
}

/* NFL lookahead spot (Phase 5+). 2026-09-07: the amber-ruled wrapper and
   the pill went with the uniform-sections pass; CESectionCard renders it
   with an amber "Trap game" badge. Only the body text rule remains. */
.slate-lookahead-body {
  font-size: 0.96rem;
  color: var(--slate-text-2);
  line-height: 1.4;
  flex: 1 1 180px;
  min-width: 0;
}

/* Drilldown action buttons at <540px: stack to 2-row grid so
   Share / Log bet / + Add to parlay don't crush against each other
   on narrow phones. Per 2026-05-14 reviewer audit S3. */
@media (max-width: 539px) {
  .slate-takeover-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    width: 100%;
    flex: 1 1 100%;
  }
  .slate-takeover-action {
    width: 100%;
    text-align: center;
    min-height: 44px;
    padding: 8px 6px;
    box-sizing: border-box;
  }
  /* FIX 2026-09-04: Share, when present, takes a full-width row above the two
     primary actions. Targeted by its own attribute rather than :first-child,
     because Share is CONDITIONAL -- slate.js renders it only when
     game.prediction_id is set. With :first-child, a game without Share
     promoted "Log bet" to full width and stranded "+ Add to parlay" alone on
     a second row: two buttons that fit side by side, taking two rows, for a
     50px taller and visibly lopsided header. The old comment here said
     "Share is the first child", which was true only when Share rendered.
     Guarded by tests/test_takeover_header_order.py. */
  .slate-takeover-actions > [data-action="share"] {
    grid-column: 1 / -1;
  }
  /* FIX 2026-09-04: `flex: 1 1 100%` forced the title onto its own row while
     the back button used 127px of 284 beside it -- 55% of a row spent on
     nothing. Basis 0 plus min-width 0 here let the title take the remainder
     of the back button's row instead, which is 36px off the header.
     `text-align: left` is not repeated here -- the max-width:639px rule
     above still applies it at this width.

     BASIS 0 IS LOAD-BEARING. `flex: 1 1 auto` was tried first, applied
     cleanly, and changed nothing: with basis auto the block's basis is its
     CONTENT width, and the context line alone exceeds the 147px available, so
     it wrapped regardless of grow. min-width: 0 is needed too -- a flex item
     will not shrink below its content width without it.

     The context line wraps to two lines at this width, so the block is 60px
     against the back button's 44px. Accepted deliberately (Bill,
     2026-09-04): shortening the text was measured across three variants and
     does not prevent the wrap.

     SCOPED TO <=539px, NOT <=639px: this block is the one that stacks the
     actions (see the Share rule above). Above 539px the actions do not
     stack, so back, title and actions would share one flex line, and a zero
     basis there collapses the title instead of sharing gracefully --
     measured at 540px: title 512x42 -> 100x136 (game name wraps to three
     lines), header 187px -> 161px. The max-width:639px rule keeps
     `flex: 1 1 100%` for exactly that band.

     The ordering fix in 9d92b189 still holds -- reading order becomes
     left-to-right within the row (back, then title) rather than
     top-to-bottom, and the game is still ahead of both action buttons.
     Guarded by tests/test_takeover_header_order.py. */
  .slate-takeover-title-block { flex: 1 1 0; min-width: 0; }
}

/* 2026-09-07: the "Signals fired" card and its .sf-* rules are gone. The
   payload is merged into the Signal stack rows (static/js/signal_merge.js;
   .slate-ss-sig-mag above). */

/* Phase B Sprint 4 #4+++: sign-in CTA strip on slate for unauthed
   visitors. Hidden by default; slate.js toggles visible when authed=false.
   Mirrors the /pick/{id} CTA banner styling. */
.slate-signin-cta {
  background: linear-gradient(135deg, #1f6feb 0%, #1556c5 100%);
  color: #fff; padding: 0.85rem 1.1rem; border-radius: 8px;
  margin: 0 0 1rem; display: flex; gap: 0.8rem;
  align-items: center; justify-content: space-between;
  flex-wrap: wrap; font-size: 0.96rem;
}
.slate-signin-cta strong { font-weight: 700; }
.slate-signin-cta-btn {
  background: #fff; color: #1f6feb; padding: 0.55rem 1.1rem;
  border-radius: 4px; text-decoration: none; font-weight: 700;
  min-height: 44px; display: inline-flex; align-items: center;
  flex-shrink: 0;
}
.slate-signin-cta-btn:hover { background: #f0f7ff; }

/* Phase B Sprint 5 #1: slate-level "lines as of HH:MM ET" header.
   Always visible (not toggled), small grayscale strip. Turns amber
   at 5min stale, red at 15min stale via .stale-warn / .stale-red. */
.slate-freshness {
  font-family: var(--slate-mono, ui-monospace, monospace);
  font-size: 0.85rem;
  color: var(--slate-text-3, #a3a3ad);
  padding: 0.35rem 0.05rem;
  margin: 0 0 0.45rem;
  display: flex; align-items: center; gap: 0.4rem;
  line-height: 1.3;
}
/* FIX 2026-07-12 (Batch 2): stale colors were dark-on-light ink (#a86c00,
   #9b1c1c) — near-invisible on the dark UI. Remapped to accent tokens. */
.slate-freshness::before {
  content: "●";
  font-size: 0.75rem;
  color: var(--accent-green);
  line-height: 1;
}
.slate-freshness.stale-warn { color: var(--accent-amber); }
.slate-freshness.stale-warn::before { color: var(--accent-amber); }
.slate-freshness.stale-red { color: var(--accent-red); font-weight: 600; }
.slate-freshness.stale-red::before { color: var(--accent-red); }

/* American / Decimal odds-format toggle — sits at the end of the freshness row.
   Colour stays neutral (it is a format control, not a recommendation signal). */
.odds-format-toggle {
  margin-left: auto;
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--slate-border, #30363d);
  border-radius: 6px;
  flex: none;
}
.odds-format-btn {
  font: inherit;
  font-size: 0.8rem;
  line-height: 1.4;
  min-height: 26px;
  padding: 3px 9px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--slate-text-3, #a3a3ad);
  cursor: pointer;
}
.odds-format-btn.active {
  background: var(--slate-bg-2, rgba(255, 255, 255, 0.08));
  color: var(--slate-text, #e6edf3);
  font-weight: 600;
}
.odds-format-btn:hover:not(.active) { color: var(--slate-text-2, #c9d1d9); }
/* Touch targets: the compact 26px reads fine with a mouse but is a fat-finger
   miss on a phone — and a mis-tap here silently reformats EVERY price on the
   board. Bump to the 44px standard the 2026-07-18 a11y sweep adopted (WCAG
   2.5.5). Also let the freshness row wrap instead of crowding the timestamp:
   at 375px the text and toggle sat 6px apart, and a full "Lines as of 3:47 PM
   EDT · refreshed 12 min ago" string needs the room.
   Pinned by tests/test_slate_touch_targets.py. */
@media (max-width: 639px) {
  .slate-freshness { flex-wrap: wrap; row-gap: 0.35rem; }
  .odds-format-toggle { margin-left: 0; }
  .odds-format-btn {
    min-height: 44px;
    padding: 3px 14px;
  }
}

/* 2026-05-17 audit proposal — honest scorecard tile on /ui landing.
   Surfaces CLV credibility above the Best Bets hero so sharps see
   the metric that actually matters before they see any picks. */
.scorecard-tile {
  background: var(--slate-bg-card, #161b22);
  border: 1px solid var(--slate-border, #30363d);
  border-left: 3px solid var(--slate-accent, #1ddc7a);
  border-radius: 8px;
  padding: 0.9rem 1.1rem;
  margin: 0 0 0.8rem;
}
.scorecard-tile-h {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 0.6rem;
}
.scorecard-tile-label {
  font-family: var(--slate-mono, ui-monospace, monospace);
  font-size: 0.8rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--slate-accent, #1ddc7a);
}
.scorecard-tile-link {
  font-size: 0.88rem; font-weight: 600;
  color: var(--slate-text-2, #c9d1d9);
  text-decoration: none;
}
.scorecard-tile-link:hover { color: var(--slate-accent, #1ddc7a); }
.scorecard-tile-stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 0.8rem;
}
.scorecard-tile-stat {
  display: flex; flex-direction: column; gap: 0.15rem;
}
.scorecard-tile-stat-l {
  font-size: 0.8rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--slate-text-3, #8b949e);
}
.scorecard-tile-stat-v {
  font-family: var(--slate-mono, ui-monospace, monospace);
  font-size: 1.35rem; font-weight: 700;
  color: var(--slate-text-1, #e6edf3);
  line-height: 1.1;
}
.scorecard-tile-stat-v.pos { color: var(--ev-pos); }
.scorecard-tile-stat-v.neg { color: var(--ev-neg); }
.scorecard-tile-stat-x {
  font-size: 0.8rem;
  color: var(--slate-text-3, #8b949e);
}

/* 2026-08-06 per-market CLV rows. Four columns: market, CLV, beat%, graded N.
   Market column is flexible; the three numeric columns are fixed-ish so the
   figures line up down the table and the row does not reflow as values change
   width. */
.scorecard-markets {
  display: flex; flex-direction: column; gap: 0.35rem;
  margin: 0.25rem 0 0.5rem;
}
.scorecard-markets-head,
.scorecard-market-row {
  display: grid;
  grid-template-columns: minmax(5.5rem, 1fr) 5rem 4.5rem 3.5rem;
  gap: 0.6rem; align-items: baseline;
}
.scorecard-markets-head {
  /* 0.75rem = 12px, the type-scale floor (tests/test_type_scale_floor.py).
     Do not shave this to buy column width — use the short header labels. */
  font-size: 0.75rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--slate-text-3, #8b949e);
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--slate-border, #30363d);
}
.scorecard-markets-head span:not(:first-child),
.scorecard-market-row > .scorecard-market-v { text-align: right; }
/* Full header wording on wide viewports; the narrow forms swap in at 640px. */
.scorecard-markets-head .h-narrow { display: none; }
.scorecard-market-m {
  font-size: 0.9rem; font-weight: 600;
  color: var(--slate-text-2, #c9d1d9);
}
.scorecard-market-v {
  font-family: var(--slate-mono, ui-monospace, monospace);
  font-size: 1.05rem; font-weight: 700;
  color: var(--slate-text-1, #e6edf3);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.scorecard-market-v.pos { color: var(--ev-pos); }
.scorecard-market-v.neg { color: var(--ev-neg); }
.scorecard-markets-note {
  font-size: 0.75rem; line-height: 1.4;
  color: var(--slate-text-3, #8b949e);
  margin-bottom: 0.6rem;
}
.scorecard-markets-note[hidden] { display: none; }
/* 639, not 640: main.css:97 opens the tablet tier at min-width:640px, and the
   rest of the Slate's phone rules stop at 639. At exactly 640px this block used
   to reorder the page for mobile while the board stayed on its desktop
   7-column grid. Pinned by tests/test_slate_touch_targets.py. */
@media (max-width: 639px) {
  /* FIX 2026-08-06: the per-market table was invisible on a phone. Measured on
     prod at 375x812 the hero started at y=801, behind 670px of nav (56), main
     header (181), sign-in CTA (155) and the freshness block (278 — 34% of the
     fold, mostly out-of-season league timestamps).

     Reorder so the hero leads. Flex rather than a DOM move because the order
     must differ from desktop, and a DOM move would need JS on resize. Scoped
     entirely inside this media query — desktop order is untouched.

     NOTE: flex stops margin collapsing between these children. That was
     checked visually at 375 and at desktop; if spacing ever looks off here,
     that is the mechanism. */
  #view-premium { display: flex; flex-direction: column; }
  #view-premium > #proof-top-sentinel { order: 1; }
  /* 2026-09-04 review (item 2/3): #top-slot became a direct child of
     #view-premium when it moved out of #proof-hero (so its visibility no
     longer depends on the hero's display:none ancestor -- see slate.js
     _renderTopSlot). Without an explicit order it fell to the
     `#view-premium > *` catch-all below, same trap #game-cards-container
     is called out for two rules down -- measured on prod at 375px, that
     landed the slot at y=1127, BELOW the freshness line, on an 8-card stub.
     Ordered ahead of the hero AND the board on purpose: it is the
     subscriber-facing lead ("what to look at now"), and the review's item 3
     reversed hiding the hero for a signed-in quiet day in favour of this --
     the slot leads, the hero and its honesty panel (#proof-status) remain
     visible underneath, never buried behind an ancestor gate. */
  #view-premium > #top-slot          { order: 2; }
  #view-premium > #proof-hero        { order: 3; }
  /* Task 9 (2026-08-29): .slate-tier-1 is retired; #game-cards-container is
     the sole surviving primary-content block and takes over its slot so the
     board still leads #slate-signin-cta/#slate-freshness on mobile. Without
     this it falls to the `#view-premium > *` catch-all below (order: 7),
     landing AFTER both — see tests/test_proof_hero_mobile_fold.py. */
  #view-premium > #game-cards-container { order: 4; }
  #view-premium > #slate-signin-cta  { order: 5; }
  #view-premium > #slate-freshness   { order: 6; }
  #view-premium > *                  { order: 7; }

  /* NOTE: the active-day un-hide is NOT here. It has the same specificity as
     the rule it overrides (`.proof-hero.is-active #proof-full`, ~280 lines
     below), and a media query adds none — so it must come LATER in source
     order to win. It lives directly after that rule. */

  /* Freshness disclosure (see _renderFreshness). The summary carries the
     count inline so the qualification survives collapse; only the per-sport
     enumeration is behind the tap. Inherits the host's stale-warn colour. */
  #slate-freshness { flex-wrap: wrap; }
  #slate-freshness .fresh-detail { width: 100%; margin-top: 2px; }
  #slate-freshness .fresh-detail > summary {
    cursor: pointer; font-size: 0.75rem; color: inherit;
    /* FIX 2026-08-11 (Slate mobile review): was 24px. This disclosure is built
       ONLY on phones (_renderFreshness gates it behind matchMedia), so there is
       no desktop size to fall back to — 24px was the only size it ever had. */
    padding: 2px 0; min-height: 44px; display: list-item;
  }
  #slate-freshness .fresh-detail > div {
    font-size: 0.75rem; line-height: 1.4; padding-top: 2px;
    color: var(--slate-text-3, #8b949e);
  }

  .scorecard-tile-stats { grid-template-columns: 1fr 1fr 1fr; gap: 0.5rem; }
  .scorecard-tile-stat-v { font-size: 1.1rem; }
  .scorecard-tile-stat-l, .scorecard-tile-stat-x { font-size: 0.75rem; }

  /* Four numeric columns are tight at 375px. Narrow the fixed columns and the
     gap rather than wrapping: a wrapped row would break the visual alignment
     that makes the three markets comparable at a glance. Market labels
     abbreviate to ML / Spread / O/U in the renderer at this width. */
  .scorecard-markets-head,
  .scorecard-market-row {
    grid-template-columns: minmax(3.2rem, 1fr) 4.2rem 3.4rem 2.6rem;
    gap: 0.35rem;
  }
  .scorecard-market-v { font-size: 0.95rem; }
  .scorecard-market-m { font-size: 0.8rem; }
  /* Stays at the 12px floor on mobile too — the short labels above, not a
     smaller font, are what makes four columns fit at 375px. */
  .scorecard-markets-head { letter-spacing: 0.02em; }
  .scorecard-markets-head .h-wide { display: none; }
  .scorecard-markets-head .h-narrow { display: inline; }

  /* (Wave 3 #15's 4-col compact slate block was removed 2026-07-12 — it
     half-overrode the Phase 4 card layout above and crushed the matchup
     cell into a 70px track. The consolidated phone layout now lives in the
     single <640px block near L728.) */
}

/* 2026-05-17 audit follow-up: white bg + giant blue button on a dark app
   was jarring and looked like a tout-style modal. Restyled as a quiet
   inline panel using the slate's own card vars so it sits flush with
   the surrounding dark theme. CTA demoted from button-brick to small
   accent-colored text link. */
.slate-empty-state {
  background: var(--slate-bg-card, #161b22);
  border: 1px dashed var(--slate-border, #30363d);
  border-radius: 8px;
  padding: 1.2rem 1.1rem;
  text-align: center;
  margin: 0.5rem 0;
}
.slate-empty-title {
  font-size: 1rem; font-weight: 600;
  color: var(--slate-text-1, #e6edf3);
  margin-bottom: 0.3rem;
}
.slate-empty-sub {
  font-size: 0.94rem; color: var(--slate-text-3, #8b949e);
  margin-bottom: 0.5rem; line-height: 1.45;
}
.slate-empty-clear {
  background: transparent; color: var(--slate-accent, #1ddc7a);
  border: none; padding: 0.3rem 0.4rem;
  font-size: 0.94rem; font-weight: 600;
  cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px; min-height: 32px;
}
.slate-empty-clear:hover { color: var(--slate-green-bright); }

/* Phase B Sprint 5 #9: keyboard navigation focus ring + help overlay. */
.slate-kbd-help {
  position: fixed; inset: 0;
  background: rgba(11, 18, 32, 0.45);
  display: none;
  align-items: center; justify-content: center;
  z-index: 200;
}
.slate-kbd-help-card {
  background: var(--bg-elevated); border: 1px solid var(--slate-border); border-radius: 8px;
  padding: 1.4rem 1.6rem;
  max-width: 420px; width: calc(100% - 2rem);
  box-shadow: 0 8px 28px rgba(0,0,0,0.18);
}
.slate-kbd-help-title {
  font-size: 1.1rem; font-weight: 700;
  margin-bottom: 0.8rem;
}
.slate-kbd-help-list {
  display: grid; grid-template-columns: 6rem 1fr;
  gap: 0.4rem 0.8rem;
  margin: 0 0 1rem; font-size: 1rem;
}
.slate-kbd-help-list dt {
  font-family: ui-monospace, monospace;
  font-weight: 700; color: var(--slate-text);
}
.slate-kbd-help-list dd {
  margin: 0; color: var(--slate-text-2);
}
.slate-kbd-help-close {
  background: #1f6feb; color: #fff;
  border: 1px solid #1f6feb;
  padding: 0.55rem 1.1rem; border-radius: 4px;
  font-size: 0.96rem; font-weight: 600;
  cursor: pointer; min-height: 44px;
}

/* F5 row-expander cells — MLB First-5-Innings (Task 7 — 2026-05-23).
   Rendered by _renderF5Block(game) inside slate-takeover-body when
   game.sport==='mlb' and game.f5 is present. */
.slate-row-f5-cells {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.slate-cell.f5-cell {
  /* FIX 2026-07-12 (Batch 2): --f5-bg was never defined, so the light #f9f9fb
     fallback always rendered — white cells on the dark drilldown. */
  background: var(--bg-elevated);
  border: 1px dashed var(--border);
  border-radius: 4px;
  padding: 6px;
  min-width: 140px;
}

/* ===== Mobile touch targets — WCAG 2.5.5 Target Size (min 44x44) =====
   FIX 2026-07-18: on a 375px viewport the primary "Take →" CTA rendered 22px
   tall (an outright WCAG 2.2 fail), the density toggle 32px, and the ▸ Compare
   / Line-Movement disclosures 34px. Expand the tap area on mobile only — the
   dense desktop layout is unchanged. Elevated priority for motor-impaired users
   (accessibility-audit skill). Placed at end-of-file so these override the
   base .slate-dense-toggle (min-height:32px) etc. by source order. The per-row
   ☆/+ are handled in the max-width:639px row block above. */
@media (max-width: 639px) {
  .ls-expand-toggle {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* FIX 2026-08-11 (Slate mobile review): four more controls the 07-18 sweep
     never enumerated. Same end-of-file placement and the same reason — these
     must beat their base rules on source order. */

  /* Removes a leg from the parlay cart. 15px font + line-height:1 + no
     vertical padding rendered ~15x20px, and on a phone the rail IS the bottom
     drawer (max-width:1023px block above), so this is the only way to drop a
     leg.

     The gutter goes on the CARD, not on .parlay-leg-pick. main.css:857
     declares a SECOND .parlay-leg — the old parlay-builder component, same
     class name — with display:flex + justify-content:space-between, and
     slate.css never sets display, so that wins and the cart leg is a flex
     row. The pick is the LEFT item and never reaches the button; the item
     that sits under it is .parlay-leg-meta, which carries the odds price.
     Measured in a browser at 375px: padding on the pick left a 33x29px
     overlap, so a tap beside the price removed the leg. Reserving the
     button's own width on the card clears it under either layout. */
  .parlay-leg {
    padding-right: 46px;
  }
  .parlay-leg-x {
    top: 0; right: 0;
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0;
  }

  /* The ONLY control on an empty slate — same dead-end argument the sentinel
     already records for .cc-empty-action. */
  .slate-empty-clear {
    min-height: 44px;
    display: inline-flex; align-items: center;
  }

}

/* ===== Beta track-record reframe (2026-07-20) ===== */
/* Full hero shown on quiet days; condenses to #proof-sticky on scroll. */
.proof-hero { margin: 0 0 12px; }
.proof-hero .tagline {
  font-family: ui-monospace, monospace; font-size: 12px; font-weight: 700;
  letter-spacing: .28em; color: var(--accent, #3a7bd5); text-transform: uppercase;
}
#proof-full { text-align: center; padding: 14px 12px 4px; }
.proof-headline { font-size: 20px; font-weight: 800; margin: 8px 0 4px; }
.proof-sub { color: var(--text-muted, #9a9aa3); font-size: 14px; margin-bottom: 12px; }
.proof-cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: 0 16px; border-radius: 8px;
  font-weight: 800; font-size: 14px; text-decoration: none;
  background: #15803d; color: #ffffff; /* AA: #ffffff on #15803d ≈ 4.6:1 (13px bold = normal text, needs 4.5) */
}
.proof-cta--ghost {
  background: transparent; color: var(--text, #cfcfe0);
  border: 1px solid rgba(255,255,255,.2); margin-left: 6px;
}
.proof-disclaimer { color: var(--text-faint, #6e6e78); font-size: 12px; margin-top: 8px; }
.proof-disclaimer a { color: var(--accent, #3a7bd5); }

/* Honest "0 qualifying bets" status card (quiet only). */
#proof-status {
  margin: 12px 0 0; padding: 12px 13px; border-radius: 10px;
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.09);
}
#proof-status .proof-status-h { font-weight: 800; font-size: 14px; }
#proof-status ul { margin: 6px 0 0; padding-left: 18px; }
#proof-status li { color: var(--text-muted, #9a9aa3); font-size: 13px; margin: 2px 0; }

/* Sticky compact strip: pinned CLV bar. Hidden until condensed/active. */
#proof-sticky {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; font-size: 12px;
  background: var(--bg, #0e0e13); border-bottom: 1px solid rgba(34,197,94,.28);
  transition: opacity .18s ease;
}
#proof-sticky.hidden { display: none; }
#proof-sticky .pv { color: #22c55e; font-weight: 800; }
#proof-sticky .pl { color: var(--text-muted, #8a8a93); }
#proof-sticky .proof-cta { margin-left: auto; min-height: 32px; padding: 0 10px; font-size: 12px; }

/* Active day: full hero hidden, strip leads. */
.proof-hero.is-active #proof-full,
.proof-hero.is-active #proof-status { display: none; }

/* FIX 2026-08-06: on a phone the rule above made the per-market CLV table
   unreachable, not merely lower — #proof-full was not rendered at all. Show it
   so the table can lead the fold; slate.js then wires the same
   IntersectionObserver `quiet` uses, so the strip still takes over once the
   hero scrolls away.

   This MUST sit after the rule it overrides. Same selector, same specificity
   (1,2,0 — `#proof-full` is an ID; corrected from 0,2,1 on 2026-09-01), and a
   media query contributes none — so source order is the only thing deciding
   it. Placed inside the mobile block up at the reorder, it
   lost silently: the rules parsed, the URL looked right, and #proof-full
   stayed display:none. #proof-status stays hidden — the "0 qualifying bets"
   panel is for the quiet state and would contradict a populated Best Bets. */
@media (max-width: 639px) {
  .proof-hero.is-active #proof-full { display: block; }
}

/* Proof-unavailable: hide the whole hero, no track-record claim. */
.proof-hero.is-proof-unavailable { display: none; }

/* Bill a11y: no motion when the user asks for none. */
@media (prefers-reduced-motion: reduce) {
  #proof-sticky { transition: none; }
}

/* A cart leg whose game started after it was added: priced pregame, so it
   cannot be placed. Marked here so the reason is visible BEFORE Build & Take
   refuses. Amber (a warning to act on), not red (not an error state). */
.parlay-leg--started { opacity: 0.75; }
.parlay-leg-started {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  vertical-align: middle;
  color: var(--accent-amber, #e8a40f);
  background: rgba(232, 164, 15, 0.12);
  border: 1px solid rgba(232, 164, 15, 0.35);
}

/* ── Mobile input zoom-prevention: .parlay-stake-input ──────────────────────
   iOS Safari zooms the viewport when a focused input renders below 16px and
   does not zoom back out. This field takes a STAKE, so that lands on the
   control that sets how much money a bet is for.

   WHY HERE AND NOT IN main.css's GUARD: .parlay-stake-input is set to 13px at
   file scope above (slate.css:630), and slate.css loads AFTER main.css
   (templates/index.html:23-24). Both are specificity (0,1,0), so source order
   decides and a media query adds none -- adding the selector to main.css's
   guard is a silent no-op, verified in a live browser. The winning rule is the
   one that has to change. Guarded by tests/test_input_zoom_guard.py, which
   asserts the cascade winner rather than membership in a list. */
@media (max-width: 639px) {
  .parlay-stake-input {
    font-size: 16px;
  }
}

/* 2026-09-03: the signed-in top slot. Hierarchy is the point -- the play is
   what matters, the lock time and bet count are context, so they are typed
   down rather than given equal weight. The accent rule appears only when
   there IS a play; the empty state carries no rule, so "nothing clears the
   floor" cannot be mistaken for a recommendation. */
.top-slot { padding: 9px 14px; }
.top-slot.has-play { border-left: 3px solid var(--slate-green); }
.top-slot-play {
  display: block; font-size: 14px; font-weight: 700;
  color: var(--slate-text); text-decoration: none;
}
.top-slot-play:hover { text-decoration: underline; }
.top-slot-meta { font-size: 12px; color: var(--slate-text-3); margin-top: 3px; }

