/* ==========================================================================
   charting-v1 design system — extends StockProof's "working paper" system
   (../../stockproof/screener/static/screener/css/app.css) directly: same
   tokens, type, spacing, masthead and card vocabulary, so a user moving
   between the two bundle products in one session sees one visual identity,
   not two. The one deliberate addition is candlestick bull/bear coloring
   (see "candlestick convention" below) -- reusing the SAME --verified/--risk
   pair StockProof already uses for portfolio gain/loss, not a new palette.
   Self-hosted fonts vendored locally (../fonts), no CDN.
   ========================================================================== */

/* ---- fonts: latin + latin-ext (latin-ext carries the ₹ glyph, U+20B9) ---- */
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/Archivo-600.cd56e2ec63d7.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/Archivo-600-ext.c5e41aa17045.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/Archivo-700.cd56e2ec63d7.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/Archivo-700-ext.c5e41aa17045.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 800; font-display: swap;
  src: url("../fonts/Archivo-800.cd56e2ec63d7.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 800; font-display: swap;
  src: url("../fonts/Archivo-800-ext.c5e41aa17045.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/IBMPlexSans-400.b2c9031d9fd6.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/IBMPlexSans-400-ext.f0e2925b3fa6.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/IBMPlexSans-500.b2c9031d9fd6.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/IBMPlexSans-500-ext.f0e2925b3fa6.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/IBMPlexSans-600.b2c9031d9fd6.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/IBMPlexSans-600-ext.f0e2925b3fa6.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/IBMPlexMono-400.79936b18df9f.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/IBMPlexMono-400-ext.8450d5944de2.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/IBMPlexMono-500.b1c8a895f5fd.woff2") format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/IBMPlexMono-500-ext.fa8d8838b94a.woff2") format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* ---- tokens (identical to StockProof's, both palettes) ---- */
:root {
  --paper: #EEF0EC;
  --surface: #FBFCFA;
  --ink: #182231;
  --verified: #0E6E63;
  --verified-ink: #0A5751;
  --attention: #875710;
  --slate: #5B6572;
  --rule: #D7DBD5;
  --rule-strong: #BFC5BD;
  --risk: #A23B2E;
  --on-accent: #FFFFFF;
  --verified-rgb: 14,110,99;
  --attention-rgb: 135,87,16;
  --risk-rgb: 162,59,46;
  --ink-rgb: 24,34,49;
  --slate-rgb: 91,101,114;
  --verified-tint: rgba(var(--verified-rgb), 0.08);
  --attention-tint: rgba(var(--attention-rgb), 0.12);
  --risk-tint: rgba(var(--risk-rgb), 0.09);

  --font-display: 'Archivo', 'Segoe UI', system-ui, sans-serif;
  --font-body: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', 'Consolas', monospace;

  --fs-display: clamp(1.7rem, 1.15rem + 2.2vw, 2.35rem);
  --fs-h2: 1.05rem;
  --fs-body: 1rem;
  --fs-sm: 0.855rem;
  --fs-xs: 0.72rem;
  --fs-stat: clamp(1.9rem, 1.4rem + 1.6vw, 2.35rem);
  --fs-stat-na: 1.4rem;

  --s1: 0.25rem; --s2: 0.5rem; --s3: 0.75rem; --s4: 1rem;
  --s5: 1.5rem; --s6: 2rem; --s7: 3rem;
  --r: 3px; --r-lg: 6px;
  --maxw: clamp(1360px, 78vw, 2700px);
  --maxw-prose: 46rem;

  /* ---- visual foundation v2 (docs/design-notes-v2.md) ---- */
  /* one step below --paper: a deeper ground plane so primary surfaces (the
     home proof strip, the chart-page rail) sit ON something instead of
     every element sharing one ground. Derived from the same palette, not a
     new hue. */
  --paper-deep: #E5E8E2;
  /* plotting-grid texture for the proof strip -- ink at low alpha, both themes */
  --grid-line: rgba(24, 34, 49, 0.07);
  /* the two ad-hoc shadows already in use (auth-card, tile hover), promoted
     to tokens so elevation is a system rather than per-rule improvisation */
  --shadow-1: 0 2px 6px rgba(var(--ink-rgb), 0.08), 0 1px 2px rgba(var(--ink-rgb), 0.06);
  --shadow-2: 0 4px 12px -6px rgba(var(--ink-rgb), 0.18);
  /* one horizontal rhythm token shared by masthead + main */
  --gutter: clamp(1rem, 2vw, 2.5rem);
  /* chart-page side rail width: wide enough for the indicator list, capped
     so an ultrawide monitor gives its extra width to the CANVAS, not the rail */
  --rail-w: clamp(20rem, 21vw, 26rem);
  --fs-hero: clamp(2.1rem, 1.4rem + 2.8vw, 3.4rem);

  color-scheme: light;
  /* Global, not scoped to one control: without this every native form
     control the UA themes itself (range sliders, checkboxes, radios,
     progress bars) renders with the browser's default blue regardless of
     this system's green accent -- the replay scrubber was the visible
     case, but it's a token-system gap, not a replay-bar-specific one.
     accent-color inherits like color, so one declaration here covers every
     current and future native control site-wide; the redundant per-control
     override that used to live on .indicator-toggle input is gone. */
  accent-color: var(--verified);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #10151E;
    --surface: #1A2330;
    --ink: #E7EAF0;
    --verified: #2FC7AF;
    --verified-ink: #5FDCC7;
    --attention: #E0A430;
    --slate: #94A1B3;
    --rule: #202A38;
    --rule-strong: #2E3A4B;
    --risk: #F28B7D;
    --on-accent: #10151E;
    --verified-rgb: 47,199,175;
    --attention-rgb: 224,164,48;
    --risk-rgb: 242,139,125;
    --slate-rgb: 148,161,179;
    /* Token bug, fixed at source: this was hardcoded 0,0,0 (light mode's
       --ink, not dark mode's), the one *-rgb token that didn't track its
       own hex value the way --verified-rgb/--attention-rgb/--risk-rgb/
       --slate-rgb all do above. It's the source for --shadow-1/--shadow-2
       (see their declaration on :root), so a black-on-near-black shadow
       was producing almost no visible elevation in dark mode -- the
       --grid-line value two lines down already derives --ink's real dark
       RGB (231,234,240) inline; this now uses the same triplet instead of
       silently disagreeing with it. */
    --ink-rgb: 231,234,240;
    --paper-deep: #0B0F16;
    --grid-line: rgba(231, 234, 240, 0.055);
    /* candlestick contrast bump (visual foundation v2 refinement, Step 4)
       -- see the comment below --bull/--bear's declaration for why these
       are their own values rather than plain --verified/--risk aliases. */
    --bull: #2ADBBF;
    --bear: #FA7C6A;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper: #10151E;
  --surface: #1A2330;
  --ink: #E7EAF0;
  --verified: #2FC7AF;
  --verified-ink: #5FDCC7;
  --attention: #E0A430;
  --slate: #94A1B3;
  --rule: #202A38;
  --rule-strong: #2E3A4B;
  --risk: #F28B7D;
  --on-accent: #10151E;
  --verified-rgb: 47,199,175;
  --attention-rgb: 224,164,48;
  --risk-rgb: 242,139,125;
  --slate-rgb: 148,161,179;
  --ink-rgb: 231,234,240;
  --paper-deep: #0B0F16;
  --grid-line: rgba(231, 234, 240, 0.055);
  --bull: #2ADBBF;
  --bear: #FA7C6A;
  color-scheme: dark;
}

/* Candlestick convention: --bull/--bear stay in the SAME teal/red family
   StockProof already assigns to .gain/.loss for portfolio P&L -- a bull
   candle IS a "gain" and a bear candle IS a "loss" in exactly the sense
   that pairing already carries, so this is still not a new color
   vocabulary. Through visual foundation v2 they were plain aliases of
   --verified/--risk; visual foundation v2 refinement Step 4 gives them
   their own more-saturated values instead (set directly per theme above
   and in the dark blocks, not via var()) -- a candlestick chart has to be
   scannable at a glance, which asked for more contrast than --verified/
   --risk's calmer "working paper" tone (tuned for body text, links,
   buttons) actually gives at typical candle-body size. --verified/--risk
   themselves, and everywhere else that reads them (links, .gain/.loss
   text, badges, buttons), are UNCHANGED -- only the candle/directional-
   indicator-line pair got bolder. --bull-ink is still a plain
   --verified-ink alias (nothing currently reads it, kept for parity). */
:root { --bull: #006D60; --bull-ink: var(--verified-ink); --bear: #A42C1D; --neutral: var(--slate); }

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--verified-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--verified); }

