/* WaveCatcher — the active-trader workbench. Dark market-internals terminal.
   One window per vendor layout scan (window = layout). Institutional quant-desk
   aesthetic: near-black ground, hairline structure, monospaced tabular numerals
   as the hero, restrained green/red reserved for signal, muted category hues for
   wayfinding. No gradients, no glows, no drop shadows, no rounded pill-cards, no
   emoji. Dark-only, fits one screen — only window bodies and the grid scroll.
   NOTE: app.js reads --line, --line-2, --ink-faint, --hi, --lo off :root via
   getComputedStyle for the canvas, and hardcodes the six category hues
   (CATCOLOR). Keep those variable names and the .tag / .wrow hues in sync.
   All five canvas-read tokens must stay CONCRETE HEX — never var() indirection,
   or getComputedStyle hands the canvas an empty string. */

:root {
  color-scheme: dark;

  /* ---- structure ---------------------------------------------------------
     Four grounds, three hairlines. --line-3 is the step between a hairline
     (--line-2) and ink: floating surfaces and active controls separate with an
     edge instead of borrowing a drop shadow. */
  --bg:        #05060a;   /* board ground / recessed wells / leftover space */
  --panel:     #090a0f;   /* panel + pane body */
  --head:      #0c0e13;   /* raised control strips: headers, scope bar, status */
  --raise:     #101318;   /* hover / pressed fill */
  --line:      #14171e;   /* hairline — row rules, chart gridlines (CANVAS) */
  --line-2:    #232830;   /* structural rule, axis, column divider (CANVAS) */
  --line-3:    #333a45;   /* floating-surface edge, active control edge */

  /* ---- ink --------------------------------------------------------------- */
  --ink:       #f0f3f7;   /* primary value / headline */
  --ink-dim:   #949ba7;   /* secondary value, prose */
  --ink-faint: #5b6270;   /* micro-labels, keys, axis ticks (CANVAS) */
  --neutral:   #313640;   /* separators, disabled, "no data", zero */

  /* ---- semantics ---------------------------------------------------------
     Green = up / new highs. Red = down / new lows. Never reassign. */
  --hi:        #24c07c;   /* up / new highs (CANVAS) */
  --lo:        #f04a5c;   /* down / new lows (CANVAS) */
  --hi-mut:    #0f3b28;   /* muted hi for fills/spines */
  --lo-mut:    #431a22;
  --warn:      #d8a01f;   /* amber — degraded, reconnecting, == --cat-pre */
  --accent:    #6b8cff;   /* cool accent — scope/selection, == --cat-brk */

  /* ---- feed health -------------------------------------------------------
     Aliases, not new colors, so the feed rail / badge / banner are provably
     the same three states everywhere they appear. */
  --ok:        var(--hi);
  --stale:     var(--warn);
  --dead:      var(--lo);
  --skel:      var(--line-2);   /* skeleton bar fill — "loading", not "zero" */

  /* category hues — LOCKED to app.js CATCOLOR; keep values identical */
  --cat-nhl:   #8b929c;
  --cat-pre:   #d8a01f;
  --cat-post:  #a06bff;
  --cat-brk:   #6b8cff;
  --cat-clim:  #ff9f43;
  --cat-mom:   #29c3d6;
  --cat-earn:  #e06b9c;
  --cat-oth:   #565d68;

  /* ---- per-screen hues ---------------------------------------------------
     One designated colour per WaveFinder screen, so a scope token, its option
     row in the panel, and the badge on the row all agree. Named --swc-<attr
     key> and read as var(--swc-<key>) by both surfaces; keep in step with the
     .bdg.<key> --k rules further down. */
  --swc-bucket0:      var(--cat-brk);
  --swc-bucket1:      var(--cat-mom);
  --swc-bucket2:      var(--cat-post);
  --swc-inst1k:       var(--warn);
  --swc-darvas:       var(--accent);
  --swc-eg100:        var(--cat-mom);
  --swc-liquid_lava:  var(--cat-clim);
  --swc-growth_250:   var(--hi);
  --swc-sugar_babies: #e05fa0;
  --swc-bg25:         var(--cat-pre);
  --swc-d9m:          var(--cat-post);

  /* ---- pattern group: per-setup hues -------------------------------------
     Same --swc-<key> contract the four Universe groups use, so a scope token,
     its option row in the panel and the badge on the row agree by construction.
     Direction-coded: long setups green, short red. The two Scanner scans have no
     direction axis (both are long by definition), so they keep the breakout blue
     rather than borrowing the up-hue and implying a direction they do not carry.
     Keep in step with the .bdg.pat rules below. NO RENAMES above this block. */
  --swc-cont_bo:        var(--cat-brk);
  --swc-rev_bull:       var(--cat-brk);
  --swc-pa_cont_up:     var(--hi);
  --swc-pa_rev_long:    var(--hi);
  --swc-pa_flush_long:  var(--hi);
  --swc-pa_cont_dn:     var(--lo);
  --swc-pa_rev_short:   var(--lo);
  --swc-pa_flush_short: var(--lo);

  /* gate hues — these never badge, but their scope tokens need a swatch */
  --swc-near_pivot:     var(--warn);
  --swc-base_stage:     var(--cat-post);
  --swc-pa_fresh:       var(--hi);
  --swc-pa_mtf:         var(--cat-mom);

  /* ---- the dated vocabulary ----------------------------------------------
     Two properties, no new colours. Applied once per ROW and once per panel
     band, never per chip — one mark per row is what keeps a badge line reading
     as a single class of fact instead of a stack of warnings.
       --pat-dated    leading rule on a block of end-of-day facts. The badge
                      line gets it; the live cells above it never do.
       --pat-forming  provisional. Direction-blind ON PURPOSE — a forming long
                      rendered in --hi reads as settled fact. */
  --pat-dated:      1px dotted var(--line-3);
  --pat-forming:    var(--warn);
  --pat-forming-bd: 1px dashed rgba(216, 160, 31, .7);

  /* ---- row spine ---------------------------------------------------------
     The pattern mark lives in the 9px left padding the row already has, just
     inside the category stripe. Zero horizontal cost, survives every column
     drop, and it is the last thing standing at the narrowest tile width. */
  --pat-spine-long:  linear-gradient(90deg, transparent 0 4px, var(--hi) 4px 6px, transparent 6px);
  --pat-spine-short: linear-gradient(90deg, transparent 0 4px, var(--lo) 4px 6px, transparent 6px);

  /* ---- panel band --------------------------------------------------------
     The scope panel gains a second band. Heavier than the 1px column gaps so it
     reads as "a different kind of question", not another column boundary. */
  --sp-band-rule: 2px solid var(--line-3);

  /* ---- type -------------------------------------------------------------
     Numbers stay monospaced and tabular everywhere. The ramp below is the
     whole scale — if a size isn't here it shouldn't be on screen. */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --num:  var(--mono);

  --fs-micro:  8px;    /* zone labels, sector chips */
  --fs-key:    9px;    /* uppercase micro-labels, axis ticks, footer */
  --fs-label: 10px;    /* panel titles, chips, tokens, sort indicators */
  --fs-row:   12px;    /* the alert row — the densest legible step */
  --fs-body:  13px;    /* prose, picker items */
  --fs-num:   15px;    /* collapsed-section headline */
  --fs-lg:    17px;    /* breadth readout */
  --fs-xl:    26px;    /* drawer hero price */

  --trk-key:   .16em;  /* uppercase micro-labels / keys */
  --trk-title: .13em;  /* panel + window titles (was .2em — too loose at 10px) */
  --trk-brand: .2em;   /* brand lockup only */

  /* ---- spacing rhythm ---------------------------------------------------- */
  --sp-1:  3px;
  --sp-2:  6px;
  --sp-3: 10px;
  --sp-4: 14px;
  --sp-5: 18px;
  --sp-6: 24px;

  /* ---- fixed chrome heights ---------------------------------------------
     The board must fit one screen, so every strip has a pinned height and the
     text-size control scales CONTENT ONLY, never chrome. */
  --chrome-rail:   3px;   /* feed rail */
  --chrome-head:  46px;   /* header row */
  --chrome-scope: 34px;   /* scope bar — always present, even with no enrich */
  --chrome-win:   30px;   /* window / pair header */
  --chrome-sub:   22px;   /* pane sector strip */
  --chrome-quiet: 34px;   /* quiet rail */
  --chrome-foot:  26px;   /* status footer */
  --row-h:        21px;   /* one alert row, desktop */
  --row-h-touch:  44px;   /* one alert row, phone */

  /* ---- motion ---------------------------------------------------------- */
  --dur-fast: .12s;   /* color / border state change */
  --dur-mid:  .18s;   /* drawer slide */
  --dur-slow: .4s;    /* gauge grow */
}

/* Text-size control (settings panel) scales content, not chrome. The attribute
   is set on <html>; the chrome heights above are deliberately untouched. */
html[data-textsize="s"]  { --fs-row: 11px; --fs-body: 12px; --row-h: 19px; }
html[data-textsize="l"]  { --fs-row: 13px; --fs-body: 14px; --row-h: 23px; }
html[data-textsize="xl"] { --fs-row: 14px; --fs-body: 15px; --row-h: 25px; }

* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; overflow: hidden;
  background: var(--bg); color: var(--ink);
  font-family: var(--sans); font-size: 13px;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  font-variant-numeric: tabular-nums;
}
/* One flex column of pinned-height strips around a single flexible board. Every
   strip's height is a --chrome-* token, so the text-size control can scale row
   content without ever pushing the board off one screen. */
.app { height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.app > * { min-width: 0; }

/* ============================================================ feed rail */
/* Three states, one alias set (--ok/--stale/--dead), so the rail, the header
   badge and the banner are provably the same three states everywhere. */
.feed-rail { flex: none; height: var(--chrome-rail); background: var(--neutral); transition: background var(--dur-fast) ease; }
.feed-rail[data-feed="live"]  { background: var(--ok); }
.feed-rail[data-feed="stale"] { background: var(--stale); animation: wbPulse 1.1s ease-in-out infinite; }
.feed-rail[data-feed="dead"]  { background: var(--dead); }
.feed-rail[data-feed="quiet"], .feed-rail[data-feed="init"] { background: var(--neutral); }
@keyframes wbPulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* ============================================================ top bar */
/* One row: identity · category rail · feed instrument · settings. The old row 2
   (attribute filter) became the scope bar below; the HIGHS/LOWS/B-U readout
   moved onto the breadth tile that actually owns those numbers. */
.topbar {
  flex: none; display: flex; align-items: center; gap: var(--sp-5);
  height: var(--chrome-head); padding: 0 var(--sp-5);
  background: var(--head); border-bottom: 1px solid var(--line-2);
}
.brand { display: flex; align-items: center; gap: 11px; flex: none; padding-right: var(--sp-5); border-right: 1px solid var(--line-2); }
/* WaveCatcher lockup — wave glyph + two-tone wordmark, following WaveFinder.
   Mixed case with tight tracking (NOT the old uppercase + .2em brand tracking):
   a product name should read as a name, not as a section label. */
/* WaveFinder's own wave asset (mint #9fefd6, baked into the PNG). Sized to the
   crop's 2.14:1 ink box so the glyph fills its slot with no dead padding. */
.brand .mark { display: block; width: 26px; height: 12px; flex: none; object-fit: contain; }
.brand .title {
  font-size: 14px; font-weight: 700; letter-spacing: -.005em; text-transform: none;
  color: var(--ink); white-space: nowrap;
}
.brand .title i { font-style: normal; color: var(--accent); }

.flabel { font-size: var(--fs-micro); letter-spacing: var(--trk-key); text-transform: uppercase; color: var(--ink-faint); font-weight: 700; flex: none; }
.tb-gap { flex: 1; min-width: var(--sp-2); }

/* ---- category rail --------------------------------------------------------
   Seven show/hide cells in one segmented control: locked hue, jargon-short code
   (codes do not translate, so the rail cannot blow out under es/CJK), and a live
   count in its own cell. OFF dims the whole cell and hollows the swatch. */
.catwrap { display: flex; align-items: center; gap: 9px; flex: none; min-width: 0; }
.catrail {
  display: flex; align-items: stretch; min-width: 0;
  border: 1px solid var(--line-2); background: var(--bg);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
.catrail::-webkit-scrollbar { height: 0; }
.catrail .lg {
  display: flex; align-items: center; gap: var(--sp-2);
  font-family: var(--num); font-size: var(--fs-label); color: var(--ink);
  background: transparent; border: none; padding: 4px 9px; cursor: pointer;
  white-space: nowrap; transition: color var(--dur-fast) ease, background var(--dur-fast) ease;
}
.catrail .lg + .lg { border-left: 1px solid var(--line-2); }
.catrail .lg:hover { background: var(--raise); }
.catrail .lg .sw { width: 3px; height: 12px; background: currentColor; flex: none; display: block; }
.catrail .lg .lg-l { letter-spacing: .06em; }
.catrail .lg .lg-n { font-weight: 600; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
/* zero is not the same as hidden: a live-but-silent category stays lit, its
   count just drops to the "no data" grey. */
.catrail .lg .lg-n.zero { color: var(--neutral); }
.catrail .lg.off { color: var(--ink-faint); }
.catrail .lg.off .sw { opacity: .35; }
.catrail .lg.off .lg-n { color: var(--neutral); }

/* ---- feed block -----------------------------------------------------------
   The instrument: state word, the clock it applies to, and how old the last
   frame is. Tinted ground on the two states you must not miss. */
.feedblock {
  display: flex; align-items: center; gap: var(--sp-4); flex: none;
  padding: 0 var(--sp-4); height: 100%;
  border-left: 1px solid var(--line-2); border-right: 1px solid var(--line-2);
  transition: background var(--dur-fast) ease;
}
.feedblock[data-feed="stale"] { background: color-mix(in srgb, var(--stale) 10%, transparent); }
.feedblock[data-feed="dead"]  { background: color-mix(in srgb, var(--dead) 12%, transparent); }
.fd-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--neutral); flex: none; }
.feedblock[data-feed="live"]  .fd-dot { background: var(--ok); animation: wbPulse 2.4s ease-in-out infinite; }
.feedblock[data-feed="stale"] .fd-dot { background: var(--stale); animation: wbPulse 1.1s ease-in-out infinite; }
.feedblock[data-feed="dead"]  .fd-dot { background: var(--dead); }
.fd-txt, .fd-age { display: flex; flex-direction: column; gap: var(--sp-1); }
.fd-age { align-items: flex-end; padding-left: 4px; }
.fd-state {
  font-family: var(--num); font-size: 12px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; line-height: 1; color: var(--ink-dim); white-space: nowrap;
}
.feedblock[data-feed="live"]  .fd-state { color: var(--ok); }
.feedblock[data-feed="stale"] .fd-state { color: var(--stale); }
.feedblock[data-feed="dead"]  .fd-state { color: var(--dead); }
.fd-sub { font-family: var(--num); font-size: var(--fs-key); letter-spacing: .07em; line-height: 1; color: var(--ink-faint); white-space: nowrap; }
.feedblock[data-feed="stale"] .fd-sub { color: var(--stale); }
.feedblock[data-feed="dead"]  .fd-sub { color: var(--dead); }
.fd-agev { font-family: var(--num); font-size: 12px; font-weight: 600; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.feedblock[data-feed="stale"] .fd-agev { color: var(--stale); }
.feedblock[data-feed="dead"]  .fd-agev { color: var(--dead); }
.feedblock[data-feed="quiet"] .fd-agev, .feedblock[data-feed="init"] .fd-agev { color: var(--ink-dim); }
.fd-agek { font-family: var(--num); font-size: var(--fs-key); letter-spacing: .07em; line-height: 1; color: var(--ink-faint); white-space: nowrap; }

.gear-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 26px; font-size: 14px; line-height: 1; color: var(--ink-faint);
  background: transparent; border: 1px solid var(--line-2); cursor: pointer;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.gear-btn:hover, .gear-btn[aria-expanded="true"] { color: var(--ink); border-color: var(--line-3); }

/* ---- dead-feed banner -----------------------------------------------------
   The one state where covering board space is correct: every number below it
   is frozen, and acting on frozen numbers is the expensive mistake. */
.deadbanner {
  flex: none; display: flex; align-items: center; gap: var(--sp-4);
  padding: 9px var(--sp-5);
  background: color-mix(in srgb, var(--dead) 14%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--dead) 45%, transparent);
}
.deadbanner[hidden] { display: none; }
.db-k { font-family: var(--num); font-size: var(--fs-label); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--dead); flex: none; }
.db-t { font-size: var(--fs-row); color: var(--ink-dim); flex: 1; min-width: 0; }
.db-btn {
  flex: none; font-family: var(--num); font-size: var(--fs-label); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink);
  background: color-mix(in srgb, var(--dead) 20%, transparent);
  border: 1px solid color-mix(in srgb, var(--dead) 60%, transparent);
  padding: 4px 12px; cursor: pointer;
}
.db-btn:hover { background: color-mix(in srgb, var(--dead) 32%, transparent); }

