/* ==========================================================================
   Trader Journal — CLAY V3: gunmetal + oxide
   Drop-in token layer. Load AFTER clay-v2.css, nothing else changes:

     <link rel="stylesheet" href="styles.css?v=..." />
     <link rel="stylesheet" href="clay-v2.css?v=..." />
     <link rel="stylesheet" href="clay-v3.css?v=20260806-clayv3" />

   Direction: masculinity and discipline.

   What changes, and why:
     1. DARK IS PRIMARY. Cold gunmetal ground (#14161a), steel surfaces,
        machined hairlines. The FOUC guard in index.html and getStoredTheme()
        in app.js flip together — light becomes the stored opt-in.
     2. Violet dies. Burnt oxide (#c2410c family, lifted/darkened per theme
        until it holds WCAG AA — verified by tests/clayV3Contrast.check.mjs,
        not by eye) is the single interaction accent: primary CTA, active nav,
        focus ring, chart line. Accent means interaction ONLY.
     3. Light mode is re-derived cold as concrete (#e6e8ea ground), not
        inverted and not cream.
     4. Depth-as-data survives. The clay stacks are re-derived for a dark
        primary ground: dark clay = subtle cool top highlight at low alpha +
        deep drops; pressed wells read as machined recesses.
     5. The wordmark goes single colour, single weight, tighter tracking; the
        TJ monogram becomes a squared ink stamp. Ids/classes untouched.

   Charts need no JS change: charts.js reads tokens off
   getComputedStyle(document.documentElement) and re-reads on 'themechange'.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. DARK — THE PRIMARY THEME. Gunmetal ground, steel surfaces, oxide accent.
   Oxide as text must hold 4.5:1 on every surface up to --surface-3, so the
   #c2410c base is lifted to #f0763d for dark use (the raw base measures
   3.1:1 on the ground — fails body text).
   -------------------------------------------------------------------------- */
:root:not([data-theme="light"]) {
  --surface-0: #080b12;        /* page — cold gunmetal */
  --surface-1: #0c1119;        /* cards, panels */
  --surface-2: #111823;        /* nav, modal, raised */
  --surface-3: #16202d;        /* hover / active fill */
  --surface-inset: #05080e;    /* wells, inputs, table heads */

  --line: #151d28;             /* machined hairlines */

  /* Amber warn, pinned for the gunmetal dark scope: the styles.css fallback
     happened to hold (7.75:1 on --surface-0) but was tuned for the violet-era
     surfaces — scoped here deliberately so a base-layer retune cannot drift it. */
  --warn: #f0b34e;
  --warn-soft: rgba(240, 179, 78, 0.12);
  /* --info was never overridden in this block and fell through to the
     violet-era value in styles.css. Deliberately the SAME hue as
     --term-live, so the product has one cyan and not two. */
  --info: #22d3ee;
  --info-soft: rgba(34, 211, 238, 0.12);
  --line-strong: #1b2532;
  --line-accent: rgba(249, 115, 22, 0.45);

  --text: #e6ecf5;
  --text-soft: #8695a8;
  --text-faint: #7c8b9e;
  --text-inverse: #080b12;     /* dark label on the bright oxide fill */

  --accent: #f97316;           /* burnt oxide, lifted for AA on gunmetal */
  --accent-strong: #fb8b3a;
  --accent-muted: rgba(249, 115, 22, 0.14);

  --pnl-pos: #34d399;
  --pnl-pos-soft: rgba(52, 211, 153, 0.12);
  --pnl-pos-line: rgba(52, 211, 153, 0.38);
  --pnl-neg: #f87171;          /* lifted a step from V2 to clear 4.5:1 on --surface-3 */
  --pnl-neg-soft: rgba(248, 113, 113, 0.12);
  --pnl-neg-line: rgba(248, 113, 113, 0.38);

  --chart-grid: #131b26;
  --chart-axis: #8695a8;
  --chart-line-deep: #9a4409;
  --chart-glow: rgba(249, 115, 22, 0.5);
  --chart-halo: rgba(249, 115, 22, 0.24);
  --chart-halo-fade: rgba(249, 115, 22, 0);
  --chart-area-top: rgba(249, 115, 22, 0.35);
  --chart-area-mid: rgba(249, 115, 22, 0.11);
  --chart-area-bottom: rgba(249, 115, 22, 0);
  --chart-canvas-top: #0a0e16;
  --chart-canvas-bottom: #070a11;
  --chart-canvas-glow: rgba(249, 115, 22, 0.06);
  --chart-canvas-shadow: rgba(0, 0, 0, 0.55);
  --chart-pos-deep: #12795a;
  --chart-pos-glow: rgba(52, 211, 153, 0.5);
  --chart-neg-deep: #96343a;
  --chart-neg-glow: rgba(248, 113, 113, 0.5);
  --chart-halo-neg: rgba(248, 113, 113, 0.24);
  --chart-halo-neg-fade: rgba(248, 113, 113, 0);
  --chart-neg-area-top: rgba(248, 113, 113, 0.04);
  --chart-neg-area-bottom: rgba(248, 113, 113, 0.42);
  --chart-highlight: rgba(255, 255, 255, 0.28);
  --chart-highlight-fade: rgba(255, 255, 255, 0);
  --chart-shade: rgba(0, 0, 0, 0.4);
  --chart-shade-fade: rgba(0, 0, 0, 0);
  --chart-track: rgba(58, 68, 83, 0.12);
  --chart-crosshair: rgba(134, 149, 168, 0.55);
  --chart-tooltip-bg: rgba(12, 17, 25, 0.96);
  --chart-tooltip-line: #1b2532;
  --chart-tooltip-shadow: rgba(0, 0, 0, 0.6);
  --chart-ring-wash: rgba(134, 149, 168, 0.05);
  --spark-pos-fade: rgba(52, 211, 153, 0);
  --spark-neg-fade: rgba(248, 113, 113, 0);

  /* Dark clay on gunmetal: deep cold drops, a whisper of cool light on the
     top edge. The warm 30–35deg cast dies with the bone ground. */
  --clay-raised:
    0 16px 30px -16px hsl(220 45% 3% / 0.80),
    0 5px 11px -6px hsl(220 45% 3% / 0.55),
    inset 0 -7px 14px -7px hsl(220 40% 2% / 0.55),
    inset 0 8px 13px -7px hsl(210 20% 85% / 0.10);
  --clay-float:
    0 24px 42px -16px hsl(220 45% 3% / 0.85),
    0 7px 15px -7px hsl(220 45% 3% / 0.60),
    inset 0 -8px 16px -8px hsl(220 40% 2% / 0.60),
    inset 0 9px 15px -8px hsl(210 20% 87% / 0.15);
  --clay-soft: 0 12px 26px -18px hsl(220 45% 3% / 0.70);
  --clay-accent:
    0 20px 34px -14px hsl(20 70% 8% / 0.90),
    0 6px 13px -6px hsl(20 70% 6% / 0.62),
    inset 0 -7px 14px -7px hsl(20 80% 12% / 0.50),
    inset 0 8px 14px -7px hsl(0 0% 100% / 0.22);
  --clay-pressed:
    inset 0 4px 9px -2px hsl(220 45% 2% / 0.72),
    inset 0 -2px 5px -3px hsl(210 20% 85% / 0.05);

  --clay-edge: hsl(210 20% 85% / 0.07);
  --clay-edge-strong: hsl(210 20% 88% / 0.12);
  --control-edge: #6b7789;     /* >= 3:1 on both card and page (1.4.11) */

  --shadow: var(--clay-raised);
  --shadow-modal:
    0 40px 70px -24px hsl(220 45% 2% / 0.85),
    0 12px 24px -10px hsl(220 45% 3% / 0.55),
    inset 0 -8px 16px -8px hsl(220 40% 2% / 0.50),
    inset 0 9px 15px -8px hsl(210 20% 87% / 0.14);
  --edge-highlight: inset 0 1px 0 hsl(210 20% 88% / 0.10);
  --focus-ring: 0 0 0 3px var(--surface-1), 0 0 0 6px var(--accent);

  --halo-accent: rgba(249, 115, 22, 0.35);
  --aurora-a: rgba(249, 115, 22, 0.12);
  --aurora-b: rgba(52, 211, 153, 0.07);
  --aurora-c: rgba(134, 149, 168, 0.08);
  --aurora-fade: rgba(249, 115, 22, 0);
  --landing-grid-line: rgba(249, 115, 22, 0.10);
  --floor-glow: rgba(249, 115, 22, 0.10);
  --nav-scrim: rgba(4, 6, 10, 0.60);

  --glass-top: #111823;
  --glass-bottom: #0d131c;
  --deck-top: #111823;
  --deck-bottom: #0d131c;
  --deck-edge: hsl(210 20% 85% / 0.10);
  --deck-wash: rgba(249, 115, 22, 0.04);
  --deck-groove: hsl(220 45% 1% / 0.70);
}

/* --------------------------------------------------------------------------
   2. LIGHT — CONCRETE. Re-derived cold, not inverted, not cream. Oxide
   darkens to #a83508 so it holds 4.5:1 down to --surface-inset while white
   CTA labels still clear 4.5:1 on top of it.
   -------------------------------------------------------------------------- */
[data-theme="light"] {
  --surface-0: #e6e8ea;        /* page — poured concrete */
  --surface-1: #f0f2f4;        /* cards, panels */
  --surface-2: #f7f8fa;        /* nav, modal, raised */
  --surface-3: #e9ecee;        /* hover */
  --surface-inset: #dde0e4;    /* wells, inputs, table heads */

  --line: #d3d7db;
  --line-strong: #b9bfc6;
  --line-accent: rgba(168, 53, 8, 0.45);

  --text: #191c1f;
  --text-soft: #566068;
  --text-faint: #545e66;
  --text-inverse: #ffffff;

  --accent: #a83508;           /* burnt oxide, darkened for AA on concrete */
  --accent-strong: #8a2b06;
  --accent-muted: rgba(168, 53, 8, 0.10);

  --pnl-pos: #146b49;
  --pnl-pos-soft: rgba(20, 107, 73, 0.12);
  --pnl-pos-line: rgba(20, 107, 73, 0.38);
  --pnl-neg: #b32b4f;
  --pnl-neg-soft: rgba(179, 43, 79, 0.11);
  --pnl-neg-line: rgba(179, 43, 79, 0.38);

  --warn: #7d520b;
  --warn-soft: rgba(125, 82, 11, 0.12);
  --info: #0b607c;
  --info-soft: rgba(11, 96, 124, 0.11);

  --chart-grid: #d0d4d9;
  --chart-axis: #566068;
  --chart-line-deep: #6e2205;
  --chart-glow: rgba(168, 53, 8, 0.32);
  --chart-halo: rgba(168, 53, 8, 0.18);
  --chart-halo-fade: rgba(168, 53, 8, 0);
  --chart-area-top: rgba(168, 53, 8, 0.24);
  --chart-area-mid: rgba(168, 53, 8, 0.08);
  --chart-area-bottom: rgba(168, 53, 8, 0);
  --chart-canvas-top: #dfe2e5;
  --chart-canvas-bottom: #d8dcdf;
  --chart-canvas-glow: rgba(168, 53, 8, 0.05);
  --chart-canvas-shadow: rgba(20, 24, 28, 0.16);
  --chart-highlight: rgba(255, 255, 255, 0.6);
  --chart-highlight-fade: rgba(255, 255, 255, 0);
  --chart-shade: rgba(20, 24, 28, 0.2);
  --chart-shade-fade: rgba(20, 24, 28, 0);
  --chart-track: rgba(86, 96, 104, 0.12);
  --chart-crosshair: rgba(86, 96, 104, 0.55);
  --chart-tooltip-bg: rgba(247, 248, 250, 0.98);
  --chart-tooltip-line: #b9bfc6;
  --chart-tooltip-shadow: rgba(20, 24, 28, 0.2);
  --chart-ring-wash: rgba(86, 96, 104, 0.05);

  /* Concrete clay: same geometry as V2's stacks, cold cast. */
  --clay-raised:
    0 18px 32px -18px hsl(215 15% 35% / 0.42),
    0 6px 12px -8px hsl(215 15% 35% / 0.28),
    inset 0 -8px 16px -8px hsl(215 18% 40% / 0.20),
    inset 0 8px 14px -8px hsl(0 0% 100% / 0.95);
  --clay-float:
    0 26px 44px -18px hsl(215 15% 33% / 0.46),
    0 8px 16px -8px hsl(215 15% 35% / 0.32),
    inset 0 -9px 18px -9px hsl(215 18% 40% / 0.24),
    inset 0 10px 16px -9px hsl(0 0% 100% / 1);
  --clay-soft: 0 12px 24px -16px hsl(215 15% 35% / 0.35);
  --clay-accent:
    0 20px 34px -16px hsl(18 85% 26% / 0.50),
    0 6px 14px -7px hsl(18 85% 22% / 0.38),
    inset 0 -7px 14px -7px hsl(18 90% 18% / 0.45),
    inset 0 8px 14px -7px hsl(0 0% 100% / 0.35);
  --clay-pressed:
    inset 0 5px 12px -3px hsl(215 18% 35% / 0.34),
    inset 0 -3px 6px -3px hsl(0 0% 100% / 0.90);

  --clay-edge: hsl(215 15% 20% / 0.06);
  --clay-edge-strong: hsl(215 15% 20% / 0.11);
  --control-edge: #75808b;     /* >= 3:1 on both card and page (1.4.11) */

  --shadow: var(--clay-raised);
  --shadow-modal:
    0 40px 70px -24px hsl(215 15% 30% / 0.42),
    0 12px 24px -10px hsl(215 15% 33% / 0.26),
    inset 0 -8px 16px -8px hsl(215 18% 40% / 0.18),
    inset 0 9px 15px -8px hsl(0 0% 100% / 1);
  --edge-highlight: inset 0 1px 0 hsl(0 0% 100% / 0.9);
  --focus-ring: 0 0 0 3px var(--surface-1), 0 0 0 6px var(--accent);

  --halo-accent: rgba(168, 53, 8, 0.24);
  --aurora-a: rgba(168, 53, 8, 0.12);
  --aurora-b: rgba(20, 107, 73, 0.08);
  --aurora-c: rgba(86, 96, 104, 0.10);
  --aurora-fade: rgba(168, 53, 8, 0);
  --landing-grid-line: rgba(168, 53, 8, 0.10);
  --floor-glow: rgba(168, 53, 8, 0.12);
  --nav-scrim: rgba(20, 24, 28, 0.38);

  --glass-top: #f7f8fa;
  --glass-bottom: #eceef1;
  --deck-top: #f7f8fa;
  --deck-bottom: #eceef1;
  --deck-edge: hsl(0 0% 100% / 0.9);
  --deck-wash: rgba(168, 53, 8, 0.04);
  --deck-groove: hsl(215 18% 35% / 0.26);
}

/* ==========================================================================
   3. MACHINED DEPTH — the component shadows clay-v2 hard-codes in warm hsl.
   Depth-as-data survives verbatim (raised = profit, pressed = loss, travel
   scales with --day-intensity / --row-intensity); only the cast goes cold.
   ========================================================================== */

/* --- 3a. Calendar --------------------------------------------------------- */
[data-theme="light"] .calendar-cell-positive {
  box-shadow:
    0 calc(8px + var(--day-intensity, 0) * 14px) calc(16px + var(--day-intensity, 0) * 16px) -14px
      hsl(158 35% 25% / calc(0.3 + var(--day-intensity, 0) * 0.3)),
    inset 0 -6px 12px -6px hsl(215 18% 40% / 0.18),
    inset 0 7px 12px -7px hsl(0 0% 100% / 1);
}

[data-theme="light"] .calendar-cell-negative {
  box-shadow:
    inset 0 calc(4px + var(--day-intensity, 0) * 6px) calc(10px + var(--day-intensity, 0) * 8px) -3px
      hsl(350 45% 32% / calc(0.22 + var(--day-intensity, 0) * 0.22)),
    inset 0 -3px 6px -3px hsl(0 0% 100% / 0.75);
}

[data-theme="light"] .calendar-cell-flat {
  box-shadow: inset 0 3px 7px -3px hsl(215 18% 35% / 0.2);
}

:root:not([data-theme="light"]) .calendar-cell-positive {
  box-shadow:
    0 calc(8px + var(--day-intensity, 0) * 14px) calc(16px + var(--day-intensity, 0) * 16px) -14px
      hsl(158 55% 7% / calc(0.5 + var(--day-intensity, 0) * 0.3)),
    inset 0 -6px 12px -6px hsl(220 40% 2% / 0.5),
    inset 0 7px 12px -7px hsl(210 20% 85% / 0.12);
}

:root:not([data-theme="light"]) .calendar-cell-negative {
  box-shadow:
    inset 0 calc(4px + var(--day-intensity, 0) * 6px) calc(10px + var(--day-intensity, 0) * 8px) -3px
      hsl(350 50% 5% / calc(0.5 + var(--day-intensity, 0) * 0.25)),
    inset 0 -2px 5px -3px hsl(210 20% 85% / 0.05);
}

:root:not([data-theme="light"]) .calendar-cell-flat {
  box-shadow: inset 0 3px 7px -3px hsl(220 40% 3% / 0.5);
}

/* --- 3b. Calendar legend --------------------------------------------------- */
[data-theme="light"] .cal-legend-up {
  box-shadow: 0 8px 14px -8px hsl(158 35% 25% / 0.5), inset 0 5px 8px -4px hsl(0 0% 100% / 1);
}

[data-theme="light"] .cal-legend-down {
  box-shadow: inset 0 5px 10px -2px hsl(350 45% 32% / 0.4);
}

:root:not([data-theme="light"]) .cal-legend-up {
  box-shadow: 0 8px 14px -8px hsl(158 55% 7% / 0.7), inset 0 5px 8px -4px hsl(210 20% 85% / 0.12);
}

:root:not([data-theme="light"]) .cal-legend-down {
  box-shadow: inset 0 5px 10px -2px hsl(350 50% 5% / 0.6);
}

/* --- 3c. Journal rows (§5c depth ramp) ------------------------------------- */
[data-theme="light"] #journal tbody tr.trade-row-win {
  box-shadow:
    0 calc(10px + var(--row-intensity, 0) * 8px) calc(20px + var(--row-intensity, 0) * 12px) -16px
      hsl(215 15% 35% / calc(0.4 + var(--row-intensity, 0) * 0.2)),
    inset 0 -6px 12px -6px hsl(215 18% 40% / 0.18),
    inset 0 7px 12px -7px hsl(0 0% 100% / 1);
}

[data-theme="light"] #journal tbody tr.trade-row-loss {
  box-shadow:
    inset 0 calc(5px + var(--row-intensity, 0) * 5px) calc(12px + var(--row-intensity, 0) * 9px) -3px
      hsl(350 40% 34% / calc(0.22 + var(--row-intensity, 0) * 0.2)),
    inset 0 -3px 6px -3px hsl(0 0% 100% / 0.8);
}

:root:not([data-theme="light"]) #journal tbody tr.trade-row-win {
  box-shadow:
    0 calc(10px + var(--row-intensity, 0) * 8px) calc(20px + var(--row-intensity, 0) * 12px) -16px
      hsl(220 45% 3% / calc(0.6 + var(--row-intensity, 0) * 0.2)),
    inset 0 -6px 12px -6px hsl(220 40% 2% / 0.5),
    inset 0 7px 12px -7px hsl(210 20% 85% / 0.12);
}

:root:not([data-theme="light"]) #journal tbody tr.trade-row-loss {
  box-shadow:
    inset 0 calc(5px + var(--row-intensity, 0) * 5px) calc(12px + var(--row-intensity, 0) * 9px) -3px
      hsl(350 50% 5% / calc(0.45 + var(--row-intensity, 0) * 0.25)),
    inset 0 -2px 5px -3px hsl(210 20% 85% / 0.05);
}

/* --- 3d. Carved wells + scrims --------------------------------------------- */
[data-theme="light"] .dash-spark-wrap {
  box-shadow: inset 0 8px 18px -6px hsl(215 18% 35% / 0.30);
}

:root:not([data-theme="light"]) .dash-spark-wrap {
  box-shadow: inset 0 8px 18px -6px hsl(220 45% 2% / 0.6);
}

.cmdk::backdrop,
.sheet::backdrop,
.cooldown-dialog::backdrop,
.account-dialog::backdrop {
  background: hsl(215 20% 12% / 0.42);
}

:root:not([data-theme="light"]) .cmdk::backdrop,
:root:not([data-theme="light"]) .sheet::backdrop,
:root:not([data-theme="light"]) .cooldown-dialog::backdrop,
:root:not([data-theme="light"]) .account-dialog::backdrop {
  background: hsl(220 40% 2% / 0.68);
}

/* --- 3e. Violet leftovers with literal hsl(265 …) shadows ------------------ */
.sheet-rules {
  box-shadow: inset 0 4px 10px -3px hsl(18 40% 35% / 0.22);
}

[data-theme="light"] .rev-chip.is-active {
  box-shadow:
    0 14px 24px -14px hsl(18 80% 28% / 0.55),
    inset 0 -5px 10px -5px hsl(18 85% 20% / 0.45),
    inset 0 6px 11px -5px hsl(0 0% 100% / 0.4);
}

:root:not([data-theme="light"]) .rev-chip.is-active {
  box-shadow:
    0 14px 26px -16px hsl(20 70% 6% / 0.75),
    inset 0 -5px 10px -5px hsl(20 60% 10% / 0.5),
    inset 0 6px 11px -5px hsl(20 80% 88% / 0.25);
}

/* ==========================================================================
   4. LABELS ON ACCENT + MONEY FILLS
   clay-v2 hard-codes `color: #fff` on the oxide/money chips. In dark the
   fills are bright (oxide #f0763d, rose #f7758d) and white fails AA on them;
   --text-inverse is the token that already flips per theme.
   ========================================================================== */.topnav-badge,
.tabbar-badge,
.dash-alert-mark,
.sheet-seg-btn.is-long.is-active,
.sheet-seg-btn.is-short.is-active {
  color: var(--text-inverse);
}

/* ==========================================================================
   5. WORDMARK + MONOGRAM
   Single colour, single weight, tighter tracking. The two-tone
   brand-word-accent/primary treatment dies (accent means interaction only —
   a logo is not an interaction). The TJ block becomes a squared ink stamp:
   flat fill, machined edge, no clay. Ids/classes untouched.
   ========================================================================== */
.brand-word-accent {
  color: var(--text);
}

.brand-mark {
  letter-spacing: -0.06em;
  gap: 0.26rem;
}

.lnd-brand-name {
  letter-spacing: -0.04em;
}

.lnd-mark {
  border-radius: 9px;
  background: var(--text);
  color: var(--surface-0);
  letter-spacing: -0.02em;
  box-shadow: 0 0 0 1px var(--clay-edge-strong);
}

.topnav-mark,
.topnav-mark.is-active {
  border-radius: 10px;
  background: var(--text);
  color: var(--surface-0);
  box-shadow: 0 0 0 1px var(--clay-edge-strong);
}

/* The stamp rule above ties with clay-v2's §10h focus restoration on
   .is-active and would win on file order — restore the ring explicitly. */
.topnav-mark:focus-visible,
.topnav-mark.is-active:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ==========================================================================
   THE TERMINAL RAMP — one instrument language for the whole site.

   Three surfaces render the same idea and each used to carry its OWN dark
   ramp under its own names: the landing's Terminal Pro (--tp-*), the Release
   edge desk (--bb-*), and the dashboard rail (plain Clay). The values were
   within a few RGB points of each other but never identical, which is why the
   site read as three products.

   Declared HERE, at :root, for a second reason that matters more than tidiness:
   the landing's ramp was pinned element-scoped on `.lnd-tp-frame, .lnd-lens`,
   and tests/clayV3Contrast.check.mjs walks :root-level blocks only. The page
   that sells this product shipped with ZERO of its colours measured. At :root
   the harness sees them, and one measurement now covers all three surfaces.

   A screen keeps its own light: these do not follow [data-theme="light"], so an
   instrument stays an instrument on concrete.
   ========================================================================== */