h1, h2, h3 { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.01em; color: var(--ink); margin: 0; }
h1 { font-size: var(--fs-display); line-height: 1.08; }
h2 { font-size: var(--fs-h2); font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; }

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--slate); }
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
code { font-family: var(--font-mono); font-size: 0.92em; }

:focus-visible { outline: 2px solid var(--verified); outline-offset: 2px; border-radius: 1px; }

input, select, textarea {
  background-color: var(--surface);
  color: var(--ink);
  border-color: var(--rule-strong);
}
select option, option, optgroup { background-color: var(--surface); color: var(--ink); }
input::placeholder, textarea::placeholder { color: var(--slate); opacity: 1; }
input:disabled, select:disabled, textarea:disabled, option:disabled { color: var(--slate); opacity: 1; cursor: not-allowed; }

/* ---- masthead: shared nav shell (claude/nav-shell-design-2026-08.md) ----
   Two rows -- .topbar (persistent: mark slot / section switcher / right
   zone) and .subnav (this product's own pages, section-local, one layer
   below). See templates/base.html's own comment for the full rationale;
   this is the CSS half. */
.masthead { background: var(--surface); border-top: 4px solid var(--verified); border-bottom: 3px double var(--rule-strong); }
.topbar { width: 100%; margin: 0 auto; padding: var(--s4) var(--gutter); box-sizing: border-box;
  display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap; }

/* Mark slot: empty until a real cross-product name exists -- a small
   geometric glyph only, deliberately no invented wordmark text (the old
   "■ CHARTING" is retired; "Charting" now lives in .section-switcher
   below). Kept clickable (home link) since a mark occupying this position
   is the universal "take me to this product's home" affordance, same as
   the old wordmark's own behavior. */
.mark-slot { display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; flex-shrink: 0; }
.mark-glyph { width: 12px; height: 12px; background: var(--verified); display: inline-block; }

/* Section switcher: Screener / Charting / F&O, same order/presence on
   every page of every product in the bundle. Active = bold filled pill
   in the accent teal, confident Archivo weight -- explicitly NOT a thin
   underline (the OLD .nav a.active treatment, now only used by .subnav
   below, one level down in the hierarchy this shell establishes). */
.section-switcher { display: flex; align-items: center; gap: var(--s2); font-family: var(--font-display); }
.section-link { font-weight: 600; font-size: var(--fs-sm); color: var(--slate); text-decoration: none;
  padding: 0.35rem 0.9rem; border-radius: 999px; }
.section-link:hover { color: var(--ink); background: var(--paper); }
.section-link.section-active { background: var(--verified); color: var(--on-accent); font-weight: 800; }
.section-link.section-active:hover { background: var(--verified); color: var(--on-accent); }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--s4); font-size: var(--fs-sm); flex-wrap: wrap; }
.account-area { display: flex; align-items: center; gap: var(--s3); }
.account-area a { font-weight: 500; color: var(--ink); text-decoration: none; }
.account-area a:hover { color: var(--verified); }
.account-area .who { color: var(--slate); font-family: var(--font-mono); font-size: var(--fs-xs); }
.account-area form { display: inline; margin: 0; }
.account-area .linkish { background: none; border: none; color: var(--ink); cursor: pointer; font: inherit; font-weight: 500; padding: 0; }
.account-area .linkish:hover { color: var(--verified); }

/* Symbol chip (right zone, chart.html only -- see base.html's
   topbar_symbol_chip block): local display only, no cross-product
   context-carrying yet (see the spec's build-sequence note). */
.symbol-chip { display: inline-flex; align-items: center; gap: var(--s1);
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink);
  background: var(--paper); border: 1px solid var(--rule-strong); border-radius: var(--r);
  padding: 0.2rem 0.55rem; }

/* Sub-nav: this product's own pages, one flat row (charting-v1's page
   count doesn't warrant StockProof's Discover/Analyze/Reference grouping
   -- see the spec's own "group sensibly if there's enough to group"
   guidance). Reuses the exact active/hover treatment the top-level nav
   used to have, one level down from where it used to live. */
.subnav { border-top: 1px solid var(--rule); padding: var(--s3) var(--gutter);
  display: flex; align-items: center; gap: var(--s4); font-size: var(--fs-sm); flex-wrap: wrap; }
.subnav a { font-weight: 500; color: var(--ink); text-decoration: none; display: inline-flex; align-items: center; gap: var(--s1);
  padding-bottom: 0.2rem; box-shadow: 0 2px 0 0 transparent; }
.subnav a:hover { color: var(--verified); }
.subnav a.active { color: var(--verified-ink); font-weight: 700; box-shadow: 0 2px 0 0 var(--verified); }

/* nav light/dark toggle -- a quiet segmented control, same shape as
   StockProof's own currency/theme toggles (screener/static/screener/css/
   app.css's .unit-toggle) so the two products' masthead controls read as
   one system. */
.theme-toggle {
  display: inline-flex; align-items: center; gap: 0.15rem;
  border: 1px solid var(--rule-strong); border-radius: 2px;
  padding: 0.05rem 0.2rem 0.05rem 0.3rem; background: var(--surface);
  font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.4;
}
.theme-toggle .theme-tag { color: var(--slate); margin-right: 0.1rem; }
.theme-toggle .theme-opt {
  padding: 0.02rem 0.3rem; border-radius: 2px; color: var(--slate);
  text-decoration: none; font-weight: 500;
}
.theme-toggle a.theme-opt:hover { color: var(--verified); background: var(--paper); }
.theme-toggle .theme-opt.active { background: var(--ink); color: var(--surface); }

.nav-toggle { display: none; margin-left: auto; background: var(--surface);
  border: 1px solid var(--rule-strong); border-radius: var(--r);
  width: 2.75rem; height: 2.75rem; padding: 0; cursor: pointer;
  align-items: center; justify-content: center; flex-shrink: 0; }
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after { display: block; width: 1.1rem; height: 2px; background: var(--ink); }
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before, .nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }

/* ---- layout ---- */
/* main is a size container (visual foundation v2): page layouts respond to
   the width they ACTUALLY have via @container queries -- deliberate,
   explicit sizing logic, not viewport guesses filtered through max-width
   math. Browsers without container-query support simply never activate the
   wide-layout rules and keep the stacked layout, which is correct. */
main { max-width: var(--maxw); margin: 0 auto; padding: var(--s6) var(--gutter) var(--s7);
  container-type: inline-size; container-name: page; }