/* ---- breadth readout (moved off the top bar, onto its own tile) ---------- */
.readout { display: flex; align-items: baseline; gap: var(--sp-4); flex: none; }
.stat { display: flex; align-items: baseline; gap: 5px; flex: none; }
.stat .k { font-size: var(--fs-micro); letter-spacing: .14em; color: var(--ink-faint); text-transform: uppercase; font-weight: 700; }
.stat .v { font-family: var(--num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); font-weight: 700; line-height: 1; color: var(--ink); }
.stat .v.hi { color: var(--hi); }
.stat .v.lo { color: var(--lo); }

/* ============================================================ settings popover */
/* Everything that used to crowd the top bar: language (was a raw native select
   that broke the visual system), the planned text-size control, motion prefs,
   and the two relocated layout actions (Tidy, WR compare). */
.settings-pop {
  position: fixed; z-index: 72; width: 262px;
  background: var(--head); border: 1px solid var(--line-3);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .55);
}
.settings-pop[hidden] { display: none; }
.st-h {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4); background: var(--panel); border-bottom: 1px solid var(--line-2);
  font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--trk-title);
  text-transform: uppercase; color: var(--ink-dim);
}
.st-x { font-size: 15px; line-height: 1; color: var(--ink-faint); background: transparent; border: none; padding: 0 2px; cursor: pointer; }
.st-x:hover { color: var(--ink); }
.st-b { padding: var(--sp-4); }
.st-k {
  display: block; font-size: var(--fs-key); letter-spacing: var(--trk-key); text-transform: uppercase;
  color: var(--ink-faint); font-weight: 700; margin-bottom: var(--sp-2);
}
/* a second section header needs air from the note above it */
.st-b > .st-k ~ .st-k { margin-top: var(--sp-5); }
/* --neutral is the "no data" grey; explanatory copy that is meant to be READ
   sits one step up at --ink-faint, or it may as well not be on screen. */
.st-note { display: block; font-size: var(--fs-key); line-height: 1.45; color: var(--ink-faint); margin-top: var(--sp-2); }
.st-grp { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px solid var(--line-2); }

.st-seg { display: flex; border: 1px solid var(--line-2); }
.st-seg button {
  flex: 1; font-family: var(--num); color: var(--ink-faint);
  background: transparent; border: none; padding: 6px 0; cursor: pointer;
  transition: color var(--dur-fast) ease, background var(--dur-fast) ease;
}
.st-seg button + button { border-left: 1px solid var(--line-2); }
.st-seg button:hover { color: var(--ink-dim); background: var(--raise); }
.st-seg button.on { color: var(--ink); background: var(--line-2); font-weight: 600; }
/* the size control previews its own effect */
.st-seg-ts .ts-s { font-size: 10px; } .st-seg-ts .ts-m { font-size: 11px; }
.st-seg-ts .ts-l { font-size: 12px; } .st-seg-ts .ts-xl { font-size: 13px; }

.st-langs { display: flex; flex-direction: column; }
.st-lang {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  background: transparent; border: none; padding: 6px 0; cursor: pointer;
}
.st-lang:hover .st-lname { color: var(--ink); }
.st-tick { width: 13px; flex: none; font-family: var(--num); font-size: var(--fs-label); color: var(--accent); }
.st-lname { flex: 1; font-size: 11.5px; color: var(--ink-dim); transition: color var(--dur-fast) ease; }
.st-lang.on .st-lname { color: var(--ink); }
.st-lcode { font-family: var(--num); font-size: var(--fs-key); color: var(--neutral); }

.st-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: 5px 0; }
.st-lbl { font-size: 11.5px; color: var(--ink-dim); }
.st-sw {
  position: relative; width: 28px; height: 15px; flex: none; border-radius: 8px;
  border: 1px solid var(--line-2); background: var(--bg); padding: 0; cursor: pointer;
  transition: background var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.st-knob {
  position: absolute; top: 1px; left: 1px; width: 11px; height: 11px; border-radius: 50%;
  background: var(--ink-faint); transition: left var(--dur-fast) ease, background var(--dur-fast) ease;
}
.st-sw.on { background: color-mix(in srgb, var(--accent) 40%, transparent); border-color: color-mix(in srgb, var(--accent) 70%, transparent); }
.st-sw.on .st-knob { left: 14px; background: var(--ink); }

/* ============================================================ scope bar */
/* The old two-row filter stack (~15 chips, 5 popovers, 2 thresholds, a focus
   mode and a collapse behaviour in 36px) collapses into ONE row: what is
   actually filtered, stated as removable tokens, plus everything else behind
   + FILTER. The row keeps its height when enrichment is absent and says why —
   display:none-ing it jumped the whole board. */
.scopebar {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  height: var(--chrome-scope); padding: 0 var(--sp-5);
  background: var(--head); border-bottom: 1px solid var(--line-2);
}
.scopebar[data-enrich="off"] .flabel { color: var(--neutral); }
.scopebar[data-enrich="off"] { background: var(--head); }
.scope-tokens {
  display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
.scope-tokens::-webkit-scrollbar { height: 0; }
.scope-right { display: flex; align-items: center; gap: var(--sp-2); flex: none; }
/* the enrichment-absent explanation has to be readable — it is the only thing
   telling the trader why half the filter system is missing */
.scope-off { font-family: var(--num); font-size: var(--fs-label); letter-spacing: .06em; color: var(--ink-faint); }
.scope-badge {
  font-family: var(--num); font-size: var(--fs-key); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); border: 1px solid var(--line-2); padding: 3px 8px; flex: none;
}
/* an active screen, stated in words, with its live count and a way off */
.sc-token {
  display: inline-flex; align-items: center; gap: var(--sp-2); flex: none;
  font-family: var(--num); font-size: var(--fs-label); color: var(--ink);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  padding: 3px 5px 3px 7px; white-space: nowrap;
}
.sc-token .sw { width: 5px; height: 5px; background: var(--swc, var(--accent)); display: block; flex: none; }
.sc-token b { font-weight: 400; color: var(--ink-dim); margin-left: 1px; font-variant-numeric: tabular-nums; }
.sc-token .x { color: var(--ink-faint); font-style: normal; padding-left: 3px; cursor: pointer; }
.sc-token .x:hover { color: var(--lo); }
.sc-add {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-family: var(--num); font-size: var(--fs-label); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-dim); background: var(--bg); border: 1px solid var(--line-3);
  padding: 3px 9px; cursor: pointer;
}
.sc-add:hover, .sc-add.on { color: var(--ink); background: var(--raise); }
.sc-add .cv { font-size: var(--fs-micro); color: var(--ink-faint); }
.sc-count { font-family: var(--num); font-size: var(--fs-label); color: var(--ink-faint); letter-spacing: .04em; padding-right: 4px; white-space: nowrap; }
.sc-count b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.sc-btn {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-family: var(--num); font-size: var(--fs-label); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); background: transparent; border: 1px solid var(--line-2);
  padding: 3px 9px; cursor: pointer; transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.sc-btn:hover { color: var(--ink); border-color: var(--ink-faint); }
.sc-btn .fx { font-size: 11px; line-height: 1; }
/* Focus is a MODE, not another filter — it gets the one filled control. */
.sc-btn.sc-focus.on { color: var(--bg); background: var(--accent); border-color: var(--accent); font-weight: 700; }

/* ============================================================ quiet rail */
/* Every scan in the layout that isn't firing, as one 34px line instead of a
   screenful of empty windows. Click one to pin it as a full tile. */
.quietrail {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  height: var(--chrome-quiet); padding: 0 var(--sp-4);
  background: var(--head); border-top: 1px solid var(--line-2);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
.quietrail::-webkit-scrollbar { height: 0; }
.quietrail[hidden] { display: none; }
.qr-sum { font-family: var(--num); font-size: var(--fs-key); color: var(--neutral); flex: none; white-space: nowrap; }
.qr-sep { width: 1px; height: 14px; background: var(--line-2); flex: none; margin: 0 2px; }
.qr-list { display: flex; align-items: center; gap: var(--sp-2); flex: none; }
.qr-item {
  display: inline-flex; align-items: center; gap: var(--sp-2); flex: none;
  font-family: var(--num); font-size: var(--fs-label); color: var(--ink-dim);
  background: var(--bg); border: 1px solid var(--line-2); padding: 3px 8px;
  cursor: pointer; white-space: nowrap; transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.qr-item:hover { color: var(--ink); border-color: var(--line-3); }
.qr-item b { font-weight: 400; color: var(--neutral); font-variant-numeric: tabular-nums; }
.qr-gap { flex: 1; min-width: 12px; }
.qr-add {
  display: inline-flex; align-items: center; gap: var(--sp-2); flex: none;
  font-family: var(--num); font-size: var(--fs-label); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-dim); background: var(--bg); border: 1px dashed var(--line-3);
  padding: 3px 12px; cursor: pointer;
}
.qr-add:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ============================================================ scope panel */
/* One structured surface replacing five popovers. Four columns: Fundamental,
   Study lists, Move + NEW 7d, Saved scopes. Every screen carries its
   plain-language definition and its live count — the old chips hid both behind
   a hover tooltip on a 10px label. */
.scope-panel {
  position: fixed; z-index: 71;
  background: var(--panel); border: 1px solid var(--line-3);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .6);
  max-height: 66vh; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--line-2) transparent;
}
.scope-panel[hidden] { display: none; }
.scope-panel::-webkit-scrollbar { width: 8px; }
.scope-panel::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--panel); }

/* 1px gaps on the --line-2 ground draw the column rules for free. Columns
   collapse to one track on a narrow viewport with no breakpoint bookkeeping. */
.sp-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
  gap: 1px; background: var(--line-2);
}
.sp-col { background: var(--panel); padding: var(--sp-4) 0 var(--sp-3); display: flex; flex-direction: column; min-width: 0; }
.sp-col-saved { background: var(--head); }

.sp-ch { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); padding: 0 var(--sp-5) var(--sp-3); }
.sp-ch2 { padding-left: 0; padding-right: 0; }
.sp-ck {
  font-size: var(--fs-key); letter-spacing: var(--trk-key); text-transform: uppercase;
  color: var(--ink-dim); font-weight: 700;
}
.sp-ck-solo { padding: 0 var(--sp-4) var(--sp-3); }
/* the AND/OR rule is stated, not implied — it is the thing that makes the
   filter model predictable and it was previously nowhere on screen */
.sp-cr { font-family: var(--num); font-size: var(--fs-key); color: var(--ink-faint); letter-spacing: .04em; white-space: nowrap; }