:root {
  --term-void:  #080b12;   /* ground, and every 1px splitter */
  --term-pane:  #0c1119;   /* pane fill */
  --term-rail:  #111823;   /* command bars, pane heads, status bars */
  --term-raise: #16202d;   /* raised cell, hover */
  --term-well:  #05080e;   /* carved wells: axes, bands, bars */
  --term-line:  #1b2532;   /* hairline, carries no information */
  --term-edge:  #6b7789;   /* the meaningful edge: markers and scale ticks */
  --term-ink:   #e6ecf5;
  --term-soft:  #8695a8;
  --term-faint: #7c8b9e;
  --term-acc:   #f97316;   /* the shipped burnt oxide */
  --term-wash:  rgba(249, 115, 22, 0.10);
  --term-warn:  #f0b34e;
  --term-pos:   #34d399;   /* the shipped dark --pnl-pos, verbatim */
  --term-neg:   #f87171;   /* the shipped dark --pnl-neg, verbatim */
  /* THE MISSING TOKEN. Read in four places in this file and declared in
     NO file, so every cyan in the product has been shipping as the
     hardcoded fallback written beside each use. LIVE / COUNTING ONLY. */
  --term-live: #22d3ee;
}

/* ==========================================================================
   THE DESK COMES UPSTAIRS — dashboard density pass.

   The dashboard was measured at 2,354px of stack below the header at 1440,
   and the trader's own complaint was that everything is about 65% bigger
   than it needs to be. It is: a 34px corner radius cannot read small, so
   every card was sized to justify its own corners.

   Desktop only, behind the same 1025px seam the topnav switches on, and
   every rule is #dashboard scoped so the journal, review and desk views keep
   their clay untouched. tests/mobileFloors.check.mjs parses styles.css and
   clay-v2.css at 375px, so nothing here is in its field of view, and nothing
   here applies to a phone either way. The 11px floor is still honoured by
   hand: nothing user facing goes below it at any width.
   ========================================================================== */
@media (min-width: 1025px) {
  /* --- PHASE 1: the analytics grid, 1291 -> 922 -------------------------
     A pane head is ONE line now: the h3 and its subtitle sit on a baseline
     row instead of stacking. That stacking is where the trader score row's
     94px of chrome lived, because the info button was wrapping onto a line
     of its own under the title block. Nothing is removed; it is folded. */
  #dashboard .panel-grid-analytics { gap: 8px; }

  #dashboard .panel-grid-analytics .panel { padding: 12px; }

  #dashboard .panel-grid-analytics .panel-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 8px;
  }

  #dashboard .panel-grid-analytics .panel-head > div {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    min-width: 0;
  }

  /* The pane title takes the desk's own voice: mono, uppercase, tracked.
     This is the density pass earning its name rather than just shrinking. */
  #dashboard .panel-grid-analytics .panel-head h3 {
    font-size: 13px;
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }

  #dashboard .panel-grid-analytics .panel-head p {
    margin: 0;
    font-size: 11px;   /* --fs-micro, the floor, exactly */
  }

  /* The info button was taking a whole 42px line to itself. */
  #dashboard .panel-grid-analytics .panel-head-inline {
    flex-wrap: nowrap;
    align-items: flex-start;
  }
  #dashboard .panel-grid-analytics .panel-head-inline > div {
    flex: 1 1 auto;
    min-width: 0;
  }
  #dashboard .panel-grid-analytics .panel-head-inline > .info-btn {
    flex: 0 0 auto;
  }

  /* Two stacked toggle groups become one row: 88px of head becomes 38. */
  #dashboard .strategy-performance-toolbar {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    width: auto;
  }

  /* Two toggle GROUPS side by side, not stacked. Setting the toolbar to row
     was not enough: its own children were wrapping inside the width the head
     left them, so the row was two rows wearing one flex-direction. */
  /* The toolbar needs 318px for its two groups and the head was leaving it
     249, so it wrapped no matter what flex-direction said. The fix is width,
     not wrapping: the toolbar takes what it needs and the TITLE shrinks,
     because a truncated subtitle costs less than a second row of chrome. */
  /* NOTE THE SELECTOR. The generic `.panel-head-inline > div` rule above is
     (1,2,1) because of its element selector, and the toolbar IS a div, so a
     (1,2,0) rule here loses to my own rule and the toolbar keeps flex 1 1
     auto and wrap. Matching .panel-grid-analytics makes this (1,3,0). */
  #dashboard .panel-grid-analytics .panel-head-inline > .strategy-performance-toolbar {
    flex: 0 0 auto;
    flex-wrap: nowrap;
  }
  #dashboard .panel-grid-analytics .strategy-toggle-group { flex: 0 0 auto; }

  /* The panes whose head has a bare h3 and p (no wrapper div) were still
     wrapping to two lines. They are the same baseline row as the others; the
     subtitle simply takes what is left and ellipsizes rather than pushing the
     head to a second line. */
  #dashboard .panel-grid-analytics .panel-head > h3 { flex: 0 0 auto; }
  #dashboard .panel-grid-analytics .panel-head > p,
  #dashboard .panel-grid-analytics .panel-head > div > p {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  #dashboard .panel-trader-score .trader-score-foot { padding-top: 8px; }

  /* --- PHASE 2: the deck, 345 -> ~191 ------------------------------------
     BOTH CARDS OR NEITHER. The deck is a grid row, so it is as tall as its
     TALLER card, and that card is the risk meter at 343px natural, not the
     balance hero at 320. The design claimed shrinking the hero alone saves
     177px; it saves 2. This is the corrected phase.

     Specificity note: .metric-card.dash-hero, not a bare .dash-hero, because
     styles.css has `.metric-card:not(.dash-rail-item)` at (0,2,0) and a
     single class loses to it. */

  /* The hero reserved 132px of bottom padding for a 118px decorative well
     under a 50px number. The sparkline keeps every pixel of DATA at 56px
     because it never had an axis. .dash-spark-wrap is absolutely positioned,
     so the padding-bottom IS that reserve: both move together or the well
     tears away from its card. */
  #dashboard .metric-card.dash-hero {
    padding: 12px 16px 12px;
    min-height: 0;
    border-radius: 14px;
  }
  #dashboard .dash-hero-top { gap: 8px; }
  #dashboard .dash-hero-value {
    font-size: 44px;   /* still the biggest number on the page, by design */
    margin-top: 2px;
  }
  #dashboard .dash-hero-meta { margin-top: 6px; min-height: 0; }
  #dashboard .dash-spark-wrap {
    height: 56px;
    border-radius: 0 0 14px 14px;
  }

  #dashboard .dash-stats { gap: 8px; }
}

/* The phone hero still reserves 126px for an unchanged 118px well, which is
   correct. If anyone ever shrinks the phone sparkline, this must be beaten at
   equal specificity here, or 126px of padding becomes a crater under it:
     @media (max-width: 720px) {
       #dashboard .metric-card.dash-hero { padding-bottom: 126px; }
     }
   The desktop pass above does not touch the phone well, so it stays a note. */

/* ==========================================================================
   THE RACK, PART 1: THE SPINE.

   The nav stops being a bar across the top and becomes a fixed left rail.
   Collapsed to 56px of icons by default, expanding to 210px as an OVERLAY on
   hover or keyboard focus. Overlay is the whole point: the layout only ever
   reserves 56px, so expanding costs the dashboard nothing and reflows
   nothing, which is what makes it feel like chrome rather than a panel.

   Desktop only, behind the same 1025px seam the top bar used, because below
   it the sidebar drawer and the bottom tab bar already own navigation and a
   third nav on a phone is how you lose the content.

   Every child class is still topnav-*, inherited verbatim from clay-v2. Only
   geometry is written here.
   ========================================================================== */
:root {
  /* Reserved gutter. Zero everywhere by default so no other breakpoint has
     to know the rail exists; the desktop block below is the only writer. */
  --rail-w: 0px;
}

.rail { display: none; }

@media (min-width: 1025px) {
  :root { --rail-w: 56px; }

  .rail {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-nav);
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    width: var(--rail-w);
    padding: 12px 8px;
    border-right: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface-2);
    box-shadow: none;
    overflow: hidden;
    transition: width 160ms var(--ease-out);
  }

  /* NO HOVER EXPANSION. The overlay was a mistake in use: sweeping the
     pointer past the left edge on the way to anything grew the rail to 210px
     ON TOP of the page, so a Save button or the first word of every heading
     sat under it, silently, for as long as the pointer lingered. Reaching for
     content is not a request to read the nav.

     Labels arrive by intent instead, via the pin at the foot, and pinning
     widens the RESERVED gutter so the page moves over rather than hides. The
     one thing that still expands on its own is keyboard focus, where the
     pointer is nowhere near the content and the label is the only way to know
     what is selected. */
  /* :has(:focus-visible), not :focus-within. A mouse click on a nav button
     leaves DOM focus parked on it — nothing in app.js blurs it — so
     :focus-within latched the rail open at 210px over the view for the rest
     of the session. :focus-visible is the browser's own "this focus came from
     the keyboard" heuristic, which is exactly the case the expansion is for. */
  .rail:has(:focus-visible) {
    width: 84px;
    box-shadow: var(--clay-float);
  }

  /* PINNED. --rail-w is what the page reserves, so widening it here is what
     makes the content move instead of being covered. */
  /* THE STAMP RAIL. 84px, not 210px. The labelled layout was never the
     problem: clay-v2's .topnav-btn is already a stamp, a 21px glyph stacked
     over a 9px mono label, and it is the rail override BELOW that flattened
     rows into icon-beside-label, which is the shape that needs 210px to hold
     a word. Standing the rows back up costs 28px instead of 154px, an 82% cut
     to what the open state charges the page, and every label still fits by
     arithmetic: 8px rail padding and 4px row padding leave 60px, against
     JOURNAL at ~47px in 10px mono, the widest of the six. */
  body.rail-pinned { --rail-w: 84px; }
  body.rail-pinned .rail { width: var(--rail-w); }

  .rail-pin {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 40px;
    padding: 8px 9px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--text-faint);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  }
  .rail-pin:hover { color: var(--text); background: var(--surface-3); }
  .rail-pin svg { flex: 0 0 auto; width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.9; }
  body.rail-pinned .rail-pin { color: var(--accent); }
  .rail-pin .topnav-label { opacity: 0; transition: opacity 120ms var(--ease-out); }
  .rail:has(:focus-visible) .rail-pin .topnav-label,
  body.rail-pinned .rail-pin .topnav-label { opacity: 1; }

  /* The bottom cluster sinks: destinations at the top, utilities at the
     foot, which is what makes a spine read as a spine. */
  .rail-spacer { flex: 1 1 auto; }

  /* Rows, not a row. Each key is a full width target with its icon locked in
     the 40px collapsed column so nothing shifts sideways as labels arrive. */
  .rail .topnav-btn,
  .rail .topnav-icon-btn,
  .rail .topnav-mark,
  .rail .topnav-more > summary,
  .rail .rail-account,
  .rail .rail-pin {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: 4px;
    width: 100%;
    /* Inheriting the stamp means inheriting its FLOOR: clay-v2 gives
       .topnav-btn min-width 62px, which the horizontal bar wanted and a 39px
       rail column cannot give. The row rendered 62px wide inside 39px and the
       rail's overflow:hidden sheared the right 23px off, which is the lit pill
       running off the edge and the count badge losing its second digit. A rail
       row is as wide as the rail, and no wider. */
    min-width: 0;
    min-height: 52px;
    padding: 6px 4px;
    border-radius: 10px;
    white-space: nowrap;
  }

  /* Collapsed and open are now the SAME layout: only the rail's width and the
     label's opacity differ, so nothing reflows, nothing shifts vertically, and
     no element changes display mode between states. */

  .rail .topnav-links {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    width: 100%;
  }

  .rail .topnav-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    width: 100%;
  }

  /* The account switcher was inheriting the top bar's horizontal label/select
     pair: a 190px control in a 56px rail, clipped to "ACCOUN" against the
     content edge, and still overflowing at the expanded 210px. It stacks like
     the mobile copy and fades with the labels. Opacity, not display, so it
     stays tabbable and its own focus is what expands the rail. */
  .rail .account-switch {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding: 4px 8px;
    opacity: 0;
    transition: opacity 120ms var(--ease-out);
  }

  .rail:has(:focus-visible) .account-switch,
  body.rail-pinned .account-switch { opacity: 1; }

  .rail .account-switch-select {
    max-width: none;
    width: 100%;
  }

  /* Labels are present for the accessibility tree at all times and revealed
     with the rail, so nothing is hidden from a screen reader by collapsing.
     Opacity, not display, for exactly that reason. */
  .rail .topnav-label {
    max-width: 100%;
    overflow: hidden;
    font-size: 10px;
    letter-spacing: .07em;
    text-overflow: ellipsis;
    opacity: 0;
    transition: opacity 120ms var(--ease-out);
  }
  .rail:has(:focus-visible) .topnav-label,
  body.rail-pinned .topnav-label {
    opacity: 1;
    transition-delay: 60ms;
  }

  /* THE BADGE, INSIDE THE RAIL. The base rule anchors it `right: 4px` against
     the BUTTON, and a rail row is 62px of intrinsic content inside a 40px
     column — flex items refuse to shrink below min-content — so the badge
     landed at x=49..66 against a 56px rail and `overflow: hidden` sliced it
     down the middle. Collapsed, it belongs to the ICON, as a corner count.
     Pinned there is a real row to sit at the end of, so the base rule stands.
     margin-left is meaningless on an absolutely positioned box; it was doing
     nothing here and is gone. */
  .rail .topnav-badge { opacity: 1; }

  .rail .topnav-badge {
    right: auto;
    left: calc(50% + 4px);
    top: 4px;
  }

  /* ONE LANGUAGE IN ONE COLUMN. The rail inherited three different treatments
     from the horizontal bar it replaced: a white stamp for the mark, raised
     #2a3038 chips for the utilities, and transparent rows for the
     destinations — at 46px, 46px and 40px. Stacked in a 56px column that
     reads as clutter rather than hierarchy. Everything is a row now: same
     height, same radius, transparent until hovered.

     The mark keeps its identity in the ACCENT, not in a slab of --text. A
     near-white block was the single heaviest object on a dark rail and it was
     carrying the least information on the page. */
  .rail .topnav-mark {
    min-height: 40px;
    height: auto;
    background: transparent;
    color: var(--accent);
    box-shadow: none;
    font: 700 15px/1 var(--font-ui);
    letter-spacing: 0.02em;
  }
  .rail .topnav-mark:hover { background: var(--surface-3); }

  /* CENTRED WHILE COLLAPSED. Every other row is an icon in a fixed 21px box,
     so they line up down the rail whatever their label; the mark is bare text
     and left-aligning it put "TJ" a few pixels off that column, which reads as
     a misplaced element rather than a logo. It only takes the shared
     flex-start once there is a row to be the start of. */
  body:not(.rail-pinned) .rail .topnav-mark {
    justify-content: center;
    padding-inline: 0;
  }

  /* THE ACCOUNT, COLLAPSED. A 190px select cannot live in a 40px column, so
     the rail shows the account as a rule under the mark and nothing more —
     the switcher itself arrives with the labels. Not display:none: the select
     stays in the tree and stays tabbable, and its own focus is what opens the
     rail to reveal it. */
  body:not(.rail-pinned) .rail .account-switch {
    height: 0;
    min-height: 0;
    padding: 0;
    margin: 0;
    overflow: hidden;
  }

  .rail .topnav-icon-btn,
  .rail .topnav-more > summary {
    min-height: 40px;
    height: auto;
    background: transparent;
    color: var(--text-faint);
    box-shadow: none;
  }
  .rail .topnav-icon-btn:hover,
  .rail .topnav-more > summary:hover {
    background: var(--surface-3);
    color: var(--text);
  }

  /* The active destination has to read at a glance with no label on screen,
     so it gets an edge marker as well as the tint — colour alone is the one
     signal a collapsed rail cannot afford to lean on. */
  .rail .topnav-btn.is-active {
    position: relative;
  }
  .rail .topnav-btn.is-active::before {
    content: "";
    position: absolute;
    left: -8px;
    top: 50%;
    width: 3px;
    height: 20px;
    transform: translateY(-50%);
    border-radius: 0 2px 2px 0;
    background: var(--accent);
  }

  /* The overflow menu opens beside the rail, not under it.

     THE BASE RULE SETS THREE OFFSETS, AND ALL THREE HAVE TO GO. clay-v2's
     .topnav-menu is `position: absolute; right: 0; top: calc(100% + 8px);
     z-index: 2`, sized and placed against the top bar it hangs from. Switching
     to fixed re-anchors every one of them to the VIEWPORT:
       right: 0   -> the panel stretched from the rail to the window edge,
                     1216px at vw 1280.
       top: 100%  -> percentages resolve against viewport HEIGHT, so top became
                     908px on a 900px window: the menu opened below the bottom
                     edge. Worse, top and bottom together stretch a height:auto
                     box between them, so it also collapsed to 16px and every
                     item inside it was unreachable.
       z-index: 2 -> under the rail's own z-index.
     Reset all three explicitly rather than relying on the shorthand-free base
     changing; a future offset added there will need the same treatment. */
  .rail .topnav-menu {
    position: fixed;
    left: calc(var(--rail-w) + 8px);
    right: auto;
    top: auto;
    bottom: 12px;
    min-width: 220px;
    z-index: calc(var(--z-nav) + 1);
  }

  /* ==========================================================================
     THE BOARD. A wireframe tape across the top edge: hairline rule, mono
     figures, no fill. It is a reference instrument, not a card — the moment it
     carries a surface and a shadow it competes with the money below it, and
     the one thing a price strip must never do is look more important than the
     balance.

     Fixed, and offset by the rail rather than sitting over it, so the two
     pieces of desk furniture meet at a corner instead of overlapping.
     ========================================================================== */
  /* THE TOP BAR. The wordmark names the product, the tape runs between, and the
     feed states its own liveness at the right end. The bar took the fixed
     position the ticker used to hold, so the ticker itself becomes a plain flex
     child — its marquee animation and its edge-fade seams are untouched. */
  .app-bar {
    position: fixed;
    top: 0;
    left: var(--rail-w);
    right: 0;
    z-index: calc(var(--z-nav) - 1);
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 0 16px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-0);
  }
  .app-wordmark {
    flex: none;
    font: 600 12px/1 var(--font-ui);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text);
    white-space: nowrap;
  }
  /* The feed badge. Cyan is this design's colour for "live", used here and on
     the catalyst countdown and nowhere else. */
  .app-live {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font: 600 10px/1 var(--font-mono);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-faint);
    white-space: nowrap;
  }
  .app-live::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--text-faint);
  }
  .app-live[data-feed="live"] { color: var(--term-live); }
  .app-live[data-feed="live"]::before { background: var(--term-live); }
  .app-live[data-feed="stale"]::before { background: var(--warn); }

  .app-ticker {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    padding: 7px 0;
    background: var(--surface-0);
    overflow: hidden;
    /* CANCELS THE LANDING BREAKOUT. #appTicker also carries .lnd-ticker, which
       is `width: 100vw; margin-left: calc(50% - 50vw)` — the landing page's
       full-bleed trick. Inside a flex bar that negative margin dragged the tape
       42px back over the wordmark. Measured before: ticker x=179 against a
       wordmark ending at 221. */
    width: auto;
    margin-left: 0;
  }

  /* The seam at each end: the tape reads as passing THROUGH the window rather
     than starting and stopping at its edges. */
  .app-ticker::before,
  .app-ticker::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 2;
    width: 48px;
    pointer-events: none;
  }
  .app-ticker::before { left: 0; background: linear-gradient(90deg, var(--surface-0), transparent); }
  .app-ticker::after { right: 0; background: linear-gradient(270deg, var(--surface-0), transparent); }

  .app-ticker .lnd-ticker-item {
    gap: 8px;
    padding: 0 18px;
    border-right: 1px solid var(--line);
  }

  /* The root is the instrument; the name is the translation. Nobody should
     have to already know what M2K means to read the board. */
  .app-ticker .lnd-ticker-sym {
    color: var(--text);
    font: 700 11px/1 var(--font-mono);
    letter-spacing: 0.1em;
  }
  .app-ticker .lnd-ticker-name {
    color: var(--text-faint);
    font: 500 10.5px/1 var(--font-ui);
    letter-spacing: 0.02em;
  }
  .app-ticker .lnd-ticker-price {
    color: var(--text);
    font: 600 12px/1 var(--font-mono);
    font-variant-numeric: tabular-nums;
  }
  .app-ticker .lnd-ticker-delta {
    font: 600 10.5px/1 var(--font-mono);
    font-variant-numeric: tabular-nums;
  }

  /* THE PAGE MOVES OVER. Every pinned view starts after the gutter, and the
     shell's centring math works inside what is left. */
  .app-layout,
  .content { padding-left: var(--rail-w); }

  .view.is-active { left: var(--rail-w); }

  /* Fixed furniture that was centred on the VIEWPORT has to re-centre on the
     content column, or it sits half a rail to the left of the thing it
     belongs to. */
  .dash-ticker-dock { left: calc(50% + (var(--rail-w) / 2)); }

  /* The last fixed box still centred on the VIEWPORT rather than the content
     column, so it sat --rail-w/2 to the left of the thing it belongs to —
     28px collapsed, 105px pinned. It carries the UNDO for the one
     irreversible action in the app, so it is worth the line. */
  .capture-toast { left: var(--rail-w); }
}


/* Locked session: same contract the top bar had. */
body.auth-locked .rail { display: none !important; }

/* The board tape is desk furniture, and a visitor reading the pitch has no
   desk. It is a body level fixed strip rather than a child of any view, so
   hiding the landing's own marquee left this one still spanning the top of
   the page: the crawl the owner asked to be rid of was this, not the hero
   tape that went with the last change. Same condition as the rail, because
   they are the same idea: chrome for someone who is working, absent for
   someone who is deciding. */
body.auth-locked .app-ticker { display: none !important; }

/* ==========================================================================
   THE RACK, PART 1B: THE SEAM AND THE DRESSING.

   Two complaints from the first day with the spine, both earned.

   ONE, the overlap. The views learned to start after the gutter, but the
   desk's own fixed furniture never did: the terminal page, the maximised
   tape, both wall sizes and the wall command sheet all pin left: 0 in
   styles.css, so the rail sat on top of the wire timers. Same offset the
   views got, applied to every fixed box the desk owns. Selector-for-selector
   with styles.css so file order is the only tiebreak needed.

   TWO, the dressing. A 21px glyph in a 40px row with 8px padding sits at 18
   and change, two points shy of centre, which is exactly the kind of almost
   that reads as unfinished. Padding does the centring so the glyph never
   shifts when the rail expands and the labels arrive. The lit key drops the
   pressed-clay shadow, which turned to mud at 56px, for an accent tint that
   reads from the corner of an eye. Hairlines split mark, destinations and
   utilities so the spine has joints instead of a pile.
   ========================================================================== */
@media (min-width: 1025px) {
  #terminal.is-active { left: var(--rail-w); }
  #terminal:has(.bb-wall[data-size="max"]) .bb-tape { left: var(--rail-w); }
  .bb-wall[data-size="max"] { left: var(--rail-w); }
  /* half is a RIGHT-hand split now, not a full-width band, so it needs no
     rail gutter — and must not be given a left offset, which would stretch it
     across the desk. Its own rule sets left: auto for exactly that reason. */
  .wcmd { left: var(--rail-w); width: auto; }

  .rail .topnav-btn,
  .rail .topnav-icon-btn,
  .rail .topnav-more > summary {
    padding: 6px 4px;
  }

  .rail .topnav-btn.is-active {
    background: var(--accent-muted);
    box-shadow: none;
  }

  .rail .topnav-mark { margin-bottom: var(--space-2); }

  .rail .topnav-links {
    border-top: 1px solid var(--line);
    padding-top: var(--space-2);
  }

  .rail .topnav-actions {
    border-top: 1px solid var(--line);
    padding-top: var(--space-2);
  }
}

/* 2. THE TRADER SCORE PANEL STOPS ESCAPING ITS OWN CARD.

   .panel-trader-score is display:grid and declares grid-template-ROWS only.
   Nothing anywhere declares its columns, so it lays out in one IMPLICIT column
   from grid-auto-columns:auto, and an auto track's base size is its items'
   min-content, a value that is never clamped to the panel's content box.

   The density pass made that min-content bigger than the card: the subtitle is
   nowrap, so its min-content is the entire string. Measured at 1240px, the
   track pinned at 349.8px inside a 240.1px content box, so the head, the radar
   canvas and the score footer all rendered 349.8px wide and 110px of them sat
   outside the card. That is the border line crossing the content. The canvas
   then ratcheted it in, because it writes its bitmap from its own overflowed
   clientWidth on every redraw.

   minmax(0, 1fr) gives the track a floor of zero, so no child's min-content
   can inflate it again. The subtitle then drops to its own line and reads,
   instead of truncating to "Composite read on win quality, rec...". Both
   declarations or neither: white-space alone would wrap it into a sliver. */