.page-head { margin-bottom: var(--s5); }
/* Two-column header (chart.html only) -- mirrors StockProof's own
   company_detail.html .detail-head: an .ident block (kicker/h1/captions)
   left, a per-record links/actions block right, wrapping to stack on a
   narrow container rather than a fixed breakpoint. A page-scoped modifier
   rather than making .page-head itself flex: every other .page-head user
   (scans/list.html, watchlist/list.html, scans/form.html, ...) puts h1 and
   .sub as DIRECT children with no wrapping .page-head-ident div, and those
   rely on .page-head's default block stacking -- turning .page-head itself
   into a flex row would put their kicker/h1/sub side by side instead of
   stacked. */
.page-head--split { display: flex; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s4) var(--s5); }
.page-head-ident { flex: 1 1 20rem; }
/* Same role StockProof's .detail-head .action fills, minus the mutating
   buttons -- charting-v1 has no per-chart watchlist toggle (that lives on
   the watchlist page instead), so this holds the one thing that already
   existed and already fit: the cross-product links. Right-aligned to
   match that precedent, both on the shared row and on its own row once
   flex-wrap stacks it below .page-head-ident on a narrow container. */
.page-head-links { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end;
  gap: var(--s1); color: var(--slate); font-size: var(--fs-sm); margin-top: var(--s1); text-align: right; }
.page-head-links a { white-space: nowrap; }
/* the "instrument label" annotation voice -- the mono/uppercase/tracked
   style the system already uses for field labels, table heads and legends,
   named once so new work stops improvising its metrics */
.anno { font-family: var(--font-mono); font-size: var(--fs-xs); text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--slate); }
/* mono overline above a page h1 -- gives every page-head the same two-deck
   anatomy the masthead already has */
.kicker { display: block; font-family: var(--font-mono); font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--verified-ink); margin-bottom: var(--s2); }
.sub { color: var(--slate); font-size: var(--fs-sm); max-width: var(--maxw-prose); }
.page-head .sub { margin-top: var(--s2); }

/* Chart-page typographic hierarchy (visual foundation v2 refinement, Step
   5): the symbol (h1) and its current price are the primary thing this
   page is about, so the price reads at the same headline weight the
   system already uses for a summary-tile's stat (.tile-value) -- not
   competing with h1, but not a caption either. Everything else in
   .page-head (.kicker above, .sub captions/timestamps/links below)
   already reads as secondary via the existing kicker/.sub treatment;
   this is the one primary element that was missing entirely, previously
   readable only off the chart canvas's own small axis label. */
.chart-price { font-family: var(--font-display); font-weight: 800; font-size: var(--fs-stat);
  line-height: 1.05; color: var(--ink); margin: var(--s1) 0 0; font-variant-numeric: tabular-nums; }
.chart-price:empty { margin: 0; }
.chart-price-delta { font-size: var(--fs-sm); font-weight: 600; margin-left: var(--s2); }
.prose, .empty-note, .help, .limitation-note { max-width: var(--maxw-prose); }
.backlink { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--slate); text-decoration: none; text-transform: uppercase; letter-spacing: 0.04em; }
.backlink:hover { color: var(--verified); }

/* ---- flash messages ---- */
.messages { list-style: none; padding: 0; margin: 0 0 var(--s5); }
.messages li { padding: var(--s3) var(--s4); border-radius: var(--r); margin-bottom: var(--s2);
  font-size: var(--fs-sm); border: 1px solid var(--rule); background: var(--surface); border-left: 3px solid var(--slate); }
.messages li.success { border-left-color: var(--verified); background: var(--verified-tint); }
.messages li.error { border-left-color: var(--risk); background: var(--risk-tint); }
.messages li.info { border-left-color: var(--attention); background: var(--attention-tint); }

/* ---- buttons ---- */
.btn, .btn-ghost { font-family: var(--font-body); font-weight: 600; font-size: var(--fs-sm);
  padding: 0.5rem 1.1rem; border-radius: var(--r); cursor: pointer; height: 2.25rem;
  box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; border: none; }
.btn { background: var(--verified); color: var(--on-accent); border: 1px solid var(--verified-ink); font-weight: 600; }
.btn:hover { background: var(--verified-ink); }
.btn-ghost { background: transparent; color: var(--slate); border: 1px solid var(--rule-strong); font-weight: 500; }
.btn-ghost:hover { background: var(--paper); border-color: var(--slate); color: var(--ink); }
.btn-small { font-family: var(--font-mono); font-size: var(--fs-xs); background: transparent;
  border: 1px solid var(--rule-strong); color: var(--ink); border-radius: var(--r);
  padding: 0.22rem 0.55rem; cursor: pointer; white-space: nowrap; letter-spacing: 0.02em; }
.btn-small:hover { background: var(--paper); border-color: var(--slate); }
/* v1.2 item 9: touch tap targets, not just narrow-viewport layout --
   (pointer: coarse) is the correct media feature for this (a touch/pen
   input, regardless of window width) rather than max-width, which this
   codebase's earlier mobile passes already used for LAYOUT and would be
   the wrong tool here: a desktop browser resized narrow has no touch
   input to make room for, and a touch device isn't always narrow (a
   tablet in landscape). At ~30px tall (0.22rem padding + line-height),
   .btn-small -- "Clear all drawings", every drawing's own Remove/Edit --
   sat well under the ~44px minimum recommended for a finger (Apple HIG /
   Material Design both land in the 44-48px range); this is the one
   shared component behind that button everywhere it appears (watchlist,
   layouts, drawings list alike), so sizing it up here is the correct
   place to do it once rather than a drawing-tools-only duplicate. */
@media (pointer: coarse) {
  .btn-small { padding: 0.55rem 0.8rem; min-height: 2.75rem; }
}

/* ---- forms: field-row + auth-card ("credentials slip") ---- */
.auth-card { max-width: 24rem; background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: var(--r); padding: var(--s5); position: relative; margin-top: var(--s4);
  box-shadow: var(--shadow-1); }
.auth-card.wide { max-width: none; }
/* The Replay control strip (v1.2 item 7): same box, tighter padding and no
   shadow -- it's a row of interactive controls, not a form-style card,
   now that its explanatory paragraph moved behind a tooltip (below). */
.auth-card.compact-card { padding: var(--s3) var(--s4); box-shadow: none; }
.auth-card.compact-card legend { display: flex; align-items: center; gap: var(--s2);
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--slate); padding: 0 var(--s2); }
.field-row { margin-bottom: var(--s4); }
.field-row label { display: block; font-family: var(--font-mono); font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--slate); margin-bottom: var(--s1); }
.field-row input, .field-row select, .field-row textarea { width: 100%; background: var(--paper); border: 1px solid var(--rule-strong);
  color: var(--ink); font-family: var(--font-body); font-size: var(--fs-sm); padding: 0.5rem 0.6rem; border-radius: var(--r); box-sizing: border-box; }
.field-row textarea { font-family: var(--font-mono); }
.field-row input:hover, .field-row select:hover, .field-row textarea:hover { border-color: var(--slate); }
.help { font-size: var(--fs-xs); color: var(--slate); margin-top: var(--s1); display: block; line-height: 1.4; }
.auth-card .btn { width: 100%; margin-top: var(--s2); }
.auth-alt { color: var(--slate); font-size: var(--fs-sm); margin-top: var(--s4); }
.errorlist { color: var(--risk); font-size: var(--fs-xs); list-style: none; padding: 0; margin: var(--s1) 0 0; }
.cooldown-note { font-family: var(--font-body); font-size: var(--fs-sm); color: var(--attention);
  background: var(--attention-tint); border: 1px solid rgba(var(--attention-rgb),0.45);
  border-left: 3px solid var(--attention); border-radius: var(--r); padding: var(--s3) var(--s4); margin-bottom: var(--s4); }