.sp-opt {
  display: grid; grid-template-columns: 15px 1fr auto; align-items: start; gap: 11px;
  width: 100%; text-align: left; background: transparent; border: none;
  padding: var(--sp-2) var(--sp-5); cursor: pointer;
  transition: background var(--dur-fast) ease;
}
.sp-opt:hover { background: var(--raise); }
.sp-box {
  width: 14px; height: 14px; border: 1px solid var(--line-2); background: transparent;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--num); font-size: var(--fs-label); line-height: 1; color: var(--accent); margin-top: 1px;
}
.sp-opt.on .sp-box { border-color: color-mix(in srgb, var(--accent) 70%, transparent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.sp-mid { min-width: 0; }
.sp-name { display: flex; align-items: center; gap: var(--sp-2); font-family: var(--num); font-size: 11.5px; font-weight: 600; letter-spacing: .02em; color: var(--ink-dim); }
.sp-opt.on .sp-name { color: var(--ink); }
.sp-name .sw { width: 5px; height: 5px; background: var(--swc, var(--accent)); display: block; flex: none; }
.sp-def { display: block; font-size: 9.5px; line-height: 1.35; color: var(--ink-faint); margin-top: 3px; }
.sp-n { font-family: var(--num); font-size: var(--fs-label); color: var(--ink-faint); margin-top: 2px; font-variant-numeric: tabular-nums; }

.sp-sub { margin: var(--sp-2) var(--sp-5) 0; padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.sp-sk { display: block; font-size: var(--fs-key); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); font-weight: 700; margin-bottom: 7px; }
.sp-presets { display: flex; gap: 4px; }
.sp-preset {
  flex: 1; font-family: var(--num); font-size: 10.5px; color: var(--ink-faint);
  background: var(--bg); border: 1px solid var(--line-2); padding: 5px 0; cursor: pointer;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.sp-preset:hover { color: var(--ink); border-color: var(--ink-faint); }
.sp-preset.on { color: var(--ink); font-weight: 600; border-color: color-mix(in srgb, var(--accent) 70%, transparent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.sp-input {
  flex: 1.2; min-width: 0; font-family: var(--num); font-size: 10.5px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line-2); padding: 5px 6px; outline: none;
}
.sp-input:focus { border-color: color-mix(in srgb, var(--accent) 70%, transparent); }
.sp-toggle { display: flex; align-items: center; gap: 9px; margin-top: 9px; cursor: pointer; font-family: var(--num); font-size: 10.5px; color: var(--ink-dim); }
.sp-toggle b { color: var(--ink); font-weight: 600; margin-left: 2px; }
/* free-form fund-Δ% threshold, inline in the toggle row. Sized to ~3 digits so it
   reads as part of the sentence ("Fund count up ≥ [10]%"), not a form field. */
.sp-fchg-in {
  width: 3.2em; margin: 0 1px 0 3px; text-align: right;
  font-family: var(--num); font-size: inherit; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line-2); padding: 1px 3px; outline: none;
}
.sp-fchg-in:focus { border-color: color-mix(in srgb, var(--accent) 70%, transparent); }
.sp-note { display: block; font-size: 9px; line-height: 1.4; color: var(--ink-faint); margin-top: 6px; }

.sp-newchips { display: flex; flex-wrap: wrap; gap: 4px; }
.sp-nchip {
  font-family: var(--num); font-size: var(--fs-label); color: var(--ink-dim);
  background: var(--bg); border: 1px solid var(--line-2); padding: 3px 8px; cursor: pointer;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.sp-nchip:hover { color: var(--ink); border-color: var(--ink-faint); }
.sp-nchip.on { color: var(--ink); border-color: color-mix(in srgb, var(--accent) 70%, transparent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.sp-nchip b { font-weight: 400; color: var(--ink-faint); margin-left: 4px; font-variant-numeric: tabular-nums; }
.sp-nchip.on b { color: var(--ink); }

/* saved scopes: a scope is the WHOLE state, so each row shows what it restores */
.sp-saved { display: flex; flex-direction: column; }
.sp-scope { display: flex; align-items: stretch; }
.sp-scope:hover { background: var(--raise); }
.sp-scope-b {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; text-align: left;
  background: transparent; border: none; border-left: 2px solid var(--accent);
  padding: 7px var(--sp-4); cursor: pointer;
}
.sp-scope-n { display: flex; align-items: baseline; gap: var(--sp-2); font-family: var(--num); font-size: 11px; color: var(--ink-dim); letter-spacing: .02em; }
/* which axes a saved workspace restores — quiet, so the NAME still leads */
.sp-axes {
  flex: none; font-style: normal; font-size: 8px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--neutral); border: 1px solid var(--line-2); padding: 0 3px;
}
.sp-scope:hover .sp-scope-n { color: var(--ink); }
.sp-scope-s { font-family: var(--num); font-size: 9.5px; color: var(--neutral); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-scope-x { flex: none; font-size: 11px; color: var(--neutral); background: transparent; border: none; padding: 0 var(--sp-3); cursor: pointer; }
.sp-scope-x:hover { color: var(--lo); }
.sp-empty { padding: 0 var(--sp-4); font-family: var(--num); font-size: var(--fs-label); color: var(--neutral); font-style: italic; }
/* Sync trouble, said out loud. The panel keeps listing the cached workspaces —
   losing them on a network blip would be worse — but "showing this device only"
   is the difference between degraded and quietly lying about what is stored. */
.sp-syncerr {
  display: block; padding: 6px var(--sp-4) 0;
  font-family: var(--num); font-size: var(--fs-label); line-height: 1.4;
  color: var(--lo);
}
.sp-spacer { flex: 1; min-height: 12px; }
.sp-savebox { padding: var(--sp-3) var(--sp-4) 0; margin-top: var(--sp-3); border-top: 1px solid var(--line-2); display: flex; flex-direction: column; gap: var(--sp-2); }
.sp-name-input {
  font-family: var(--num); font-size: 10.5px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line-2); padding: 6px 8px; outline: none;
}
.sp-name-input:focus { border-color: color-mix(in srgb, var(--accent) 70%, transparent); }
/* the two save axes — scope and layout are independent, so they are checkboxes,
   not a segmented either/or */
.sp-incs { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.sp-inc-k {
  font-family: var(--num); font-size: var(--fs-key); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-faint); margin-right: var(--sp-1);
}
.sp-inc {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--num); font-size: 10px; letter-spacing: .04em;
  color: var(--neutral); background: transparent; border: none; padding: 2px 0; cursor: pointer;
  transition: color var(--dur-fast) ease;
}
.sp-inc.on { color: var(--ink-dim); }
.sp-inc:hover { color: var(--ink); }
.sp-save[disabled] { opacity: .4; cursor: not-allowed; }
.sp-save[disabled]:hover { color: var(--ink-dim); border-color: var(--line-3); }
.sp-save {
  font-family: var(--num); font-size: var(--fs-label); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-dim); background: var(--bg); border: 1px dashed var(--line-3); padding: 6px 0; cursor: pointer;
}
.sp-save:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ============================================================ breadth tile */
/* Breadth is a normal window tile (first grid cell): a vertical bull/bear gauge
   beside a compact Alerts/Sec chart, with the mode toggle in the tile header. */
/* Breadth spans 2 grid columns so the remaining tiles are even-numbered and the
   bull-left / bear-right scan pairs align cleanly to the grid (each pair starts on
   an odd column). Clamps to the available track count on narrower grids. */
/* Breadth is its own band above the tile columns: it is the only tile whose
   value comes from SIZE (a readable chart), so it does not compete for room
   with the content-sized pair tiles below it. */
.breadth.win {
  flex: 1; min-height: 0; min-width: 0;
  background: var(--panel); display: flex; flex-direction: column;
}
.breadth .br-body { flex: 1; min-height: 0; display: flex; gap: 8px; padding: 8px 8px 6px; }

/* vertical breadth gauge — segments sized by flex-grow from setGauge()
   (bear/red on top, bull/green on bottom). */
.gauge { flex: none; width: 40px; display: flex; flex-direction: column; border: 1px solid var(--line-2); overflow: hidden; background: var(--bg); }
.g-seg { flex-basis: 0; min-height: 15px; display: flex; align-items: center; justify-content: center; position: relative; transition: flex-grow .4s ease; }
.g-up { flex-grow: 50; background: var(--hi); }
.g-dn { flex-grow: 50; background: var(--lo); }
.g-pct { font-family: var(--num); font-variant-numeric: tabular-nums; font-size: 10px; font-weight: 700; letter-spacing: .02em; color: rgba(5, 6, 10, .82); }

.br-spark { position: relative; flex: 1; min-width: 0; min-height: 0; background: var(--panel); }
#chart { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
/* small current-rate tag on the chart: bull (green) / bear (red) */
.br-rate-tag { position: absolute; top: 1px; right: 4px; display: flex; gap: 9px; font-family: var(--num); font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; pointer-events: none; }
.br-rate-tag .up { color: var(--hi); }
.br-rate-tag .dn { color: var(--lo); }
.br-rate-tag .up::before { content: "▲"; font-size: 7px; margin-right: 2px; vertical-align: 1px; }
.br-rate-tag .dn::before { content: "▼"; font-size: 7px; margin-right: 2px; vertical-align: 1px; }
.overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--num); font-size: 11px; letter-spacing: .14em; color: var(--ink-faint);
  text-transform: uppercase; pointer-events: none;
}
/* segmented mode control */
.modes { display: inline-flex; border: 1px solid var(--line-2); background: var(--bg); }
.modes button {
  font: inherit; font-family: var(--num); font-size: 10px; letter-spacing: .04em; text-transform: lowercase;
  color: var(--ink-dim); background: transparent; border: none; padding: 3px 9px; cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.modes button + button { border-left: 1px solid var(--line-2); }
.modes button:hover { color: var(--ink); background: var(--raise); }
.modes button.on { background: var(--line-2); color: var(--ink); }

/* ============================================================ board */
/* The old board was `auto-fit` columns over uniform auto rows, so a window
   holding 3 alerts got the same height as one holding 12 — roughly 40% of the
   screen was empty panel. Now: a fixed breadth band, then two independently
   scrolling columns of pair tiles. Tiles are sized to their content, and the
   space a short tile doesn't use falls through to the board GROUND (--bg)
   rather than becoming empty --panel. */
.grid {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  gap: 1px; background: var(--line-2); overflow: hidden;
}
.board-top { flex: none; height: 214px; display: flex; gap: 1px; min-width: 0; }
/* the band's tile scrolls internally rather than growing the band */
.band-slot { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--bg); overflow: hidden; }
.band-slot > .pair { min-height: 0; }
.band-slot > .pair .pair-b { flex: 1; min-height: 0; }
.band-slot > .pair .pane { min-height: 0; }
.band-slot > .pair .win-body { flex: 1; max-height: none; }
.board { flex: 1; min-height: 0; display: flex; gap: 1px; overflow: hidden; }
.board-col {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px;
  overflow-y: auto; overflow-x: hidden; background: var(--bg);
  scrollbar-width: thin; scrollbar-color: var(--line-2) transparent;
}
.board-col::-webkit-scrollbar { width: 8px; }
.board-col::-webkit-scrollbar-track { background: transparent; }
.board-col::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--bg); }

/* Breadth keeps a classic single-window header (it is not a pair) — the shared
   header rules live here since .pair-h took over for every other tile. */
.win { background: var(--panel); display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.win-h {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  height: var(--chrome-win); padding: 0 12px;
  background: var(--head); border-bottom: 1px solid var(--line-2);
}
.win-h .nm {
  font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--trk-title);
  text-transform: uppercase; color: var(--ink-dim); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; flex: none;
}
.win-h .wh-gap { flex: 1; min-width: 0; }

/* A PAIR TILE is one instrument: one header, one sort control, two panes. */
.pair { flex: none; background: var(--panel); display: flex; flex-direction: column; min-width: 0; }
.pair-h {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  height: var(--chrome-win); padding: 0 12px;
  background: var(--head); border-bottom: 1px solid var(--line-2);
}
.pair-h[draggable="true"] { cursor: grab; }
.pair-h[draggable="true"]:active { cursor: grabbing; }
.pair.dragging { opacity: .45; }
.pair-h .nm {
  font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--trk-title);
  text-transform: uppercase; color: var(--ink); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
/* category marker — the tile's hue, so a scan is findable by colour at a glance */
.pair-sw { width: 3px; height: 12px; flex: none; display: block; }
.pair-h .wh-gap { flex: 1; min-width: 0; }
/* ▲n / ▼n: the two sides' live counts, in the header, so a collapsed bear side
   still reports its number. */
.pair-ct { display: flex; align-items: baseline; gap: var(--sp-2); flex: none; font-family: var(--num); font-size: 11px; font-variant-numeric: tabular-nums; }
.pair-ct .pc { display: inline-flex; align-items: baseline; gap: 3px; font-weight: 600; }
.pair-ct .pc::before { font-size: 9px; }
.pair-ct .pc.up { color: var(--hi); }
.pair-ct .pc.up::before { content: "▲"; }
.pair-ct .pc.dn { color: var(--lo); }
.pair-ct .pc.dn::before { content: "▼"; }
.pair-ct .pc-sep { color: var(--neutral); }
/* a side at zero reads as quiet, not as a signal */
.pair-ct .ct.zero { color: var(--neutral); }
.pair-ct .pc.up .ct.zero, .pair-ct .pc.dn .ct.zero { color: var(--neutral); }
.pair-ct .col { font-family: var(--num); font-size: var(--fs-key); color: var(--ink-faint); text-transform: uppercase; letter-spacing: .1em; white-space: nowrap; }
.pair-h .win-sort {
  flex: none; font-family: var(--num); font-size: var(--fs-key); line-height: 1; min-width: 15px; text-align: center;
  color: var(--ink-faint); background: transparent; border: 1px solid var(--line-2); padding: 2px 4px;
  cursor: pointer; font-variant-numeric: tabular-nums;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.pair-h .win-sort:hover { color: var(--ink); border-color: var(--ink-faint); }
.pair-h .win-sort[data-sort="pct"], .pair-h .win-sort[data-sort="dol"], .pair-h .win-sort[data-sort="rank"] {
  color: var(--ink-dim); border-color: var(--line-3);
}
.win-x {
  font-family: var(--sans); font-size: 14px; line-height: 1; color: var(--neutral);
  background: transparent; border: none; padding: 0 1px; cursor: pointer; flex: none;
  transition: color var(--dur-fast) ease;
}
.win-x:hover { color: var(--lo); }

/* Two panes side by side: bull left, bear right. container-type lets a row
   react to ITS pane's width (the $ change cell drops before the % does). */
.pair-b { display: flex; align-items: stretch; min-width: 0; }
.pane {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--panel); container-type: inline-size;
  border-left: 2px solid var(--neutral);
}
.pair-b > .pane + .pane { border-left-width: 2px; }
.pane[data-side="bull"] { border-left-color: color-mix(in srgb, var(--hi) 80%, transparent); }
.pane[data-side="bear"] { border-left-color: color-mix(in srgb, var(--lo) 80%, transparent); }
/* A single-sided tile spans the full width rather than leaving half empty. */
.pair[data-single="true"] .pane { flex: 1; }

/* the body no longer stretches to fill a fixed row — it is as tall as its rows,
   and only scrolls once it would dominate the column */
.win-body { flex: none; max-height: 46vh; overflow-y: auto; overflow-x: hidden; min-height: 0; }

/* A side that has never fired: ONE line. This is the density fix in miniature —
   it replaces a 220px empty panel with 26px that still says which side is quiet
   and why. */
.pane-quiet {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--sp-2);
  padding: 0 12px; min-height: 26px; background: var(--bg);
  font-family: var(--num); font-size: var(--fs-key); white-space: nowrap; overflow: hidden;
}
.pane-quiet[data-side="bull"] { border-left-color: color-mix(in srgb, var(--hi) 35%, transparent); }
.pane-quiet[data-side="bear"] { border-left-color: color-mix(in srgb, var(--lo) 35%, transparent); }
.pq-k { letter-spacing: var(--trk-key); text-transform: uppercase; color: var(--neutral); }
.pq-r { letter-spacing: .06em; color: var(--neutral); overflow: hidden; text-overflow: ellipsis; }

/* rows: line 1 = time · symbol · type-badge · price. left spine tinted by
   category ties each row back to the legend; badges container (empty) reserved
   for WaveFinder attribute badges in U2. */
/* The row anchors BOTH text-size tokens: --fs-row sizes everything inside it
   (.r1 cells and the .badges strip, which is a sibling of .r1 and so would
   otherwise inherit the 13px body size), --row-h sets its rhythm. Chrome heights
   stay on their own --chrome-* tokens, so scaling here never pushes the board
   past one screen — the promise the settings note makes. */
.wrow { padding: 3px 9px 4px; border-bottom: 1px solid var(--line); border-left: 2px solid transparent; font-size: var(--fs-row); min-height: var(--row-h); }
.wrow:hover { background: var(--raise); }
.wrow.fresh { animation: flash 1.1s ease-out; }
/* First alert of this ticker in this window today. Not the row-flash class
   above, and not the pattern-setup gate. Green even on a short (red) symbol. */
.r1 .dfresh {
  margin-left: 6px; color: var(--hi); font-size: .64em; font-weight: 700;
  letter-spacing: .08em; vertical-align: 1px;
}
/* S badge. Not green, not the Fresh mark. Compact, visible with the filter off. */
.r1 .sos {
  margin-left: 4px; color: var(--cat-mom); font-size: .64em; font-weight: 700;
  letter-spacing: .04em; vertical-align: 1px;
  border: 1px solid color-mix(in srgb, var(--cat-mom) 55%, transparent);
  padding: 0 3px; line-height: 1.35;
}
.wrow.hidden-fresh { display: none; }
.wrow.hidden-sos { display: none; }
.sos-chip {
  margin: 0 0 6px; height: 22px; min-width: 22px; padding: 0 7px;
  font: inherit; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  color: var(--cat-mom); background: transparent;
  border: 1px solid color-mix(in srgb, var(--cat-mom) 55%, transparent);
  cursor: pointer;
}
.sos-chip.on { background: color-mix(in srgb, var(--cat-mom) 16%, transparent); }
.sos-chip:not(.on) { color: var(--ink-dim); border-color: var(--line-3); }
@keyframes flash {
  0%   { background: color-mix(in srgb, var(--accent) 16%, transparent); }
  100% { background: transparent; }
}
.wrow[data-cat="nhl"]  { border-left-color: color-mix(in srgb, var(--cat-nhl)  62%, transparent); }
.wrow[data-cat="pre"]  { border-left-color: color-mix(in srgb, var(--cat-pre)  66%, transparent); }
.wrow[data-cat="post"] { border-left-color: color-mix(in srgb, var(--cat-post) 66%, transparent); }
.wrow[data-cat="brk"]  { border-left-color: color-mix(in srgb, var(--cat-brk)  66%, transparent); }
.wrow[data-cat="clim"] { border-left-color: color-mix(in srgb, var(--cat-clim) 66%, transparent); }
.wrow[data-cat="mom"]  { border-left-color: color-mix(in srgb, var(--cat-mom)  66%, transparent); }
.wrow[data-cat="earn"] { border-left-color: color-mix(in srgb, var(--cat-earn) 66%, transparent); }

/* Row type scales with the text-size control: .r1 owns --fs-row, and every cell
   below sizes in em so the whole row tracks one token instead of drifting apart.
   (These were hardcoded px, which is why S/M/L/XL visibly did nothing.) */
.r1 { display: grid; grid-template-columns: 52px auto 1fr auto auto; align-items: baseline; gap: 8px; white-space: nowrap; font-family: var(--num); font-size: var(--fs-row); font-variant-numeric: tabular-nums; }
.r1 .t { color: var(--ink-faint); font-size: .834em; letter-spacing: .01em; }
.r1 .sym { color: var(--ink); font-weight: 600; letter-spacing: .02em; }
.r1 .sym.up { color: var(--hi); }
.r1 .sym.dn { color: var(--lo); }
.r1 .px { color: var(--ink-dim); text-align: right; font-size: .917em; font-variant-numeric: tabular-nums; }
/* U1: today's split %/$ change, after the price. % is primary (heavier); $ is a
   secondary, dimmer companion that drops first on a narrow window. Both up/dn
   color-coded via the shared --hi/--lo hues, tabular so columns don't jitter. */
.r1 .chg-wrap { display: inline-flex; align-items: baseline; justify-content: flex-end; gap: 6px; font-size: .917em; font-variant-numeric: tabular-nums; letter-spacing: .01em; }
.r1 .chg { white-space: nowrap; }
.r1 .chg.pct { font-weight: 600; }
.r1 .chg.dol { opacity: .82; font-size: .834em; }
.r1 .chg.up { color: var(--hi); }
.r1 .chg.dn { color: var(--lo); }
/* Narrow scan windows (esp. with breadth 2-wide + the drawer open) can't fit every
   cell. Drop the least-important first so symbol + price + %chg ALWAYS show and
   never clip: $chg goes first, then the timestamp (reclaiming its 52px column). */
@container (max-width: 208px) { .r1 .chg.dol { display: none; } }
@container (max-width: 184px) {
  .r1 { grid-template-columns: auto 1fr auto auto; }
  .r1 .t { display: none; }
}

/* category-colored type badge — hues LOCKED to app.js CATCOLOR */
.tag {
  justify-self: start; padding: 0 4px; font-size: .709em; font-weight: 600; letter-spacing: .05em;
  line-height: 1.5; text-transform: uppercase; cursor: help;
  color: var(--ink-dim);
  border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
  background: color-mix(in srgb, currentColor 12%, transparent);
}
.tag:empty { border-color: transparent; background: transparent; }
.tag[data-cat="nhl"]  { color: var(--cat-nhl); }
.tag[data-cat="pre"]  { color: var(--cat-pre); }
.tag[data-cat="post"] { color: var(--cat-post); }
.tag[data-cat="brk"]  { color: var(--cat-brk); }
.tag[data-cat="clim"] { color: var(--cat-clim); }
.tag[data-cat="mom"]  { color: var(--cat-mom); }
.tag[data-cat="earn"] { color: var(--cat-earn); }
.tag[data-cat="oth"]  { color: var(--ink-faint); }