#dashboard .panel-grid-analytics .panel-trader-score {
  grid-template-columns: minmax(0, 1fr);
}

#dashboard .panel-grid-analytics .panel-trader-score .panel-head > div > p {
  flex: 1 1 100%;
  white-space: normal;
}

/* 3. THE TAPES SLOW DOWN.

   Two strips, not one, and slowing either alone leaves the other fast: the
   board marquee (.lnd-ticker-track, 32s) and the Release Edge tape
   (.bb-tape-run, 42s). animation-duration only, never the shorthand, so the
   hover park and the reduced-motion stops both survive. Set on the class and
   never per element, because .bb-tape-run is two elements whose halves would
   tear at the seam if their durations disagreed. */
@media (prefers-reduced-motion: no-preference) {
  .lnd-ticker-track { animation-duration: 76s; }
  .bb-tape-run { animation-duration: 100s; }
}

/* 4. THE RAIL LOSES A LEFTOVER AND FINDS ITS ACCOUNT.

   .topnav-rule is still the OLD horizontal bar's divider: a 1px by 26px
   vertical tick. Stacked into a column rail it is a sliver hugging the left
   padding edge, spending itself plus two flex gaps saying nothing, which is
   the unexplained space under the mark. The rail draws its joints as hairlines
   on .topnav-links and .topnav-actions now, so the tick has no job left.

   The ACCOUNT kicker separately inherits an 11px bottom margin from the bare
   label rule, which .account-switch-label never resets, so the 4px column gap
   the rail intends is really 15px. */
@media (min-width: 1025px) {
  .rail .topnav-rule { display: none; }
  .rail .account-switch-label { margin: 0; }

  /* The account chip, painted by setupRailAccount() from the switcher's own
     selected option so the two can never disagree. The site hides that
     switcher below two accounts, which is right for a visitor and wrong for
     the owner: with one account the rail carried no account identity at all.
     Collapsed it is an initial in a tile; pinned, the name arrives on the same
     reveal as every other label. */
  .rail-account {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 40px;
    margin-bottom: var(--space-2);
    padding: 8px 9px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--text-soft);
    font: inherit;
    text-align: left;
    cursor: default;
    transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  }

  .rail-account.is-switchable { cursor: pointer; }
  .rail-account.is-switchable:hover { color: var(--text); background: var(--surface-3); }

  /* The tile sits in the same 21px optical column as the nav glyphs, so the
     account reads as the rail's first row rather than a stray badge. */
  .rail-account-mark {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border: 1px solid var(--line-strong);
    border-radius: 7px;
    background: var(--surface-inset);
    color: var(--text);
    font-size: 11px;
    font-weight: 600;
  }

  .rail-account-name {
    overflow: hidden;
    font-size: 12px;
    letter-spacing: .04em;
    text-transform: uppercase;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

@media (max-width: 1024px) {
  .rail-account { display: none; }
}


/* ==========================================================================
   THREE ADJUSTMENTS FROM USE.
   ========================================================================== */

@media (min-width: 1025px) {
  /* 2. THE MONITOR TAKES THE RIGHT MARGIN.

     The edge rail sat 25px off the window edge because it inherits the view's
     own horizontal padding, which is right for reading columns of text and
     wasteful for a video tile: those 25px are the difference between a legible
     stream and a thumbnail. Pulling the panel into that padding gives the
     picture the width without moving anything else on the dashboard, since
     only this one grid item is offset. Lands the panel 9px off the edge. */
  #dashboard .dash-edge-mini { margin-right: -16px; }

  /* CORRECTION, because -16px only worked at one window width. The view's
     gutter is not a constant: .view.is-active:not(#terminal) sets
     padding-inline: max(--space-6, (100% - --shell-w) / 2), and because the
     view is POSITION FIXED that 100% resolves against the viewport rather
     than the element. So the gutter is 24.8px at a 1240 window and 60px at
     1600, and a fixed negative margin closed it at exactly one size and left
     44px of dead margin at the other, which is precisely what "still not
     occupying the margin" looked like.

     Restating the same expression in viewport units tracks it at every width:
     the panel lands 9px off the edge whether the window is 1240 or 2560.
     Scoped to 1240 and up, where the rail actually sits in column 2; below
     that it spans the full width and a one sided bleed would read as a
     mistake. */
  @media (min-width: 1240px) {
    #dashboard .dash-edge-mini {
      margin-right: calc(9px - max(var(--space-6), (100vw - min(1480px, 96vw)) / 2));
    }
  }

  /* 3. THE ACCOUNT CHIP IS A DOOR, NOT A LABEL.

     Clicking it did nothing whenever there was one account, because there was
     nothing to switch to, and a native select cannot be opened by script
     anyway. It now goes where accounts are actually managed. */
  .rail-account { cursor: pointer; }
  .rail-account:hover { color: var(--text); background: var(--surface-3); }
}

/* ==========================================================================
   THE ACCOUNT PICKER.

   The chip used to send you to a panel on the dashboard, which is not
   choosing an account, it is being told where to go and do it yourself. It is
   a flyout now, riding the same contract the overflow menu already has in
   this rail: click invoked, closes on Escape with focus returned, closes on
   an outside click. That contract is why this is not the hover overlay that
   was rejected. Hover expansion was silent and unasked for and it covered
   what you were reading; a menu you opened is a menu you wanted.

   It is positioned from --rail-w, so it lands beside the rail identically at
   56px and at 84px, and escapes the rail's overflow:hidden by being fixed.
   ========================================================================== */
@media (min-width: 1025px) {
  .rail .rail-account-more { position: static; width: 100%; }

  .rail .rail-account-more > summary {
    list-style: none;
    cursor: pointer;
  }
  .rail .rail-account-more > summary::-webkit-details-marker { display: none; }

  .rail .rail-account-more .topnav-menu {
    top: 76px;
    bottom: auto;
    min-width: 220px;
  }

  .rail-account-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 40px;
    padding: 6px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-soft);
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
  }
  .rail-account-row:hover { color: var(--text); background: var(--surface-3); }
  .rail-account-row.is-active { color: var(--text); }
  .rail-account-row .rail-account-check { margin-left: auto; color: var(--accent); }
  .rail-account-row.is-add {
    margin-top: 4px;
    border-top: 1px solid var(--line);
    border-radius: 0 0 8px 8px;
    color: var(--accent);
  }

  /* The mark leans in while its menu is out. */
  .rail .rail-account-more[open] .rail-account-mark { transform: scale(1.12); }
  .rail .rail-account-mark { transition: transform 140ms var(--ease-out), border-color 140ms var(--ease-out); }
}

/* The switch itself: the new initial stamps in with an accent flash, which is
   the moment the owner asked to be able to see. Replayed by removing and
   re-adding the class, so a repeat switch to the same account still reads. */
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
  @keyframes railAcctPop {
    0%   { transform: scale(0.6); border-color: var(--accent); }
    70%  { transform: scale(1.08); border-color: var(--accent); }
    100% { transform: scale(1); }
  }
  .rail-account-mark.is-popping { animation: railAcctPop 240ms var(--ease-out); }

  @keyframes railMenuIn {
    from { opacity: 0; transform: translateX(-8px) scale(0.97); }
    to   { opacity: 1; transform: none; }
  }
  .rail .topnav-more[open] > .topnav-menu,
  .rail .rail-account-more[open] > .topnav-menu {
    transform-origin: left center;
    animation: railMenuIn 150ms var(--ease-out);
  }
}

/* ==========================================================================
   SESSION INTELLIGENCE — DEDICATED, ANSWER-FIRST WORKSPACE

   One route, four focused tabs, one conclusion and one visualization per tab.
   Surfaces stay quiet; green and rose are reserved for signed performance.
   ========================================================================== */

#dashboard .panel-grid-analytics > .panel-span-6 {
  grid-column: span 6;
}

#session-intelligence [hidden],
#topstepTimeZoneConfirm[hidden],
#sessionTradeDrawer[hidden],
#dashUnjournalled[hidden] {
  display: none;
}

/* Dashboard keeps only a quiet route into the dedicated analysis page. */
.dash-session-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: transparent;
  color: var(--text-soft);
  font: 650 12px/1 var(--font-ui);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 4px;
  cursor: pointer;
}

.dash-session-link:hover {
  color: var(--text);
  text-decoration-color: currentColor;
}

.dash-session-link:focus-visible {
  outline: none;
  border-radius: 4px;
  box-shadow: var(--focus-ring);
}

/* Dedicated page header --------------------------------------------------- */
.session-page {
  width: 100%;
  min-width: 0;
  max-width: 1320px;
  margin-inline: auto;
  overflow: visible;
}

/* Desktop views are fixed beneath the rail and therefore own their scrolling.
   Keep every report row intrinsic so richer Topstep content increases the
   scroller's scrollHeight instead of being compressed into the viewport. */
@media (min-width: 900px) {
  #session-intelligence.is-active {
    align-content: start;
    grid-template-rows: max-content max-content max-content;
    grid-auto-rows: auto;
    min-height: 0;
    padding-bottom: max(var(--space-8), 48px);
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    scrollbar-gutter: stable;
    -webkit-overflow-scrolling: touch;
  }

  /* THE SCROLLER IS THE WHOLE VIEW, not the reading column.
     .session-page caps itself at 1320px and centres with auto margins, and
     because that same element IS the fixed scroller, everything outside
     1320px was not a scroll surface at all: at a 1999px window that is 312px
     of dead margin down each side, 624px of screen where the wheel reached
     BODY, found nothing scrollable, and did nothing. On a wide monitor the
     pointer sits there most of the time, which is why the page read as
     unscrollable while scrollTop moved perfectly from script.

     The box goes full bleed; the COLUMN is centred by padding instead. Two
     traps are answered here. The selector names the class twice to reach
     (1,2,0), because styles.css carries .view.is-active:not(#terminal) at
     that weight (:not() takes its argument's weight) and a bare id plus
     class loses to it. And the padding is written in viewport units because
     this element is position FIXED, where a percentage resolves against the
     viewport rather than the element, so only (100vw - rail) actually
     centres. Kept as a separate rule so the block above stays byte identical
     to the contract its own check pins. */
  #session-intelligence.session-page.is-active {
    width: auto;
    max-width: none;
    margin-inline: 0;
    /* Full bleed: the almanac is a dashboard, not a reading column. */
    padding-inline: max(var(--space-6), 20px);
  }
}

/* The dashboard contract: at reference width the page IS the viewport.
   The view stops scrolling; the matrix well, the side column and each
   margin pane own any overflow instead, so nothing is ever unreachable. */
@media (min-width: 1240px) {
  #session-intelligence.session-page.is-active {
    overflow-y: hidden;
    grid-template-rows: max-content minmax(0, 1fr) max-content max-content;
    row-gap: 12px;
    padding-top: 8px;
    padding-bottom: 14px;
  }

  #session-intelligence .session-page-head {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 7px 24px;
    padding: 2px 4px 8px;
  }

  #session-intelligence .session-display {
    font-size: 27px;
  }

  #session-intelligence .session-metabar {
    flex-wrap: nowrap;
    font-size: 10.5px;
  }

  /* The global 48px touch target is a phone rule; on the dashboard these two
     selects are inline text and must not set the band's height. */
  #session-intelligence .session-meta-select {
    min-height: 0;
    height: auto;
    line-height: 1.3;
  }

  /* The value speaks for itself at dashboard width; the label comes back in
     the stacked layout below the breakpoint. */
  #session-intelligence .session-meta-item[data-meta="range"] > .session-meta-label,
  #session-intelligence .session-meta-item[data-meta="source"] > .session-meta-label,
  #session-intelligence .session-meta-item[data-meta="count"] > .session-meta-label {
    display: none;
  }

  #session-intelligence .session-almanac-layout {
    min-height: 0;
  }

  #session-intelligence .session-almanac-visual {
    min-height: 0;
    grid-template-rows: auto minmax(0, 1fr) auto;
  }

  #session-intelligence .session-almanac-scroll {
    overflow: auto;
    min-height: 0;
  }

  #session-intelligence .session-almanac-side {
    min-height: 0;
    overflow-y: auto;
  }

  /* The chart is the only elastic thing in the side column: it takes the
     slack so the column itself never needs a scrollbar. */
  #session-intelligence .session-almanac-side > .session-cf-pane {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }

  #session-intelligence .session-almanac-side > .session-tilt-pane {
    flex: 0 0 auto;
  }

  #session-intelligence .session-cf-chart {
    flex: 1 1 auto;
    min-height: 96px;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  #session-intelligence .session-cf-chart svg {
    height: 100%;
    min-height: 0;
  }

  #session-intelligence .session-cf-chart .session-empty-copy {
    min-height: 0;
  }

  /* The skipped cells wrap in full rather than scrolling; the chart above
     is the elastic row and gives back whatever they need. */
  #session-intelligence .session-cf-ledger {
    max-height: none;
    overflow: visible;
    flex: none;
  }

  #session-intelligence .session-duration-band {
    gap: 6px 12px;
  }

  #session-intelligence .session-strip {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    height: 262px;
  }

  /* Below a wide desktop the matrix needs the pixels more than the side
     column does: hiding a traded hour to save width is not an option. */
  #session-intelligence .session-almanac-layout {
    grid-template-columns: minmax(0, 1fr) minmax(340px, 420px);
  }
}

@media (min-width: 1560px) {
  #session-intelligence .session-almanac-layout {
    grid-template-columns: minmax(0, 1fr) minmax(400px, 440px);
  }
}

/* Laptop tier: the same dashboard, one size down, so 1440x800 still fits. */
@media (min-width: 1240px) and (max-width: 1679px) {
  #session-intelligence .session-page-head {
    gap: 5px 18px;
    padding-bottom: 6px;
  }

  #session-intelligence .session-display {
    font-size: 22px;
  }

  #session-intelligence .session-metabar {
    font-size: 10px;
  }

  #session-intelligence .session-meta-item {
    padding-inline: 8px;
  }

  #session-intelligence .session-meta-item[data-meta="source"] > strong {
    max-width: 16ch;
  }
}

/* Below the dashboard breakpoint the band unstacks into readable rows. */
@media (max-width: 1239px) {
  .session-page-head {
    grid-template-columns: minmax(0, 1fr);
  }

  .session-page-head > .session-page-kicker,
  .session-page-head > .session-data-flag,
  .session-page-head > .session-display,
  .session-page-head > .session-metabar,
  .session-page-head > .session-now-strip {
    grid-area: auto;
  }

  .session-data-flag,
  .session-metabar,
  .session-now-strip {
    grid-column: 1;
    justify-self: start;
    justify-content: flex-start;
  }

  .session-display {
    overflow: visible;
    white-space: normal;
    text-wrap: balance;
  }

  .session-metabar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    width: 100%;
    font-size: 11px;
    border-block: 1px solid var(--line);
  }

  .session-meta-item {
    display: grid;
    align-content: center;
    gap: 5px;
    min-height: 52px;
    padding: 8px 12px;
    border-left: 0;
    border-right: 1px solid var(--line);
  }

  .session-metabar > .session-meta-item:first-child {
    padding-left: 12px;
  }

  .session-metabar > .session-meta-item:last-child {
    padding-right: 12px;
    border-right: 0;
  }

  .session-meta-item > strong,
  .session-meta-select {
    max-width: 100%;
    font-size: 11.5px;
  }
}

/* The header band: kicker, a two-tone display line, one inline mono meta
   row, and the live clock. Three tight rows, no boxed cards. */
/* Explicit placement: the caveat flag is optional, and auto-placement would
   let its absence pull the display line up beside the kicker. */
.session-page-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 20px;
  padding: clamp(10px, 1.6vw, 16px) 4px 12px;
  border-bottom: 1px solid var(--line);
}

.session-page-head > .session-page-kicker {
  grid-area: 1 / 1;
}

.session-page-head > .session-data-flag {
  grid-area: 1 / 2;
}

.session-page-head > .session-display {
  grid-area: 2 / 1;
}

.session-page-head > .session-metabar {
  grid-area: 2 / 2;
}

.session-page-head > .session-now-strip {
  grid-area: 3 / 2;
}

/* An honest caveat, never a "sample data" sticker: hidden when the report
   carries none, and every string below is a counted figure. */