/* ---- ledger table ---- */
.table-scroll { overflow-x: auto; border: 1px solid var(--rule-strong); border-radius: var(--r); background: var(--surface); }
table.ledger { width: 100%; border-collapse: collapse; min-width: 480px; }
table.ledger thead th { font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--slate);
  text-align: left; padding: var(--s3) var(--s3); white-space: nowrap;
  border-bottom: 3px double var(--rule-strong); background: var(--surface); }
table.ledger thead th.num { text-align: right; }
table.ledger tbody td { padding: var(--s3) var(--s3); border-bottom: 1px solid var(--rule); font-size: var(--fs-sm); vertical-align: baseline; }
table.ledger tbody tr:last-child td { border-bottom: none; }
table.ledger tbody tr:hover td { background: var(--verified-tint); }
table.ledger tbody tr.matched td { background: var(--verified-tint); }

/* ---- cards: paper-card (stat) + summary-tile (headline stat) ---- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s4); margin: var(--s4) 0; }
.summary-tile { background: var(--surface); border: 1px solid var(--rule-strong);
  border-left: 3px solid var(--verified); border-radius: var(--r); padding: var(--s4);
  display: flex; flex-direction: column; gap: var(--s2); }
.summary-tile:hover { box-shadow: var(--shadow-2); }
.summary-tile .tile-label { font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.03em; color: var(--slate); text-transform: uppercase; }
.summary-tile .tile-value { font-family: var(--font-display); font-weight: 800; font-size: var(--fs-stat);
  line-height: 1.05; color: var(--ink); font-variant-numeric: tabular-nums; }
.summary-tile .tile-value.gain { color: var(--verified-ink); }
.summary-tile .tile-value.loss { color: var(--risk); }
.gain { color: var(--verified-ink); }
.loss { color: var(--risk); }

/* ---- alert / caveat box (StockProof's .factor-alert treatment): a
   limitation that must be READ, never collapsed behind a <details> --
   used for the "not a recommendation" line and the autocorrelation caveat. ---- */
.alert-box { border: 1px solid rgba(var(--risk-rgb), 0.35); border-left: 4px solid var(--attention);
  background: var(--attention-tint); border-radius: var(--r); padding: var(--s3) var(--s4);
  margin: var(--s4) 0; font-size: var(--fs-sm); color: var(--ink); }
.alert-box.risk { border-left-color: var(--risk); background: var(--risk-tint); }
.alert-box strong { color: var(--ink); }
.empty-note, .partial-note { color: var(--slate); font-size: var(--fs-sm); background: var(--surface);
  border: 1px dashed var(--rule-strong); border-radius: var(--r); padding: var(--s5); }
.error-note { background: var(--risk-tint); border: 1px solid rgba(var(--risk-rgb),0.45);
  border-left: 3px solid var(--risk); padding: var(--s3) var(--s4); border-radius: var(--r); color: var(--ink); }

/* ---- definition / footnote line ---- */
.definition { color: var(--slate); font-size: var(--fs-xs); margin: var(--s2) 0 var(--s4); }

/* ---- distribution table (backtest return histogram) ---- */
table.dist-table { width: 100%; border-collapse: collapse; margin-top: var(--s3); }
table.dist-table th { font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--slate); text-align: left; padding: var(--s2) var(--s3);
  border-bottom: 2px solid var(--rule-strong); }
table.dist-table td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--rule); font-size: var(--fs-sm); vertical-align: middle; }
.dist-bar-cell { width: 55%; }
.dist-bar-track { background: var(--paper); border-radius: 3px; overflow: hidden; height: 0.85rem; }
.dist-bar { background: var(--verified); height: 100%; border-radius: 3px; }

/* ---- scan builder: available-names reference panel ---- */
.names-ref { font-size: var(--fs-sm); }
.names-ref h2 { font-size: var(--fs-xs); margin: 0 0 var(--s2); }
.names-ref ul { list-style: none; margin: 0; padding: 0; }
.names-ref li { padding: 0.15rem 0; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--slate); }
.panel-pair { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: flex-start; }
/* The scan-builder form (scans/form.html) is the one .panel-pair user with
   an actual form card, not a rail panel -- `max-width: none` here let it
   grow to fill whatever space was left beside .names-ref on a wide
   viewport, which read as one small card lost in a mostly-empty page
   rather than the compact, capped card every other .auth-card is. Capped
   at --maxw-prose (the existing content-width token, not a new value)
   instead of removed outright: wide enough for the rule textarea to
   breathe, narrow enough to still read as a card. */
.panel-pair > .auth-card { flex: 1 1 320px; max-width: var(--maxw-prose); margin-top: 0; }
.panel-pair > .names-ref { flex: 0 0 16rem; }
/* .auth-card .btn's width:100% is right for a narrow "credentials slip"
   (login, register, watchlist-add) where the card itself is ~24rem --
   full-width there reads as one clear call to action. At the wider cap
   above, the same rule produced one heavy full-width primary button next
   to a comparatively small form. Right-sized back to how buttons size
   everywhere else in the product (scans:list's "New scan", the toolbar
   buttons): inline, content-width, not a stretched block. */
.panel-pair > .auth-card .btn { width: auto; margin-top: var(--s3); }

/* ---- chart page ---- */
#fundamentals-panel { margin: var(--s4) 0 0; }
#fundamentals-panel summary { font-family: var(--font-display); font-weight: 700; cursor: pointer;
  color: var(--ink); }
#fundamentals-panel summary::marker { color: var(--slate); }
#fundamentals-content { margin-top: var(--s3); display: flex; flex-direction: column; gap: var(--s2); }
.fundamentals-row { display: flex; justify-content: space-between; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s2) 0; border-bottom: 1px solid var(--rule); font-size: var(--fs-sm); }
.fundamentals-row:last-child { border-bottom: none; }
.fundamentals-label { color: var(--slate); font-family: var(--font-mono); font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.04em; align-self: center; }
.fundamentals-value { font-family: var(--font-mono); text-align: right; }
#scan-performance-panel { margin: var(--s4) 0; }
#scan-performance-list { list-style: none; margin: var(--s3) 0 0; padding: 0; }
#scan-performance-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2) var(--s4);
  padding: var(--s3) 0; border-bottom: 1px solid var(--rule); }
#scan-performance-list li:last-child { border-bottom: none; }
.scan-performance-name { font-weight: 700; }
.scan-performance-stats { font-size: var(--fs-sm); color: var(--slate); }
/* ---- compact control strip (v1.2 item 7): chart-type/timeframe/Renko +
   the fullscreen toggle in one row, all sized like #fullscreen-toggle
   already was -- no stacked .field-row block label, an .sr-only label
   instead (the same pattern #replay-speed already used). See
   docs/chart-rail-collapse-proposal.md. ---- */
#chart-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); margin-bottom: var(--s3); }
#chart-toolbar .control-chip { display: flex; align-items: center; gap: var(--s2); }
#chart-toolbar select, #chart-toolbar input[type="number"], #chart-toolbar input[type="text"] {
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.02em;
  padding: 0.4rem 0.6rem; border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink); cursor: pointer; }
#chart-toolbar input[type="number"] { width: 5rem; cursor: text; }
#chart-toolbar input[type="text"] { width: 11rem; cursor: text; }
#chart-toolbar select:hover, #chart-toolbar input[type="number"]:hover, #chart-toolbar input[type="text"]:hover {
  border-color: var(--verified); color: var(--verified-ink); }
#chart-toolbar input[type="text"]:focus { border-color: var(--verified); outline: none; }
/* fullscreen-toggle is the row's last child regardless of the Renko chip's
   hidden/shown state, and stays pinned to the strip's right edge either way */