/* WaveFinder attribute badges (U3) — small chips under the row, after the price,
   aligned past the time+symbol columns. Restrained, distinct hue per attr; an
   unknown attr key falls back to the neutral default. */
.badges { display: flex; flex-wrap: wrap; gap: 3px; padding-left: 60px; margin-top: 2px; }
.badges:empty { display: none; }
.bdg {
  font-family: var(--num); font-size: .667em; font-weight: 600; letter-spacing: .02em;
  line-height: 1.35; padding: 0 3px; text-transform: uppercase;
  --k: var(--ink-dim);
  color: var(--k); border: 1px solid color-mix(in srgb, var(--k) 34%, transparent);
  background: color-mix(in srgb, var(--k) 10%, transparent);
}
.bdg.bucket0 { --k: var(--cat-brk); }
.bdg.bucket1 { --k: var(--cat-mom); }
.bdg.bucket2 { --k: var(--cat-post); }
.bdg.inst1k  { --k: var(--warn); }
/* WaveFinder watchlist badges — darvas pops on accent, the rest share a
   restrained slate tint so watchlist membership reads distinct from the
   momentum/bucket signals without adding colour noise. */
.bdg.darvas { --k: var(--accent); }
.bdg.eg100, .bdg.liquid_lava, .bdg.growth_250, .bdg.sugar_babies, .bdg.bg25,
.bdg.pct_100, .bdg.pct_20_wk, .bdg.dollar_20_wk, .bdg.d9m,
.bdg.pct_50, .bdg.pct_40_day { --k: var(--cat-nhl); }

/* SECTOR DISTRIBUTION strip (U4) — a thin sub-header under each scan window's
   header carrying a compact stacked colour bar + top-N "SECTOR pct%" chips. Shows
   which sectors dominate the scan today over its currently-shown rows. Hidden until
   enrich populates a mix (the [hidden] attr keeps it collapsed with no reserved gap). */
/* ONE line, per the design mock: bar + dominant theme named at its right end.
   The bar takes the slack; the label is sized to be read, not squinted at. */
.smix {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  padding: 5px 11px 6px; background: var(--head); border-bottom: 1px solid var(--line-2);
}
.smix[hidden] { display: none; }
.smix-bar {
  flex: 1; min-width: 0; display: flex; height: 6px; border-radius: 2px; overflow: hidden;
  border: 1px solid var(--line); background: var(--panel);
}
.smix-bar i { display: block; height: 100%; min-width: 1px; }
/* the dominant theme — the whole point of the strip, so it gets real size */
.smix-top {
  flex: none; display: inline-flex; align-items: center; white-space: nowrap;
  font-family: var(--num); font-size: 9.5px; letter-spacing: .04em;
}
.smix-top .smix-cl { color: var(--ink-dim); text-transform: uppercase; letter-spacing: .08em; }
.smix-top b { color: var(--ink); font-weight: 700; margin-left: 5px; font-variant-numeric: tabular-nums; }
.smix-sw { display: inline-block; width: 6px; height: 6px; border-radius: 1px; margin-right: 5px; flex: none; }
/* rows present but all filtered out — say so rather than collapsing to nothing */
.smix-none {
  flex: 1; font-family: var(--num); font-size: 9px; letter-spacing: .06em;
  color: var(--neutral); font-style: italic;
}

/* GLOBAL DIM — the U3 filter interaction: matching rows stay full-strength,
   non-matching rows fade + desaturate. Spans every window at once (JS toggles
   .dim per row); composes with the category hide filter (dim on visible rows). */
.wrow, .tl-row { transition: opacity .12s ease, filter .12s ease; }
.wrow.dim, .tl-row.dim { opacity: .28; filter: saturate(.3); }
/* FOCUS mode (U5) — hide non-matching rows outright, scoping every window to the
   selected universe. display:none unions cleanly with the category hide-filter. */
.wrow.hidden-scope, .tl-row.hidden-scope { display: none; }

/* an "off-<cat>" class on the grid hides that category's rows in every window */
.grid.off-nhl  .wrow[data-cat="nhl"],
.grid.off-pre  .wrow[data-cat="pre"],
.grid.off-post .wrow[data-cat="post"],
.grid.off-brk  .wrow[data-cat="brk"],
.grid.off-clim .wrow[data-cat="clim"],
.grid.off-mom  .wrow[data-cat="mom"],
.grid.off-earn .wrow[data-cat="earn"],
.grid.off-oth  .wrow[data-cat="oth"] { display: none; }

/* calm empty state — default windows show a "waiting for RTH" hold */
.empty { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px; color: var(--ink-faint); font-family: var(--num); }
.empty .glyph { font-size: 11px; letter-spacing: .24em; text-transform: uppercase; color: var(--neutral); }
.empty .sub { font-size: 9px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
.empty .dots { display: flex; gap: 5px; }
.empty .dots i { width: 4px; height: 4px; background: var(--neutral); border-radius: 50%; }

/* The add-window ghost tile is retired: a whole grid cell spent on "+ ADD
   WINDOW" was exactly the kind of space this redesign reclaims. Adding a window
   now happens from the quiet rail, next to the scans you would actually add. */

/* add-window picker (U11) — floating dark menu positioned over the ghost tile */
.picker {
  position: fixed; z-index: 50; min-width: 240px; max-width: 320px; max-height: 60vh;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--head); border: 1px solid var(--line-2);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .55);
}
.picker[hidden] { display: none; }
.picker-h {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  height: 28px; padding: 0 8px 0 11px; background: var(--panel); border-bottom: 1px solid var(--line-2);
  font-size: 10px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim);
}
.pk-x { font-family: var(--sans); font-size: 15px; line-height: 1; color: var(--ink-faint); background: transparent; border: none; padding: 0 2px; cursor: pointer; }
.pk-x:hover { color: var(--ink); }
.picker-b { flex: 1; overflow-y: auto; padding: 4px 0; scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
.picker-b::-webkit-scrollbar { width: 8px; }
.picker-b::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--head); }
.pk-grp { padding: 7px 11px 3px; font-family: var(--num); font-size: 8.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }
.pk-item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; font-family: var(--sans); font-size: 11.5px;
  color: var(--ink-dim); background: transparent; border: none; padding: 4px 12px; cursor: pointer;
  transition: color .1s ease, background .1s ease;
}
.pk-item:hover { color: var(--ink); background: var(--raise); }
.pk-item.on { color: var(--ink); }
.pk-item .pk-box {
  flex: none; width: 13px; height: 13px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-2); font-size: 10px; line-height: 1; color: var(--hi);
}
.pk-item.on .pk-box { border-color: var(--hi); }
.pk-item .pk-lbl { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pk-none { padding: 3px 12px 4px; font-family: var(--num); font-size: 10px; color: var(--neutral); font-style: italic; }
.pk-actions {
  display: flex; gap: 6px; padding: 7px 10px; margin-top: 4px; position: sticky; bottom: 0;
  background: var(--panel); border-top: 1px solid var(--line-2);
}
.pk-actions button {
  flex: 1; font-family: var(--num); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-dim); background: var(--bg); border: 1px solid var(--line-2); padding: 5px 6px; cursor: pointer;
}
.pk-actions button:hover { color: var(--ink); border-color: var(--ink-faint); }
.pk-actions .pk-apply { color: var(--ink); border-color: var(--line-2); }
.pk-actions .pk-apply:disabled { color: var(--neutral); cursor: default; border-color: var(--line-2); opacity: .55; }

/* docked news window — placeholder + live headlines (U4) */
.win.news .win-h .nm { color: var(--ink-faint); }
.news-body { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--ink-faint); font-family: var(--num); text-align: center; padding: 12px; }
.news-body .ng { font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--neutral); }
.news-body .nh { font-size: 9px; letter-spacing: .1em; color: var(--ink-faint); line-height: 1.6; }
/* list mode: top-aligned, scrolls inside the news body */
.news-body.list { align-items: stretch; justify-content: flex-start; overflow-y: auto; overflow-x: hidden; padding: 0; text-align: left; }
.news-item { padding: 6px 12px 7px; border-bottom: 1px solid var(--line); }
.news-item:hover { background: var(--raise); }
.news-item .ni-meta { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; font-family: var(--num); font-size: 9px; text-transform: uppercase; letter-spacing: .05em; }
.news-item .ni-src { color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.news-item .ni-t { color: var(--neutral); margin-left: auto; flex: none; font-variant-numeric: tabular-nums; }
.news-item .ni-title { display: block; color: var(--ink-dim); font-family: var(--sans); font-size: 11px; line-height: 1.4; text-decoration: none; }
.news-item .ni-title:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--line-2); }
.news-body.list { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
.news-body.list::-webkit-scrollbar { width: 8px; }
.news-body.list::-webkit-scrollbar-track { background: transparent; }
.news-body.list::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--panel); }
.news-body.list::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }

/* selected ticker — subtle mark on every row carrying the clicked symbol */
.wrow.sel, .tl-row.sel td { background: color-mix(in srgb, var(--accent) 9%, transparent); }
.wrow.sel:hover, .tl-row.sel:hover td { background: color-mix(in srgb, var(--accent) 14%, transparent); }

/* U6 keyboard cursor — a LEADING EDGE, not another wash.
   It has to be legible on top of .sel, and it must not be confusable with it:
   .sel marks every row board-wide carrying the drawer's symbol (markNewsRow), so
   a second tint would light up rows the cursor is nowhere near and the trader
   could not tell which one ↓ moves from. An inset edge marks exactly one row and
   survives the .sel background underneath it. */
.wrow.cur, .tl-row.cur td { box-shadow: inset 2px 0 0 var(--accent); }
.wrow.cur { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.tl-row.cur td { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.wrow.cur.sel, .tl-row.cur.sel td { background: color-mix(in srgb, var(--accent) 13%, transparent); }

/* U7 — the drawer is showing a symbol the board has already moved past. Dim the
   body rather than blanking it: stale-but-readable beats a skeleton flash, but
   it must not read as current. */
.dr-body.dr-stale { opacity: .55; transition: opacity .12s ease; }

/* ============================================================ detail panel: News | Chart (U4 + U13) */
.win.detail .detail-h { gap: 8px; }
.detail-h .tabs { display: inline-flex; gap: 1px; flex: none; }
.detail-h .tab { font-family: var(--num); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); background: transparent; border: none; padding: 2px 6px; cursor: pointer; transition: color .12s ease; }
.detail-h .tab:hover { color: var(--ink-dim); }
.detail-h .tab.on { color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }
.detail-h .dsym { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-family: var(--num); font-size: 11px; color: var(--ink-dim); letter-spacing: .02em; }
.detail-h .dsym .ds-sym { color: var(--ink); font-weight: 600; }
.dsym .ds-px { color: var(--ink-dim); }
.dsym .ds-chg.up { color: var(--hi); }
.dsym .ds-chg.dn { color: var(--lo); }
.chart-ctl { display: inline-flex; align-items: center; gap: 6px; flex: none; }

/* supply/demand zone control dropdown (#7) — floating dark panel */
/* master on/off toggle switch */
/* tab-driven visibility of body panes + chart controls */
.detail-body { flex: 1; min-height: 0; display: flex; }
.news-pane, .news-pane { display: flex; }
.win.detail[data-tab="news"] .win.detail[data-tab="news"] .chart-ctl { display: none; }
.win.detail[data-tab="chart"] .news-pane { display: none; }
.win.detail.inert-chart .tab[data-tab="chart"] { display: none; }
.win.detail.inert-chart .chart-ctl { display: none; }
/* chart canvas + status message */

/* chart colour themes — a few CSS vars scoped to the chart wrapper (U13) */
/* default theme uses concrete hex mirroring the :root tokens (--hi/--lo/--line…)
   so the canvas can read them via getComputedStyle without nested-var() resolution. */
.chartwrap {
  --c-up: #24c07c; --c-dn: #f04a5c;
  --c-grid: #14171e; --c-axis: #232830; --c-tick: #545b67;
  --c-sma: #6b8cff; --c-vol: #313640;
}

/* pop-out chart overlay (U13) */

/* ============================================================ top lists (in a window cell) */
.lists-body { flex: 1; overflow-y: auto; }
.tl { border-bottom: 1px solid var(--line-2); }
.tl:last-child { border-bottom: none; }
.tl-h {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 9px 12px 5px; font-size: 11px; font-weight: 600; letter-spacing: .01em; color: var(--ink);
  background: var(--panel);
}
.tl-h .col { color: var(--ink-faint); font-family: var(--num); font-size: 9px; font-weight: 400; text-transform: uppercase; letter-spacing: .1em; white-space: nowrap; }
.tl-table { width: 100%; border-collapse: collapse; font-family: var(--num); font-size: var(--fs-row); font-variant-numeric: tabular-nums; }
.tl-table td { padding: 2px 12px; white-space: nowrap; line-height: 1.62; }
.tl-table td.rank { color: var(--ink-faint); width: 22px; font-size: .834em; text-align: right; padding-right: 8px; }
.tl-table td.sym { color: var(--ink); font-weight: 600; letter-spacing: .02em; }
/* U15: direction-colored top-list symbols (reuses the alert-row .sym.up/.dn hues) */
.tl-table td.sym.up { color: var(--hi); }
.tl-table td.sym.dn { color: var(--lo); }
/* U16: WaveFinder badges inline after the symbol, kept compact so the table stays readable */
.tl-row .badges { display: inline-flex; flex-wrap: wrap; gap: 3px; padding-left: 6px; margin-top: 0; vertical-align: baseline; }
.tl-row .badges:empty { display: none; }
.tl-table td.val { text-align: right; color: var(--hi); font-weight: 600; }
.tl-table td.val.neg { color: var(--lo); }
.tl-table td.px { text-align: right; color: var(--ink-dim); width: 60px; padding-left: 6px; }
.tl-table tr:hover td { background: var(--raise); }

/* ============================================================ U7 ticker drawer */
/* Right-side slide-out overlaying the live workbench (desktop) / full-screen
   takeover (mobile). Same dark-terminal palette; the backdrop dims but the grid
   keeps streaming behind it. */
/* The container spans the viewport for positioning only — it must NOT take the
   pointer, or every board click lands on the drawer instead of the row. That was
   the old `.dr-backdrop`, which also carried `data-dr-close`, so clicking a
   ticker closed the drawer rather than switching to it. */
.drawer { position: fixed; inset: 0; z-index: 55; pointer-events: none; }
.drawer[hidden] { display: none; }
.dr-panel { pointer-events: auto; }

/* Collapse tab — a hairline on the panel's leading edge. Deliberately quiet:
   it is always on screen while the drawer is open, so it has to disappear into
   the chrome until you look for it. */
.dr-collapse {
  position: absolute; left: -13px; top: 50%; transform: translateY(-50%);
  width: 13px; height: 54px; padding: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel); color: var(--ink-faint);
  border: 1px solid var(--line-2); border-right: none;
  border-radius: 3px 0 0 3px;
  font-size: 11px; line-height: 1; cursor: pointer;
  opacity: .55; transition: opacity .12s ease, color .12s ease;
}
.dr-collapse:hover, .dr-collapse:focus-visible { opacity: 1; color: var(--ink); }
.dr-collapse:focus-visible { outline: 1px solid var(--line-2); outline-offset: 1px; }
.dr-panel {
  position: absolute; top: 0; right: 0; height: 100%; width: 460px; max-width: 92vw;
  display: flex; flex-direction: column; background: var(--panel);
  border-left: 1px solid var(--line-2); box-shadow: -14px 0 44px rgba(0, 0, 0, .5);
  animation: drSlide .18s ease-out;

  /* ── Drawer design tokens (U2) ──────────────────────────────────────────
     One system for the whole drawer, DERIVED from the approved Institutional
     Ownership card (`.dr-inst`): big tabular numerals as the hero, hairline
     structure, a restrained tinted accent, generous vertical rhythm. Scoped to
     .dr-panel so the workbench palette (:root) is untouched; every drawer
     section below is driven from these tokens rather than ad-hoc values.

     Spacing scale — one vertical/horizontal rhythm (px, terminal-tight). */
  --dr-sp-1: 4px;    /* hairline gaps, badge padding */
  --dr-sp-2: 6px;    /* inner row padding (y), tight stacks */
  --dr-sp-3: 9px;    /* label→value, header→body */
  --dr-sp-4: 13px;   /* section padding (y), card padding (x) */
  --dr-sp-5: 16px;   /* section padding (x) */
  --dr-sp-6: 24px;   /* headline metric column gap */

  /* Type scale — one ramp; the two large steps are the drawer's single bold
     move (big headline numbers), everything else stays quiet. */
  --dr-fs-key:   9px;      /* micro keys, axis ticks, group labels */
  --dr-fs-label: 10px;     /* section headers (.dr-sec-h) */
  --dr-fs-body:  11.5px;   /* prose: news titles, bull/bear */
  --dr-fs-num:   13px;     /* tabular cell / row values */
  --dr-fs-lg:    18px;     /* secondary headline number */
  --dr-fs-xl:    26px;     /* hero price */
  --dr-fs-2xl:   30px;     /* primary headline (# Funds) */

  /* Tracking — uppercase micro-labels vs. section titles. */
  --dr-trk-key: .1em;
  --dr-trk-sec: .14em;

  /* Number treatment — one mono, always tabular (applied via .dr-num-* rules
     and font-variant-numeric on every numeric run). */
  --dr-num: var(--num);

  /* Card surface — the shared inner primitive (metric cells, level/zone/analyst
     rows, thesis blocks): raised ground, hairline edge, near-square radius
     (the terminal forbids pill-cards), a 2px left accent stripe for role. */
  --dr-card-bg: var(--head);
  --dr-card-bd: var(--line-2);
  --dr-card-radius: 2px;
  --dr-card-stripe: 2px;
  --dr-card-accent: var(--neutral);   /* default stripe; up/dn variants override */

  /* Semantic color roles — reuse the terminal palette, named by meaning so the
     drawer reads one up/down color and one heading ramp everywhere. */
  --dr-up:     var(--hi);
  --dr-dn:     var(--lo);
  --dr-ink:    var(--ink);      /* headline / primary value */
  --dr-dim:    var(--ink-dim);  /* secondary value / prose */
  --dr-faint:  var(--ink-faint);/* labels / keys */
  --dr-mute:   var(--neutral);  /* separators, dates */
  --dr-accent: var(--accent);
}
@keyframes drSlide { from { transform: translateX(100%); } to { transform: translateX(0); } }
.dr-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* header: close/back + SYM · name */
.dr-head {
  flex: none; display: flex; align-items: center; gap: var(--dr-sp-4); height: 48px; padding: 0 var(--dr-sp-4);
  background: var(--head); border-bottom: 1px solid var(--dr-card-bd);
}
.dr-close {
  flex: none; display: inline-flex; align-items: center; gap: var(--dr-sp-1);
  font-family: var(--sans); color: var(--dr-faint); background: transparent;
  border: 1px solid transparent; border-radius: var(--dr-card-radius); padding: 4px 7px; cursor: pointer; line-height: 1;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.dr-close:hover { color: var(--dr-ink); border-color: var(--dr-card-bd); background: var(--raise); }
.dr-close .dr-x { font-size: 18px; }
.dr-close .dr-back { display: none; font-family: var(--dr-num); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
.dr-htitle { display: flex; align-items: baseline; gap: var(--dr-sp-3); min-width: 0; }
.dr-htitle .dh-sym { font-family: var(--dr-num); font-size: 16px; font-weight: 700; letter-spacing: .04em; color: var(--dr-ink); }
.dr-htitle .dh-name { font-size: 11px; color: var(--dr-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.dr-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
.dr-scroll::-webkit-scrollbar { width: 8px; }
.dr-scroll::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--panel); }
.dr-loading { padding: 48px var(--dr-sp-5); text-align: center; font-family: var(--dr-num); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--dr-faint); }

/* hero — the price is the drawer's headline; big tabular numeral, change on a
   quieter baseline beneath it, then meta + badges. */
.dr-hero { padding: var(--dr-sp-5) var(--dr-sp-5) var(--dr-sp-4); border-bottom: 1px solid var(--dr-card-bd); }
.dr-hero .dh-si { font-size: var(--dr-fs-label); letter-spacing: .05em; color: var(--dr-faint); text-transform: uppercase; margin-bottom: var(--dr-sp-3); }
.dr-hero .dh-line { font-family: var(--dr-num); font-size: var(--dr-fs-xl); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.015em; line-height: 1; color: var(--dr-ink); display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 var(--dr-sp-3); }
.dr-hero .dh-line .dh-px { color: var(--dr-ink); }
.dr-hero .dh-line .dh-chg { font-size: 14px; font-weight: 600; margin-left: 0; }
.dr-hero .dh-line .dh-chg.up, .dh-meta .up, .dt-ud .up, .dt-peer.up i, .dc-d.up { color: var(--dr-up); }
.dr-hero .dh-line .dh-chg.dn, .dh-meta .dn, .dt-ud .dn, .dt-peer.dn i, .dc-d.dn { color: var(--dr-dn); }
.dr-hero .dh-meta { margin-top: var(--dr-sp-3); font-family: var(--dr-num); font-size: 11px; color: var(--dr-dim); font-variant-numeric: tabular-nums; }
.dr-hero .dh-meta .sep { color: var(--dr-mute); margin: 0 var(--dr-sp-3); }
.dr-hero .dh-badges { margin-top: var(--dr-sp-3); }
.dr-hero .dh-badges .badges { padding-left: 0; margin-top: 0; }

/* generic section — flat, hairline-divided (the exemplar's container); the card
   surface lives on the INNER primitives, not the section itself. */
.dr-sec { padding: var(--dr-sp-4) var(--dr-sp-5); border-bottom: 1px solid var(--dr-card-bd); }
.dr-sec-h { display: flex; align-items: baseline; gap: var(--dr-sp-3); font-size: var(--dr-fs-label); font-weight: 600; letter-spacing: var(--dr-trk-sec); text-transform: uppercase; color: var(--dr-faint); margin-bottom: var(--dr-sp-3); }
.dr-empty { font-family: var(--dr-num); font-size: var(--dr-fs-label); letter-spacing: .1em; text-transform: uppercase; color: var(--dr-mute); padding: var(--dr-sp-1) 0; }

/* shared card primitive — one look for every inner row/cell surface across the
   drawer (levels, zones, thesis blocks, analyst rows): raised ground, near-square
   radius, a 2px left accent stripe carrying up/down/neutral role. */
.dr-card { background: var(--dr-card-bg); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); }
.dr-card.up { border-left-color: color-mix(in srgb, var(--dr-up) 70%, transparent); }
.dr-card.dn { border-left-color: color-mix(in srgb, var(--dr-dn) 70%, transparent); }