.session-data-flag {
  justify-self: end;
  margin: 0;
  padding: 3px 8px;
  border: 1px solid var(--warn);
  border-radius: 999px;
  background: var(--warn-soft);
  color: var(--warn);
  font: 700 11px/1.2 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.session-data-flag[hidden] {
  display: none;
}

.session-display {
  min-width: 0;
  margin: 0;
  overflow: hidden;
  font: 450 clamp(20px, 2.2vw, 30px)/1.06 var(--font-ui);
  letter-spacing: -0.03em;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.session-display-a {
  color: var(--text-soft);
}

.session-display-b {
  color: var(--text);
  font-weight: 720;
}

.session-display-b::before {
  content: " ";
}

.session-metabar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-self: end;
  min-width: 0;
  font: 600 11px/1.3 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

.session-meta-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  padding: 1px 11px;
  border-left: 1px solid var(--line);
}

.session-metabar > .session-meta-item:first-child {
  padding-left: 0;
  border-left: 0;
}

.session-metabar > .session-meta-item:last-child {
  padding-right: 0;
}

.session-meta-label {
  flex: none;
  color: var(--text-soft);
  font-weight: 700;
}

.session-meta-item > strong {
  overflow: hidden;
  max-width: 22ch;
  color: var(--text);
  font: inherit;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-meta-item > strong.is-positive {
  color: var(--pnl-pos);
}

.session-meta-item > strong.is-negative {
  color: var(--pnl-neg);
}

.session-confidence.is-reliable {
  color: var(--pnl-pos);
}

.session-confidence.is-developing {
  color: var(--accent);
}

.session-confidence.is-early {
  color: var(--text-soft);
}

/* A real select stripped of chrome: the native menu and the change event are
   untouched, only the box is gone. */
.session-meta-select {
  max-width: 17ch;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-weight: 700;
  letter-spacing: inherit;
  text-transform: uppercase;
  text-overflow: ellipsis;
  cursor: pointer;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
}

.session-meta-select:hover {
  color: var(--accent);
}

.session-meta-select:focus-visible {
  outline: none;
  box-shadow: 0 2px 0 var(--accent);
}

.session-page-kicker,
.session-visual-head > div > p,
.session-queue-kicker {
  margin: 0;
  color: var(--text-faint);
  font: 700 11px/1.2 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.session-context-field,
.session-context-readout {
  display: grid;
  align-content: center;
  gap: 6px;
  min-width: 0;
  min-height: 62px;
  margin: 0;
  padding: 9px 14px;
  border-right: 1px solid var(--line);
}

.session-context-field > span,
.session-context-readout > span {
  color: var(--text-faint);
  font: 700 11px/1.1 var(--font-mono);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.session-context-field select {
  width: 100%;
  min-width: 0;
  min-height: 30px;
  padding: 0 24px 0 0;
  border: 0;
  border-radius: 0;
  background-color: transparent;
  color: var(--text);
  font: 650 12px/1.2 var(--font-ui);
  box-shadow: none;
}

.session-context-field select:focus-visible {
  outline: none;
  box-shadow: 0 2px 0 var(--accent);
}

.session-context-readout strong {
  overflow: hidden;
  color: var(--text);
  font: 650 12px/1.25 var(--font-ui);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tabs -------------------------------------------------------------------- */

/* A single report surface. Everything inside is separated by rhythm and
   hairlines, never by a wall of nested cards. */

/* No more than three readings. They share one band instead of becoming cards. */.session-score-pnl.is-positive,
.session-score-row.is-positive .session-score-pnl,
.session-duration-band.is-positive .session-duration-band-head > span {
  color: var(--pnl-pos);
}.session-score-pnl.is-negative,
.session-score-row.is-negative .session-score-pnl,
.session-duration-band.is-negative .session-duration-band-head > span {
  color: var(--pnl-neg);
}

/* Shared visualization frame --------------------------------------------- */
.session-visual {
  min-width: 0;
  margin-top: clamp(26px, 4vw, 44px);
}

.session-visual-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px 24px;
  margin-bottom: 18px;
}

.session-visual-head h4 {
  margin: 6px 0 0;
  color: var(--text);
  font: 700 16px/1.25 var(--font-ui);
  letter-spacing: -0.015em;
}

.session-visual-head > p {
  margin: 0;
  color: var(--text-soft);
  font: 500 11.5px/1.4 var(--font-ui);
}

.session-visual-note,
.session-honesty-note {
  margin: 14px 0 0;
  padding-left: 12px;
  border-left: 2px solid var(--line-strong);
  color: var(--text-soft);
  font: 500 11.5px/1.5 var(--font-ui);
}

.session-chart-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px 14px;
  color: var(--text-faint);
  font: 650 11px/1 var(--font-mono);
}

.session-chart-legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.session-chart-legend span::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--line-strong);
}

.session-chart-legend .is-positive::before {
  background: var(--pnl-pos);
}

.session-chart-legend .is-negative::before {
  background: var(--pnl-neg);
}

.session-empty-copy {
  margin: 0;
  padding: 34px 16px;
  color: var(--text-faint);
  font: 500 12px/1.5 var(--font-ui);
  text-align: center;
}

/* Sessions: one horizontal comparison ----------------------------------- */
.session-scorecard {
  display: grid;
  min-width: 0;
  border-top: 1px solid var(--line);
}

.session-score-row {
  display: grid;
  grid-template-columns: minmax(150px, 0.72fr) minmax(180px, 1.6fr) minmax(155px, auto);
  grid-template-areas: "score-main score-track score-meta";
  align-items: center;
  gap: 14px 24px;
  min-width: 0;
  padding: 17px 0;
  border-bottom: 1px solid var(--line);
}

.session-score-main {
  grid-area: score-main;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}

.session-score-name {
  overflow: hidden;
  color: var(--text);
  font: 700 13px/1.2 var(--font-ui);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-score-pnl,
.session-score-value {
  color: var(--text);
  font: 700 12px/1 var(--font-mono);
  white-space: nowrap;
}

.session-score-meta {
  grid-area: score-meta;
  margin: 0;
  color: var(--text-faint);
  font: 600 11px/1.35 var(--font-mono);
  text-align: right;
}

.session-score-track {
  grid-area: score-track;
  display: block;
  height: 9px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.session-score-fill {
  display: block;
  width: calc(var(--session-score-size, 0) * 100%);
  min-width: 2px;
  height: 100%;
  border-radius: inherit;
  background: var(--line-strong);
}

.session-score-row.is-positive .session-score-fill {
  background: var(--pnl-pos);
}

.session-score-row.is-negative .session-score-fill {
  background: var(--pnl-neg);
}

.session-score-row.is-early .session-score-fill,
.session-score-row.is-empty .session-score-fill {
  background: var(--line-strong);
}

/* Topstep execution quality: one compact evidence read below the session
   comparison, not another chart or another wall of metric cards. */
.session-topstep-execution {
  display: grid;
  gap: 15px;
  min-width: 0;
  margin-top: clamp(24px, 4vw, 38px);
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.session-topstep-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px 24px;
}

.session-topstep-head p {
  margin: 0;
  color: var(--text-faint);
  font: 700 11px/1.2 var(--font-mono);
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.session-topstep-head h4 {
  margin: 6px 0 0;
  color: var(--text);
  font: 700 16px/1.25 var(--font-ui);
  letter-spacing: -0.015em;
}

.session-topstep-summary {
  max-width: 78ch;
  margin: 0;
  color: var(--text-soft);
  font: 500 12.5px/1.55 var(--font-ui);
}

.session-topstep-signals {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  min-width: 0;
  margin: 0;
  border-block: 1px solid var(--line);
}

.session-topstep-signals > div {
  display: grid;
  gap: 7px;
  min-width: 0;
  padding: 14px 18px;
  border-right: 1px solid var(--line);
}

.session-topstep-signals > div:first-child {
  padding-left: 0;
}

.session-topstep-signals > div:last-child {
  border-right: 0;
}

.session-topstep-signals dt {
  color: var(--text-faint);
  font: 700 11px/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.session-topstep-signals dd {
  min-width: 0;
  margin: 0;
  color: var(--text);
  font: 650 12.5px/1.4 var(--font-ui);
  overflow-wrap: anywhere;
}

.session-topstep-note {
  margin: 0;
  color: var(--text-faint);
  font: 500 11px/1.5 var(--font-ui);
}

/* ── The almanac pane: the instrument itself ──────────────────────────────
   A fixed-height box in the dashboard's main row. The matrix well is the
   only thing that ever scrolls, and only sideways when the columns cannot
   fit. Cell washes are P&L tokens behind a ::before layer whose opacity is
   the cell's --almanac-heat, so both themes keep their own palette and no
   colour is ever pinned per element. */
.session-almanac-visual {
  margin-top: 0;
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  row-gap: 10px;
  min-height: 0;
}

.almanac-pane-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  min-height: 40px;
}

.almanac-pane-title h4 {
  margin: 0;
  font: 700 14px/1.15 var(--font-ui);
  color: var(--text);
}

.almanac-pane-title h4 span {
  font-weight: 400;
  color: var(--text-soft);
}

.almanac-pane-basis {
  margin: 3px 0 0;
  color: var(--text-faint);
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.almanac-pane-edge {
  margin: 0;
  text-align: right;
  color: var(--text-soft);
  font: 600 10.5px/1.3 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

.session-almanac-scroll {
  overflow: auto;
  min-height: 0;
  padding-bottom: 2px;
}

.session-almanac-matrix {
  display: grid;
  grid-template-columns:
    34px minmax(58px, 0.8fr) repeat(10, minmax(56px, 1fr)) minmax(124px, 158px);
  grid-template-rows: 20px repeat(var(--alm-rows, 5), minmax(44px, 1fr)) 32px;
  gap: 5px 4px;
  min-width: 830px;
  min-height: 100%;
  align-items: stretch;
}

.almanac-corner {
  min-height: 0;
}

.almanac-col-label {
  align-self: end;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding-bottom: 3px;
  color: var(--text-soft);
  font: 600 10px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.almanac-col-margin {
  justify-content: flex-end;
  color: var(--accent);
  letter-spacing: 0.14em;
}

.almanac-col-chip {
  padding: 2px 4px;
  border-radius: 3px;
  font: 700 8.5px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.almanac-col-chip.is-vein {
  background: var(--pnl-pos-soft);
  color: var(--pnl-pos);
  border: 1px solid var(--pnl-pos-line);
}

.almanac-col-chip.is-scar {
  background: var(--pnl-neg-soft);
  color: var(--pnl-neg);
  border: 1px solid var(--pnl-neg-line);
}

.almanac-row-label {
  display: flex;
  align-items: center;
  color: var(--text-soft);
  font: 700 10px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.almanac-cell {
  position: relative;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 3px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-inset);
  color: var(--text);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
}

.almanac-cell::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
}

.almanac-cell.is-positive::before {
  background: var(--pnl-pos);
  opacity: calc(0.08 + var(--almanac-heat, 0) * 0.24);
}

.almanac-cell.is-negative::before {
  background: var(--pnl-neg);
  opacity: calc(0.08 + var(--almanac-heat, 0) * 0.24);
}

.almanac-cell.is-positive {
  border-color: var(--pnl-pos-line);
}

.almanac-cell.is-negative {
  border-color: var(--pnl-neg-line);
}

/* The proven vein and the deepest scar wear the brighter border. */
.almanac-cell.is-vein {
  border-color: var(--pnl-pos);
}

.almanac-cell.is-scar {
  border-color: var(--pnl-neg);
}

.almanac-cell > strong {
  position: relative;
  font: 700 17px/1 var(--font-mono);
}

.almanac-cell.is-positive > strong {
  color: var(--pnl-pos);
}

.almanac-cell.is-negative > strong {
  color: var(--pnl-neg);
}

.almanac-cell > span {
  position: relative;
  color: var(--text-faint);
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.05em;
}

/* Withheld, never estimated: hatched, the count then the word. */
.almanac-cell.is-withheld {
  background:
    repeating-linear-gradient(-45deg, transparent 0 5px, var(--chart-track) 5px 6px)
    var(--surface-inset);
}

.almanac-cell.is-withheld > strong {
  color: var(--text-soft);
  font-size: 13px;
}

.almanac-cell.is-withheld > span {
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Empty: a plain dark cell with a hairline, no text. */
.almanac-cell.is-empty {
  background: var(--surface-0);
  border-color: var(--line);
}

button.almanac-cell {
  cursor: pointer;
  text-align: left;
  transition: border-color 140ms ease, transform 140ms ease;
}

button.almanac-cell:hover,
button.almanac-cell[aria-selected="true"] {
  border-color: var(--line-accent);
  transform: translateY(-1px);
}

button.almanac-cell:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The clock's own cell: accent marker top left, tilt owns top right. */
.almanac-cell.is-now {
  box-shadow: inset 0 0 0 1px var(--line-accent);
}

.almanac-cell.is-now::after {
  content: "";
  position: absolute;
  top: 6px;
  left: 6px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--chart-glow);
}

@media (prefers-reduced-motion: no-preference) {
  .almanac-cell.is-now::after {
    animation: almanacNowPulse 2.2s ease-in-out infinite;
  }
}

@keyframes almanacNowPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.almanac-tilt {
  position: absolute;
  top: 5px;
  right: 5px;
  padding: 2px 4px;
  border-radius: 3px;
  background: var(--warn-soft);
  color: var(--warn);
  font: 700 9px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.02em;
}

/* ── The almanac margin: what each weekday amounts to ─────────────────── */
.almanac-margin {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  padding: 6px 8px 6px 12px;
  border-left: 1px solid var(--line-strong);
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

.almanac-margin.is-empty {
  border-left-color: var(--line);
}

.almanac-margin-top {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.almanac-margin-top strong {
  font: 700 15px/1 var(--font-mono);
  color: var(--text);
}

.almanac-margin.is-positive .almanac-margin-top strong {
  color: var(--pnl-pos);
}

.almanac-margin.is-negative .almanac-margin-top strong {
  color: var(--pnl-neg);
}

.almanac-margin.is-withheld .almanac-margin-top strong {
  color: var(--text-soft);
  font-size: 13px;
}

.almanac-margin-top em {
  font: 500 10px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}

.almanac-margin-tag {
  align-self: flex-start;
  padding: 2px 5px;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--text-soft);
  font: 700 8.5px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.almanac-margin-tag.is-today {
  color: var(--accent);
  border-color: var(--line-accent);
}

.almanac-margin-tag.is-best {
  color: var(--pnl-pos);
  border-color: var(--pnl-pos-line);
  background: var(--pnl-pos-soft);
}

.almanac-margin-tag.is-worst {
  color: var(--pnl-neg);
  border-color: var(--pnl-neg-line);
  background: var(--pnl-neg-soft);
}

.almanac-margin-tag.is-wh {
  color: var(--text-faint);
}

.almanac-margin-bar {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--chart-track);
  overflow: hidden;
}

.almanac-margin-bar i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--text-soft);
}

.almanac-margin.is-positive .almanac-margin-bar i {
  background: var(--pnl-pos);
}

.almanac-margin.is-negative .almanac-margin-bar i {
  background: var(--pnl-neg);
}

.almanac-margin-say {
  color: var(--text-faint);
  font: 400 italic 10px/1.25 var(--font-mono);
  letter-spacing: 0.02em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Footer net row ───────────────────────────────────────────────────── */
.almanac-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-top: 1px solid var(--line-strong);
  color: var(--text-soft);
  font: 600 12px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.almanac-foot.is-positive {
  color: var(--pnl-pos);
}

.almanac-foot.is-negative {
  color: var(--pnl-neg);
}

.almanac-foot small {
  color: var(--text-faint);
  font: 500 9.5px/1 var(--font-mono);
  letter-spacing: 0.05em;
}

.almanac-foot.is-withheld {
  color: var(--text-faint);
  background:
    repeating-linear-gradient(-45deg, transparent 0 5px, var(--chart-track) 5px 6px);
}

.almanac-foot-label {
  align-self: center;
  border-top: 1px solid var(--line-strong);
  padding-top: 8px;
}

.almanac-foot-total {
  justify-content: flex-end;
  padding-left: 12px;
  border-left: 1px solid var(--line-strong);
  color: var(--text);
  font-weight: 700;
  font-size: 13px;
}

/* ── Legend strip ─────────────────────────────────────────────────────── */
.almanac-legend {
  display: flex;
  align-items: center;
  gap: 18px;
  min-height: 34px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  color: var(--text-faint);
  font: 500 10px/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.almanac-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.almanac-legend-note {
  margin-left: auto;
  text-align: right;
  color: var(--text-soft);
}

.almanac-sw {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  border: 1px solid var(--line);
  flex: none;
}

.almanac-sw.is-neg-2 {
  background: var(--pnl-neg);
  border-color: var(--pnl-neg-line);
}

.almanac-sw.is-neg-1 {
  background: var(--pnl-neg-soft);
  border-color: var(--pnl-neg-line);
}

.almanac-sw.is-pos-1 {
  background: var(--pnl-pos-soft);
  border-color: var(--pnl-pos-line);
}

.almanac-sw.is-pos-2 {
  background: var(--pnl-pos);
  border-color: var(--pnl-pos-line);
}

.almanac-sw.is-wh {
  background:
    repeating-linear-gradient(-45deg, transparent 0 4px, var(--chart-axis) 4px 5px)
    var(--surface-inset);
}

.almanac-sw.is-tilt {
  border: 0;
  background: none;
  width: auto;
  color: var(--warn);
  font: 700 10px/1 var(--font-mono);
  font-style: normal;
}

/* Laptop heights: the same instrument, one size down. */
@media (max-height: 900px) {
  .session-almanac-visual {
    padding: 8px 12px;
    row-gap: 8px;
  }

  .almanac-pane-head {
    min-height: 32px;
  }

  .almanac-pane-title h4 {
    font-size: 13px;
  }

  .almanac-pane-basis {
    display: none;
  }

  .session-almanac-matrix {
    grid-template-rows: 18px repeat(var(--alm-rows, 5), minmax(38px, 1fr)) 28px;
    gap: 4px;
  }

  .almanac-cell {
    padding: 5px 7px;
    gap: 2px;
  }

  .almanac-cell > strong {
    font-size: 14px;
  }

  .almanac-cell > span {
    font-size: 9.5px;
  }

  .almanac-margin {
    gap: 3px;
    padding-block: 4px;
  }

  .almanac-margin-top strong {
    font-size: 13px;
  }

  .almanac-margin-tag,
  .almanac-margin-say {
    display: none;
  }

  .almanac-legend {
    min-height: 26px;
    padding-top: 6px;
    gap: 12px;
    font-size: 9.5px;
  }
}

/* Phone: the 11px microtype floor wins over every density tier above. */
@media (max-width: 720px) {
  .almanac-cell > span,
  .almanac-col-label,
  .almanac-row-label,
  .almanac-col-chip,
  .almanac-margin-top em,
  .almanac-margin-tag,
  .almanac-margin-say,
  .almanac-foot small,
  .almanac-tilt,
  .almanac-legend,
  .almanac-sw.is-tilt,
  .almanac-pane-basis,
  .almanac-pane-edge {
    font-size: 11px;
  }
}

/* ── One-surface layout: matrix left, engine and radar right ─────────────── */
.session-now-strip {
  display: flex;
  grid-column: 2;
  justify-content: flex-end;
  justify-self: end;
}

.session-now-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--surface-1);
  color: var(--text-soft);
  font: 600 11px/1.15 var(--font-mono);
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
}

.session-now-chip::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-faint);
  flex: none;
}

.session-now-chip.is-positive {
  border-color: var(--pnl-pos-line);
  color: var(--pnl-pos);
}

.session-now-chip.is-positive::before {
  background: var(--pnl-pos);
  box-shadow: 0 0 8px var(--chart-pos-glow);
}

.session-now-chip.is-negative {
  border-color: var(--pnl-neg-line);
  color: var(--pnl-neg);
}

.session-now-chip.is-negative::before {
  background: var(--pnl-neg);
}

@media (prefers-reduced-motion: no-preference) {
  .session-now-chip.is-positive::before,
  .session-now-chip.is-negative::before {
    animation: almanacNowPulse 2.2s ease-in-out infinite;
  }
}

.session-almanac-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  align-items: stretch;
}

@media (min-width: 1240px) {
  .session-almanac-layout {
    grid-template-columns: minmax(0, 1fr) minmax(360px, 440px);
  }
}

.session-almanac-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.session-side-pane {
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  display: grid;
  gap: 12px;
  align-content: start;
}

.session-cf-pane {
  border-color: var(--line-accent);
}

.session-side-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.session-side-head p {
  margin: 0 0 2px;
  color: var(--text-soft);
  font: 700 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.session-side-head h4 {
  margin: 0;
  color: var(--text);
  font: 600 14px/1.3 var(--font-ui);
}

.session-cf-gapchip {
  color: var(--text-faint);
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.session-cf-gapchip strong {
  color: var(--pnl-pos);
  font: 700 13px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.session-cf-gapchip strong.is-negative {
  color: var(--pnl-neg);
}

.session-cf-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
}

.session-cf-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  color: var(--text-soft);
  font: 500 11px/1.4 var(--font-ui);
}

.session-cf-stat strong {
  font: 700 13px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.session-cf-stat.is-ghost-stat strong {
  color: var(--pnl-pos);
}

.session-cf-stat small {
  color: var(--text-faint);
  font: 500 10.5px/1.4 var(--font-ui);
}

.session-cf-stat strong.is-negative {
  color: var(--pnl-neg);
}

.session-side-note {
  margin: 0;
  color: var(--text-faint);
  font: 500 11px/1.5 var(--font-ui);
}

.session-tilt-mark-note {
  color: var(--text-faint);
  font: 600 9.5px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.session-tilt-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 8px;
}

.session-tilt-tile {
  background: var(--surface-inset);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 9px 11px;
  display: grid;
  gap: 5px;
}

.session-tilt-tile > span {
  color: var(--text-soft);
  font: 600 9.5px/1.25 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.session-tilt-tile strong {
  color: var(--text);
  font: 700 17px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.session-tilt-tile strong.is-warn {
  color: var(--warn);
}

.session-tilt-tile strong.is-positive {
  color: var(--pnl-pos);
}

.session-tilt-tile strong.is-negative {
  color: var(--pnl-neg);
}

/* The decode line and its counted facts, side by side. */
.session-tilt-read {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.session-tilt-facts {
  flex: none;
  display: grid;
  gap: 2px;
  margin: 0;
  min-width: 0;
}

.session-tilt-facts > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.session-tilt-facts dt {
  color: var(--text-faint);
  font: 500 9.5px/1.3 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.session-tilt-facts dd {
  margin: 0;
  color: var(--text-soft);
  font: 700 9.5px/1.3 var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The status bar: provenance and the clock, once, at the foot. */
.session-status-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 4px 0;
  border-top: 1px solid var(--line);
  color: var(--text-faint);
  font: 500 10px/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

.session-status-left,
.session-status-right {
  overflow: hidden;
  min-width: 0;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-status-right {
  color: var(--text-soft);
  text-align: right;
}

.session-tilt-decode {
  margin: 0;
  padding-left: 10px;
  border-left: 2px solid var(--warn);
  color: var(--text);
  font: 500 12px/1.5 var(--font-ui);
}

.session-almanac-edgeline {
  margin: 0;
  color: var(--text-soft);
  font: 600 10.5px/1.5 var(--font-mono);
  letter-spacing: 0.05em;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.session-almanac-legendrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

/* The margin panes: the almanac keeps the ledgers in its margins. */

/* ── The counterfactual engine ───────────────────────────────────────────── */
.session-cf-chart {
  background: var(--surface-inset);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px;
}

.session-cf-chart svg {
  display: block;
  width: 100%;
  height: 240px;
}

.session-cf-chart .session-empty-copy {
  min-height: 170px;
  display: grid;
  place-items: center;
}

.session-cf-line {
  fill: none;
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

.session-cf-line.is-real {
  stroke: var(--chart-axis);
}

.session-cf-line.is-ghost {
  stroke: var(--pnl-pos);
}

.session-cf-gap-fill {
  fill: var(--pnl-pos-soft);
  stroke: none;
}

/* When any stretch of the hypothetical line dips below the real one, the
   ribbon stops claiming a color it cannot prove pointwise. */
.session-cf-gap-fill.is-mixed {
  fill: var(--chart-track);
}

.session-cf-zero {
  stroke: var(--chart-grid);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.session-cf-grid {
  stroke: var(--chart-grid);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.session-cf-tick,
.session-cf-xlabel {
  fill: var(--text-faint);
  font: 500 9px var(--font-mono);
  letter-spacing: 0.06em;
}

.session-cf-xlabel.is-end {
  text-anchor: end;
}

/* Where the two lines part company. */
.session-cf-skip {
  stroke: var(--warn);
  stroke-width: 1;
  stroke-dasharray: 3 4;
  vector-effect: non-scaling-stroke;
}

.session-cf-skiplabel {
  fill: var(--warn);
  font: 600 8.5px var(--font-mono);
  letter-spacing: 0.08em;
}

.session-cf-endlabel {
  text-anchor: end;
  font: 700 10px var(--font-mono);
}

.session-cf-endlabel.is-real {
  fill: var(--text);
}

.session-cf-endlabel.is-ghost {
  fill: var(--pnl-pos);
}

@media (prefers-reduced-motion: no-preference) {
  .session-cf-line {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    animation: sessionCfDraw 1.1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  }

  .session-cf-line.is-ghost {
    animation-delay: 0.45s;
  }

  .session-cf-gap-fill {
    opacity: 0;
    animation: sessionCfFade 0.7s ease 1.5s forwards;
  }
}

@keyframes sessionCfDraw {
  to { stroke-dashoffset: 0; }
}

@keyframes sessionCfFade {
  to { opacity: 1; }
}

.session-cf-legend .is-real::before {
  background: var(--chart-axis);
}

.session-cf-legend .is-ghost::before {
  background: var(--pnl-pos);
}

.session-cf-ledger {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.session-cf-ledger:empty {
  display: none;
}

.session-cf-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 5px 9px;
  border: 1px solid var(--pnl-neg-line);
  border-radius: 7px;
  background: var(--pnl-neg-soft);
  color: var(--pnl-neg);
  font: 600 10.5px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.session-cf-chip strong {
  color: var(--text);
  font-weight: 700;
}

/* ── The tilt radar ──────────────────────────────────────────────────────── */

/* ══ THE BOTTOM STRIP ═══════════════════════════════════════════════════════
   Four ledgers on one skeleton: a hairline head, a body that owns any
   overflow, a hairline foot. The HEIGHT is set once on the container, so no
   pane can push the page into a scrollbar no matter what the data does. */
.session-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 12px;
  align-items: stretch;
  min-width: 0;
}

.strip-pane {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 8px;
  min-width: 0;
  min-height: 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-1);
}

.strip-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

.strip-title {
  margin: 0;
  color: var(--text);
  font: 700 11px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.strip-caps {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

.strip-cap {
  overflow: hidden;
  color: var(--text-soft);
  font: 600 10px/1.2 var(--font-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The body is the ONLY scroll surface in this region. */
.strip-body {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
}

.strip-foot {
  margin: 0;
  padding-top: 6px;
  border-top: 1px solid var(--line);
  color: var(--text-faint);
  font: 600 9.5px/1.3 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.strip-empty {
  margin: 0;
  align-self: center;
  color: var(--text-faint);
  font: 500 11px/1.45 var(--font-ui);
}

/* Below a sample floor a figure is not printed small, it is not printed. */
.strip-withheld {
  color: var(--text-faint);
  font: 600 10px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── Hold time ───────────────────────────────────────────────────────────── */
.hold-medians {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  align-items: end;
  gap: 0 16px;
}

.hold-med {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.hold-med-end {
  justify-items: end;
  text-align: right;
}

.hold-med strong {
  color: var(--text);
  font: 700 22px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.hold-med strong.is-positive { color: var(--pnl-pos); }
.hold-med strong.is-negative { color: var(--pnl-neg); }

.hold-med span {
  color: var(--text-soft);
  font: 600 9.5px/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hold-bands {
  display: grid;
  align-content: start;
  gap: 3px;
  min-width: 0;
}

.hold-band {
  display: grid;
  grid-template-columns: minmax(0, 92px) minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  min-height: 22px;
  min-width: 0;
  padding: 2px 4px;
  border: 1px solid transparent;
  border-radius: 6px;
}

.hold-band.is-best {
  border-color: var(--pnl-pos-line);
  background: var(--pnl-pos-soft);
}

.hold-band-label {
  overflow: hidden;
  color: var(--text-soft);
  font: 600 10px/1.2 var(--font-mono);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hold-band.is-best .hold-band-label { color: var(--text); }

.hold-band-bar {
  display: block;
  height: 4px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.hold-band-bar::before {
  display: block;
  width: calc(var(--fill, 0) * 100%);
  min-width: 2px;
  height: 100%;
  border-radius: inherit;
  background: var(--line-strong);
  content: "";
}

.hold-band.is-positive .hold-band-bar::before { background: var(--pnl-pos); }
.hold-band.is-negative .hold-band-bar::before { background: var(--pnl-neg); }

.hold-band-val {
  color: var(--text);
  font: 700 11.5px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.hold-band.is-positive .hold-band-val { color: var(--pnl-pos); }
.hold-band.is-negative .hold-band-val { color: var(--pnl-neg); }

/* ── Sessions ledger ─────────────────────────────────────────────────────── */
.session-ledger {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
}

.ledger-row {
  display: grid;
  grid-template-columns: minmax(0, 66px) minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  min-width: 0;
}

.ledger-name {
  overflow: hidden;
  color: var(--text-soft);
  font: 600 10px/1.2 var(--font-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ledger-bar {
  display: block;
  height: 6px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.ledger-bar::before {
  display: block;
  width: calc(var(--fill, 0) * 100%);
  min-width: 2px;
  height: 100%;
  border-radius: inherit;
  background: var(--line-strong);
  content: "";
}

.ledger-row.is-positive .ledger-bar::before { background: var(--pnl-pos); }
.ledger-row.is-negative .ledger-bar::before { background: var(--pnl-neg); }

.ledger-val {
  min-width: 68px;
  color: var(--text);
  font: 700 12.5px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.ledger-row.is-positive .ledger-val { color: var(--pnl-pos); }
.ledger-row.is-negative .ledger-val { color: var(--pnl-neg); }

/* ── Who closed it ───────────────────────────────────────────────────────── */
.exit-bar {
  display: flex;
  height: 12px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.exit-seg {
  min-width: 2px;
  height: 100%;
}

.exit-seg.is-target { background: var(--pnl-pos); }
.exit-seg.is-manual { background: var(--warn); }
.exit-seg.is-stop   { background: var(--pnl-neg); }
.exit-seg.is-mixed  { background: var(--line-strong); }

.exit-shares {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}

.exit-share {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.exit-share strong {
  color: var(--text);
  font: 700 15px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.exit-share.is-target strong { color: var(--pnl-pos); }
.exit-share.is-manual strong { color: var(--warn); }
.exit-share.is-stop   strong { color: var(--pnl-neg); }
.exit-share.is-mixed  strong { color: var(--text-soft); }

.exit-share span {
  overflow: hidden;
  color: var(--text-soft);
  font: 600 9.5px/1.2 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.exit-cost {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-inset);
}

.exit-cost-label {
  color: var(--text-soft);
  font: 600 9.5px/1.25 var(--font-mono);
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.exit-cost-value {
  color: var(--text);
  font: 700 15px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.exit-cost-value.is-positive { color: var(--pnl-pos); }
.exit-cost-value.is-negative { color: var(--pnl-neg); }

/* ── Journal coverage ────────────────────────────────────────────────────── */
.cov-figures {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  min-width: 0;
}

.cov-figures strong {
  color: var(--text);
  font: 700 26px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.cov-of {
  color: var(--text-faint);
  font: 600 15px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.cov-pct {
  margin-left: auto;
  color: var(--text-soft);
  font: 700 15px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.cov-pct.is-positive { color: var(--pnl-pos); }
.cov-pct.is-warn     { color: var(--warn); }

.cov-streak { color: var(--accent); }

.cov-flag {
  padding: 1px 5px;
  border: 1px solid var(--warn);
  border-radius: 4px;
  color: var(--warn);
  font-size: 9px;
  line-height: 1.4;
  letter-spacing: 0.07em;
}

.cov-bar {
  height: 8px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.cov-bar > span {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}

.cov-rows {
  display: grid;
  gap: 4px;
  margin: 0;
  min-width: 0;
}

.cov-rows > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
  padding-bottom: 3px;
  border-bottom: 1px solid var(--line);
}

.cov-rows > div:last-child { border-bottom: 0; }

.cov-rows dt {
  color: var(--text-soft);
  font: 600 9.5px/1.3 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.cov-rows dd {
  overflow: hidden;
  margin: 0;
  color: var(--text);
  font: 700 10.5px/1.3 var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* Laptop: 1440x800 leaves about 710px of viewport after chrome. The strip
   gives back 40px and steps its three display figures down; only the hold
   band well loses rows, and it scrolls. */
@media (min-width: 1240px) and (max-height: 900px) {
  #session-intelligence .session-strip { height: 202px; }
  #session-intelligence .hold-band { min-height: 16px; }
  #session-intelligence .hold-med strong { line-height: 1; }
  #session-intelligence .strip-foot { font-size: 9px; }
  /* The status bar and the chart both give back pixels before anything
     inside the matrix has to shrink. */
  #session-intelligence .session-status-bar { padding-top: 4px; font-size: 9.5px; }
  #session-intelligence .session-cf-chart { min-height: 68px; }
  #session-intelligence .session-cf-ledger { gap: 4px; }
  #session-intelligence .session-cf-chip { padding: 3px 7px; }
  #session-intelligence .hold-med strong { font-size: 19px; }
  #session-intelligence .cov-figures strong { font-size: 21px; }
  #session-intelligence .exit-share strong { font-size: 13px; }
  #session-intelligence .session-ledger { gap: 2px; }
  #session-intelligence .ledger-row { min-height: 22px; }

  /* Every duration band the account actually traded has to fit: hiding a
     band would be data hiding, so the rows get tighter instead. */
  #session-intelligence .hold-bands { gap: 1px; }
  #session-intelligence .hold-band { min-height: 18px; padding-block: 0; }
  #session-intelligence .hold-medians { padding-bottom: 2px; }
  #session-intelligence .cov-rows { gap: 1px; }
  #session-intelligence .strip-pane { gap: 6px; padding-block: 8px; }
}

/* Microtype floor. Nothing under 11px once the strip stacks. */
@media (max-width: 640px) {.strip-cap,
.strip-foot,
.strip-withheld,
.hold-med span,
.hold-band-label,
.hold-band-val,
.ledger-name,
.exit-share span,
.exit-cost-label,
.cov-flag,
.cov-rows dt,
.cov-rows dd {
    font-size: 11px;
  }

  .hold-band {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 4px;
  }

  .hold-band-bar { grid-column: 1 / -1; }

  .exit-shares { flex-wrap: wrap; }
}

/* One details panel at a time, opened only after an almanac cell is selected. */
.session-trade-drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: calc(var(--z-modal) + 1);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  width: min(440px, 100vw);
  height: 100dvh;
  padding: 24px;
  border-left: 1px solid var(--line-strong);
  background: var(--surface-1);
  box-shadow: -24px 0 50px rgba(0, 0, 0, 0.35);
}

.session-trade-drawer > header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
}

.session-trade-drawer > header p {
  margin: 0;
  color: var(--text-faint);
  font: 700 11px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.session-trade-drawer > header h4 {
  margin: 7px 0 0;
  color: var(--text);
  font: 700 21px/1.15 var(--font-ui);
}

.session-trade-drawer > header button {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--text-soft);
  font: 400 23px/1 var(--font-ui);
  cursor: pointer;
}

#sessionTradeDrawerBody {
  min-width: 0;
  overflow-y: auto;
  padding-top: 12px;
}

.session-drawer-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas:
    "drawer-main drawer-pnl drawer-action"
    "drawer-meta drawer-pnl drawer-action";
  align-items: center;
  gap: 5px 14px;
  min-width: 0;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}

.session-drawer-main {
  grid-area: drawer-main;
  min-width: 0;
  color: var(--text);
  font: 700 12.5px/1.3 var(--font-ui);
}

.session-drawer-meta {
  grid-area: drawer-meta;
  min-width: 0;
  margin: 0;
  color: var(--text-faint);
  font: 600 11px/1.35 var(--font-mono);
}

.session-drawer-pnl {
  grid-area: drawer-pnl;
  color: var(--text);
  font: 700 12px/1 var(--font-mono);
  white-space: nowrap;
}

.session-drawer-pnl.is-positive {
  color: var(--pnl-pos);
}

.session-drawer-pnl.is-negative {
  color: var(--pnl-neg);
}

.session-drawer-action {
  grid-area: drawer-action;
  min-width: 44px;
  min-height: 44px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--text-soft);
  font: 650 11px/1 var(--font-ui);
  cursor: pointer;
}

.session-drawer-action:hover {
  border-color: var(--line-strong);
  color: var(--text);
}

.session-drawer-action:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.session-drawer-empty {
  margin: 0;
  padding: 32px 4px;
  color: var(--text-faint);
  font: 500 12px/1.5 var(--font-ui);
}

@media (max-width: 520px) {
  .session-trade-drawer {
    width: 100vw;
    padding: 20px 18px calc(20px + env(safe-area-inset-bottom, 0px));
    border-left: 0;
  }

  .session-trade-drawer > header h4 {
    max-width: 235px;
    font-size: 20px;
  }

  .session-drawer-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "drawer-main drawer-main"
      "drawer-meta drawer-meta"
      "drawer-pnl drawer-action";
    gap: 8px 12px;
    padding: 17px 0;
  }

  .session-drawer-main {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }

  .session-drawer-meta {
    overflow-wrap: anywhere;
  }

  .session-drawer-pnl {
    line-height: 1.35;
    white-space: normal;
  }

  .session-drawer-action {
    min-width: 108px;
  }

  body:has(#sessionTradeDrawer:not([hidden])) .tabbar,
  body:has(#sessionTradeDrawer:not([hidden])) .tabbar-fab,
  body:has(#sessionTradeDrawer:not([hidden])) .tabbar-import {
    visibility: hidden;
    pointer-events: none;
  }
}

/* Hold time: one stacked duration comparison ----------------------------- */
.session-duration-bands {
  display: grid;
  min-width: 0;
  border-top: 1px solid var(--line);
}

.session-duration-band {
  display: grid;
  grid-template-columns: minmax(180px, 0.8fr) minmax(220px, 1.45fr) minmax(160px, auto);
  grid-template-areas: "duration-head duration-track duration-meta";
  align-items: center;
  gap: 14px 24px;
  min-width: 0;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}

.session-duration-band-head {
  grid-area: duration-head;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}

.session-duration-band-head strong {
  overflow: hidden;
  color: var(--text);
  font: 700 12.5px/1.2 var(--font-ui);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-duration-band-head > span {
  color: var(--text);
  font: 700 11.5px/1 var(--font-mono);
  white-space: nowrap;
}

.session-duration-track,
.session-duration-band-track {
  grid-area: duration-track;
  display: block;
  height: 9px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.session-duration-track > span,
.session-duration-band-track > span,
.session-duration-fill,
.session-duration-band-fill {
  display: block;
  width: calc(var(--session-duration-size, 0) * 100%);
  min-width: 2px;
  height: 100%;
  border-radius: inherit;
  background: var(--line-strong);
}

.session-duration-band.is-positive .session-duration-track > span,
.session-duration-band.is-positive .session-duration-band-track > span,
.session-duration-band.is-positive .session-duration-fill,
.session-duration-band.is-positive .session-duration-band-fill {
  background: var(--pnl-pos);
}

.session-duration-band.is-negative .session-duration-track > span,
.session-duration-band.is-negative .session-duration-band-track > span,
.session-duration-band.is-negative .session-duration-fill,
.session-duration-band.is-negative .session-duration-band-fill {
  background: var(--pnl-neg);
}

.session-duration-band-meta {
  grid-area: duration-meta;
  margin: 0;
  color: var(--text-faint);
  font: 600 11px/1.35 var(--font-mono);
  text-align: right;
}

/* Coverage stays operational and separate from performance analytics. */
.session-coverage-visual {
  margin-top: 28px;
}

.session-coverage-bar {
  height: 11px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

.session-coverage-bar > span {
  display: block;
  width: var(--session-coverage, 0%);
  height: 100%;
  border-radius: inherit;
  background: var(--pnl-pos);
}

.session-queue-kicker {
  margin-bottom: 0;
}

/* One-time Topstep source-timezone confirmation lives in Import, not report. */
.topstep-timezone-confirm {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(190px, 0.55fr) auto;
  align-items: end;
  gap: 14px 20px;
  padding: 16px 0;
  border-block: 1px solid var(--line);
}

.topstep-timezone-confirm h3 {
  margin: 5px 0 0;
  color: var(--text);
  font: 700 15px/1.25 var(--font-ui);
}

.topstep-timezone-confirm p:last-child {
  max-width: 68ch;
  margin: 7px 0 0;
  color: var(--text-soft);
  font: 450 11.5px/1.45 var(--font-ui);
}

.topstep-timezone-confirm label {
  display: grid;
  gap: 7px;
  margin: 0;
  color: var(--text-faint);
  font: 700 11px/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.topstep-timezone-confirm select {
  width: 100%;
  min-width: 0;
  min-height: 42px;
  color: var(--text);
  font: 600 12px/1 var(--font-ui);
  letter-spacing: 0;
  text-transform: none;
}

/* Responsive composition ------------------------------------------------- */
@media (max-width: 899px) {
  /* Phones use the document scroller. Keep the route in normal flow and make
     focus/anchor moves stop above the fixed dock rather than underneath it. */
  #session-intelligence.is-active {
    align-content: start;
    min-height: 0;
    overflow: visible;
  }

  #session-intelligence :is(button, select, [tabindex]) {
    scroll-margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);
  }

  body.app-on:has(#session-intelligence.is-active) .site-footer {
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);
  }

  html:has(#session-intelligence.is-active) {
    scroll-padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);
  }
}

@media (max-width: 1180px) {
  #dashboard .panel-grid-analytics > .panel-span-6 {
    grid-column: span 1;
  }
}

@media (max-width: 900px) {
  #dashboard .panel-grid-analytics > .panel-span-6 {
    grid-column: 1 / -1;
  }

  .session-score-row,
  .session-duration-band {
    grid-template-columns: minmax(135px, 0.75fr) minmax(160px, 1.4fr);
  }

  .session-score-row {
    grid-template-areas:
      "score-main score-meta"
      "score-track score-track";
  }

  .session-duration-band {
    grid-template-areas:
      "duration-head duration-meta"
      "duration-track duration-track";
  }
}


@media (max-width: 720px) {
  .session-page-head {
    padding-inline: 0;
  }

  .session-visual-head {
    align-items: flex-start;
    flex-direction: column;
  }

  .session-chart-legend {
    justify-content: flex-start;
  }

  .session-topstep-head {
    flex-direction: column;
  }

  .session-topstep-signals {
    grid-template-columns: minmax(0, 1fr);
  }

  .session-topstep-signals > div,
  .session-topstep-signals > div:first-child {
    padding: 13px 0;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }

  .session-topstep-signals > div:last-child {
    border-bottom: 0;
  }

  /* The matrix keeps its instrument density inside its own scroller, but
     every label steps up to the 11px phone floor. */.almanac-col-label,
.almanac-row-label,
.almanac-cell > span,
.almanac-wh,
.almanac-tilt,
.almanac-net span,
.almanac-foot small,
.session-cf-chip {
    font-size: 11px;
  }

  .almanac-cell {
    min-height: 58px;
  }

  .almanac-cell > strong {
    font-size: 14px;
  }

  .session-tilt-mark-note,
  .session-tilt-tile > span,
  .session-almanac-edgeline,
  .session-side-head p,
  .almanac-col-chip,
  .session-cf-stat small {
    font-size: 11px;
  }

  .session-almanac-edgeline {
    text-align: left;
  }

  .session-score-row,
  .session-duration-band {
    gap: 10px 14px;
  }

  .session-score-meta,
  .session-duration-band-meta {
    font-size: 11px;
  }

  .topstep-timezone-confirm {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }

  .topstep-timezone-confirm .btn {
    width: 100%;
  }
}


@media (max-width: 520px) {

  .session-context-field,
  .session-context-readout {
    min-height: 56px;
    padding-inline: 10px;
  }

  .session-score-row,
  .session-duration-band {
    grid-template-columns: minmax(0, 1fr);
  }

  .session-score-row {
    grid-template-areas:
      "score-main"
      "score-track"
      "score-meta";
  }

  .session-duration-band {
    grid-template-areas:
      "duration-head"
      "duration-track"
      "duration-meta";
  }

  .session-score-meta,
  .session-duration-band-meta {
    text-align: left;
  }

  .session-metric-switch {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    width: 100%;
  }

  .session-metric-switch button {
    min-width: 0;
    padding-inline: 5px;
  }
}


@media (prefers-reduced-motion: reduce) {.dash-session-link {
    transition: none;
  }
}


/* ==========================================================================
   THE CALENDAR, RECUT.

   The legend is gone: three swatches explaining that green means green were
   the least trusting sentence on the page. Its pixels now hold real figures,
   a strip of GREEN/RED/BEST/WIN DAYS under the grid and a weekly spine of
   signed nets in an 8th column. Empty days stop announcing themselves: a
   quiet day is a number, not 25 repetitions of "no trades" with a dash. On
   the phone the grid becomes pure tone at mini-cal grammar and the agenda
   rows underneath are the touch path.
   ========================================================================== */
#calendar .cal-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-4);
}
#calendar #calendarHeading { font-size: 24px; font-weight: 700; }
#calendar .cal-meta { font-size: 12px; color: var(--text-faint); }
#calendar .cal-net { text-align: right; }
#calendar .cal-net-label {
  margin: 0;
  font: 600 11px/1 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-faint);
}
#calendar .cal-net-value { margin: 0; font: 700 32px/1 var(--font-mono); }

#calendar .calendar-grid {
  grid-template-columns: repeat(7, minmax(0, 1fr)) minmax(88px, 0.6fr);
}
#calendar .calendar-week-net {
  background: var(--surface-inset);
  border-radius: 8px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  justify-content: center;
}
#calendar .calendar-week-net span {
  font: 600 11px/1 var(--font-mono);
  text-transform: uppercase;
  color: var(--text-faint);
}
#calendar .calendar-week-net b { font: 700 13px/1 var(--font-mono); }

#calendar .calendar-cell-flat .calendar-cell-day { color: var(--text-faint); }

#calendar .cal-strip {
  margin: var(--space-3) 0 0;
  text-align: center;
  font: 600 11px/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}

#calendar .cal-agenda-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  background: var(--surface-inset);
  border: 0;
  border-radius: 10px;
  padding: 8px 12px;
  margin-top: 6px;
  cursor: pointer;
  color: inherit;
  font-size: 13px;
  text-align: left;
}
#calendar .cal-agenda-net { font: 700 14px/1 var(--font-mono); }
#calendar .calendar-agenda-note { color: var(--text-faint); font-size: 13px; padding: var(--space-3) 0; }

@media (min-width: 900px) {
  #calendar .cal-agenda { display: none; }
}


@media (max-width: 899px) {
  #calendar .cal-head { grid-template-columns: minmax(0, 1fr) auto; }
  #calendar .cal-nav { grid-column: 1 / -1; }
  #calendar .calendar-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
  #calendar .calendar-weekday-net,
  #calendar .calendar-week-net { display: none; }
  #calendar .calendar-cell { min-height: 44px; padding: 6px; }
  #calendar .calendar-cell-pnl,
  #calendar .calendar-cell-pnl-compact,
  #calendar .calendar-cell-meta { display: none; }
  #calendar .cal-net-value { font-size: 24px; }
}


/* ==========================================================================
   THE MONTH CARD, SECOND CUT: MINI EVERYTHING, DENSE EVERYTHING.

   The first cut was a poster: OG-ratio, 64px balance, a calendar sized like a
   co-hero, and 148px of dead card above the label because both columns were
   vertically centered. The owner's own screenshot was the review: foot text
   printed on the sparkline, week one rendered as a green slab because the
   positive wash painted the whole grid, and the range toggle floated level
   with nothing. His verdict became the spec: mini calendar, balance smaller
   but still first seen, everything compact and full of information.

   So the card stops being a monument and becomes an instrument band. Height
   is content-driven (a six-row month is simply a little taller than a
   five-row one, no squeeze and no hole). Both columns anchor to the TOP, so
   the first line of the card is the balance label and its range control, and
   the dead corner cannot exist. The sparkline is confined under the balance
   column only, as ground for the money story, and fades out before the
   calendar begins; the calendar column owns its own foot line with the brand
   mark at its end, so nothing overprints anything. Tone lives ONLY on traded
   tiles: a green month earns its ring and its lit days, never a slab.
   ========================================================================== */
#dashboard .dash-month-card {
  position: relative;
  isolation: isolate;
  display: grid;
  /* THE PROPORTIONS, INVERTED. The last cut fixed the calendar at 330px and
     let the balance column take 1fr, which at the owner's real ~2000px
     viewport stretched the hero into exactly the blank he keeps reporting:
     six NOW figures adrift in 1300px of card. The lesson, twice learned now,
     is that the BALANCE is the fixed-size thing (a dollar figure needs no
     more room at 2000px than at 1440) and the CALENDAR is the thing that can
     spend width usefully, because wider tiles make the money on them bigger
     and more readable. So the balance column is capped and the calendar
     takes every remaining pixel at every viewport. Blank cannot exist:
     there is no flexible empty region left in the card. */
  grid-template-columns: clamp(300px, 26vw, 400px) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
  overflow: hidden;
  padding: 20px var(--space-6);
  border-radius: var(--radius-xl);
  background: var(--surface-1);
  box-shadow: var(--clay-raised);
}
#dashboard .dash-month-card.is-pos { box-shadow: var(--clay-raised), 0 0 0 1px var(--pnl-pos-line); }
#dashboard .dash-month-card.is-neg { box-shadow: var(--clay-raised), 0 0 0 1px var(--pnl-neg-line); }

#dashboard .dash-month-card .dash-hero {
  grid-column: auto;
  position: static;
  background: none;
  box-shadow: none;
  min-height: 293px;
  padding: 0 0 83px;           /* Zone C: 11px air + 72px spark ground */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0;
  min-width: 0;
  overflow: visible;
}
#dashboard .dash-month-card .mini-cal { grid-column: auto; }

/* Minimized, still first: 36px instead of 64, and still the largest thing on
   the card, docked at the top where the eye enters. */
#dashboard .dash-month-card .dash-hero-value {
  margin-top: 4px;
  font-size: 28px;
  letter-spacing: -0.02em;
}

/* Zone A's delta line: the one survivor of the old chip row. The lit range
   tab already names the period, so the copy is value and percent only. */
#dashboard .dash-hero-range {
  display: block;
  margin-top: 6px;
  font: 600 13px/14px var(--font-mono);
}
#dashboard .dash-hero-range.is-pos { color: var(--pnl-pos); }
#dashboard .dash-hero-range.is-neg { color: var(--pnl-neg); }

/* Zone B: the NOW grid. fr tracks, never fixed px, so the edges close. */
#dashboard .dash-now {
  margin-top: 10px;
  padding-top: 7px;
  border-top: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 16px;
  row-gap: 12px;
}
#dashboard .dash-now-label {
  display: block;
  font: 600 11px/11px var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  margin-bottom: 5px;
}
#dashboard .dash-now-fig { font: 700 17px/17px var(--font-mono); color: var(--text); }
#dashboard .dash-now-fig.is-pos { color: var(--pnl-pos); }
#dashboard .dash-now-fig.is-neg { color: var(--pnl-neg); }
#dashboard .dash-now-fig.is-idle { color: var(--text-faint); font-weight: 600; }
#dashboard .dash-now-fig.is-hot { color: var(--accent); }
#dashboard .dash-now-sub { font: 600 11px/1 var(--font-mono); color: var(--text-faint); }

/* The sparkline is the balance story's ground, not the card's wallpaper: it
   spans only the hero column and dies before the calendar starts. */
#dashboard .dash-month-card .dash-spark-wrap {
  position: absolute;
  left: 0;
  bottom: 0;
  width: clamp(300px, 26vw, 400px);   /* the balance column, exactly */
  opacity: 0.45;               /* ground, not wallpaper */
  height: 72px;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, black 62%, transparent 96%);
  mask-image: linear-gradient(to right, black 62%, transparent 96%);
}
#dashboard .dash-month-card .dash-spark-wrap canvas { width: 100%; height: 100%; }
#dashboard .dash-ground-caps {
  position: absolute;
  left: 0;
  bottom: 58px;
  width: clamp(280px, 24vw, 380px);
  display: flex;
  justify-content: space-between;
  font: 600 10px/1 var(--font-mono);
  color: var(--text-faint);
  letter-spacing: 0.06em;
  pointer-events: none;
}

/* The mark rides the calendar's foot line, right-aligned: one line, two
   voices, zero collisions. */
#dashboard .month-card-mark {
  position: static;
  margin: 0;
  opacity: 0.6;
  white-space: nowrap;
}
#dashboard .dash-est-chip {
  font: 600 11px/1 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  border: 1px solid var(--pnl-pos-line);
  border-radius: 4px;
  padding: 2px 5px;
}

/* THE MINI CALENDAR, actually mini: tiles around 26px, tone as the density. */
#dashboard .mini-cal {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  /* Stretch, not a pin: the balance column's content sets the card's height
     and the calendar fills exactly that, so the columns agree by
     construction at every viewport and every month length, and the tiles
     split whatever height there is. A number here would be a second source
     of truth waiting to drift, which is how the last squash happened. */
  align-self: stretch;
  height: auto;
}
#dashboard .mini-cal-open {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: inherit;
}
/* Sentence case, not caps. "August 2026" is a date, and the uppercase mono
   treatment is this design's device for LABELS — spending it on the one line
   that is already the panel's title is what made the head shout. */
#dashboard .mini-cal-month {
  font: 600 12px/1 var(--font-ui);
  letter-spacing: 0.01em;
  letter-spacing: 0.12em;
  color: var(--text-faint);
}
#dashboard .mini-cal-net { font: 700 20px/1 var(--font-mono); }
#dashboard .mini-cal-weekdays,
#dashboard .mini-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  /* THE 1px GAP IS THE SHARED HAIRLINE. The grid paints --line behind cells
     that paint --surface-1, so every rule is drawn exactly once — no
     border-collapse arithmetic and no doubled edges where two cells meet. */
  gap: 1px;
}
#dashboard .mini-cal-grid {
  background: var(--line);
  border: 1px solid var(--line);
}
/* The grid is the only valve: it takes whatever the head, weekdays and foot
   leave of the 293, and 1fr rows split it evenly. A five row month gets
   taller tiles in the same box, so the columns can never desync. */
#dashboard .mini-cal-grid {
  flex: 1;
  grid-auto-rows: minmax(0, 1fr);
  min-height: 0;
}
/* Weekday headers are named chips, not initials. "S M T W T F S" has two Ts
   and two Ss and makes the reader count columns. */
/* Plain caps text, no chips. The reference's weekday row is a LABEL, and a
   chip on each one made seven more boxes above a grid of boxes. */
#dashboard .mini-cal-weekdays span {
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: center;
  padding: 3px 0;
}

/* THE LEDGER TILE. A day that did not trade is a NUMBER, not a box: giving
   every cell a surface made a month of two trading days look like a month of
   thirty, and it was the single biggest reason this grid read as busy. Only
   .is-trade gets a fill and an edge. */
#dashboard .mini-cal-day {
  position: relative;
  /* Square, and on its own fill. An untraded day is a CELL in a ruled table
     now, not a bare numeral floating in a gap — which is what the render shows.
     This reverses the earlier "only traded days get a surface" decision: the
     ruling is what separates them now, and the tint still marks the trade. */
  border-radius: 0;
  background: var(--surface-1);
  border: 1px solid transparent;
  padding: 4px 6px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  /* gap:0 on purpose. .mc-amt carries margin-top:auto, so the air between the
     day index and the money is whatever the row has spare — a fixed gap on top
     of that is what pushed a 3-line tile past a 43px cell. */
  gap: 0;
  min-height: 0;
  overflow: hidden;
  font: 400 11px/1 var(--font-mono);
  color: var(--text-faint);
  text-align: left;
}
/* The index goes TOP-RIGHT in a traded tile: the money is the thing being read
   and it reads left-aligned, so the date gets out of its way. */
#dashboard .mini-cal-day .mc-ix {
  align-self: flex-end;
  font: 500 10px/1 var(--font-mono);
  font-style: normal;
  color: var(--text-faint);
  opacity: 0.75;
}
/* margin-top:auto pins the money and its count to the BOTTOM of the tile, so
   every figure in a week sits on one line however tall the row runs. */
#dashboard .mini-cal-day .mc-amt {
  margin-top: auto;
  font: 700 clamp(11px, 0.72vw, 12.5px)/1.1 var(--font-mono);
  letter-spacing: -0.02em;
  color: var(--text);
}
/* The trade count is a third line in a tile that is ~45px tall on the desktop
   grid and ~30px on a phone. Phones do not get it — a 9px count is under the
   type floor and a 11px one costs the money its room. */
#dashboard .mini-cal-day .mc-n {
  display: none;
  color: var(--text-faint);
  opacity: 0.8;
  white-space: nowrap;
}
#dashboard .mini-cal-day.is-trade:hover {
  filter: brightness(1.06);
}
#dashboard .mini-cal-day.is-trade:focus-visible { box-shadow: inset 0 0 0 1px var(--accent); }
#dashboard .mini-cal-day.is-scratch {
  background: var(--surface-inset);
  border-color: color-mix(in srgb, var(--text) 10%, transparent);
}
#dashboard .mini-cal-day.is-scratch .mc-amt { color: var(--text-faint); font-weight: 600; }
#dashboard .mini-cal-day.is-blank { background: none; }
/* Adjacent-month days: present, so the month reads continuous, but clearly not
   part of it. */
/* NO ELEMENT OPACITY ON TEXT. Opacity multiplies the token AFTER the cascade,
   so a contrast harness measures the declared 5.45:1 while the screen shows
   1.60:1 — the one hole no token audit can see. Full strength is also what the
   render actually shows for an adjacent-month day. */
#dashboard .mini-cal-day.is-outside { color: var(--text-faint); }
#dashboard .mini-cal-day.is-trade { font-weight: 600; color: var(--text); cursor: pointer; }
/* Fill carries magnitude, the edge carries the sign. The border is what makes
   a small losing day still legible against a big winning one. */
#dashboard .mini-cal-day.is-trade.pnl-positive {
  background: color-mix(in srgb, var(--pnl-pos) calc(var(--day-intensity, 0) * 16% + 7%), var(--surface-1));
  border-color: color-mix(in srgb, var(--pnl-pos) 38%, transparent);
}
#dashboard .mini-cal-day.is-trade.pnl-positive .mc-amt { color: var(--pnl-pos); }
#dashboard .mini-cal-day.is-trade.pnl-negative {
  background: color-mix(in srgb, var(--pnl-neg) calc(var(--day-intensity, 0) * 16% + 7%), var(--surface-1));
  border-color: color-mix(in srgb, var(--pnl-neg) 38%, transparent);
}
#dashboard .mini-cal-day.is-trade.pnl-negative .mc-amt { color: var(--pnl-neg); }