#fullscreen-toggle { margin-left: auto; }
#renko-settings { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
/* the display:flex above outweighs the UA's [hidden]{display:none}, so the
   attribute chart.js toggles (renkoSettings.hidden = !isRenko) never
   actually hid the row -- restate it at higher specificity */
#renko-settings[hidden] { display: none; }
#replay-controls { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
#replay-controls button { font-family: var(--font-mono); font-size: var(--fs-xs);
  padding: 0.4rem 0.75rem; border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink); cursor: pointer; }
#replay-controls button:hover:not(:disabled) { border-color: var(--verified); color: var(--verified-ink); }
#replay-controls button:disabled { opacity: 0.5; cursor: not-allowed; }
#replay-controls select, #replay-controls input[type="date"] { width: auto; }
#replay-scrubber { flex: 1 1 12rem; min-width: 8rem; }
#replay-status { font-size: var(--fs-xs); color: var(--slate); white-space: nowrap; }
.indicator-toggle:has(input:disabled) { opacity: 0.5; }
#drawing-toolbar button:disabled { opacity: 0.5; cursor: not-allowed; }
#drawing-toolbar button:disabled:hover { border-color: var(--rule-strong); color: var(--ink); }

/* ---- collapsible rail panels (v1.2 item 5): native <details>, closed by
   default -- see docs/chart-rail-collapse-proposal.md. Shared chrome for
   both #indicator-toggles and #drawing-toolbar-card, now <details
   class="rail-panel"> rather than a permanently-expanded <fieldset>. ---- */
.rail-panel { border: 1px solid var(--rule-strong); background: var(--surface);
  border-radius: var(--r); }
/* <summary> DELIBERATELY keeps its native display (list-item) rather than
   becoming display:flex directly -- that was tried first and it broke
   native keyboard activation: Enter/Space stopped toggling the <details>
   even though the element stayed focusable and mouse clicks still worked
   (confirmed live -- a real, narrow Chromium quirk where changing a
   <summary>'s own display away from list-item silently drops its default
   keydown handling, not just its marker). list-style:none suppresses the
   native disclosure triangle WITHOUT changing display, which keeps that
   native behavior intact; the actual icon-first row layout lives on the
   inner .rail-panel-summary-row span instead. */
.rail-panel > summary { list-style: none; cursor: pointer; user-select: none;
  padding: var(--s2) var(--s3); color: var(--ink); }
.rail-panel > summary::-webkit-details-marker { display: none; }
.rail-panel > summary:hover { color: var(--verified-ink); }
.rail-panel > summary:focus-visible { outline: 2px solid var(--verified); outline-offset: -2px; }
/* v1.2 item 9: this row IS how a touch user reaches the drawing-tools
   panel at all (it's collapsed by default -- see the rail-collapse work).
   Full-width already makes it a forgiving target horizontally; this adds
   the same vertical headroom .btn-small/#drawing-toolbar button get. */
@media (pointer: coarse) {
  .rail-panel > summary { padding: var(--s3) var(--s3); min-height: 2.75rem; }
}
.rail-panel-summary-row { display: flex; align-items: center; gap: var(--s1);
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.02em;
  text-transform: uppercase; white-space: nowrap; }
.rail-panel-icon { flex: 0 0 auto; width: 1rem; height: 1rem; color: var(--slate); }
.rail-panel-title { flex: 1 1 auto; }
.rail-panel-count { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--slate);
  background: var(--paper); border: 1px solid var(--rule); border-radius: 999px;
  padding: 0.05rem 0.5rem; font-size: var(--fs-xs); }
.rail-panel-chevron { flex: 0 0 auto; width: 0.7rem; height: 0.7rem; color: var(--slate);
  transition: transform 0.15s ease; }
.rail-panel[open] > summary .rail-panel-chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .rail-panel-chevron { transition: none; } }
.rail-panel-body { padding: 0 var(--s4) var(--s4); border-top: 1px solid var(--rule); padding-top: var(--s4); }

/* Always a single vertical list -- the rail is narrow in every layout
   (stacked or workbench), so there is no width at which a wrapping tag-
   cloud reads better than a plain settings list, and (per the proposal)
   dropping the wrap entirely is what fixes the rail's horizontal-overflow
   bug at its root: a column-direction flex container that's ALSO wrap-
   capable starts a second column instead of scrolling vertically the
   moment its height is constrained by its own parent (#chart-rail's
   max-height) -- see the bug's traced mechanism in the proposal doc. */
#indicator-toggles .rail-panel-body { display: flex; flex-direction: column; flex-wrap: nowrap; gap: var(--s2); }
.indicator-toggle { display: flex; align-items: center; gap: var(--s1);
  font-size: var(--fs-sm); font-weight: 400; color: var(--ink); }
.indicator-toggle label { display: inline-flex; align-items: center; }
.indicator-toggle input { margin-right: var(--s2); }

/* Indicator captions (visual foundation v2 refinement, Step 2): every
   caption's full text is kept verbatim -- it documents a real, deliberate
   convention decision (Aroon's 25-vs-14 default, VWAP's anchored-not-
   intraday caveat, Ichimoku's truncation note, ...) -- just relocated
   behind a small info icon instead of sitting as permanent paragraph text
   under every checkbox. CSS-only disclosure (:hover for a mouse,
   :focus-within so a keyboard Tab or a touch tap -- which focuses the
   button -- reveals it too, and blurring/tapping elsewhere hides it
   again) -- no JS needed, matching this page's other no-JS disclosures
   (the mobile nav toggle aside, which needs its aria-expanded state
   tracked). The icon sits OUTSIDE the <label> (a sibling, not nested
   inside it) specifically so clicking it doesn't also toggle the
   indicator's own checkbox -- a <button> nested inside a <label for>
   would double-fire the checkbox's default click-forwarding behavior. */
.info-tip { position: relative; display: inline-flex; }
.info-icon { display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; border-radius: 50%; border: 1px solid var(--rule-strong);
  background: var(--surface); color: var(--slate); font-family: var(--font-mono);
  font-style: italic; font-size: 0.65rem; line-height: 1; padding: 0; cursor: help;
  position: relative; }
.info-icon:hover, .info-icon:focus-visible { border-color: var(--verified); color: var(--verified-ink); }
/* v1.2 item 9: the visible "i" badge stays small (growing it to a full
   44px circle inline with checkbox/legend text would look absurd) --
   instead an invisible ::after pseudo-element expands the actual
   TAPPABLE area around it. A pseudo-element is hit-tested as part of its
   own element, so this genuinely enlarges what registers as "tapped the
   icon", not just what's painted. See .btn-small's comment above for why
   (pointer: coarse), not a width breakpoint. */
@media (pointer: coarse) {
  .info-icon::after { content: ""; position: absolute; inset: -0.85rem; }
}
.info-bubble { position: absolute; left: 0; top: calc(100% + 0.35rem); z-index: 5;
  width: max-content; max-width: min(22rem, calc(100vw - 3rem)); background: var(--ink); color: var(--paper);
  font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 400; line-height: 1.45;
  padding: var(--s2) var(--s3); border-radius: var(--r); box-shadow: var(--shadow-2);
  opacity: 0; visibility: hidden; transform: translateY(-2px); pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease; }
.info-tip:hover .info-bubble, .info-tip:focus-within .info-bubble { opacity: 1; visibility: visible; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .info-bubble { transition: none; } }