/* metric grid — grouped label+value cells; the grid clips to a rounded card so
   the 1px hairline dividers read as one clean unit. */
.dr-metrics { display: flex; flex-direction: column; gap: var(--dr-sp-4); }
.dr-grp .dr-grp-h { font-family: var(--dr-num); font-size: var(--dr-fs-key); letter-spacing: .16em; text-transform: uppercase; color: var(--dr-faint); margin-bottom: var(--dr-sp-2); }
/* raised metric tiles on the section ground; the 1px gap shows the section bg so
   a ragged trailing row (empty cells) recedes instead of reading as a grey block,
   matching the frameless tile language of the level/zone/analyst rows. */
.dr-cells { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1px; background: var(--panel); border-radius: var(--dr-card-radius); overflow: hidden; }
.dr-cell { display: flex; flex-direction: column; gap: 3px; padding: var(--dr-sp-2) var(--dr-sp-4); background: var(--dr-card-bg); }
.dr-cells:empty { display: none; }
.dc-k { font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); }
.dc-v { font-family: var(--dr-num); font-size: var(--dr-fs-num); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--dr-ink); }
.dc-v .dc-d { font-size: var(--dr-fs-label); font-weight: 600; margin-left: var(--dr-sp-1); }
.dr-dsdate { margin-top: var(--dr-sp-3); font-family: var(--dr-num); font-size: var(--dr-fs-key); letter-spacing: .06em; text-transform: uppercase; color: var(--dr-mute); }

/* chart section — hosts the relocated interactive candle chart */
/* The chart's controls share the section header row. On their own row they cost
   ~34px of chart height for five buttons; the header's right half was empty. */
.dr-chart .dr-sec-h { align-items: center; }
.dr-chart .dr-sec-h .dr-chart-ctl { margin-left: auto; margin-bottom: 0; gap: 4px; }
.dr-chart .dr-chart-ctl { justify-content: flex-end; }
/* Compact enough to sit on a 10px label line without growing it. */
.dr-chart .dr-chart-ctl button { font-size: 10px; padding: 0 5px; line-height: 1.5; }
.dr-chart .dr-chart-ctl .ctl-gap { flex: 0 0 6px; }
.dr-chart 
/* levels / zones — both are instances of the shared card primitive */
.dr-lvls { display: flex; flex-direction: column; gap: var(--dr-sp-1); }
.dr-lvl { display: grid; grid-template-columns: auto 1fr auto; align-items: baseline; gap: var(--dr-sp-3); padding: var(--dr-sp-2) var(--dr-sp-4); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); background: var(--dr-card-bg); font-family: var(--dr-num); font-size: 11px; }
.dr-lvl.demand { border-left-color: var(--dr-up); }
.dr-lvl.supply { border-left-color: var(--dr-dn); }
.dr-lvl .dl-k { font-family: var(--sans); font-size: var(--dr-fs-label); letter-spacing: .04em; text-transform: uppercase; color: var(--dr-dim); }
.dr-lvl .dl-v { color: var(--dr-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.dr-lvl .dl-m { color: var(--dr-faint); font-size: var(--dr-fs-label); text-align: right; white-space: nowrap; }
.dr-lvl.demand .dl-v { color: var(--dr-up); }
.dr-lvl.supply .dl-v { color: var(--dr-dn); }
.dr-zones { margin-top: var(--dr-sp-3); display: flex; flex-direction: column; gap: var(--dr-sp-1); }
.dr-zone { display: grid; grid-template-columns: auto 1fr auto auto; align-items: baseline; gap: var(--dr-sp-3); padding: var(--dr-sp-2) var(--dr-sp-4); font-family: var(--dr-num); font-size: 11px; background: var(--dr-card-bg); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); }
.dr-zone.demand { border-left-color: color-mix(in srgb, var(--dr-up) 70%, transparent); }
.dr-zone.supply { border-left-color: color-mix(in srgb, var(--dr-dn) 70%, transparent); }
.dr-zone .dz-t { font-size: var(--dr-fs-key); letter-spacing: .06em; text-transform: uppercase; }
.dr-zone.demand .dz-t { color: var(--dr-up); }
.dr-zone.supply .dz-t { color: var(--dr-dn); }
.dr-zone .dz-r { color: var(--dr-ink); font-variant-numeric: tabular-nums; }
.dr-zone .dz-s { color: var(--dr-faint); font-size: var(--dr-fs-label); }
.dr-zone .dz-a { color: var(--dr-dim); font-variant-numeric: tabular-nums; text-align: right; }

/* industry group + peers */
.dt-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--dr-sp-1) var(--dr-sp-4); font-family: var(--dr-num); }
.dt-head .dt-g { font-family: var(--sans); font-size: 12px; font-weight: 600; color: var(--dr-ink); }
.dt-head .dt-rank { font-size: 12px; font-weight: 600; color: var(--dr-dim); font-variant-numeric: tabular-nums; }
.dt-head .dt-ud { font-size: 11px; font-variant-numeric: tabular-nums; }
.dt-peers { display: flex; flex-wrap: wrap; gap: var(--dr-sp-2); margin-top: var(--dr-sp-3); }
.dt-peer { display: inline-flex; align-items: baseline; gap: var(--dr-sp-2); font-family: var(--dr-num); font-size: 10.5px; padding: 3px 7px; border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); color: var(--dr-dim); }
.dt-peer i { font-style: normal; font-size: 9.5px; font-variant-numeric: tabular-nums; }

/* U5: volume card — Today-vs-Avg20D fill bar + weekly cells */
.dr-volbar { margin-bottom: var(--dr-sp-3); }
.dr-volbar .dv-labels { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--dr-sp-2); }
.dr-volbar .dv-lab { display: inline-flex; align-items: baseline; gap: var(--dr-sp-2); font-family: var(--dr-num); }
.dr-volbar .dv-k { font-family: var(--sans); font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); }
.dr-volbar .dv-v { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--dr-ink); }
.dr-volbar .dv-avg .dv-v { color: var(--dr-dim); }
.dr-volbar .dv-track { height: 7px; background: var(--dr-card-bg); border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); overflow: hidden; }
.dr-volbar .dv-track i { display: block; height: 100%; min-width: 2px; background: var(--dr-mute); }
.dr-volbar .dv-track i.hot { background: var(--dr-up); }

/* U5: reformatted S/D zone rows + Between-Zones pill */
.dr-zpill { font-family: var(--dr-num); font-size: var(--dr-fs-key); font-weight: 600; letter-spacing: .06em; text-transform: none; padding: 2px 7px; border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); color: var(--dr-dim); background: var(--dr-card-bg); }
.dr-zrow { display: grid; grid-template-columns: auto 1fr auto auto; align-items: baseline; gap: var(--dr-sp-3); padding: var(--dr-sp-2) var(--dr-sp-4); font-family: var(--dr-num); font-size: 11px; background: var(--dr-card-bg); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); }
.dr-zrow.demand { border-left-color: color-mix(in srgb, var(--dr-up) 70%, transparent); }
.dr-zrow.supply { border-left-color: color-mix(in srgb, var(--dr-dn) 70%, transparent); }
.dr-zrow .dz-k { display: inline-flex; align-items: center; gap: var(--dr-sp-2); font-family: var(--sans); font-size: 9.5px; letter-spacing: .04em; text-transform: uppercase; }
.dr-zrow.demand .dz-k { color: var(--dr-up); }
.dr-zrow.supply .dz-k { color: var(--dr-dn); }
.dr-zrow .dz-r { color: var(--dr-ink); font-variant-numeric: tabular-nums; }
.dr-zrow .dz-s { color: var(--dr-faint); font-size: var(--dr-fs-label); }
.dr-zrow .dz-a { color: var(--dr-dim); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* U6: per-zone D/W/M timeframe chip (nested in the label cell so the grid holds) */
.dz-tf { font-family: var(--dr-num); font-size: 8.5px; font-weight: 700; font-style: normal; letter-spacing: .04em; line-height: 1; color: var(--dr-faint); background: var(--panel); border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); padding: 2px 4px; }
.dr-zrow.demand .dz-tf { color: color-mix(in srgb, var(--dr-up) 80%, var(--dr-faint)); border-color: color-mix(in srgb, var(--dr-up) 35%, var(--dr-card-bd)); }
.dr-zrow.supply .dz-tf { color: color-mix(in srgb, var(--dr-dn) 80%, var(--dr-faint)); border-color: color-mix(in srgb, var(--dr-dn) 35%, var(--dr-card-bd)); }

/* U2: Levels header row — "Levels" + context pill on the left, D/W/M selector on the
   right, same line. Wraps (not overflows) on narrow/mobile width. */
.dr-sec-h.dr-levels-h { flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--dr-sp-2) var(--dr-sp-3); }
.dr-levels-h .dr-lvls-hl { display: inline-flex; align-items: center; gap: var(--dr-sp-3); min-width: 0; }
/* selector sits inline on the header's right — drop its standalone-row bottom margin */
.dr-levels-h .dr-sdtf { margin-bottom: 0; }

/* U2: collapse control for the full zone list (nearest pair always shows above it) */
.dr-zwrap { margin-top: var(--dr-sp-3); display: flex; flex-direction: column; gap: var(--dr-sp-1); }
.dr-zwrap .dr-zones { margin-top: var(--dr-sp-1); }
.sd-zx { align-self: flex-start; font-family: var(--dr-num); font-size: var(--dr-fs-key); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--dr-dim); background: var(--dr-card-bg); border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); padding: 3px 9px; cursor: pointer; transition: color .12s ease, background .12s ease; }
.sd-zx:hover { color: var(--dr-ink); background: var(--raise); }