/* The month head: nav, month, and the net that the tiles deliberately omit. */
#dashboard .mini-cal-head {
  display: flex;
  align-items: center;
  gap: 2px;
}
#dashboard .mini-cal-step {
  min-height: 0;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--text-faint);
  font: 400 15px/1 var(--font-ui);
  cursor: pointer;
}
#dashboard .mini-cal-step:hover:not(:disabled) { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text); }
#dashboard .mini-cal-step:disabled { opacity: 0.25; cursor: default; }
#dashboard .mini-cal-head .mini-cal-net { margin-left: auto; }
/* TODAY is an orange outline with a dot beneath the numeral — the reference's
   marker. It is the only place accent is spent inside the grid, so it cannot be
   confused with a P&L colour. */
#dashboard .mini-cal-day.is-today {
  border-color: var(--accent);
  box-shadow: none;
  color: var(--text);
}
#dashboard .mini-cal-day.is-today:not(.is-trade)::after {
  content: "";
  width: 3px;
  height: 3px;
  margin-top: 3px;
  border-radius: 50%;
  background: var(--accent);
}
#dashboard .mini-cal-day.is-today:not(.is-trade) { align-items: center; justify-content: center; }
#dashboard .mini-cal-day.is-future { color: var(--text-faint); }  /* same reason */