/* An explicit CSS height in EVERY state is what makes chart sizing
   deliberate rather than emergent (the fullscreen work's core finding --
   see the item-6 report): without a rule here, #chart-container's normal-
   mode size is whatever the chart library's autoSize wrapper last set it
   to, so exiting fullscreen had no external size to shrink back to and
   the canvas silently stayed fullscreen-sized. Every state (normal,
   fullscreen, workbench) therefore sets height in CSS, and autoSize's
   ResizeObserver tracks it.

   The value is viewport-relative (visual foundation v2): 58dvh means a
   tall monitor finally gets a TALL chart instead of a fixed 480px
   letterbox that only ever got wider; the 420px floor keeps small
   laptops at least as usable as the old fixed value, and the 900px cap
   stops a portrait/4K monitor from pushing every control off-screen.
   chart.js's createChart({height: 480}) is only the pre-first-layout
   value; autoSize takes over from this rule immediately. */
#chart-container { margin-top: var(--s4); border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--surface); overflow: hidden; height: clamp(420px, 58dvh, 900px); }

/* ---- chart workbench (visual foundation v2): canvas column + rail ----
   Once the page container is wide enough, #chart-workspace stops being a
   vertical stack and becomes a two-region grid: the canvas column keeps
   everything that manipulates the chart directly (type/timeframe/replay/
   the canvas itself), and the rail holds reference-and-settings panels
   (indicators, drawing tools, fundamentals, scan performance). Extra
   width on a wide monitor buys MORE VISIBLE CONTENT AT ONCE, never a
   wider letterbox. Below the threshold nothing here applies and the
   stacked layout is exactly the pre-v2 one. */
@container page (min-width: 1200px) {
  /* fit-content(--rail-w), not a flat var(--rail-w): a FIXED column width
     would hold the rail at the same ~21vw regardless of whether its
     panels are collapsed or expanded, defeating the point of collapsing
     them -- two ~40px "Indicators 17 >" / "Drawing tools 7 >" summary
     rows don't need the same width a 17-row checkbox list does.
     fit-content sizes the column to what its content actually needs, up
     to the --rail-w cap: narrow with both panels collapsed (the default),
     growing back to --rail-w the moment a panel is expanded and its
     content needs the room. This is what makes the canvas column
     genuinely dominate the fullscreen viewport by default, not just the
     rail's vertical space. */
  #chart-workspace { display: grid; grid-template-columns: minmax(0, 1fr) fit-content(var(--rail-w));
    gap: var(--s5); align-items: start; }
  /* the rail scrolls independently and stays put while the user works the
     chart -- a workbench, not a page */
  /* overflow-x:hidden is deliberate, not the implicit "one axis non-
     visible forces the other to auto" behavior overflow-y:auto alone
     would give: an .info-bubble tooltip (position:absolute, opacity:0/
     visibility:hidden until hover) still occupies its full ~22rem layout
     box even while invisible, and CSS visibility/opacity never remove an
     element from layout -- so with only overflow-y set, that inert box
     was inflating #chart-rail's scrollWidth and producing a permanent
     horizontal scrollbar for content nobody could see (confirmed live:
     scrollWidth 561px in a ~305px rail, entirely from indicator captions'
     tooltip boxes, not from the checkbox list itself -- see
     docs/chart-rail-collapse-proposal.md's follow-up note). Explicit
     overflow-x:hidden clips a rarely-hovered tooltip at the rail's edge
     instead of leaving a permanent scrollbar for content that's normally
     invisible -- the right trade-off between the two. */
  #chart-rail { position: sticky; top: var(--s4);
    max-height: calc(100dvh - var(--s6)); overflow-y: auto; overflow-x: hidden; }
}
/* the rail's own gap is the spacing system for its panels, in stacked and
   workbench mode alike -- zero out the panels' standalone margins */
#chart-rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; margin-top: var(--s4); }
#chart-rail > *, #chart-rail #fundamentals-panel, #chart-rail #indicator-toggles { margin-top: 0; margin-bottom: 0; }
/* THE OVERFLOW FIX (v1.2 items 5/7 -- see docs/chart-rail-collapse-
   proposal.md): #chart-rail is itself a column-direction flex container
   with a max-height (above). Flex items default to flex-shrink:1, so
   without this, the rail's own overflow-y:auto never gets to do its job
   -- the flexbox algorithm SQUEEZES each panel's height to fit first,
   which is what turned #indicator-toggles' internal wrap-capable layout
   into a silent second column (confirmed live: scrollWidth 561px in a
   346px box). flex-shrink:0 makes every panel keep its natural height,
   so "more content than fits" is always the rail's scrollbar's problem,
   never an invisible shrink. */
#chart-rail > * { flex-shrink: 0; }
#chart-main { min-width: 0; }
@container page (min-width: 1200px) {
  #chart-rail { margin-top: 0; }
}

/* ---- fullscreen chart toggle (v1.2 item 6) ---- */
#fullscreen-toggle { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.02em;
  padding: 0.4rem 0.75rem; border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink); cursor: pointer; }
#fullscreen-toggle:hover { border-color: var(--verified); color: var(--verified-ink); }

/* #chart-workspace itself lays out normally (no rules needed) until
   .is-fullscreen is toggled on it -- see chart.js's applyFullscreenUI.
   position: fixed lifts it out of <main>'s max-width/padding entirely, so
   it always fills the true viewport regardless of page layout.

   #chart-container gets an explicit height (75dvh) rather than a flexbox
   flex-grow share of whatever's left after its siblings -- a first attempt
   at this used flex: 1 1 auto, and on a real ~720px-tall viewport with
   several controls (chart-toolbar, chart-controls, replay, indicator
   toggles, events-status, drawing toolbar) all wanting their natural
   height, the combined total exceeded the viewport, so flex-shrink pulled
   the canvas down to its min-height FLOOR (256px) -- SMALLER than the
   normal-layout 480px default, the opposite of what "fullscreen" should
   do. A direct height keeps the canvas a guaranteed, generous size on any
   real screen regardless of how many controls are stacked around it; if
   they don't all fit in the remaining ~25dvh, #chart-workspace's own
   overflow-y: auto makes them reachable by scrolling instead of stealing
   space from the canvas. #chart-container has no CSS height of its own in
   the normal (non-fullscreen) layout above -- see chart.js's
   createChart({autoSize:true}) comment and the 3dcca20 fix it documents --
   so this height change is exactly what gives the container's
   ResizeObserver something new to observe; autoSize.js does the rest. */
#chart-workspace.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: var(--paper);
  /* Tighter than the normal-mode workbench gutter/grid-gap (--s5) --
     fullscreen's whole point is maximizing real screen use, so the margin
     given up to breathing room is deliberately smaller here specifically
     (not changed globally: the normal workbench keeps its roomier
     spacing). The higher-specificity #id.class selector overrides the
     plain #id workbench gap rule regardless of source order. */
  padding: var(--s4);
  gap: var(--s4);
  overflow-y: auto;
  height: 100vh;
  height: 100dvh; /* mobile browsers resize the *viewport*, not just hide/show chrome over a fixed 100vh -- see the item-6 report's mobile findings */
}
#chart-workspace.is-fullscreen #chart-toolbar { margin-bottom: var(--s2); }
#chart-workspace.is-fullscreen #chart-container,
#chart-workspace.is-fullscreen #data-unavailable {
  height: 75vh;
  height: 75dvh;
  min-height: 20rem; /* floor so a short/landscape mobile viewport never collapses the canvas to near-0 -- see the item-6 report's mobile findings */
}

/* Page chrome that stays OUTSIDE #chart-workspace (see chart.html) hides
   while it's fullscreen, and the page itself stops scrolling behind the
   fixed overlay. */
body.chart-fullscreen-active { overflow: hidden; }
body.chart-fullscreen-active .masthead,
body.chart-fullscreen-active .page-head,
body.chart-fullscreen-active #events-disclosure,
body.chart-fullscreen-active #fundamentals-panel,
body.chart-fullscreen-active #scan-performance-panel { display: none; }