/* U6: S/D timeframe selector — D / W / M multi-select toggle row atop the section */
.dr-sdtf { display: flex; align-items: center; gap: var(--dr-sp-3); margin-bottom: var(--dr-sp-3); }
.dr-sdtf .sd-tf-lab { font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); }
.sd-tfseg { display: inline-flex; border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); overflow: hidden; }
.sd-tf { font-family: var(--dr-num); font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--dr-faint); background: var(--dr-card-bg); border: none; padding: 3px 10px; cursor: pointer; transition: color .12s ease, background .12s ease; }
.sd-tf + .sd-tf { border-left: 1px solid var(--dr-card-bd); }
.sd-tf:hover { color: var(--dr-ink); background: var(--raise); }
.sd-tf.on { color: var(--dr-ink); background: var(--line-2); }
.sd-tf.on:hover { background: var(--line-2); }

/* U5: IBD ratings — fixed 4-cell row (reuses .dr-cells cell styling) */
.dr-ratcells { grid-template-columns: repeat(4, 1fr); }
.dr-ratcells .dc-v { font-size: var(--dr-fs-lg); font-weight: 700; }

/* U5: industry-group breadth bar + 1W/1M/3M perf row */
.dt-breadth { display: flex; align-items: center; gap: var(--dr-sp-3); margin-top: var(--dr-sp-3); }
.dt-breadth .dt-blab { font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); flex: none; }
.dt-breadth .dt-btrack { flex: 1; display: flex; height: 7px; background: var(--dr-card-bg); border: 1px solid var(--dr-card-bd); border-radius: var(--dr-card-radius); overflow: hidden; }
.dt-breadth .dt-btrack i { display: block; height: 100%; }
.dt-breadth .dt-btrack i.up { background: var(--dr-up); }
.dt-breadth .dt-btrack i.dn { background: var(--dr-dn); }
.dt-breadth .dt-bn { flex: none; font-family: var(--dr-num); font-size: var(--dr-fs-label); font-variant-numeric: tabular-nums; }
.dt-breadth .dt-bn .up { color: var(--dr-up); }
.dt-breadth .dt-bn .dn { color: var(--dr-dn); }
.dt-perfs { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--dr-sp-1) var(--dr-sp-4); margin-top: var(--dr-sp-3); }
.dt-perfs .dt-plab { font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); }
.dt-perf { display: inline-flex; align-items: baseline; gap: var(--dr-sp-1); font-family: var(--dr-num); }
.dt-perf .dt-pk { font-size: var(--dr-fs-key); letter-spacing: .04em; color: var(--dr-faint); }
.dt-perf .dt-pv { font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.dt-perf .dt-pv.up { color: var(--dr-up); }
.dt-perf .dt-pv.dn { color: var(--dr-dn); }

/* bull / bear — thesis blocks are instances of the shared card primitive */
.db-upd { font-family: var(--dr-num); font-size: var(--dr-fs-key); color: var(--dr-mute); letter-spacing: .04em; }
.dr-bb { padding: var(--dr-sp-3) var(--dr-sp-4); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); background: var(--dr-card-bg); margin-bottom: var(--dr-sp-2); }
.dr-bb:last-child { margin-bottom: 0; }
.dr-bb.bull { border-left-color: var(--dr-up); }
.dr-bb.bear { border-left-color: var(--dr-dn); }
.dr-bb .db-k { display: block; font-size: var(--dr-fs-key); letter-spacing: .12em; text-transform: uppercase; color: var(--dr-faint); margin-bottom: var(--dr-sp-1); }
.dr-bb.bull .db-k { color: color-mix(in srgb, var(--dr-up) 80%, var(--dr-faint)); }
.dr-bb.bear .db-k { color: color-mix(in srgb, var(--dr-dn) 80%, var(--dr-faint)); }
.dr-bb p { margin: 0; font-size: var(--dr-fs-body); line-height: 1.55; color: var(--dr-dim); }

/* news list — .news-sum is a placeholder slot filled by U8 (/news-summary) */
.dr-news { padding: var(--dr-sp-3) 0; border-bottom: 1px solid var(--line); }
.dr-news:last-child { border-bottom: none; }
.dr-news .dn-meta { display: flex; align-items: baseline; gap: var(--dr-sp-3); margin-bottom: var(--dr-sp-1); font-family: var(--dr-num); font-size: var(--dr-fs-key); text-transform: uppercase; letter-spacing: .05em; }
.dr-news .dn-src { color: var(--dr-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dr-news .dn-t { color: var(--dr-mute); margin-left: auto; flex: none; font-variant-numeric: tabular-nums; }
.dr-news .dn-title { display: block; color: var(--dr-dim); font-family: var(--sans); font-size: var(--dr-fs-body); line-height: 1.42; text-decoration: none; }
.dr-news .dn-title:hover { color: var(--dr-ink); text-decoration: underline; text-decoration-color: var(--line-2); }
.dr-news .news-sum:empty { display: none; }
.dr-news .news-sum { margin-top: var(--dr-sp-1); font-size: 10.5px; line-height: 1.45; color: var(--dr-faint); }

/* U4: catalyst strip — inline "why it's moving" bar directly under the hero.
   WIIM item → amber ⚡ CATALYST pill + accent stripe; no WIIM → greyed "Latest".
   Single row: pill/label + one-liner + age. The whole bar links to the article. */
.dr-catalyst {
  --dr-cat: #e8b339;
  display: flex; align-items: baseline; gap: var(--dr-sp-3);
  padding: var(--dr-sp-3) var(--dr-sp-5);
  border-bottom: 1px solid var(--dr-card-bd);
  text-decoration: none; cursor: pointer;
}
.dr-catalyst.is-wiim {
  background: color-mix(in srgb, var(--dr-cat) 8%, transparent);
  box-shadow: inset var(--dr-card-stripe) 0 0 0 color-mix(in srgb, var(--dr-cat) 80%, transparent);
}
.dr-catalyst:hover .cat-sum { color: var(--dr-ink); }
.dr-catalyst .cat-pill {
  flex: none; display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--dr-num); font-size: var(--dr-fs-key); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: #1a1205;
  background: var(--dr-cat); padding: 2px 5px; border-radius: var(--dr-card-radius); line-height: 1;
}
.dr-catalyst .cat-bolt { font-size: 10px; line-height: 1; }
.dr-catalyst .cat-lab {
  flex: none; font-family: var(--dr-num); font-size: var(--dr-fs-key); font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--dr-mute);
}
.dr-catalyst .cat-sum {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--sans); font-size: var(--dr-fs-body); line-height: 1.4; color: var(--dr-dim);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.dr-catalyst.is-latest .cat-sum { color: var(--dr-faint); }
.dr-catalyst .cat-sum.pending { opacity: .55; }
.dr-catalyst .cat-age {
  flex: none; font-family: var(--dr-num); font-size: var(--dr-fs-key); color: var(--dr-mute);
  font-variant-numeric: tabular-nums; text-transform: uppercase; letter-spacing: .05em;
}

/* U6: RS summary line (under hero) */
.dr-rsline { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--dr-sp-1) var(--dr-sp-3); padding: var(--dr-sp-3) var(--dr-sp-5); border-bottom: 1px solid var(--dr-card-bd); font-family: var(--dr-num); font-size: 11px; font-variant-numeric: tabular-nums; color: var(--dr-dim); }
.dr-rsline .rl-c { white-space: nowrap; }
.dr-rsline .rl-c b { font-weight: 600; color: var(--dr-faint); font-size: var(--dr-fs-key); letter-spacing: .06em; text-transform: uppercase; margin-right: 2px; }
.dr-rsline .rl-c .up { color: var(--dr-up); }
.dr-rsline .rl-c .dn { color: var(--dr-dn); }
.dr-rsline .rl-sep { color: var(--dr-mute); }

/* U1: Institutional Ownership — the design EXEMPLAR the drawer tokens are derived
   from. Values here are the source of --dr-fs-2xl / --dr-fs-lg / the badge look;
   routed through tokens so the whole drawer inherits this card's treatment. */
.dr-inst .di-head { display: flex; align-items: flex-end; flex-wrap: wrap; gap: var(--dr-sp-4) var(--dr-sp-6); margin-bottom: var(--dr-sp-5); }
.dr-inst .di-metric { display: flex; flex-direction: column; gap: var(--dr-sp-1); min-width: 0; }
.dr-inst .di-k { font-size: var(--dr-fs-key); letter-spacing: var(--dr-trk-key); text-transform: uppercase; color: var(--dr-faint); }
.dr-inst .di-v { font-family: var(--dr-num); font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; color: var(--dr-ink); }
.dr-inst .di-primary .di-v { font-size: var(--dr-fs-2xl); letter-spacing: -.01em; }
.dr-inst .di-metric:not(.di-primary) .di-v { font-size: var(--dr-fs-lg); color: var(--dr-dim); }
.dr-inst .di-badge { align-self: center; font-family: var(--dr-num); font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; padding: 3px var(--dr-sp-3); border: 1px solid var(--dr-mute); border-radius: var(--dr-card-radius); white-space: nowrap; }
.dr-inst .di-badge.up { color: var(--dr-up); border-color: color-mix(in srgb, var(--dr-up) 45%, transparent); background: color-mix(in srgb, var(--dr-up) 12%, transparent); }
.dr-inst .di-badge.dn { color: var(--dr-dn); border-color: color-mix(in srgb, var(--dr-dn) 45%, transparent); background: color-mix(in srgb, var(--dr-dn) 12%, transparent); }
.dr-inst .di-badge .di-badge-q { margin-left: 5px; padding-left: 5px; border-left: 1px solid currentColor; opacity: .6; font-weight: 500; letter-spacing: .3px; }
.dr-inst .di-trend-h { font-size: var(--dr-fs-key); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--dr-faint); margin-bottom: var(--dr-sp-3); }
.dr-inst .di-svg { width: 100%; height: 52px; display: block; }
.dr-inst .di-svg polyline { fill: none; stroke: var(--dr-mute); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.dr-inst .di-svg.up polyline { stroke: var(--dr-up); }
.dr-inst .di-svg.dn polyline { stroke: var(--dr-dn); }
.dr-inst .di-axis { display: flex; margin-top: var(--dr-sp-2); }
.dr-inst .di-tick { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 0; }
.dr-inst .di-tl { font-family: var(--dr-num); font-size: var(--dr-fs-key); letter-spacing: .02em; color: var(--dr-faint); white-space: nowrap; }
.dr-inst .di-tf { font-family: var(--dr-num); font-size: var(--dr-fs-label); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--dr-dim); white-space: nowrap; }