/* The calendar's foot: figures left, the mark right, one 11px line. */
#dashboard .mini-cal-footrow {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 2px var(--space-3);
}
#dashboard .mini-cal-footrow .month-card-mark {
  font-size: 10px;
  letter-spacing: 0.08em;
  line-height: 1.4;
}
#dashboard .mini-cal-foot {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
  font: 600 11px/1.4 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Below 1240 the instrument grid is off and .dash-head reappears, so the
   control cluster needs an honest shape inside the hero. 11px, not 10 — this is
   a BASE rule and mobileFloors.check.mjs floors base type at 11px on a phone. */
#dashboard .dash-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  font-size: 11px;
}

/* NET DAILY P&L SPINE. Bars grow from a centreline: profit up, loss down, both
   scaled against the month's own peak so the shape is a distribution rather
   than a ranking. --mag is written per bar by renderDayBars; 46% leaves the
   centre gap that makes the zero line readable without drawing one. */
.day-bars {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 3px;
  min-height: 140px;
}
.day-bar { position: relative; }
.day-bar::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: calc(var(--mag) * 46%);
  background: var(--pnl-neg);
  border-radius: 0 0 2px 2px;
}
.day-bar[data-s="1"]::before {
  top: auto;
  bottom: 50%;
  background: var(--pnl-pos);
  border-radius: 2px 2px 0 0;
}

/* ESTIMATE ONE-LINER (the banner, demoted to a footnote under the card) */
#dashboard .estimated-analytics-notice {
  font-size: 12px;
  padding: 4px 2px;
  background: none;
  border: 0;
  /* No nowrap/ellipsis: below 1240 the page scrolls, so a two-line footnote
     costs nothing, and truncating a provenance sentence mid-word costs trust.
     Above 1240 this note is clipped out of view entirely by the grid rule. */
}

/* Seam releases: the ratio guard is load bearing at >=1240 where the TV
   column fixes the card's width; below that the ratio would fight the
   narrower column, so it releases. */
@media (max-width: 1239px) {
  #dashboard .dash-month-card { aspect-ratio: auto; }
}
@media (max-width: 899px) {
  #dashboard .dash-month-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: 16px;
  }
  #dashboard .dash-month-card .dash-hero-value { font-size: 36px; }
  #dashboard .dash-month-card .dash-spark-wrap {
    position: static;
    height: 72px;
    z-index: auto;
    -webkit-mask-image: none;
    mask-image: none;
  }
  #dashboard .mini-cal-weekdays,
  #dashboard .mini-cal-grid { gap: 3px; }
  /* The spec's tile arithmetic missed the view's own gutter: at 375 the
     tiles measured 42px against the 44px floor. The calendar earns the
     missing width by bleeding into the card's padding, tiles at 44.6px,
     instead of the fallback that would have made them inert. */
  #dashboard .mini-cal { margin-inline: -6px; align-self: stretch; height: auto; }
  /* Touch floor: rows stop being 1fr shares of a pinned box and become
     honest 44px targets. */
  #dashboard .mini-cal-grid { flex: none; grid-auto-rows: auto; }
  #dashboard .mini-cal-day { height: 44px; }
  /* The 9px corner index cannot meet the 11px phone floor, so it does not
     ship there; the aria-label and title carry the date. */
  #dashboard .mini-cal-day .mc-ix { display: none; }
  #dashboard .dash-ground-caps { display: none; }
  #dashboard .dash-now { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 14px; }
  #dashboard .dash-month-card .dash-hero { min-height: 0; padding-bottom: 0; }
  #dashboard .mini-cal-open,
  #dashboard .mini-cal-foot { padding-inline: 6px; }
  #dashboard .mini-cal-foot { min-height: 44px; }
  #dashboard .mini-cal-footrow .month-card-mark { display: none; }
}

/* THE PULSE ROW: six tiles, none of which can read a permanent zero. */
#dashboard .dash-quad { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
#dashboard .dash-quad-card { padding: 12px; }
#dashboard .dash-quad-card .metric-value { font-size: 22px; }
  /* Avg win / loss can carry words ("no losses"); a phrase at figure size
     wraps, so it sets like a phrase. */
  #dashboard .dash-quad-card [data-metric="avgWinLoss"] { font-size: 17px; white-space: nowrap; }
#dashboard .dash-quad-card .metric-label { font-size: 11px; }

/* EQUITY FOOTNOTES: five figures that used to each rent a whole card. */
#dashboard .eq-footnotes { display: flex; flex-wrap: wrap; gap: 4px 20px; margin-top: var(--space-3); }
#dashboard .eq-footnotes .metric-card {
  display: flex;
  gap: 8px;
  align-items: baseline;
  background: none;
  box-shadow: none;
  border: 0;
  padding: 0;
  min-height: 0;
}
#dashboard .eq-footnotes .metric-label {
  font: 600 11px/1 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0;
}
#dashboard .eq-footnotes .metric-value { font: 600 13px/1 var(--font-mono); margin: 0; }
#dashboard .eq-footnotes .metric-delta,
#dashboard .eq-footnotes .metric-sub { display: none; }

/* BOTTOM GRID: three even panels. */
#dashboard .panel-grid-bottom { grid-template-columns: repeat(12, minmax(0, 1fr)); }
#dashboard .panel-grid-bottom > .panel-span-4 { grid-column: span 4; }

/* DEPTH CAPTIONS: the unlock ledger above each locked chart. */
#dashboard .depth-caption {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-faint);
  margin: var(--space-3) 0 var(--space-2);
}

@media (max-width: 899px) {
  #dashboard .dash-quad { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  #dashboard .dash-quad-card .metric-value { font-size: 18px; }
  #dashboard .panel-grid-bottom { grid-template-columns: minmax(0, 1fr); }
  #dashboard .panel-grid-bottom > .panel-span-4 { grid-column: auto; }
}

/* The card's dateline: a crop of just this card still says when it was
   taken. First voice in the label row, quietest thing on it. */
#dashboard .dash-card-date {
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.1em;
  color: var(--text-faint);
  padding-right: var(--space-3);
  border-right: 1px solid var(--line);
  margin-right: var(--space-3);
  white-space: nowrap;
}

/* ==========================================================================
   THE PLAYBOOK ROW'S EMPTY TRACK, FILLED.

   .dash-boards is a two column grid (1.55fr / 1fr) whose second column lost
   its tenant in an earlier renovation: the playbook sat in column one and
   column two rendered as a permanent dark field beside it, at every width.
   The owner saw it immediately; the audits missed it because they scanned
   vertical gaps between siblings, not empty tracks inside a row. Discipline
   Monitor moves up from the bottom grid to hold it: behavioral quality
   beside setup quality, the two boards that answer "HOW am I trading".
   ========================================================================== */
#dashboard .dash-boards-playbook .dash-board-slot {
  height: 100%;
  min-height: 0;
}

/* No setups yet: the playbook self-hides and discipline takes the whole
   row rather than guarding a hole in column one. */
#dashboard .dash-boards-playbook:has(> #dashPlaybook[hidden]) {
  grid-template-columns: minmax(0, 1fr);
}

/* The bottom grid's two remaining panels split it evenly. */
@media (min-width: 1240px) {
  #dashboard .panel-grid-bottom > .panel-span-4 { grid-column: span 6; }
}

/* ==========================================================================
   THE MASTHEAD STOPS STEALING THE PLAYER'S TAPS.

   A YouTube embed that autoplays starts MUTED, and YouTube puts its own "tap
   to unmute" affordance in the top left of the picture. The wall's masthead
   is an absolutely positioned scrim across that exact strip, and in half size
   it sat at opacity 0.5 with no pointer-events change at all: half invisible
   and fully clickable. The channel <select> occupies the top left, so the tap
   meant for unmute opened our picker instead, every time, which is precisely
   the bug reported. The link and max keys did the same to YouTube's own top
   right controls.

   Visible and clickable are now the same state. While a monitor is LIVE the
   masthead is transparent AND inert, and it comes back together on hover or
   keyboard focus. :has(iframe) is the liveness test because the iframe is
   created only by playMonitor, so no JS flag can drift out of step with it.
   ========================================================================== */
/* Specificity: the half size rule is .bb-wall[data-size="half"] .bb-mon-h at
   (0,3,0), and :has(iframe) contributes only an element, so a bare
   .bb-mon:has(iframe) .bb-mon-h is (0,2,1) and loses. Naming .bb-wall makes
   it (0,3,1). Without this the masthead stays half visible while inert,
   which is the exact "looks clickable, is not" state this block exists to
   abolish. */
.bb-wall .bb-mon:has(iframe) .bb-mon-h {
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms var(--ease-out);
}
.bb-mon:has(iframe) .bb-mon-pick,
.bb-mon:has(iframe) .bb-mon-link,
.bb-mon:has(iframe) .bb-mon-full {
  pointer-events: none;
}
.bb-wall .bb-mon:has(iframe):hover .bb-mon-h,
.bb-wall .bb-mon:has(iframe):focus-within .bb-mon-h {
  opacity: 1;
}
.bb-mon:has(iframe):hover .bb-mon-pick,
.bb-mon:has(iframe):hover .bb-mon-link,
.bb-mon:has(iframe):hover .bb-mon-full,
.bb-mon:has(iframe):focus-within .bb-mon-pick,
.bb-mon:has(iframe):focus-within .bb-mon-link,
.bb-mon:has(iframe):focus-within .bb-mon-full {
  pointer-events: auto;
}

/* TOUCH HAS NO HOVER, so fading on hover would hand a tablet a masthead it
   can never summon back. There the bar stops being an overlay entirely and
   sits in normal flow above the picture, where it cannot cover the player at
   all and needs no fading. */
@media (pointer: coarse) {
  .bb-wall .bb-mon-h,
  .bb-mon:has(iframe) .bb-mon-h {
    position: static;
    opacity: 1;
    pointer-events: auto;
    background: var(--term-rail);
    border-bottom: 1px solid var(--term-line);
  }
  .bb-mon:has(iframe) .bb-mon-pick,
  .bb-mon:has(iframe) .bb-mon-link,
  .bb-mon:has(iframe) .bb-mon-full {
    pointer-events: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bb-mon:has(iframe) .bb-mon-h { transition: none; }
}

/* ==========================================================================
   TWO MORE WALL SHAPES: SOLO AND STACK.

   The owner's instruction was that half is the resting format, with an option
   for one big picture, and an option for one big picture over three small
   ones. Both are column modes like half, so they inherit half's reserved desk
   padding, its measured --wall-w, its phone band fallback and its single
   column desk collapse: the only thing written here is the grid inside.

   Both size themselves from the SAME available height and express the picture
   at a true ratio, so a short window narrows the block rather than growing
   black bars, which is the rule half already follows. Heads are measured, not
   guessed: a coarse pointer gets a 48px bar rather than 26 and the arithmetic
   has to know.
   ========================================================================== */
.bb-wall {
  --wall-avail: calc(100vh - var(--chrome-h, 86px) - var(--wall-head-h, 57px));
}

@media (min-width: 1100px) {
  /* THE RATIO IS DECLARED ON THE PICTURE, not computed into the grid's
     height. The first pass sized the grid by arithmetic and lost twice: the
     masthead is an absolute overlay so it contributes no height, and the
     screen carries its own max-height from the band rules, which pinned the
     picture at 264px and stretched it to 4.3:1 no matter what the grid said.
     A declared aspect-ratio cannot be argued with by a clamp it outlives.

     The grid still carries a WIDTH cap, because width is the only lever that
     keeps the block inside the available height: at 16:9 a solo picture is
     W*9/16 tall, and a stack is W*9/16 plus a third of that, which is exactly
     W*3/4. Cap the width and the height follows. */
  .bb-wall:is([data-size="solo"], [data-size="stack"]) .bb-mon-screen,
  .bb-wall:is([data-size="solo"], [data-size="stack"]) .bb-mon iframe {
    aspect-ratio: 16 / 9;
    width: 100%;
    height: auto;
    max-height: none;
  }

  /* SOLO. One picture, as wide as the column or as wide as the height allows. */
  .bb-wall[data-size="solo"] .bb-wall-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    aspect-ratio: auto;
    width: min(100%, calc(var(--wall-avail) * 16 / 9));
    height: auto;
    margin-inline: auto;
    align-self: center;
    border: 0;
    gap: 1px;
  }
  /* display:none rather than a DOM move: the iframe document is never
     re-created, which is the contract that keeps a stream alive. */
  .bb-wall[data-size="solo"] .bb-mon:nth-child(n + 2) { display: none; }

  /* STACK. One big picture over three thirds: W*9/16 plus W/3*9/16 = W*3/4. */
  .bb-wall[data-size="stack"] .bb-wall-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto auto;
    aspect-ratio: auto;
    width: min(100%, calc(var(--wall-avail) * 4 / 3));
    height: auto;
    margin-inline: auto;
    align-self: center;
    border: 0;
    gap: 1px;
  }
  .bb-wall[data-size="stack"] .bb-mon:first-child { grid-column: 1 / -1; }
}


/* ==========================================================================
   THE INSTRUMENT PANEL. One screen, no scroll.

   The dashboard spent about 2003px of column in a 1043px box, so it scrolled
   by 946 at the owner's window. The fix is not to delete panels, it is to
   stop stacking them.

   PROMOTE, DO NOT RE-PARENT. display:contents on three grouping wrappers
   dissolves their boxes and promotes their children to grid items of
   #dashboard, so every card can be placed on a four row grid WITHOUT one node
   moving in index.html. That matters more than it reads: ui.metricNodes is
   captured once at module load by querySelectorAll, renderWall writes
   innerHTML on the wall grid, and the TV iframe dies if its DOM is rebuilt.
   A box that stops existing is invisible to all three.
   ========================================================================== */