#events-status { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s4); }
.event-status-line { padding: var(--s2) var(--s3); border-radius: var(--r); font-size: var(--fs-sm);
  font-family: var(--font-mono); border: 1px solid var(--rule); }
.linkish { background: none; border: none; color: var(--verified-ink); cursor: pointer; font: inherit;
  padding: 0; text-decoration: underline; }
.linkish:hover { color: var(--verified); }
.watch-badge { display: inline-block; margin-right: var(--s2); padding: 0.05rem 0.4rem; border-radius: var(--r);
  background: var(--verified-tint); border: 1px solid rgba(var(--verified-rgb),0.4); color: var(--ink);
  font-family: var(--font-mono); font-size: var(--fs-xs); }
.event-status-available { background: var(--verified-tint); border-color: rgba(var(--verified-rgb),0.4); color: var(--ink); }
.event-status-unconfirmed_symbol { background: var(--attention-tint); border-color: rgba(var(--attention-rgb),0.4); color: var(--ink); }
.event-status-unavailable { background: var(--risk-tint); border-color: rgba(var(--risk-rgb),0.4); color: var(--ink); }
/* Structurally not applicable to this instrument (an index has no
   dividends/PIT filings/bulk deals) -- deliberately NOT in the
   available/attention/risk tint family those three sit in. Those three are
   fetch outcomes on a scale from fine to broken; this one isn't on that
   scale at all, so it reads as quiet, neutral chrome (dashed rule, muted
   ink) rather than as a milder shade of "something went wrong". */
.event-status-not_applicable { background: var(--paper); border-style: dashed;
  border-color: var(--rule-strong); color: var(--slate); }

#drawing-toolbar { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
#drawing-toolbar button { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: 0.02em;
  padding: 0.4rem 0.75rem; border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--surface); color: var(--ink); cursor: pointer; }
#drawing-toolbar button:hover { border-color: var(--verified); color: var(--verified-ink); }
#drawing-toolbar button.active { background: var(--ink); color: var(--surface); border-color: var(--ink); }
/* v1.2 item 9: these seven ARE the drawing tools -- the one tap target
   this whole feature most needs to get right on a touch device. Same
   (pointer: coarse) reasoning as .btn-small's comment above. */
@media (pointer: coarse) {
  #drawing-toolbar button { padding: 0.7rem 0.9rem; min-height: 2.75rem; }
}
#drawing-hint { font-size: var(--fs-xs); color: var(--slate); font-family: var(--font-mono); min-height: 1.2em; margin-top: var(--s3); }

#drawings-panel { display: flex; gap: var(--s6); align-items: flex-start; flex-wrap: wrap; margin-top: var(--s4); }
#drawings-list { list-style: none; margin: 0; padding: 0; min-width: 16rem; }
#drawings-list li { display: flex; justify-content: space-between; align-items: center; padding: var(--s2) 0;
  border-bottom: 1px solid var(--rule); font-size: var(--fs-sm); }

/* ---- saved chart layouts (v1.2 item 6) ---- */
#layout-save-form { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
#layout-save-form .field-row { margin-bottom: 0; flex: 1 1 12rem; }
#layouts-list { list-style: none; margin: var(--s3) 0 0; padding: 0; }
.layout-row { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) 0;
  border-bottom: 1px solid var(--rule); font-size: var(--fs-sm); }
.layout-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.layout-delete-form { margin: 0; }

.ca-badge { display: inline-block; margin-left: var(--s2); padding: 0.05rem 0.4rem; border-radius: var(--r);
  background: var(--attention); color: var(--on-accent); font-family: var(--font-mono); font-size: var(--fs-xs);
  cursor: pointer; user-select: none; letter-spacing: 0.02em; }

/* ---- home / entry page (visual foundation v2) ----
   .main-bleed lets the home page own its full width: the proof band runs
   edge to edge, and each section re-centers its own content with the
   shared --maxw/--gutter rhythm.

   Real depth zones (depth-polish pass): --paper-deep, previously used only
   for the hero band, now grounds the WHOLE home page content. The masthead
   keeps its own --surface + double-rule border (this file's .masthead), so
   it stays crisply separated at the top; below it, one continuous deep
   zone holds the strip and the cards (.proof-band no longer cuts it off
   with its own hard border-bottom), and both read as elevated --surface
   objects sitting ON that ground -- the figure/ground move the strip
   always made, now applied consistently instead of stopping at the
   band's edge. */
main.main-bleed { max-width: none; padding: 0; background: var(--paper-deep); }
main.main-bleed > .messages { max-width: var(--maxw); margin: var(--s4) auto 0; padding: 0 var(--gutter); }

.proof-band { padding: var(--s7) 0 var(--s5); }
.proof-band-inner { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }
.hero-title { font-size: var(--fs-hero); line-height: 1.05; }
.proof-lede { margin: var(--s3) 0 0; max-width: var(--maxw-prose); }

/* Jump-to-chart search (accounts/views.py's jump_to_chart_view): the
   home page's own fix for the confirmed "reaching a chart requires the
   watchlist detour" gap. Reuses .field-row/.btn as-is for the label and
   button, just laid out in one row instead of a stacked auth-card form --
   this is a single-purpose navigation utility, not a data-entry card.
   The input's background is --surface (not .field-row's default --paper)
   to match the figure/ground grammar the rest of this page already
   established: real interactive objects sit as --surface on the
   --paper-deep hero ground, exactly like the strip and the cards below. */
.symbol-jump { margin: var(--s5) 0 0; display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: var(--s3); max-width: var(--maxw-prose); }
.symbol-jump .field-row { margin-bottom: 0; flex: 1 1 16rem; }
.symbol-jump input { background: var(--surface); }
.symbol-jump .btn { flex: 0 0 auto; }

/* the strip itself: real data, drawn like an instrument trace on graph
   paper -- grid lines from the SVG (year boundaries), ink line, teal
   match ticks. See accounts/hero.py for what the numbers are. */
.proof-strip { margin: var(--s6) 0 0; }
/* hero scale (execution pass, technique 5): tall enough that the trace is
   the largest single object on the page, and elevated with --shadow-1 so
   it reads as THE primary object sitting on the --paper-deep ground --
   the same figure/ground move the chart-page rail makes. */
.proof-svg { display: block; width: 100%; height: clamp(220px, 42dvh, 460px);
  background: var(--surface); border: 1px solid var(--rule-strong); border-radius: var(--r);
  box-shadow: var(--shadow-1); }
.proof-grid { stroke: var(--grid-line); stroke-width: 1px; vector-effect: non-scaling-stroke; }
.proof-line { stroke: var(--ink); stroke-width: 1.75px; }
/* Width/density-aware tick weight (visual foundation v2 refinement, Step
   3): an isolated match keeps a crisp, fully-opaque tick; matches packed
   close together in a dense run (.w2/.w3, computed server-side --
   accounts/hero.py's _weight_class) get progressively thinner and fainter
   so a dense run reads as a soft cluster instead of overlapping into one
   solid filled block. The media query below thins EVERY tick further
   still on a narrow viewport -- the width half of the fix, since a
   non-scaling-stroke tick stays the same few screen pixels wide even as
   the viewBox is squeezed narrower, so the SAME x-spacing (in view units)
   overlaps sooner on a phone than on a desktop. */