/* U6: analyst activity — dated rows are instances of the shared card primitive */
.dr-analyst .da-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--dr-sp-1) var(--dr-sp-4); margin-bottom: var(--dr-sp-3); font-family: var(--dr-num); font-variant-numeric: tabular-nums; }
.dr-analyst .da-n { font-size: 12px; font-weight: 600; color: var(--dr-up); }
.dr-analyst .da-n.dn { color: var(--dr-dn); }
.dr-analyst .da-n:not(.up):not(.dn) { color: var(--dr-dim); }
.dr-analyst .da-t { font-size: var(--dr-fs-label); letter-spacing: .04em; color: var(--dr-faint); }
.dr-analyst .da-row { display: grid; grid-template-columns: 1fr auto auto; align-items: baseline; gap: var(--dr-sp-3); padding: var(--dr-sp-2) var(--dr-sp-4); font-family: var(--dr-num); font-size: 11px; background: var(--dr-card-bg); border-radius: var(--dr-card-radius); border-left: var(--dr-card-stripe) solid var(--dr-card-accent); margin-bottom: var(--dr-sp-1); }
.dr-analyst .da-row:last-child { margin-bottom: 0; }
.dr-analyst .da-act { color: var(--dr-ink); font-family: var(--sans); font-size: 10.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dr-analyst .da-tcw { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.dr-analyst .da-tc.up { color: var(--dr-up); }
.dr-analyst .da-tc.dn { color: var(--dr-dn); }
.dr-analyst .da-d { color: var(--dr-mute); font-size: var(--dr-fs-label); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ============================================================ status bar */
/* The footer is now bookkeeping only — session, window counts, enrichment,
   clock. Feed health graduated to the rail + header instrument, because a
   footer dot is exactly where a dead feed went unnoticed for 24h. */
.statusbar {
  flex: none; display: flex; align-items: center; gap: var(--sp-4);
  height: var(--chrome-foot); padding: 0 var(--sp-5);
  background: var(--head); border-top: 1px solid var(--line-2);
  font-family: var(--num); font-size: var(--fs-label); color: var(--ink-faint);
  letter-spacing: .04em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden;
}
.statusbar .sess { color: var(--ink-dim); font-weight: 600; }
.statusbar .fs-sep { color: var(--neutral); }
.statusbar .spacer { flex: 1; min-width: var(--sp-2); }
.statusbar .updated { color: var(--ink-faint); }
/* A configured-but-dead enrich service must not read like a healthy one — the
   whole reason the 2026-07-30 outage was invisible for hours. */
.statusbar .fs-enrich.bad { color: var(--lo); font-weight: 600; }
.statusbar .fs-cmp { color: var(--ink-dim); }
.statusbar .fs-cmp.ok { color: var(--hi); }
.statusbar .fs-cmp.warn { color: var(--warn); }
.statusbar .fs-cmp[hidden] { display: none; }
.statusbar .demo-badge {
  font-family: var(--num); font-size: var(--fs-key); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--warn);
  border: 1px solid color-mix(in srgb, var(--warn) 50%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  padding: 2px 8px;
}
.statusbar .demo-badge[hidden] { display: none; }

/* ============================================================ scrollbars */
.grid, .win-body { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
.grid::-webkit-scrollbar, .win-body::-webkit-scrollbar { width: 8px; }
.grid::-webkit-scrollbar-track, .win-body::-webkit-scrollbar-track { background: transparent; }
.grid::-webkit-scrollbar-thumb, .win-body::-webkit-scrollbar-thumb { background: var(--line-2); border: 2px solid var(--panel); border-radius: 0; }
.grid::-webkit-scrollbar-thumb:hover, .win-body::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }

/* ============================================================ motion pref */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 0s; --dur-mid: 0s; --dur-slow: 0s; }
  .wrow.fresh { animation: none; }
  .statusbar .dot[data-live="1"] { animation: none; }
}
/* Settings "reduce motion" toggle — same suppression, user-driven rather than
   OS-driven, so it must win over the tokens' animated defaults. */
html[data-motion="reduce"] {
  --dur-fast: 0s; --dur-mid: 0s; --dur-slow: 0s;
}
html[data-motion="reduce"] .wrow.fresh,
html[data-motion="reduce"] .statusbar .dot[data-live="1"],
html[data-motion="reduce"] .feed-rail,
html[data-motion="reduce"] .fd-dot { animation: none; }
/* Settings "row flash on new alert" toggle, default on. */
html[data-rowflash="off"] .wrow.fresh { animation: none; }

/* ============================================================ responsive */
/* grid column counts are handled by auto-fit (no fixed breakpoints needed) */

/* tablet (~768px) — the attr filter row already owns its own line and scrolls;
   here the r1 row wraps (brand + readout first, legend below) and the category
   legend becomes horizontally scrollable too. (U17) */
@media (max-width: 860px) {
  /* the category rail scrolls sideways rather than crowding the feed block out */
  .topbar { gap: var(--sp-3); padding: 0 var(--sp-3); }
  .catwrap { min-width: 0; flex: 1; }
  .catwrap .flabel { display: none; }
  .fd-age { display: none; }   /* keep the state word; the age needs the room more */
}

/* U7 drawer — full-screen takeover on phones/small tablets (≤640px): the panel
   fills the viewport, the backdrop is dropped, and the header control reads as a
@media (max-width: 640px) {
  .dr-backdrop { display: none; }
  .dr-panel { width: 100vw; max-width: none; height: 100dvh; border-left: none; box-shadow: none; animation: none; }
  .dr-close { border-color: var(--line-2); padding: 6px 10px; }
  .dr-close .dr-x { display: none; }
  .dr-close .dr-back { display: inline; }
  .dr-chart }

/* ---- drawer navigation + loading vocabulary ---------------------------- */
/* The drawer was one ~12-section scroll with no navigation. */
/* The rail scrolls horizontally with a hidden scrollbar. With 8+ sections the
   last tabs (Thesis, News) sit past the right edge with nothing to say so — they
   read as missing rather than off-screen. A soft edge fade restores that signal
   without spending vertical space on a scrollbar. */
.dr-nav {
  flex: none; display: flex; gap: 0; height: 30px;
  background: var(--bg); border-bottom: 1px solid var(--line-2);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  scroll-behavior: smooth;
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 22px), transparent 100%);
          mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 22px), transparent 100%);
}
/* fully scrolled — nothing more to the right, so drop the fade */
.dr-nav.at-end { -webkit-mask-image: none; mask-image: none; }
.dr-nav::-webkit-scrollbar { height: 0; }
.dr-nb {
  flex: none; font-family: var(--num); font-size: var(--fs-key); letter-spacing: .11em;
  text-transform: uppercase; color: var(--ink-dim); background: transparent;
  border: none; border-bottom: 2px solid transparent; padding: 0 11px; cursor: pointer;
  white-space: nowrap; transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.dr-nb:hover { color: var(--ink); }
.dr-nb.on { color: var(--ink); border-bottom-color: var(--accent); }

/* a collapsed section keeps its headline number visible, so it still answers
   its question without being expanded */
.dr-sec-h[data-drc] { cursor: pointer; user-select: none; }
.dr-sec-head {
  margin-left: auto; font-family: var(--num); font-size: var(--fs-num); font-weight: 700;
  color: var(--ink); letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums;
}
.dr-sec-car { font-size: 11px; color: var(--dr-faint); width: 12px; text-align: right; flex: none; transition: transform var(--dur-fast) ease; }
.dr-sec[data-open="true"] .dr-sec-car { transform: rotate(180deg); }
/* when open, the number belongs to the body, not the header */
.dr-sec[data-open="true"] .dr-sec-head { display: none; }
.dr-sec[data-open="false"] > *:not(.dr-sec-h) { display: none; }
.dr-sec[data-open="false"] .dr-sec-h { margin-bottom: 0; }

/* Loading != no data != zero. */
.dr-skelwrap { padding: var(--sp-4) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-5); }
.dr-skelwrap .dr-sec { border: none; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.dr-skel { display: block; background: var(--skel); animation: drSkel 1.4s ease-in-out infinite; }
@keyframes drSkel { 0%, 100% { opacity: .32; } 50% { opacity: .14; } }
html[data-motion="reduce"] .dr-skel { animation: none; opacity: .24; }
@media (prefers-reduced-motion: reduce) { .dr-skel { animation: none; opacity: .24; } }
/* the visible skeleton is decoration; this is what a screen reader gets */
.dr-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Bottom navigation — phone only. Board / Breadth / Scope / Windows, the four
   things the phone is actually used for. */
.mnav { display: none; flex: none; background: var(--head); border-top: 1px solid var(--line-2); }
.mnav button {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 9px 0 11px; background: transparent; border: none;
  border-top: 2px solid transparent; cursor: pointer;
}
.mnav .mn-i { font-size: 14px; line-height: 1; color: var(--ink-faint); }
.mnav .mn-l { font-family: var(--num); font-size: var(--fs-key); letter-spacing: .11em; text-transform: uppercase; color: var(--ink-faint); }
.mnav button.on { border-top-color: var(--accent); }
.mnav button.on .mn-i, .mnav button.on .mn-l { color: var(--ink); }

/* ============================================================ phone */
/* Not a reflow. The desktop board's two columns of two-pane tiles do not
   survive 414px — stacking them means reaching the fourth scan past three
   sparse ones, which is what the brief calls out. On a phone the board shows
   ONE PAIR AT A TIME with a ▲/▼ side switch, rows are 44px, pair headers stick,
   and navigation moves into the thumb zone. */
@media (max-width: 640px) {
  /* ---- chrome ---- */
  .topbar { height: 52px; padding: 0 var(--sp-4); gap: 11px; }
  .brand { padding-right: 0; border-right: none; }
  .brand .title { display: none; }        /* the mark alone is the lockup here */
  .catwrap { display: none; }             /* categories move into the Scope view */
  .feedblock { flex: 1; min-width: 0; border: none; padding: 0; gap: var(--sp-3); }
  .fd-txt { min-width: 0; }
  .fd-state, .fd-sub { overflow: hidden; text-overflow: ellipsis; }
  .fd-age { display: flex; }
  .gear-btn { width: 34px; height: 34px; font-size: 15px; }

  .scopebar { height: 38px; padding: 0 var(--sp-4); }
  .scope-tokens { gap: var(--sp-2); }
  .sc-token { padding: 4px 6px; }
  .sc-count { display: none; }            /* the ratio rides the Scope view */
  .scope-right .sc-btn .fx { font-size: 13px; }

  /* ---- board: one column, one pair at a time ---- */
  /* .board-top and .board are display:contents (see "board frame"), so they
     generate NO box: flex-direction, height and overflow on them do nothing.
     The scroll surface is .grid — see the phone override in the board-frame
     block. A rule here that reads like a scroll surface is worse than no rule;
     it is what made the unscrollable-phone bug invisible for a whole session. */
  .breadth.win { height: 190px; flex: none; }
  .board-col { overflow: visible; flex: none; }
  /* the band tile rejoins the single column instead of sitting beside breadth */
  .band-slot { flex: none; overflow: visible; }
  .band-slot > .pair .win-body { max-height: none; }

  /* Breadth is its own bottom-nav view, not a permanent 190px tax on the board.
     Only the CHART is swapped out — the tile that shares the top band on desktop
     stays in the column flow, or it would vanish from the phone entirely. */
  body[data-mview="board"] .breadth.win { display: none; }
  body[data-mview="breadth"] .board,
  body[data-mview="breadth"] .quietrail,
  body[data-mview="breadth"] .band-slot { display: none; }
  body[data-mview="breadth"] .board-top { flex: 1; min-height: 0; }
  body[data-mview="breadth"] .breadth.win { flex: 1; height: auto; }
  /* Scope/Windows open their surfaces over the board rather than replacing it,
     so the tape stays visible behind — the phone is for CHECKING the tape. */

  .pair-h {
    position: sticky; top: 0; z-index: 4; height: 38px;
    background: var(--bg); border-bottom: 1px solid var(--line-2);
  }
  .pair-h .nm { color: var(--ink); font-size: var(--fs-label); }
  /* the ▲n/▼n readout becomes the SIDE SWITCH: tap a side to show its rows */
  .pair-ct { border: 1px solid var(--line-2); gap: 0; }
  .pair-ct .pc {
    padding: 4px 11px; font-size: 11px; font-weight: 600; cursor: pointer;
    background: transparent; border: none;
  }
  .pair-ct .pc + .pc { border-left: 1px solid var(--line-2); }
  .pair-ct .pc-sep { display: none; }
  .pair-ct .pc.sel.up { color: var(--bg); background: var(--hi); }
  .pair-ct .pc.sel.dn { color: var(--bg); background: var(--lo); }
  .pair-ct .pc.sel .ct, .pair-ct .pc.sel .ct.zero { color: inherit; }
  /* one pane at a time — the switch picks which */
  .pair-b { display: block; }
  .pair-b > .pane { display: none; width: 100%; }
  .pair[data-mside="bull"] .pair-b > .pane[data-side="bull"],
  .pair[data-mside="bear"] .pair-b > .pane[data-side="bear"] { display: flex; }
  .pair[data-mside="bear"] .pair-b > .pane-quiet[data-side="bear"],
  .pair[data-mside="bull"] .pair-b > .pane-quiet[data-side="bull"] { display: flex; }
  .pair[data-single="true"] .pair-b > .pane { display: flex; }
  .win-body { max-height: none; }

  /* ---- 44px rows ----
     The column TEMPLATE is left alone: overriding it collapses the five row
     cells into three and the row wraps onto three lines. Only the padding and
     type scale change. */
  .wrow { padding: 6px var(--sp-4); min-height: var(--row-h-touch); display: flex; flex-direction: column; justify-content: center; }
  .r1 { font-size: 13px; align-items: baseline; }
  .r1 .t { font-size: 10px; }
  .badges { padding-left: 0; margin-top: 3px; }
  .tl-table td { padding: 8px 12px; }
  .win-x { padding: 4px 9px; font-size: 15px; }
  .pair-h .win-sort { padding: 4px 6px; font-size: var(--fs-label); }

  /* ---- bottom nav in the thumb zone ---- */
  .mnav { display: flex; }
  .quietrail { height: auto; padding: var(--sp-2) var(--sp-3); }
  .statusbar { display: none; }   /* bookkeeping is desktop-only; the nav needs the room */

  /* the rail's own phone treatment lives beside the rail, in the phone override
     at the end of the rail-bar block — .railbar is defined after this media query
     and would otherwise win on source order */

  /* surfaces that were popovers become sheets */
  .scope-panel { top: auto !important; bottom: 0; left: 0 !important; right: 0 !important; max-height: 72vh; border-left: none; border-right: none; }
  /* 16px is iOS's no-zoom threshold: focus a smaller field and Safari zooms the
     whole page to it, which is what made naming a workspace on a phone feel
     broken even once the caret stopped being destroyed under it. The desktop
     10.5px is a density choice that has no reason to apply to a full-width sheet.
     The two numeric fields in this panel still zoom; they are not what anyone
     opens this sheet to type into. */
  .sp-name-input { font-size: 16px; padding: 9px 10px; }
  .settings-pop { top: auto !important; bottom: 0; right: 0 !important; left: 0 !important; width: auto; max-height: 72vh; overflow-y: auto; }

  /* chart pop-out fills the screen */
    }

/* DEMO MODE badge (remove-safe; only appears with ?demo= in the URL). It lives
   in the status footer now — see .statusbar .demo-badge — rather than floating
   over the board in its own private palette. */
#demo-badge { cursor: help; }

/* ---- Replica compare mode (U6) ---- */
.wr-row { border-left: 2px solid var(--acc, #58a6ff); }
.wr-row .sym::after { content: 'WR'; font-size: 8px; opacity: .7; margin-left: 4px; vertical-align: super; color: var(--acc, #58a6ff); }
#cmp-btn.on {
  color: var(--ink); font-weight: 700; border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
#cmp-btn.on .tidy-ico { color: var(--accent); }
#cmp-btn .cmp-ov { font-size: 10px; margin-left: 4px; }
#cmp-btn .cmp-ov.ok { color: var(--up, #3fb950); }
#cmp-btn .cmp-ov.warn { color: var(--dn, #f85149); }

/* ============================================================ pattern group
   Row badges, the direction spine and the Setup band. Filled-vs-outlined is the
   WHOLE badge taxonomy — an outlined badge is list membership, a filled one is
   "a setup fired on end-of-day bars". Do not add a third axis. */

/* Direction spine: lives in the row's existing 9px left padding, just inside the
   category stripe. Costs zero horizontal room, so it survives every column drop
   and is the last mark standing at the narrowest tile width. */
.wrow.pat-long,
.tl-row.pat-long   { background-image: var(--pat-spine-long); background-repeat: no-repeat; }
.wrow.pat-short,
.tl-row.pat-short  { background-image: var(--pat-spine-short); background-repeat: no-repeat; }

/* One dated mark per ROW. The badge line carries it; the live cells above it
   never do — that is the whole point of marking the block rather than the chip. */
.wrow.has-pat .badges,
.tl-row.has-pat .badges { border-left: var(--pat-dated); padding-left: 7px; }

/* The composite pattern chip. Filled = Day 1 (the entry window). */
.bdg.pat {
  color: var(--bg);
  background: var(--swc, var(--accent));
  border: none;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
/* Ageing replaces the fill with an outline in the SAME direction hue, so the
   signal quiets down without ever changing what it says. */
.bdg.pat.chase  { color: var(--swc, var(--accent)); background: transparent; border: 1px solid var(--swc, var(--accent)); }
.bdg.pat.mature { color: var(--ink-faint); background: transparent; border: 1px solid var(--line-3); }
/* Provisional. Amber and dashed REGARDLESS of direction — a forming long
   rendered in the up-hue reads as settled fact, which is the one thing the
   dated vocabulary exists to prevent. */
.bdg.pat.forming { color: var(--pat-forming); background: rgba(216, 160, 31, .10); border: var(--pat-forming-bd); }

/* Agreement ticks: children INSIDE the chip, never a second chip. */
.bdg.pat .pat-dots { display: inline-flex; gap: 1px; }
.bdg.pat .pat-dots b { width: 2px; height: 6px; background: currentColor; display: block; }
.bdg.pat .pat-warn { font-style: normal; line-height: 1; }

/* Narrow-tile drop order (plan R14): ticks go first, then the code truncates to
   its leading direction arrow. The spine never drops. */
@container (max-width: 300px) { .bdg.pat .pat-dots { display: none; } }
@container (max-width: 240px) { .bdg.pat { font-size: 0; gap: 0; } .bdg.pat::first-letter { font-size: 8px; } }

/* ---- scope panel: two bands ------------------------------------------------
   Pattern answers a different question from the four Universe groups, so the
   panel separates them with a rule heavier than the 1px column gaps. */
.sp-band + .sp-band { border-top: var(--sp-band-rule); }
.sp-bandh { display: flex; align-items: baseline; gap: 12px; padding: 11px 16px 0; flex-wrap: wrap; }
.sp-bk { font-size: 9px; letter-spacing: var(--trk-label); text-transform: uppercase; color: var(--ink); font-weight: 700; }
.sp-bsub { font-size: 11px; color: var(--ink-faint); }
.sp-bandh .sp-cr { margin-left: auto; }

/* Pattern's own as-of stamp. Amber when stale — and it must NOT move the feed
   rail, which reports the SSE stream on a different clock entirely. */
.sp-asof { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono); font-size: 9.5px; color: var(--ink-faint); border-left: var(--pat-dated); padding-left: 10px; }
.sp-asof i { font-style: normal; font-size: 8px; letter-spacing: var(--trk-label); text-transform: uppercase; font-weight: 700; }
.sp-asof.stale { color: var(--warn); }

.sp-setup { display: grid; grid-template-columns: 300px 1fr 300px 320px; gap: 1px; background: var(--line-2); margin-top: 11px; }
@media (max-width: 1200px) { .sp-setup { grid-template-columns: 1fr 1fr; } }
.sp-tfcol { background: var(--head); }
.sp-seg { flex: 1; font-family: var(--mono); font-size: 10.5px; color: var(--ink-faint); background: var(--bg); border: 1px solid var(--line-2); padding: 6px 0; cursor: pointer; }
.sp-seg.on { color: var(--ink); background: rgba(107, 140, 255, .14); border-color: rgba(107, 140, 255, .7); font-weight: 700; }
.sp-seg b { font-weight: 400; color: var(--ink-faint); font-size: 9.5px; padding-left: 4px; }
.sp-more { width: 100%; font-family: var(--mono); font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim); background: transparent; border: 1px dashed var(--line-3); padding: 5px 0; margin-top: 10px; cursor: pointer; }
.sp-more.on { color: var(--ink); border-style: solid; }
.sp-warn { margin-top: 12px; padding: 9px 10px; background: var(--bg); border-left: 2px solid var(--warn); font-size: 10px; line-height: 1.5; color: var(--ink-dim); }
.sp-inert { padding: 12px 16px; font-size: 10.5px; line-height: 1.5; color: var(--ink-faint); }
.sp-gate { grid-template-columns: 28px 1fr auto; }

@media (prefers-reduced-motion: reduce) { .bdg.pat { transition: none; } }

/* The base-timeframe cap: a PARAMETER, not a filter, so it carries no ✕ and is
   styled as a dashed frame rather than a lit token. It sits ahead of the pattern
   tokens so the timeframe can never scroll out of view while a pattern token is
   on — a badge whose timeframe you cannot see means something you cannot check. */
.sc-token.sc-cap {
  background: transparent;
  border: 1px dashed var(--line-3);
  color: var(--ink-dim);
  cursor: default;
}

/* Sub-group header inside "Which setup". Uses the panel's existing sub-label
   grammar (MIN FUNDS, NEW ENTRANTS) rather than a new device, because the fact it
   carries — which setups the timeframe governs — is structural, not decorative.
   The first one is live: it rewrites when the base timeframe changes. */
.sp-subhead {
  display: block;
  margin: 9px 16px 5px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
.sp-col > .sp-subhead:first-of-type { border-top: none; padding-top: 0; }

/* ---- drawer: PATTERN section ----------------------------------------------
   One bold gesture only. The pivot is the single prescriptive number in the
   product — every other figure in this drawer describes what already happened —
   so it is drawn as a POSITION on a track rather than another row of digits.
   Everything around it stays quiet. */
.dp-fired { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.dp-name { font-size: 12px; color: var(--ink); }
.dp-sub { font-size: 10.5px; color: var(--ink-faint); }

/* Six dots, two families, split by a hairline. The row badge can only afford a
   tick count; here there is room to say WHICH timeframes agreed. */
.dp-tfs { display: flex; align-items: center; gap: 3px; margin-bottom: 12px; }
.dp-dot { width: 7px; height: 7px; display: block; background: var(--neutral); }
.dp-dot.long { background: var(--hi); }
.dp-dot.short { background: var(--lo); }
.dp-dot.cur { outline: 1px solid var(--line-3); outline-offset: 1px; }
.dp-fsep { width: 1px; height: 9px; background: var(--line-2); margin: 0 4px; display: block; }
.dp-tflab { font-family: var(--mono); font-size: 8.5px; color: var(--ink-faint); letter-spacing: .04em; margin-left: 8px; }

.dp-base { font-size: 11px; color: var(--ink-dim); margin-bottom: 9px; }

/* The track. The rail is the price continuum, the vertical mark is the pivot,
   the dot is where price actually sits. Below the pivot the gap reads as
   distance still to cover; past it, the fill flips to signal a live breakout. */
.dp-track { position: relative; height: 18px; margin: 0 2px 7px; }
.dp-rail { position: absolute; top: 8px; left: 0; right: 0; height: 2px; background: var(--line-2); display: block; }
.dp-pivot { position: absolute; top: 1px; left: 50%; width: 2px; height: 16px; background: var(--warn); display: block; }
.dp-px { position: absolute; top: 5px; width: 8px; height: 8px; margin-left: -4px; border-radius: 50%; background: var(--ink); display: block; }
.dp-track.over .dp-px { background: var(--hi); }

.dp-legend { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.dp-l { display: flex; flex-direction: column; font-size: 9px; color: var(--ink-faint); letter-spacing: .04em; }
.dp-l b { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--ink-dim); letter-spacing: .02em; }
.dp-lto { align-items: center; font-family: var(--mono); font-size: 11px; color: var(--warn); }
.dp-lto.up { color: var(--hi); }
.dp-lpv { align-items: flex-end; }

.dp-none { font-size: 10.5px; line-height: 1.5; color: var(--ink-faint); }
.dp-why { font-size: 10.5px; line-height: 1.5; color: var(--ink-faint); margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--line); }

/* ---- one scroll surface per gesture ---------------------------------------
   The tile body used to scroll INSIDE a column that also scrolls. A swipe landed
   on the inner surface first, was captured until it bottomed out, then chained —
   so the same gesture did different things depending on state the user cannot
   see. Nested scrolling is a failure mode, not a feature.

   The column is now the only thing that moves. A tile shows its newest rows and
   SAYS how many it is holding back, which turns "there is more here" from
   something you discover by accident into something you can read. */
.win-body { flex: none; max-height: none; overflow: visible; }
/* The cap is applied by syncWinMore in JS, not by nth-child. It has to be:
   nth-child counts DOM position, and hiding rows with .hidden-scope does not
   renumber them, so a filtered-in row sitting 13th or later stayed invisible
   even though the scope bar had just counted it. JS can count SHOWN rows;
   selectors cannot. This rule is only the paint. */
.wrow.cap-hid, .tl-row.cap-hid { display: none; }

.win-more {
  flex: none; width: 100%; background: transparent; border: none;
  border-top: 1px dotted var(--line-2);
  font-family: var(--mono); font-size: 9px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-faint);
  padding: 5px 0; cursor: pointer;
}
.win-more:hover { color: var(--ink-dim); background: var(--raise); }
.win-more:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }

/* A gesture that bottoms out must not chain into the page or the browser's
   pull-to-refresh — that is the other half of "one surface per gesture". */
.board-col, .dr-scroll { overscroll-behavior: contain; }

/* ---- phone: nothing is pinned ---------------------------------------------
   .board-top is a fixed 214px band on desktop. On a phone that band plus one
   tile already exceeds the viewport, so the board below it was unreachable —
   the screenshot showed a top list occupying the whole screen with no way past
   it. Below 860px the band joins the scroll and the columns stack. */
@media (max-width: 860px) {
  /* One column, one scroll surface: the PAGE. The two board columns stop being
     their own scrollers, so a phone swipe always moves the same thing. */
  .app { overflow-y: auto; overscroll-behavior-y: contain; }
  .grid { grid-template-columns: 1fr; grid-template-rows: auto auto auto; }
  .grid > .board-top > .breadth { grid-column: 1; grid-row: 1; height: auto; min-height: 180px; }
  .grid > .board > #col-a { grid-column: 1; grid-row: 2; }
  .grid > .board > #col-b { grid-column: 1; grid-row: 3; }
  .board-col { overflow: visible; }
}

/* The scope-bar cap is now a control: the element that REPORTS the base
   timeframe is the element that changes it. Routing that through
   + FILTER > Reading on made the reader travel to a different surface to act on
   the thing they were already looking at. It keeps the dashed, ✕-less frame
   because it is still a parameter, not a removable filter. */
.sc-token.sc-cap { cursor: pointer; }
.sc-token.sc-cap:hover { border-color: var(--ink-faint); color: var(--ink); }
.sc-token.sc-cap:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
.cap-cv { font-style: normal; font-size: 8px; color: var(--ink-faint); padding-left: 3px; }

.tfpop {
  position: fixed; z-index: 60; min-width: 208px;
  background: var(--panel); border: 1px solid var(--line-3);
  padding: 9px 10px 10px;
}
.tfp-h { font-size: 9px; letter-spacing: var(--trk-label); text-transform: uppercase; color: var(--ink); font-weight: 700; margin-bottom: 8px; }
.tfp-k { font-size: 8.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); font-weight: 700; margin: 8px 0 5px; }
.tfp-row { display: flex; gap: 4px; }
.tfp-b {
  flex: 1; font-family: var(--mono); font-size: 11px; color: var(--ink-faint);
  background: var(--bg); border: 1px solid var(--line-2); padding: 6px 0; cursor: pointer;
}
.tfp-b.on { color: var(--ink); background: rgba(107, 140, 255, .14); border-color: rgba(107, 140, 255, .7); font-weight: 700; }
.tfp-b:hover { color: var(--ink); }
.tfp-note { font-size: 9px; line-height: 1.45; color: var(--ink-faint); margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--line); }

/* ---- board frame ----------------------------------------------------------
   Breadth is half-width market context, and the right column scrolls from the
   TOP — level with breadth, not beneath it. That is one grid, not a fixed band
   stacked on a board: a band would pin whatever tile sat in it, and pinning a
   ranked list above the scroll is what made Biggest Gainers unreachable.

   .board-top and .board become display:contents so their children join the same
   grid; the wrappers stay in the DOM because app.js syncs tiles into them. */
.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto minmax(0, 1fr);
}
.board-top, .board { display: contents; }
.band-slot { display: none; }
.grid > .board-top > .breadth { grid-column: 1; grid-row: 1; height: 214px; min-width: 0; }
.grid > .board > #col-a { grid-column: 1; grid-row: 2; }
/* the right column spans BOTH rows, so its first tile sits beside breadth and
   the whole column scrolls as one surface */