@media (min-width: 1240px) {
  #dashboard.is-active .dash-stats,
  #dashboard.is-active .dash-month-card,
  #dashboard.is-active .dash-boards { display: contents; }

  /* LOAD BEARING. .dash-stats[hidden] is (0,2,0) and the rule above is
     (1,2,0), which would leave the metric grid visible through the empty
     state. Restated at winning specificity. */
  #dashboard.is-active .dash-stats[hidden] { display: none; }

  #dashboard.is-active {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    /* 1093 view box - 32 padding-block - 48 row gaps = 1013 of track.
       80 + 140 + 188 + 0.42x605 + 0.58x605 = 408 + 254 + 351 = 1013. */
    /* FIVE BANDS now. A: the hero, HALVED by order (156 with its eaten gap →
       76). B: the Session Horizon. C: the AI micro desk. D stays the CHART
       band and E the LEDGER band at the 0.42/0.58 split that fixed the
       portrait-bars regression — both simply own less of the screen, which is
       what the halved hero and the two new instruments paid for. */
    grid-template-rows: 80px 140px 188px minmax(0, 0.42fr) minmax(0, 0.58fr);
    gap: 12px;
    /* padding-inline belongs to .view.is-active:not(#terminal) at (1,2,0),
       which centres on --shell-w. Only the block axis is ours; a padding
       shorthand here would read as a fight it silently loses. */
    padding-block: 16px;
    overflow: hidden;            /* the promise, stated in CSS */
    align-content: stretch;
    /* Items stretch to their track. This was the DEFAULT until a second
       @media (min-width:1240px) block in styles.css set align-items:start on
       the same selector; that block is gone now, and this states the behaviour
       instead of inheriting it. Six height:100% workarounds died with it. */
    align-items: stretch;
    row-gap: 12px;
  }

  /* overflow:hidden on a grid ITEM cancels its automatic minimum size, so a
     panel reports a smaller contribution than its content and CLIPS instead
     of shrinking. .dash-month-card and .panel both carry it for their rounded
     corners, and every row budget below assumes shrinking. Not optional. */
  #dashboard.is-active > *,
  #dashboard.is-active .dash-stats > *,
  #dashboard.is-active .dash-month-card > *,
  #dashboard.is-active .dash-boards > * { min-height: 0; min-width: 0; }

  /* The floor has to reach the NESTED panels too, or a row item that is
     itself a grid reports its children's intrinsic height and overflows the
     track it was placed in: measured, the analytics pair came back 723px tall
     inside a 384px row and the monitor 925px inside 451px. A grid item can
     only shrink if everything it contains can. */
  #dashboard.is-active .panel-grid-analytics > *,
  #dashboard.is-active .dash-sessions > *,
  #dashboard.is-active .dash-aidesk > *,
  #dashboard.is-active .dash-edge-mini > *,
  #dashboard.is-active .dash-board-slot > *,
  #dashboard.is-active #dashPlaybook > * { min-height: 0; min-width: 0; }
  /* height:auto, not 100%. The old comment here blamed the automatic minimum
     size for stretch "not binding" and answered with height:100% on six
     panels. The real cause was align-items:start, set on this same selector
     from a second 1240 block in styles.css. With that deleted the items
     stretch by construction, and 100% would re-state the track as an ANSWER
     rather than a ceiling — which is exactly what stopped .panel-chart canvas
     from sizing to its own flex box. The min-height:0 floors above are what
     let them shrink; they were already correct. */
  #dashboard.is-active .panel-grid-analytics,
  #dashboard.is-active .dash-edge-mini,
  #dashboard.is-active .dash-board-slot,
  #dashboard.is-active #dashPlaybook,
  #dashboard.is-active #dashMiniCal,
  #dashboard.is-active #propTracker {
    height: auto;
    overflow: hidden;
  }
  /* The canvas sizes from its flex box, and drawLineChart reads that box back
     via clientHeight rather than trusting its data-height attribute. */
  #dashboard.is-active .panel-chart,
  #dashboard.is-active .panel-chart canvas { min-height: 0; height: auto; }
  /* One source of truth for tile height. styles.css min-height:96px is what
     both floated the metric tiles AND clipped 15px out of them; the track
     sizes the item now. */
  #dashboard.is-active .metric-card { min-height: 0; }

  #dashboard.is-active .panel-grid-analytics > .panel,
  #dashboard.is-active .dash-board-slot,
  #dashboard.is-active #dashPlaybook {
    display: flex;
    flex-direction: column;
    min-height: 0;
    gap: 8px;
    padding: 16px;
  }
  /* MANDATORY companion. The rule above is (1,2,0) and BEATS [hidden] at
     (0,1,0): without this a hidden #dashPlaybook stays a 629x432 ghost panel. */
  #dashboard.is-active #dashPlaybook[hidden] { display: none; }
  /* The ledger is six rows in a 432px panel. Its slack is pooled BELOW the
     table, which is what the reference does — rows do not stretch to fill. */
  #dashboard.is-active #dashLedger .dash-ledger { flex: 0 0 auto; }
  #dashboard.is-active #dashLedger .panel-head { display: flex; align-items: center; gap: 8px; }
  /* Six columns in a 309px panel: the reason column is the one that gives, and
     it ellipsises rather than wrapping a row to two lines. */
  #dashboard.is-active .dash-ledger th,
  #dashboard.is-active .dash-ledger td { font-size: 11px; padding: 3px 0; }
  #dashboard.is-active .dash-ledger th { font-size: 9px; }
  /* Desktop-only tightening. All three are under the 11px phone floor that
     mobileFloors.check.mjs enforces, so they live here, not in the base rules. */
  #dashboard.is-active .queue-badge { font-size: 10px; }
  #dashboard.is-active .dash-ledger-foot { font-size: 10px; }
  #dashboard.is-active .dash-ledger .lq-src { font-size: 9px; }
  /* THE FOOT WAS EATING THE RADAR. Stacked — label over numeral, then the rail,
     then a two-line caption — it measured 109px of a 341px panel at 1440x900,
     leaving the canvas 148 and pinning drawRadarChart to its 46px floor: a 92px
     circle, the exact regression charts.js:1515 documents.

     One ROW instead: the numeral left, the rail taking the rest. The caption
     stays in the DOM for screen readers and for the narrow layouts below 1240,
     where the page scrolls and it costs nothing. */
  #dashboard.is-active .trader-score-foot {
    flex: 0 0 auto;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 14px;
    row-gap: 0;
    padding-top: 10px;
    border-top: 1px solid var(--line);
  }
  #dashboard.is-active .trader-score-value-wrap { display: flex; align-items: baseline; gap: 8px; }
  #dashboard.is-active .trader-score-value { font: 700 26px/1 var(--font-mono); margin: 0; }
  #dashboard.is-active .trader-score-value-wrap .metric-label { margin: 0; white-space: nowrap; }
  #dashboard.is-active .trader-score-caption { display: none; }

  /* THE RULE: the last list in a panel is flex:1. A box cannot then be taller
     than its content, and its content cannot be shorter than the box. This is
     what makes the blank space unreachable BY CONSTRUCTION rather than by
     tuning it away. The emptiness that remains is pooled in ONE place on
     purpose — beside the figure you read first — never distributed as slack
     into every box. Do not "fill" the band-B gap later; that is the design. */
  /* .panel-trader-score ships its own `grid-template-rows: auto minmax(0,1fr)
     auto`, but the flex rule above is (1,2,0) and overrides its display, so the
     canvas has to claim the middle track as a flex item instead. Without this
     it measured 435x155 and drawRadarChart pinned the radar to its 46px floor. */
  #dashboard.is-active #traderScoreChart,
  #dashboard.is-active #equityChart,
  #dashboard.is-active .dash-playbook-grid,
  #dashboard.is-active #riskViolations { flex: 1 1 0; min-height: 0; }
  /* THE BARS DO NOT STRETCH. flex:1 in a 449px panel made 10 bars into a 449px
     bar chart that was mostly air — the "too big / blank space" complaint,
     measured. They take the height the shape needs and the figures below take
     the rest, so the panel is full of information instead of scaled-up bars. */
  #dashboard.is-active #dashDayBars {
    flex: 0 1 auto;
    min-height: 0;
    height: clamp(110px, 64%, 250px);
  }
  /* .rule-cost-list is a GRANDCHILD; the flex item is .rule-cost, so that is
     what has to grow. */
  #dashboard.is-active .rule-cost {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  #dashboard.is-active .rule-cost-list { flex: 1 1 0; min-height: 0; overflow-y: auto; }

  #dashboard.is-active .dash-playbook-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: minmax(0, 1fr);
    overflow-y: auto;
  }

  /* The nine-figure stat strip under the curve. Explicit rows, because with
     `auto` the flex algorithm sizes the strip before the grid wraps and clips
     a row. */
  /* SEVEN FIGURES, ONE ROW, ABOVE THE CURVE. Hairlines, not a grid of cards:
     this panel is one surface and dividers do the work borders used to. The
     streak pair is gone — band A's GREEN STREAK cell already carries it, and
     repeating it here was the only reason the strip needed three rows. */
  #dashboard.is-active .eq-footnotes {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    grid-template-rows: auto;
    column-gap: 0;
    row-gap: 0;
    flex: 0 0 auto;
    align-content: center;
    margin: 0 0 8px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--line);
  }
  #dashboard.is-active .eq-footnotes > article + article {
    padding-left: 12px;
    border-left: 1px solid var(--line);
  }
  /* One LINE per figure, not two. Stacking a label over a value needs ~22px a
     row; laying them out as label-left / value-right fits the same nine figures
     in 15px rows and gives the curve back 20px. */
  #dashboard.is-active .eq-footnotes > article {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    padding-right: 12px;
  }
  #dashboard.is-active .eq-footnotes .metric-label {
    font-size: 10px;
    line-height: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #dashboard.is-active .eq-footnotes .metric-value {
    font-size: 15px;
    line-height: 18px;
    white-space: nowrap;
  }
  #dashboard.is-active .eq-footnotes .metric-delta { display: none; }
  #dashboard.is-active .eq-footnotes > * { margin: 0; padding: 0; }

  /* Chrome cull. A raised edge means "this is a panel" again, instead of being
     the default state of every box on screen. */
  #dashboard.is-active .dash-quad-card,
  #dashboard.is-active .dash-play-tile {
    background: none;
    box-shadow: none;
    border: 0;
    border-radius: 0;
  }
  /* Nested raise. A panel that is already lifted does not need its scoreboard,
     its note and its own canvas lifted again inside it — that is what made
     every box read as equally important. The panel keeps the edge; its
     contents state rank with type instead. */
  #dashboard.is-active .score-item,
  #dashboard.is-active .rule-cost-note,
  #dashboard.is-active .dash-play-bar,
  #dashboard.is-active #equityChart { box-shadow: none; }
  /* What still carries a shadow on this view, on purpose: the six panels, the
     three command-bar buttons, the range pill, and two state markers (today on
     the calendar, the desk's live dot). Buttons and state markers are
     affordances, not surfaces — culling those would cost meaning, not chrome. */

  /* Ink the panels do not need at this size. */
  #dashboard.is-active .eq-scrub-hint { display: none; }  /* the canvas aria-label
                                        already says how to drive it */
  #dashboard.is-active .panel-head p,
  #dashboard.is-active .dash-board-head p { display: none; }  /* 12px sub-copy x5 */
  #dashboard.is-active .panel-head { margin: 0; }
  #dashboard.is-active .mini-cal-footrow .month-card-mark { display: none; }

  /* Type ramp: 30 / 20 / 16 / 12 / 11 — the 44-band ramp, restepped for a hero
     HALF the height. The ranks survive; only the steps shrink. */
  #dashboard.is-active [data-metric="totalPnl"] {
    font: 700 30px/32px var(--font-mono);
    letter-spacing: -0.03em;
    margin: 0;
  }
  #dashboard.is-active [data-metric="accountBalance"] {
    font: 600 12px/14px var(--font-mono);
    color: var(--text-faint);
  }
  /* The <p> that wraps the clause carries the base paragraph margin (16px top
     AND bottom), which is what pushed the hero 17px past its 108px track. */
  #dashboard.is-active .dash-hero-equity { margin: 2px 0 0; line-height: 16px; }
  /* The headline <p> still carried the base paragraph margin (16px top AND
     bottom) — invisible in the 156px band, a 32px overflow in the halved one.
     Same class of bug as .dash-hero-equity above, fixed the same way. */
  #dashboard.is-active .dash-hero-headline { margin: 0; display: flex; align-items: center; gap: 8px; }
  /* The eye was sized against the 48px headline; against 30px it stretched
     the line to 42px and clipped the figure. */
  /* min-height:0 or the base button touch-target floor re-inflates it to 42px
     and stretches the 32px headline line under it. */
  #dashboard.is-active .balance-eye { height: 24px; width: 24px; min-height: 0; min-width: 0; padding: 3px; }
  /* THE OPEN-POSITIONS STRIP CANNOT LIVE IN AN 80px HERO. It only exists while
     a trade is open, so on the one-glass grid it becomes a floating live
     ticket pinned bottom-right — DOM identity untouched (renderers query it
     by id), no grid budget consumed, gone the moment the position closes. */
  #dashboard.is-active .dash-hero #progressTradeSummary {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 40;
    width: 380px;
    max-width: 380px;
    margin: 0;
    padding: 10px 12px;
    background: var(--surface-2);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-lg, 10px);
    box-shadow: var(--clay-float);
  }
  /* MANDATORY companion: the rule above out-specifies [hidden]. */
  #dashboard.is-active .dash-hero #progressTradeSummary[hidden] { display: none; }
  /* 10px caps on an 11px line clipped every descender ("Today" lost the y). */
  #dashboard.is-active .dash-now-label { line-height: 14px; }
  #dashboard.is-active .dash-hero-top { margin: 0; }
  #dashboard.is-active #dashEquityTag {
    font: 600 11px/16px var(--font-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-right: 6px;
  }
  #dashboard.is-active .dash-now-fig { font: 700 16px/18px var(--font-mono); }
  #dashboard.is-active #dashHeroToday,
  #dashboard.is-active #dashNowOpen { font-size: 20px; line-height: 22px; }
  #dashboard.is-active .score-item strong { font-size: 20px; }  /* a discipline
                                        score is a behaviour trigger, not a footnote */
  #dashboard.is-active .dash-play-value { font-size: 17px; }    /* a per-setup
                                        expectancy is a 30-second figure, not a 1-second one */

  /* ── THE INSTRUMENT OPTS OUT OF THE READING COLUMN ─────────────────────
     .view.is-active:not(#terminal) (styles.css) is (1,2,0), because
     :not(#terminal) contributes an ID. #dashboard.is-active is (1,1,0) and
     LOSES — measured, it left the computed padding at "16px 259.5px", so 519px
     of a 1943px content area was gutter and the panels lived in 1424px.
     .view.is-active#dashboard is (1,2,0), a tie, broken by source order because
     index.html loads clay-v3.css after styles.css. Measured after: padding
     "16px", column pitch 107.664px -> 148.25px.
     styles.css already blesses the precedent for #terminal: "it is an
     instrument, not a document." So is this. */
  .view.is-active#dashboard { padding-inline: var(--space-4); }

  /* ── BAND A: which book, which window ────────────────────────────────── */
  /* THE COMMAND BAR IS GONE. Its controls moved onto the strip below, which is
     the whole point of band A being ONE surface: the buttons now sit beside the
     figure they act on instead of on a separate rail above it. The greeting and
     the Session Intelligence link both live elsewhere already — renderGreeting
     early-returns on a missing node, and Session Intelligence is a rail row. */
  #dashboard.is-active > .dash-head { display: none; }

  /* ── BAND B: one raised surface, seven figures, ZERO DOM MOVES ─────────
     .dash-now is a CHILD of .dash-hero in index.html, so it cannot be placed
     on this grid, and display:contents on it would delete the surface it sits
     on. Instead the hero spans the full row and becomes the two-track grid;
     .dash-now takes track 2 and lays its own six cells out. */
  /* BAND A IS ONE SURFACE — and HALF the surface it was. The old band spanned
     rows 1-2 (36 + 12 + 108 = 156); the order was "reduce the hero by 50%",
     so it is now ONE 76px row and the head row is gone with it (the greeting
     lives on the Session Horizon band, written by the same renderGreeting). */
  #dashboard.is-active .dash-hero {
    grid-area: 1 / 1 / 2 / -1;
    padding: 8px 16px;
    display: grid;
    grid-template-columns: minmax(260px, 3fr) minmax(0, 9fr) auto;
    grid-auto-rows: min-content;
    align-content: center;
    column-gap: 0;
    background: var(--surface-1);
    border: 1px solid var(--line);
    border-radius: var(--radius-xl);
  }
  #dashboard.is-active .dash-hero > * { grid-column: 1; }
  #dashboard.is-active .dash-hero .dash-now {   /* (1,3,0), beats the line above */
    grid-column: 2;
    grid-row: 1 / span 9;
    align-self: stretch;
    margin: 0;
    padding: 0;
    border-top: 0;
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    column-gap: 0;
    row-gap: 0;
    align-content: center;
  }
  #dashboard.is-active .dash-now > * { padding: 0 16px; border-left: 1px solid var(--line); }
  /* The first cell has nothing to its left to be divided FROM. With the rule
     there the band reads as a strip of separate tiles; without it, one surface
     split by hairlines, which is what the render shows. */
  #dashboard.is-active .dash-now > *:first-child { border-left: 0; }
  /* Track 3. `order` gives the reference's visual sequence — import first, the
     orange primary at the right end — while the DOM keeps log-before-import for
     the three adjacent-sibling rules in styles.css that would silently cancel
     if the source order flipped. */
  #dashboard.is-active .dash-hero .dash-controls {
    grid-column: 3;
    grid-row: 1 / span 9;
    align-self: center;
    justify-self: end;
    display: flex;
    align-items: center;
    gap: 12px;
    padding-left: 16px;
    margin-top: 0;
    border-left: 1px solid var(--line);
  }
  #dashboard.is-active .dash-controls #dashQuickImportBtn { order: -1; margin-left: 0; }
  /* LOAD BEARING: .btn ships a 52px box and .dash-range-btn a 40px one, and both
     overflow a 156px band that also has to carry a 48px headline. */
  #dashboard.is-active .dash-controls .btn,
  #dashboard.is-active .dash-controls .nav-btn { min-height: 0; height: 30px; padding-block: 0; padding-inline: 12px; font-size: 12px; }
  #dashboard.is-active .dash-controls .dash-range {
    height: 26px;
    padding: 2px;
    align-self: center;
    /* The render's pill select. Restyling the EXISTING 1M/3M/ALL group rather
       than adding selects with nothing behind them: all three ranges have real
       state and a real handler. */
    border: 1px solid var(--control-edge);
    border-radius: 999px;
    background: var(--surface-inset);
  }
  #dashboard.is-active .dash-controls .dash-range-btn { min-height: 0; height: 22px; padding: 0 10px; font-size: 11px; }
  #dashboard.is-active .dash-controls .kbd-hint { display: none; }
  /* Desktop-only Market TV type. All three are under the 11px phone floor
     mobileFloors.check.mjs enforces, so they live here, not in the base rules. */
  #dashboard.is-active .dem-cat-h,
  #dashboard.is-active .dem-cat-f,
  #dashboard.is-active .bb-news-at { font-size: 10px; }
  #dashboard.is-active .dash-hero #progressTradeSummary {
    grid-column: 2 / -1;
    grid-row: 10;
    justify-self: end;
    max-width: 380px;
    margin: 8px 0 0;
    overflow: hidden;
  }
  #dashboard.is-active .dash-now-sub { display: block; margin-top: 3px; }
  #dashboard.is-active #dashNowEventSub { font-size: 10px; letter-spacing: 0.07em; text-transform: uppercase; }
  #dashboard.is-active .dash-now-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  /* The hero's long tail. Each keeps rendering into a hidden node, which costs
     nothing; #dashSparkline stays in the DOM so ui.dashSparkline is never null. */
  #dashboard.is-active .dash-hero .dash-spark-wrap,
  #dashboard.is-active .dash-hero .dash-ground-caps,
  #dashboard.is-active .dash-hero .metric-note,
  #dashboard.is-active .dash-hero-range,
  #dashboard.is-active .dash-hero #dashCardDate { display: none; }
  #dashboard.is-active .dash-quad { display: none; }   /* emptied; its four keepers
                                        moved into the stat strip under the curve */

  /* Desktop-only tightening for the two new bands. Every size here is under
     the 11px phone floor that mobileFloors.check.mjs enforces, so they live
     here, not in the base rules. */
  #dashboard.is-active .dsh-title,
  #dashboard.is-active .dad-title { font-size: 10px; line-height: 14px; }
  #dashboard.is-active .dsh-clock { font-size: 10px; line-height: 14px; }
  #dashboard.is-active .dsh-local { font-size: 10px; }
  #dashboard.is-active .dsh-chip { font-size: 9px; line-height: 12px; }
  #dashboard.is-active .dsh-count { font-size: 10px; line-height: 12px; }
  #dashboard.is-active .dad-tag { font-size: 9px; line-height: 14px; }
  #dashboard.is-active .dad-bias { font-size: 9px; line-height: 12px; }
  #dashboard.is-active .dad-conf-label { font-size: 9px; line-height: 12px; }
  #dashboard.is-active .dad-foot span { font-size: 9px; line-height: 12px; }

  /* ── BAND B: SESSION HORIZON ── four venue cards over the 24h rail.
     The rail is an instrument, not slack: it keeps its 20px and the cards
     flex. Card padding is cut to what its three rows actually need — at the
     old 8px it overdrew the track by 12px and the meter row clipped. */
  #dashboard.is-active .dash-sessions { grid-area: 2 / 1 / 3 / -1; }
  #dashboard.is-active .dsh-cards { flex: 1 1 auto; min-height: 0; }
  #dashboard.is-active .dsh-rail { flex: 0 0 20px; }
  #dashboard.is-active .dsh-card { padding: 5px 10px; gap: 3px; }
  /* ── BAND C: AI MICRO DESK ── four bias cards. The analysis is the card's
     major element by explicit order, so it owns a firm 3-line box (45px at
     11/15) — the 152px track left it a single clipped line. */
  #dashboard.is-active .dash-aidesk { grid-area: 3 / 1 / 4 / -1; }
  #dashboard.is-active .dad-analysis { flex: 0 0 auto; height: 45px; -webkit-line-clamp: 3; }

  /* ── BAND C: where is my edge ──────────────────────────────────────────
     display:contents places the two chart panels directly on the outer grid,
     which is what kills .panel-span-8's `grid-column: span 8` clamp without a
     new override. Before: both panels at x=794, stacked, splitting one row. */
  #dashboard.is-active .panel-grid-analytics { display: contents; }
  /* THE REFERENCE'S BOARD, with the Release Edge at DOUBLE width.
     Content region is lines 1-9 (8 cols, 1270px at 1999); the rail takes 9-13
     (4 cols, 629px) and spans both bands. 1270 + 12 + 629 = 1911. */
  /* SHAPE FOLLOWS CONTENT. Band D is short and wide, so the two TIME SERIES
     live there — a curve and a 30-day bar chart both want landscape, and the
     bars measured 0.68 (portrait) in the old arrangement. Band E is tall, so it
     takes the three things that stack: a radar, a list and a month grid. */
  /* FOUR PANELS, which is what the reference has. It carries an equity chart, a
     Market TV column, a calendar and a review queue — no radar and no separate
     net-daily panel, because its equity panel plots the drawdown on the same
     axes. Six panels in eight columns is what made every one of them cramped.

     The Edge score and the Net Daily bars keep rendering into hidden nodes, so
     nothing downstream reads a null and both are one `display` away from coming
     back. NOT the rail: that holds the iframe. */
  #dashboard.is-active .panel-grid-analytics > .panel-span-8 { grid-area: 4 / 1 / 5 / 9; }
  #dashboard.is-active .panel-grid-analytics > .panel-span-4,
  #dashboard.is-active #dashEdgeScore { display: none; }
  #dashboard.is-active #dashLedger { grid-area: 5 / 5 / 6 / 9; }

  /* LOAD BEARING. styles.css `.panel-chart canvas { display: block }` is
     (0,2,0) and BEATS the UA [hidden] rule — verified: hidden === true and
     computed display === "block". The canvas STAYS in the DOM so
     ui.drawdownChart and drawAllCharts never read null; only its box goes.
     Its numbers survive as maxDrawdown and currentDrawdown in the stat strip. */
  #dashboard.is-active #drawdownChart { display: none; }

  /* The reference has no playbook and no discipline panel on this board. They
     keep rendering into hidden nodes — cheap, and nothing downstream reads a
     null — but they do not take a track. NOT the rail: that holds the iframe. */
  #dashboard.is-active .dash-board-slot,
  #dashboard.is-active #dashPlaybook { display: none; }
  #dashboard.is-active #propTracker { grid-area: 5 / 1 / 6 / 5; }
  #dashboard.is-active:has(#propTracker:not([hidden])) #dashMiniCal { display: none; }

  /* ── THE DESK RAIL: full C+D height, right column ──────────────────────
     NEVER display:none on this element or any ancestor, in any state, at any
     breakpoint — the warning above extends here. The iframe dies with its box.
     `grid-template-rows: auto minmax(0,1fr)` bounds the .dem-panel track, which
     is what makes the overflow-y:auto below actually bind. */
  #dashboard.is-active .dash-edge-mini {
    grid-area: 4 / 9 / 6 / -1;   /* 4 columns: twice the 2-column slot it had */
    grid-template-rows: auto minmax(0, 1fr);
    position: static;            /* kills sticky, grid-row 1/30 and the bleed */
    margin: 0;
  }
  #dashboard.is-active .dem-news { min-height: 0; }  /* grandchild: the floors
                                        above stop one level short of it */
  /* The 16:9 tile eats the news column. Capping it hands the feed its height
     back BEFORE any scrolling is needed. Do not remove this without checking
     where #dashEdgeMiniNews's bottom edge lands. */
  #dashboard.is-active .dem-tv { max-height: 168px; }

  /* ── BAND D: what did it cost ──────────────────────────────────────────
     The calendar keeps its month — "ugly" was about appearance, not existence —
     but drops from 586x478 to 469x432, cells at 45px, above the 44px floor this
     file already uses. ESCAPE HATCH if it still reads thin: it is ONE grid-area
     swap with #dashPlaybook or the discipline slot. No new code. Do not
     rebuild this decision from scratch. */
  /* Desktop-only calendar type. Both values are under the 11px phone floor, so
     they live here rather than in the base rules. */
  #dashboard.is-active .mini-cal-weekdays span { font-size: 10px; }
  #dashboard.is-active .mini-cal-day .mc-n { display: block; font: 500 9px/1 var(--font-mono); }
  #dashboard.is-active #dashMiniCal { grid-area: 5 / 1 / 6 / 5; }

  /* ── THE BOARD CLOSES WHEN A PANEL IS OFF ──────────────────────────────
     .dash-edge-mini ships `hidden` and renderPlaybook toggles
     #dashPlaybook.hidden. Without these, a fresh account shows a 309px hole
     down the right and a 629px hole in band D. */
  #dashboard.is-active:has(.dash-edge-mini[hidden]) .panel-grid-analytics > .panel-span-8,
  #dashboard.is-active:has(.dash-edge-mini[hidden]) #dashLedger { grid-column-end: -1; }
  /* No rail (no terminalPro) means the empty-state card takes the whole board
     rather than leaving four columns of hole beside it. */
  #dashboard.is-active:has(.dash-edge-mini[hidden]) #dashboardEmptyState { grid-column-end: -1; }

  /* The calendar lost its surface with the wrapper that carried it. */
  #dashboard.is-active #dashMiniCal {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    border-radius: var(--radius-xl);
    background: var(--surface-1);
    box-shadow: var(--clay-raised);
  }

  /* The only regions allowed to scroll are inside a panel, never the page. */
  #dashboard.is-active .dash-playbook-grid,
  #dashboard.is-active #riskViolations,
  #dashboard.is-active .dem-panel {
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* The estimate advisory is a 130-character sentence. Pinning it bottom-left
     at max-width 320px with nowrap+ellipsis (see the base rule above) did not
     make it a footnote, it made it the fragment "...fees estimated from Topstep's
     published schedule" cut to "...fe…", sitting over the rail as the last thing
     the eye hits. On this grid the EST chip beside ACCOUNT BALANCE already
     mirrors it, tooltip and all — renderEstimatedAnalyticsBoundary() has always
     written both.

     So the sentence goes visually away but STAYS in the accessibility tree.
     display:none would have taken the note with it, and the chip is a bare
     <span title> that no screen reader announces. [hidden] still wins on
     display, so a zero-episode journal drops it entirely. */
  #dashboard.is-active #estimatedAnalyticsNotice {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* The empty state owns the panel; JS keeps toggling [hidden] and nothing else. */
  /* :not(#dashEdgeMini) IS LOAD BEARING. .dash-edge-mini is a DIRECT CHILD of
     #dashboard, so this `> *` cull matched it and set display:none — which
     kills the iframe inside it. renderDashboardMetrics un-hides the empty state
     whenever the journal has no trades, so deleting your last trade, or landing
     on a fresh account, silently stopped a playing stream. Reproduced live
     before the fix: display:none on #dashEdgeMini, the iframe's own ancestor.

     Excluded by ID, not class: the class form would put the literal string
     `.dash-edge-mini` into a rule whose block is display:none, which is exactly
     what the guard in tests/instrumentPanel.check.mjs scans for. */
  #dashboard.is-active:has(#dashboardEmptyState:not([hidden])) > *:not(.dash-head):not(#dashEdgeMini):not(#dashboardEmptyState),
  #dashboard.is-active:has(#dashboardEmptyState:not([hidden])) .dash-stats > *,
  #dashboard.is-active:has(#dashboardEmptyState:not([hidden])) .dash-month-card > *,
  #dashboard.is-active:has(#dashboardEmptyState:not([hidden])) .dash-boards > * { display: none; }
  /* Columns 1-9, not 1--1. The rail is no longer culled with the rest of the
     board (see above), so a full-width card would sit on top of it. This is the
     same eight-column content region every other band uses. */
  #dashboard.is-active #dashboardEmptyState { grid-area: 2 / 1 / -1 / 9; place-self: center; }

  /* The drawers are off the grid entirely: they are settings, not instruments. */
  #dashboard.is-active > #deskSettings,
  #dashboard.is-active > #dashDepth,
  #dashboard.is-active > .panel-grid-bottom,
  #dashboard.is-active > #adminPanelsMount { display: none; }
}

/* ── SHORT SCREENS ───────────────────────────────────────────────────────
   The fr rows absorb any height; the 332px of FIXED rows do not. At 1440x900
   the budget is 843 - 32 padding - 48 gaps = 763 of track, and
   64 + 132 + 136 + 0.48x431 + 0.52x431 = 763. Everything below is the type
   ramp stepping down with it — same ranks, smaller steps. */