.proof-match { stroke: var(--verified); stroke-width: 2px; opacity: 1; }
.proof-match.w2 { stroke-width: 1.4px; opacity: 0.72; }
.proof-match.w3 { stroke-width: 1px; opacity: 0.5; }
@media (max-width: 640px) {
  .proof-match { stroke-width: 1.4px; }
  .proof-match.w2 { stroke-width: 1px; opacity: 0.65; }
  .proof-match.w3 { stroke-width: 0.75px; opacity: 0.45; }
}
.proof-years { position: relative; height: 1.4em; margin-top: var(--s1); }
.proof-years span { position: absolute; transform: translateX(3px); white-space: nowrap; }
/* the strip's evidence at stat scale (execution pass, technique 1): the
   measured figures -- bar count, match count -- lead in mono; the
   symbol/rule identifier chips sit at the row's far end (they name what
   is measured, they aren't measurements) and drop under the figures when
   the row wraps on a narrow viewport. */
.proof-figures { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s4) var(--s6);
  margin: var(--s4) 0 0; }
.proof-figure { display: flex; flex-direction: column; gap: var(--s1); }
.pf-num { font-family: var(--font-mono); font-size: var(--fs-stat); line-height: 1;
  color: var(--ink); display: inline-flex; align-items: baseline; gap: var(--s2); }
.pf-label { color: var(--slate); }
.proof-idchips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  margin-left: auto; }
@media (max-width: 640px) { .proof-idchips { margin-left: 0; } }
.proof-chip { display: inline-flex; align-items: baseline; gap: 0.3rem;
  border: 1px solid var(--rule-strong); background: var(--surface); border-radius: var(--r);
  padding: 0.25rem 0.6rem; font-size: var(--fs-xs); color: var(--ink); }
code.proof-chip { font-family: var(--font-mono); }
.proof-match-swatch { width: 3px; height: 0.72em; background: var(--verified);
  display: inline-block; align-self: center; }
.proof-footnote { margin-top: var(--s3); max-width: var(--maxw-prose); }
.proof-empty { margin-top: var(--s6); }

/* draw-in: the line traces itself, then the match ticks appear -- the
   product's actual order of operations (ingest, then evaluate). Static
   final state under prefers-reduced-motion.

   The dash lives only INSIDE the keyframes, ending on dasharray none:
   with vector-effect: non-scaling-stroke, Chrome computes the dash
   pattern in screen space instead of pathLength units, so a persistent
   stroke-dasharray: 1 leaves the "finished" line as a 1px dotted pattern
   that visually vanishes wherever the path is smooth (found in browser
   verification at 1440/3440). The base rule carries no dash at all, so
   the no-animation path (reduced motion, old browsers) is always solid. */
.proof-line { animation: proof-draw 2.2s cubic-bezier(0.4, 0, 0.2, 1) 0.7s both; }
@keyframes proof-draw {
  0%     { stroke-dasharray: 1; stroke-dashoffset: 1; }
  99.99% { stroke-dasharray: 1; stroke-dashoffset: 0; }
  100%   { stroke-dasharray: none; stroke-dashoffset: 0; }
}
.proof-matches { opacity: 0; animation: proof-reveal 0.9s ease 2.9s forwards; }
@keyframes proof-reveal { to { opacity: 1; } }

/* One page-load entrance (execution pass, technique 3): kicker →
   headline → subhead → jump-to-chart search → strip → workspace label →
   cards → session row, each settling in with a short fade + small rise --
   papers onto a desk, not elements sliding in from off-screen. The
   strip's draw-in above is retimed (0.7s delay) to take its place in
   this same sequence, so the page has ONE choreography, not scattered
   animation. Static under prefers-reduced-motion, same rule as the strip
   has always followed. */
.enter { opacity: 0; animation: home-enter 0.4s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: var(--enter-delay, 0s); }
@keyframes home-enter {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
.enter-1 { --enter-delay: 0s; }
.enter-2 { --enter-delay: 0.09s; }
.enter-3 { --enter-delay: 0.18s; }
.enter-4 { --enter-delay: 0.30s; }
.enter-5 { --enter-delay: 0.42s; }
.enter-6 { --enter-delay: 0.66s; }
.enter-7 { --enter-delay: 0.76s; }
.enter-8 { --enter-delay: 0.86s; }
.enter-9 { --enter-delay: 0.96s; }
.enter-10 { --enter-delay: 1.10s; }

@media (prefers-reduced-motion: reduce) {
  .proof-line { animation: none; }
  .proof-matches { animation: none; opacity: 1; }
  .enter { animation: none; opacity: 1; }
  .ledger-card { transition: none; }
}

/* dead-gap fix (depth-polish pass): the footnote already carries its own
   margin-top, so the band's own bottom padding is trimmed (above) and this
   top padding is trimmed too -- the remaining space is claimed
   deliberately by .home-section-label below, not left empty. */
.home-body { max-width: var(--maxw); margin: 0 auto; padding: var(--s4) var(--gutter) var(--s7); }
.home-section-label { display: block; color: var(--slate); margin: 0 0 var(--s3); }

/* Ledger cards (execution pass, techniques 1/2/4; widened + accented in
   the depth-polish pass): three deliberately UNEQUAL cards, weighted by
   actual importance -- the watchlist is the product's navigation hub
   (CLAUDE.md), scans second, alerts a log you check -- each leading with
   this user's real figure at stat scale (accounts/home_stats.py). A zero
   figure recedes to slate: an honest zero-state, never hidden, never
   faked. Cards rest flat and lift on hover with the existing elevation
   tokens -- no new shadow values.

   The width ratio alone (previously 5:4:3, primary ~1.7x tertiary) read
   as "three similar boxes, mildly resized" rather than a deliberate
   hierarchy -- widened to 7:4:3 (primary ~2.3x tertiary) and paired with
   a single unambiguous marker: a --verified top accent bar on the
   PRIMARY card only. One marker on the one card that matters, not
   decoration repeated on all three (which would just relabel "equal"). */
.ledger-cards { display: grid; grid-template-columns: 7fr 4fr 3fr; gap: var(--s4); }
.ledger-card { position: relative; display: flex; flex-direction: column; gap: var(--s3); padding: var(--s5);
  border: 1px solid var(--rule-strong); background: var(--surface); border-radius: var(--r-lg);
  text-decoration: none; color: var(--ink); min-height: 9.5rem;
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease; }
.ledger-card:hover { box-shadow: var(--shadow-2); transform: translateY(-2px);
  border-color: var(--verified); }
.ledger-card:focus-visible { outline: 2px solid var(--verified); outline-offset: 2px; }
.lc-primary::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  border-radius: var(--r-lg) var(--r-lg) 0 0; background: var(--verified); }
.lc-name { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-h2); }
.lc-figure { font-family: var(--font-mono); font-size: var(--fs-stat); line-height: 1;
  margin-top: auto; }
.lc-primary .lc-figure { font-size: calc(var(--fs-stat) * 1.3); }
.lc-zero { color: var(--slate); }
.lc-meaning { color: var(--slate); }
@media (max-width: 900px) {
  .ledger-cards { grid-template-columns: 1fr; }
  .lc-primary .lc-figure { font-size: var(--fs-stat); }
  .ledger-card { min-height: 0; }
}

.home-session { display: flex; align-items: baseline; gap: var(--s4); margin-top: var(--s6); }
.home-logout { margin: 0; }

/* ---- utility ---- */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ---- responsive ---- */
@media (max-width: 860px) {
  .panel-pair > .names-ref { flex-basis: 100%; }
}
@media (max-width: 640px) {
  .topbar { padding: var(--s3) var(--s4); }
  .nav-toggle { display: flex; }
  .subnav { display: none; width: 100%; flex-direction: column; align-items: flex-start; gap: var(--s3); }
  .subnav.open { display: flex; }
  main { padding: var(--s5) var(--s4) var(--s6); }
  .stat-grid { grid-template-columns: 1fr 1fr; }
}