.grid > .board > #col-b { grid-column: 2; grid-row: 1 / span 2; }

/* ---- phone ----------------------------------------------------------------
   The board is a two-column grid on desktop, so a phone needs an explicit
   single-column fallback or it renders two columns on a 390px screen.
   That is ALL this does. The one-window-at-a-time rail switcher that briefly
   lived here is reverted: it was built live against a different idea than the
   design doc's, never rendered correctly, and the iPhone/iPad tiers are now
   with design (docs/design/2026-07-30-touch-tiers-design-brief.md).
   Pairs keep their pre-existing ≤640px behaviour of showing one side at a time. */
@media (max-width: 860px) {
  /* Stacking the columns turns row 2 from minmax(0, 1fr) into a content-sized
     `auto` row, and THAT is what has to bring its own scroll surface with it.
     On desktop the bounded row is what gives .board-col something to scroll
     inside; once every row is `auto`, both columns are handed their full
     natural height (measured: 1873px and 1297px), so scrollHeight ===
     clientHeight and their overflow-y never fires. The grid then holds ~3170px
     of board inside a ~695px viewport remainder with overflow:hidden, and the
     entire board below the fold becomes unreachable — no gesture anywhere on
     the page scrolls it.
     So the phone moves the scroll to the grid itself: ONE surface for the whole
     board, which is also the one-scroll-surface-per-gesture rule the columns
     were already following on desktop. */
  .grid { grid-template-columns: 1fr; grid-template-rows: auto auto auto; overflow-y: auto; }
  .grid > .board-top > .breadth { grid-column: 1; grid-row: 1; height: auto; min-height: 180px; }
  .grid > .board > #col-a { grid-column: 1; grid-row: 2; }
  .grid > .board > #col-b { grid-column: 1; grid-row: 3; }
}

/* ---- rail bar --------------------------------------------------------------
   "+ Add window" was a CHILD of the quiet rail, and the rail hides itself the
   moment every scan is on the board. Pinning them all therefore removed the only
   route back to the Add Window picker — the board could be added to but never
   added to again. They are siblings now: the rail comes and goes, the button
   does not. */
.railbar {
  flex: none; display: flex; align-items: center; gap: var(--sp-3);
  height: var(--chrome-quiet); padding: 0 var(--sp-4);
  background: var(--head); border-top: 1px solid var(--line-2);
  min-width: 0;
}
/* the rail keeps its own scroll but no longer owns the bar's chrome */
.railbar > .quietrail {
  height: auto; padding: 0; background: none; border-top: none;
  min-width: 0; flex: 1;
}

/* ---- phone: the rail is the whole bar -------------------------------------
   This override sits HERE, after the rules above, rather than in the main phone
   block further up the file: .railbar is declared after that block, so an
   equal-specificity phone rule there loses on source order and the bar keeps its
   14px side padding. Same reason the board-frame section carries its own phone
   override instead of pushing it upstairs.

   "+ Add window" is the SECOND route to the picker on a phone — the Windows tab
   in the nav directly below it calls the same togglePicker(). Two controls for
   one surface, and this one was charging the quiet rail a slice of the only
   horizontal room it has, so the scan chips ran out of width and clipped
   mid-name against it.

   PHONE ONLY, deliberately. On desktop .mnav is display:none, which makes this
   button the ONLY way back to the picker — which is why index.html makes it a
   SIBLING of the rail rather than a child: the rail hides itself once every scan
   is pinned, and it used to take the button with it. Hiding it globally would
   re-open exactly that trap. */
@media (max-width: 640px) {
  .railbar > .qr-add,
  .railbar > .qr-gap { display: none; }
  .railbar { padding: 0; }
  /* the rail owns the inset now, so the scroll surface runs edge to edge and the
     last chip can be scrolled clear of the screen edge instead of under it */
  .railbar > .quietrail { padding: var(--sp-2) var(--sp-4); }
}

/* ============================================ WaveRider chart (plan 2026-09-10-001 U12) */
/* The vendor renders into an iframe it sizes itself, so the host just needs a
   definite box — an auto-height parent collapses it to zero and the chart mounts
   into nothing. */
.wr-chart-host { position: relative; width: 100%; height: 260px; min-height: 200px; }
.wr-chart-host iframe { border: 0; width: 100%; height: 100%; }
.wr-chart-msg { padding: 6px 8px; font-family: var(--num); font-size: 10.5px; color: var(--ink-faint); }

/* Mark-detail card. Positioned by the package's anchor maths (pane-relative x,
   bottom offset by the mark strip), so left/bottom are set inline. */
.wc-markcard { position: absolute; z-index: 6; width: 320px; max-height: 320px; overflow-y: auto;
  background: var(--panel, #0d1016); border: 1px solid var(--line, #191c22);
  border-top: 2px solid var(--accent); border-radius: 3px; padding: 6px 8px;
  font-family: var(--num); font-size: 11px; box-shadow: 0 6px 20px rgba(0,0,0,.45); }
.wc-markcard-h { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.wc-markcard-date { color: var(--ink-dim); font-size: 10px; letter-spacing: .06em; }
.wc-markcard-x { margin-left: auto; background: none; border: none; color: var(--ink-faint);
  cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; }
.wc-markcard-x:hover { color: var(--ink); }
.wc-markcard-sec { padding: 3px 0; border-top: 1px solid var(--line); }
.wc-markcard-sec:first-of-type { border-top: none; }
.wc-markcard-row { display: flex; justify-content: space-between; gap: 10px; padding: 1px 0; }
.wc-markcard-k { color: var(--ink-faint); }
.wc-markcard-v { color: var(--ink); }
.wc-markcard-news { color: var(--ink-dim); line-height: 1.35; white-space: normal; }

/* U12/KTD4: the controls WaveCatcher keeps when the vendor owns the toolbar —
   next ticker and pop-out. Same flat idiom as the strip they replace. */
/* .pop-btn kept its markup through U14 (it opens the popout WINDOW now, not the
   retired in-page overlay), so it needs styling here after the old chart rules
   that carried it were removed. */
.chart-ctl .wr-prev, .chart-ctl .wr-next, .chart-ctl .pop-btn { font-family: var(--num); font-size: 11px;
  color: var(--ink-dim); background: transparent; border: 1px solid var(--line);
  border-radius: 2px; padding: 1px 7px; cursor: pointer; line-height: 1.4; }
.chart-ctl .wr-prev:hover, .chart-ctl .wr-next:hover, .chart-ctl .pop-btn:hover { color: var(--ink); border-color: var(--line-2); }
.chart-ctl .ctl-gap { flex: 1 1 auto; }

/* U2/KTD9 — undocked chart. The host element NEVER moves in the DOM; only its
   positioning changes. Re-parenting it would discard the vendor iframe's
   browsing context and silently kill the widget handle. */
.dr-chart.wr-undocked .wr-chart-host {
  position: fixed; right: 24px; bottom: 24px; z-index: 40;
  width: min(720px, 60vw); height: 420px;
  background: var(--panel, #0d1016); border: 1px solid var(--line, #191c22);
  border-radius: 3px; box-shadow: 0 12px 40px rgba(0,0,0,.55);
}
/* Drag strip for the floating panel. Only rendered while undocked, and always a
   CHILD of the host — wrapping the host would re-parent the vendor iframe and
   kill the widget (KTD9). It overlays the chart's top edge rather than adding
   height, so undocking costs no chart area. */
.dr-chart.wr-undocked .wr-chart-host .wr-drag {
  position: absolute; left: 0; right: 0; top: 0; height: 18px; z-index: 2;
  cursor: grab; background: linear-gradient(var(--panel, #0d1016), rgba(13,16,22,0));
  border-radius: 3px 3px 0 0;
}
.dr-chart.wr-undocked .wr-chart-host .wr-drag::after {
  content: ''; position: absolute; left: 50%; top: 6px; width: 26px; height: 2px;
  margin-left: -13px; border-radius: 2px; background: var(--line-2, #2a2f3a);
}
.dr-chart.wr-undocked .wr-chart-host.wr-dragging { user-select: none; }
.dr-chart.wr-undocked .wr-chart-host.wr-dragging .wr-drag { cursor: grabbing; }
/* While dragging, the pointer crosses the iframe; without this the frame eats
   the move events even with pointer capture held on the strip. */
.dr-chart.wr-undocked .wr-chart-host.wr-dragging iframe { pointer-events: none; }

/* The section keeps its height while the chart floats, so the drawer does not
   collapse into a gap where the chart was. */
.dr-chart.wr-undocked .wr-chart-host::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
}
.wr-chart-placeholder { display: none; border: 1px dashed var(--line, #191c22);
  border-radius: 2px; font-family: var(--num); font-size: 10.5px; color: var(--ink-faint);
  align-items: center; justify-content: center; }
/* A floating or popped-out chart is not in the drawer, so its 260px reservation
   is dead space in the one column that has real information to show. Collapse it
   to a single line that still NAMES the state — a silent gap reads as broken. */
.dr-chart.wr-undocked > .wr-chart-placeholder,
.dr-chart.wr-popped > .wr-chart-placeholder { display: flex; height: auto; padding: 5px 8px; }
.dr-chart.wr-popped > .wr-chart-host { display: none; }

/* …and it sinks to the bottom of the scroll, under the sections that now matter
   more. `order` rather than a DOM move: relocating the section would re-parent
   the vendor iframe and kill the widget (KTD9). `:has()` keeps the flex context
   scoped to the one case that needs it, so the normal drawer layout is
   untouched. */
.dr-scroll:has(> .dr-chart.wr-undocked),
.dr-scroll:has(> .dr-chart.wr-popped) { display: flex; flex-direction: column; }
.dr-chart.wr-undocked, .dr-chart.wr-popped { order: 99; }