@media (min-width: 1240px) and (max-height: 960px) {
  #dashboard.is-active { grid-template-rows: 64px 132px 136px minmax(0, 0.48fr) minmax(0, 0.52fr); }
  #dashboard.is-active .dash-hero {
    padding: 4px 14px;
    grid-template-columns: minmax(240px, 3fr) minmax(0, 9fr) auto;
  }
  /* 13px of label over a 28px figure over a 15px clause is 58 in a 56px
     content box; the label gives its 2px, not the figure. */
  #dashboard.is-active .dash-hero-top .metric-label { font-size: 9px; line-height: 11px; }
  /* "GREEN STREAK" wraps to two lines in a 120px cell and the tallest now-cell
     hits 60 in a 56px box. One line at 9px fits every label with room. */
  #dashboard.is-active .dash-now-label { font-size: 9px; line-height: 11px; white-space: nowrap; }
  #dashboard.is-active .dash-hero-equity { line-height: 13px; }
  #dashboard.is-active .dsh-chip { padding: 1px 5px; }
  /* The analysis-less AI card still overdraws its 85px box by 3: the gap and
     the foot's air give it back. */
  #dashboard.is-active .dad-card { gap: 5px; }
  #dashboard.is-active .dad-foot { padding-top: 4px; }
  /* A 64px hero has no second line to give: the controls STAY in track 3.
     (Pre-halving they dropped under the figures here; that row is gone.) */
  #dashboard.is-active .dash-hero .dash-controls {
    grid-column: 3;
    grid-row: 1 / span 9;
    justify-self: end;
    padding-left: 12px;
    border-left: 1px solid var(--line);
    margin-top: 0;
  }
  #dashboard.is-active [data-metric="totalPnl"] { font-size: 26px; line-height: 28px; }
  #dashboard.is-active [data-metric="accountBalance"] { font-size: 11px; line-height: 13px; }
  #dashboard.is-active .dash-hero-equity { margin-top: 2px; }
  #dashboard.is-active #dashHeroToday,
  #dashboard.is-active #dashNowOpen { font-size: 18px; line-height: 20px; }
  #dashboard.is-active .dash-now-fig { font-size: 14px; line-height: 16px; }
  /* The two new bands step down with the ramp. The analysis has no 3-line
     budget at this height; it stands down instead of printing a clipped line. */
  #dashboard.is-active .dsh-rail { flex-basis: 16px; }
  /* Lane tops are sized for the 20px rail; on the 16px one the 4th lane sat
     at 17+3 and clipped. */
  #dashboard.is-active .dsh-lane:nth-child(1) { top: 1px; }
  #dashboard.is-active .dsh-lane:nth-child(2) { top: 5px; }
  #dashboard.is-active .dsh-lane:nth-child(3) { top: 9px; }
  #dashboard.is-active .dsh-lane:nth-child(4) { top: 13px; }
  #dashboard.is-active .dad-analysis { display: none; }
  #dashboard.is-active .eq-footnotes { flex: 0 0 52px; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(3, 15px); }
  #dashboard.is-active .mini-cal-footrow { display: none; }
  #dashboard.is-active .dem-tv { max-height: 120px; }
}

/* ==========================================================================
   SESSION HORIZON + AI MICRO DESK — the C1 bands.
   Base rules hold at every width (below 1240 they are ordinary stacked
   panels); the 1240 grid block above only PLACES them. Same card grammar as
   the rest of the board: surface-1 ground, machined hairlines, clay depth,
   accent for NOW only, pos/neg for bias only.
   ========================================================================== */
.dash-sessions,
.dash-aidesk {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 16px;
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--clay-raised);
  min-height: 0;
}
.dsh-head,
.dad-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-width: 0;
}
.dsh-title,
.dad-title {
  font: 600 11px/15px var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
}
/* The greeting the desktop board otherwise never shows: renderGreeting writes
   it here as well as into the hidden .dash-head. */
.dsh-greeting {
  font: 600 13px/14px var(--font-ui);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.dsh-clock {
  margin-left: auto;
  font: 500 11px/15px var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
}
.dsh-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 8px;
  min-height: 0;
}
.dsh-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg, 10px);
  box-shadow: var(--edge-highlight);
  min-width: 0;
}
.dsh-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}
.dsh-city {
  font: 700 12px/16px var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}
.dsh-local {
  font: 500 11px/16px var(--font-mono);
  color: var(--text-faint);
  white-space: nowrap;
}
.dsh-chip {
  font: 700 11px/14px var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  color: var(--text-faint);
}
.dsh-card.is-open .dsh-chip { color: var(--pnl-pos); border-color: var(--pnl-pos-line); background: var(--pnl-pos-soft); }
.dsh-card.is-pre .dsh-chip { color: var(--warn); border-color: var(--warn); background: var(--warn-soft); }
.dsh-card.is-open .dsh-city { color: var(--text); }
.dsh-card.is-closed { opacity: 0.72; }
.dsh-count {
  font: 500 11px/13px var(--font-mono);
  color: var(--text-soft);
  white-space: nowrap;
}
.dsh-meter {
  display: block;
  height: 3px;
  border-radius: 999px;
  background: var(--chart-track);
  overflow: hidden;
}
.dsh-meter i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--pnl-pos);
}
/* The 24h rail: one lane per venue, arcs in viewer-local time, the NOW cursor
   in accent — the only accent on the band. */
.dsh-rail {
  position: relative;
  height: 20px;
  border-radius: 6px;
  background: var(--surface-inset);
  box-shadow: var(--clay-pressed);
  overflow: hidden;
}
.dsh-lane {
  position: absolute;
  inset-inline: 0;
  height: 3px;
}
.dsh-lane:nth-child(1) { top: 2px; }
.dsh-lane:nth-child(2) { top: 7px; }
.dsh-lane:nth-child(3) { top: 12px; }
.dsh-lane:nth-child(4) { top: 17px; }
.dsh-arc {
  position: absolute;
  top: 0;
  height: 100%;
  border-radius: 999px;
  background: var(--line-strong);
}
.dsh-arc.is-open { background: var(--pnl-pos); }
.dsh-arc.is-pre { background: var(--warn); }
.dsh-now {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  box-shadow: 0 0 8px var(--halo-accent);
}

.dad-tag {
  margin-left: auto;
  font: 600 11px/15px var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
}
.dad-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 8px;
  min-height: 0;
}
.dad-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-lg, 10px);
  box-shadow: var(--edge-highlight);
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.dad-card.is-bullish { border-left-color: var(--pnl-pos); }
.dad-card.is-bearish { border-left-color: var(--pnl-neg); }
.dad-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.dad-sym {
  font: 700 13px/16px var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--text);
}
.dad-price { font: 500 12px/16px var(--font-mono); color: var(--text-soft); }
.dad-bias {
  margin-left: auto;
  font: 700 11px/14px var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
}
.dad-card.is-bullish .dad-bias { color: var(--pnl-pos); background: var(--pnl-pos-soft); border: 1px solid var(--pnl-pos-line); }
.dad-card.is-bearish .dad-bias { color: var(--pnl-neg); background: var(--pnl-neg-soft); border: 1px solid var(--pnl-neg-line); }
.dad-conf {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  align-items: center;
  gap: 8px;
}
.dad-conf-fig { font: 700 15px/16px var(--font-mono); color: var(--text); }
.dad-conf-label {
  font: 600 11px/14px var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.dad-conf-meter {
  display: block;
  height: 3px;
  border-radius: 999px;
  background: var(--chart-track);
  overflow: hidden;
}
.dad-conf-meter i { display: block; height: 100%; border-radius: 999px; background: var(--text-soft); }
.dad-analysis {
  margin: 0;
  font: 400 11px/15px var(--font-ui);
  color: var(--text-soft);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
  min-height: 0;
}
.dad-foot {
  margin-top: auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding-top: 5px;
  border-top: 1px solid var(--line);
}
.dad-foot span {
  font: 600 11px/14px var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.dad-foot b { font: 700 11px/12px var(--font-mono); color: var(--text); }

/* The Net Daily panel's figures. One row, label over value, so the space the
   bars do not need carries the numbers they cannot state. */
.day-bars-foot {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.day-bars-foot span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.day-bars-foot i {
  font: 500 11px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.day-bars-foot b {
  font: 700 15px/1.1 var(--font-mono);
  letter-spacing: -0.02em;
  color: var(--text);
  white-space: nowrap;
}

/* ══ HIDE THE P&L ═════════════════════════════════════════════════════════
   NOT a privacy control — a DISCIPLINE one. Traders hide the running figure so
   the next decision is made on the setup in front of them rather than on how
   the day is going so far. That is why it masks the P&L and leaves the account
   balance, the win rate and the curve alone: the process figures are the ones
   you are meant to still be reading.

   MASKED IN CSS, NOT JS. renderDashboardMetrics rewrites this figure on every
   5s price poll, so a JS-written mask would be wiped within seconds. A ::after
   over a transparent figure cannot be. */
.dash-hero-headline {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}
.balance-eye {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
}
.balance-eye:hover { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text-soft); }
.balance-eye:focus-visible { outline: 2px solid var(--focus-ring, var(--accent)); outline-offset: 1px; }
.balance-eye svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* One icon at a time: open means "it is showing, click to hide". */
.balance-eye .eye-shut { display: none; }
.balance-eye[aria-pressed="true"] .eye-open { display: none; }
.balance-eye[aria-pressed="true"] .eye-shut { display: inline; }
.balance-eye[aria-pressed="true"] { color: var(--accent); }

/* WHAT "HIDDEN" HAS TO COVER. The account balance goes with the P&L: it is
   starting capital PLUS the P&L, so leaving it up while the figure above it is
   masked hands the number straight back to anyone who knows the account size.
   One eye, both figures.

   Masking the headline alone did not hide the P&L either:
   the same number was still legible in the month net, the queue total, the
   equity legend and the curve's own axis, so the toggle claimed something it
   did not do. Every ABSOLUTE MONEY figure derived from P&L is masked.

   WHAT STAYS, on purpose — this is a discipline control, and these are the
   figures you are meant to still be reading: win rate, profit factor, trade
   counts, streaks, the discipline scores, every date, and the SHAPE of the
   equity curve. You can see that you are trading well without seeing what it
   paid.

   THE REVIEW QUEUE'S PER-TRADE COLUMN STAYS TOO. That panel is a work list —
   you are there to journal individual trades, and what a trade made is part of
   reviewing it. The running TOTAL beneath it is still masked, because that
   aggregate is the figure this control exists to get out of your eyeline.

   SCOPED UNDER #dashboard AS WELL. The panel styles several of these at (1,1,1)
   and an unscoped body-class rule is (0,2,1) — that is how the first version
   painted its dots over a still-legible number. */
body.is-pnl-hidden [data-metric="totalPnl"],
body.is-pnl-hidden [data-metric="accountBalance"],
body.is-pnl-hidden [data-metric="expectancy"],
body.is-pnl-hidden [data-metric="avgWinLoss"],
body.is-pnl-hidden [data-metric="maxDrawdown"],
body.is-pnl-hidden [data-metric="currentDrawdown"],
body.is-pnl-hidden #dashHeroToday,
body.is-pnl-hidden #dashHeroWeek,
body.is-pnl-hidden #dashNowOpen,
body.is-pnl-hidden #dashNowHighs,
body.is-pnl-hidden #miniCalNet,
body.is-pnl-hidden #dashQueueTotal,
body.is-pnl-hidden #equityLegendValue,
body.is-pnl-hidden #equityLegendDepth,
body.is-pnl-hidden .mini-cal-day .mc-amt,
body.is-pnl-hidden .mc-avg,
body.is-pnl-hidden #dashboard.is-active [data-metric="totalPnl"],
body.is-pnl-hidden #dashboard.is-active [data-metric="accountBalance"],
body.is-pnl-hidden #dashboard.is-active [data-metric="expectancy"],
body.is-pnl-hidden #dashboard.is-active [data-metric="avgWinLoss"],
body.is-pnl-hidden #dashboard.is-active [data-metric="maxDrawdown"],
body.is-pnl-hidden #dashboard.is-active [data-metric="currentDrawdown"],
body.is-pnl-hidden #dashboard.is-active #dashHeroToday,
body.is-pnl-hidden #dashboard.is-active #dashHeroWeek,
body.is-pnl-hidden #dashboard.is-active #dashNowOpen,
body.is-pnl-hidden #dashboard.is-active #dashNowHighs,
body.is-pnl-hidden #dashboard.is-active .mini-cal-day .mc-amt {
  position: relative;
  /* Each figure keeps its BOX, so nothing on the board reflows on the toggle. */
  color: transparent;
}
body.is-pnl-hidden [data-metric="totalPnl"]::after,
body.is-pnl-hidden [data-metric="accountBalance"]::after,
body.is-pnl-hidden [data-metric="expectancy"]::after,
body.is-pnl-hidden [data-metric="avgWinLoss"]::after,
body.is-pnl-hidden [data-metric="maxDrawdown"]::after,
body.is-pnl-hidden [data-metric="currentDrawdown"]::after,
body.is-pnl-hidden #dashHeroToday::after,
body.is-pnl-hidden #dashHeroWeek::after,
body.is-pnl-hidden #dashNowOpen::after,
body.is-pnl-hidden #dashNowHighs::after,
body.is-pnl-hidden #miniCalNet::after,
body.is-pnl-hidden #dashQueueTotal::after,
body.is-pnl-hidden #equityLegendValue::after,
body.is-pnl-hidden #equityLegendDepth::after,
body.is-pnl-hidden .mini-cal-day .mc-amt::after,
body.is-pnl-hidden .mc-avg::after {
  content: "•••";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  color: var(--text-faint);
  letter-spacing: 0.04em;
}
/* The headline is 44px and carries the sign, so it gets the longer mask. */
body.is-pnl-hidden [data-metric="totalPnl"]::after { content: "••••••"; }
/* The queue TOTAL is right-aligned; its mask has to follow. */
body.is-pnl-hidden #dashQueueTotal::after { justify-content: flex-end; }
/* Calendar tiles pin their money bottom-left, so the mask sits where the figure
   was rather than floating in the middle of the cell. */
body.is-pnl-hidden .mini-cal-day .mc-amt::after { align-items: flex-end; }

/* ══ THE LEDGER ══════════════════════════════════════════════════════════
   Four overrides, none of them cosmetic. styles.css styles `table` GLOBALLY —
   min-width:980px, padded bordered cells, and a sticky uppercase thead with its
   own background. Measured, this table renders 980px wide inside a 597px panel
   without the first override. The reference is a table with nothing on it, so
   undoing all of it IS the design. */
.dash-ledger {
  width: 100%;
  min-width: 0;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.dash-ledger th,
.dash-ledger td {
  padding: 4px 0;
  border: 0;
  background: none;
  text-align: left;
}
.dash-ledger thead th {
  position: static;
  background: none;
  text-transform: none;
  letter-spacing: 0;
  font: 500 11px/14px var(--font-mono);
  color: var(--text-faint);
}
.dash-ledger td { font: 500 13px/16px var(--font-mono); color: var(--text); }
.dash-ledger th:last-child,
.dash-ledger td:last-child { text-align: right; }
.dash-ledger .is-pos { color: var(--pnl-pos); }
.dash-ledger .is-neg { color: var(--pnl-neg); }
.dash-ledger-empty { color: var(--text-faint); }

/* The queue's badge, its foot, and the source stamp. */
.queue-badge {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--text-inverse);
  font: 700 11px/1 var(--font-mono);
}
.dash-ledger-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  font: 500 11px/1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.dash-ledger-foot #dashQueueTotal { font-size: 12px; font-weight: 700; letter-spacing: -0.01em; text-transform: none; }
/* THE TABLE IS FIXED, NOT AUTO. `max-width: 0` is the standard shrink-to-
   ellipsis trick for an AUTO table, but it starves the column to its share:
   measured at 1999x1150 it gave REASON 66px while "Model signal bounce" needs
   126px and "Trend Continuation" 119px — 7 of 8 real setup names clipped, which
   is the owner's "every REASON reads Trend".

   Deleting max-width alone swings the other way: the auto algorithm then hands
   REASON 329px and drops DATE to 137px, and the columns move every time the
   data does. Percentages on a fixed layout hold both still. */
.dash-ledger { table-layout: fixed; width: 100%; }
.dash-ledger th:nth-child(1), .dash-ledger td:nth-child(1) { width: 21%; }  /* date + clock */
.dash-ledger th:nth-child(2), .dash-ledger td:nth-child(2) { width: 11%; }  /* symbol */
.dash-ledger th:nth-child(3), .dash-ledger td:nth-child(3) { width: 9%; }   /* side */
.dash-ledger th:nth-child(4), .dash-ledger td:nth-child(4) { width: 16%; }  /* net p&l */
.dash-ledger th:nth-child(5), .dash-ledger td:nth-child(5) { width: 26%; }  /* reason */
.dash-ledger th:nth-child(6), .dash-ledger td:nth-child(6) { width: 17%; }  /* source */

.dash-ledger .lq-reason {
  /* max-width DELETED. nowrap + ellipsis stay: under `fixed` they now only
     catch a runaway free-text Custom setup, which is what they are for. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-soft);
}
/* THE PROVENANCE STAMP. Three states, because this app has three: a
   broker-provided P&L, one calculated from fill prices with estimated fees, and
   a hand-typed row. The reference shows only the first two, and labelling a
   manual entry "broker verified" is exactly the claim this badge exists to
   stop. */
.dash-ledger .lq-src {
  display: inline-block;
  padding: 1px 5px;
  border: 1px solid currentColor;
  border-radius: 4px;
  font: 600 11px/1.4 var(--font-mono);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.85;
}
/* The render's chips are a TINT, not an outline — a bordered chip in every row
   drew a second grid on top of the table's own rules. `is-verified`, not
   `is-broker`: the renderer's class follows the LABEL so the two cannot drift. */
.dash-ledger .lq-src {
  border-color: transparent;
  opacity: 1;
}
.dash-ledger .lq-src.is-verified {
  color: var(--pnl-pos);
  background: color-mix(in srgb, var(--pnl-pos) 12%, transparent);
}
.dash-ledger .lq-src.is-estimated {
  color: var(--text-muted, var(--text-faint));
  background: color-mix(in srgb, var(--text-faint) 14%, transparent);
}
.dash-ledger .lq-src.is-manual {
  color: var(--text-faint);
  background: color-mix(in srgb, var(--text-faint) 10%, transparent);
}
/* The clock beside the date. Faint and after, so the date still leads the column
   but two rows from the same session are visibly different trades. */
.dash-ledger .lq-at {
  margin-left: 6px;
  font-style: normal;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* A reason that is genuinely absent reads as absent, not as a setup name. */
.dash-ledger .lq-reason.is-none { color: var(--text-faint); }

/* The edge score's rail: a red-amber-green track with the score as a dot. */
.score-rail {
  display: block;
  position: relative;
  height: 4px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--pnl-neg) 0%, var(--warn) 50%, var(--pnl-pos) 100%);
}
.score-rail::after {
  content: "";
  position: absolute;
  top: 50%;
  left: clamp(0%, var(--score-at, 0%), 100%);
  width: 9px;
  height: 9px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 0 2px var(--surface-1);
}

/* ══ THE LEGEND ══════════════════════════════════════════════════════════
   Two series on one plot need naming, and this is what lets drawLineChart run
   with readout:null — which hands the axis 20px of padTop back. The dots are
   the series colours: orange for equity, the neutral track wash for depth. */
.eq-legend {
  display: flex;
  align-items: baseline;
  gap: 18px;
  margin: 0 0 6px;
  font: 500 11px/1.3 var(--font-mono);
  color: var(--text-faint);
}
.eq-legend-item {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.eq-legend-item::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  align-self: center;
}
.eq-legend-item.is-equity::before { background: var(--accent); }
.eq-legend-item.is-depth::before { background: var(--chart-axis, var(--text-faint)); }
.eq-legend-item b {
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.eq-legend-item.is-depth b.is-live { color: var(--pnl-neg); }


/* ══ MARKET TV — the column names itself and states its own liveness ══════ */
.dem-title {
  font: 600 12px/1 var(--font-ui);
  letter-spacing: 0.02em;
  color: var(--text);
}
/* The as-of line carries a dot whose colour IS its state. One statement, read
   off data-feed, so the badge can never disagree with the words beside it. */
.dem-clock[data-feed]::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  vertical-align: baseline;
  background: var(--text-faint);
}
.dem-clock[data-feed="live"]::before { background: var(--term-live); }
.dem-clock[data-feed="stale"]::before { background: var(--warn); }
.dem-clock[data-feed="down"]::before { background: var(--pnl-neg); }

/* ══ NEXT CATALYST ═══════════════════════════════════════════════════════
   The one release about to move the tape, promoted out of the list so it is
   readable from across the room. The countdown is driven by the terminal's
   existing 1s [data-starts] walk — no second timer. */
.dem-cat {
  padding: 10px 12px;
  border-top: 1px solid var(--line);
}
.dem-cat-h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin: 0 0 6px;
  font: 600 11px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.dem-cat-h em { font-style: normal; letter-spacing: 0.08em; }
.dem-cat-b {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
}
.dem-cat-t {
  font: 700 18px/1.1 var(--font-ui);
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Cyan is this design's colour for "counting", the way green and red are its
   colours for money. It is used nowhere else on the board. */
.dem-cat .dem-cd {
  font: 700 18px/1.1 var(--font-mono);
  letter-spacing: -0.01em;
  color: var(--term-live);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dem-cat-f {
  margin: 6px 0 0;
  font: 500 11px/1.3 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Headline rows carry their own timestamp now. The stamp has shipped in the
   ingest since it was written and was rendered nowhere; an empty cell is
   itself a signal, because the sample wire never carries one. */
.bb-news-at {
  font: 500 11px/1.4 var(--font-mono);
  color: var(--text-faint);
  margin-right: 6px;
}


/* ══════════════════════════════════════════════════════════════════════════
   WATCH — the dashboard monitor, big.

   The picture goes large WITHOUT MOVING ONE NODE. #dashEdgeMiniTv already
   contains the iframe, so promoting that element to position:fixed resizes the
   picture in place: no re-parenting (which reloads a stream), no display:none
   on an ancestor (which kills one). The button writes a single class on the
   rail and the whole size lives here.

   The height term in the width is what stops a short wide screen pushing the
   picture up over the figures — on a 1150-tall screen it resolves to 920x517
   at the bottom right, leaving the top two bands and the curve entirely clear.
   ═══════════════════════════════════════════════════════════════════════ */
#dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 5;
  display: grid;
  align-content: center;
  width: min(46vw, (100dvh - 260px) * 16 / 9);
  height: calc(min(46vw, (100dvh - 260px) * 16 / 9) * 9 / 16);
  max-height: none;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: #000;
  overflow: hidden;
  box-shadow: 0 32px 80px rgb(0 0 0 / 0.62);
}
/* The docked tile keeps its slot in the rail's flow, so the rail does not
   collapse and reflow the board the moment the picture leaves it. */
#dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv::before {
  content: "";
  display: block;
}
#dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv .bb-mon,
#dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv .bb-mon-pic,
#dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv iframe {
  width: 100%;
  height: 100%;
  max-height: none;
  border-radius: 0;
}
#dashEdgeMiniOut[aria-pressed="true"] {
  color: var(--accent);
}

/* A phone gets one size, anchored to the TOP: the fixed tab bar and its
   new-trade button both paint over the dashboard's stacking context, so a
   bottom anchor lands underneath them. The top only has to clear the sticky
   header. */
@media (max-width: 899px) {
  #dashboard.is-active .dash-edge-mini.is-tv-out .dem-tv {
    width: calc(100vw - 16px);
    height: calc((100vw - 16px) * 9 / 16);
    left: 8px;
    right: auto;
    bottom: auto;
    top: calc(56px + env(safe-area-inset-top, 0px));
  }
}

/* ponytail: dashboard-scoped popout. .view { display: none }, toggled by
   switchView, means navigating to Journal or Edge takes the fixed TV and its
   iframe with it. A stream that survives navigation needs #dashboard to stop
   being display:none, which is a shell-wide layout change, not a TV change. */

/* The 11px microtype floor, last word in the file so no later density tier
   can undercut it on a phone. */
@media (max-width: 720px) {
  .session-tilt-facts dt,
  .session-tilt-facts dd,
  .session-status-bar,
  .session-cf-tick,
  .session-cf-xlabel,
  .session-cf-skiplabel,
  .session-cf-endlabel,
  #session-intelligence .session-status-bar,
  #session-intelligence .strip-foot {
    font-size: 11px;
  }
}
