/* ==========================================================================
   The Honest Terminal — design system implementation of DESIGN.md.

   Every value below is a token or derives from one. Nothing hardcodes a hex or
   a spacing literal in a component rule: that is what let the old sheet drift
   (the same #0f141b was typed 20+ times and the four signal states each
   re-invented their own fill/border pair). Components consume tokens only, so a
   palette change is one edit and a new component cannot invent a new grey.

   Layer order: tokens → base → shell → primitives → surfaces → data → states.
   ========================================================================== */

/* ---------------------------------------------------------------- 1. tokens */
:root{
  /* --- tonal chassis: surfaces recede terminal-ink → panel → inset → well --- */
  --bg:#0e1117;          /* app ground + sticky header */
  --card:#161b22;        /* raised panel: cards, table header */
  --inset:#0f141b;       /* recessed: tiles, inputs, disclosure bodies, hover */
  --well:#0b0f15;        /* deepest recess: code, confirm read-outs, popovers */
  --line:#272e3a;        /* the system's only border */
  --line-soft:#1e242e;   /* interior rules inside a panel (rows, dividers) */
  --txt:#d7dde6;         /* primary reading text — never #fff */
  --mut:#8b97a7;         /* labels, metadata, secondary values, "no data" */
  --dim:#5d6877;         /* tertiary: column rules, disabled glyphs */

  /* --- signal colours: market meaning only (never decoration) --- */
  --grn:#26a17b; --red:#e5534b; --amb:#d9a441; --acc:#3b82f6;
  --teal:#3fb6a8;        /* TARGET state only */

  /* Each signal owns a fill + border pair so every badge, tile and row accent
     across 12 tabs reads identically. Previously each was retyped per site. */
  --grn-fill:#10261f; --grn-line:#1f3b30;
  --red-fill:#2a1716; --red-line:#3b2422;
  --amb-fill:#2a2410; --amb-line:#3a3115;
  --acc-fill:#1c2740; --acc-line:#1e3050;
  /* Blue is the darkest of the four signal hues, and #3b82f6 label text on the
     #1c2740 fill measures 4.04:1 — below the AA floor DESIGN.md itself sets. The
     interactive hue is unchanged everywhere it carries meaning (focus rings,
     borders, links); only the label *inside* an accent fill is lifted, to 5.2:1. */
  --acc-on-fill:#5c98f8;
  --teal-fill:#122a2a; --teal-line:#1e3a38;
  --neu-fill:#20242c; --neu-line:var(--line);
  --zebra:#12171f;       /* even data row */

  /* --- control faces (buttons) --- */
  --ctl:#1f2733; --ctl-hover:#242e3c; --ctl-active:#1a222d;
  --ctl-primary-fg:#cfe0ff; --ctl-primary-hover:#233151;

  /* --- incidental surfaces --- */
  --code-fg:#cfe3ff;
  --skel-hi:#161d27;
  --scroll-thumb:#2c343d; --scroll-thumb-hover:#3a444f;
  --overlay-shadow:#000a;

  /* --- spacing scale --- */
  --s1:4px; --s2:6px; --s3:8px; --s4:12px; --s5:16px; --s6:24px;

  /* --- radii --- */
  --r-xs:5px; --r-sm:6px; --r-md:8px; --r-card:10px; --r-pill:999px;

  /* --- type --- */
  --font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,monospace;
  --t-symbol:20px; --t-title:14px; --t-body:13px; --t-data:12px;
  --t-micro:11px; --t-label:10px;

  /* --- controls: one height so a row of mixed controls aligns on a baseline --- */
  --ctl-h:28px;
  --row-h:30px;          /* data row: one line of 12px mono + padding */

  /* --- layout --- */
  --shell:1760px;        /* wide enough for a 14-column scanner on a 1440+ panel */
  --shell-narrow:1280px; /* prose/onboarding views stay at a readable measure */
  --stick-top:0px;       /* live header height — set by app.js, used by sticky th */

  /* --- motion --- */
  --ease:cubic-bezier(.4,0,.2,1); --dur:120ms;

  color-scheme:dark;
  accent-color:var(--acc);
}

/* ------------------------------------------------------- 1b. light theme */
/* Opt-in, not automatic. DESIGN.md's north star is a dark terminal, so the OS
   preference does not silently repaint the product; `data-theme="light"` (set by
   the header toggle, persisted per browser) does. Only tokens are redefined —
   no component rule knows a theme exists, which is the whole point of the token
   layer. Signal hues are darkened for AA contrast on light surfaces: #26a17b
   green reads at 2.4:1 on white and would fail as text. */
:root[data-theme="light"]{
  color-scheme:light;

  --bg:#eef1f5; --card:#ffffff; --inset:#f4f6f9; --well:#e9edf2;
  --line:#d3d9e2; --line-soft:#e6eaf0;
  --txt:#151b23; --mut:#5a6674; --dim:#8a95a3;

  --grn:#0d7a56; --red:#c0332b; --amb:#8a6206; --acc:#1c5ed6; --teal:#0f766e;
  --grn-fill:#e2f4ec; --grn-line:#a5d9c4;
  --red-fill:#fdebe9; --red-line:#f3b5b0;
  --amb-fill:#fdf2d9; --amb-line:#e3cb8c;
  --acc-fill:#e6eefb; --acc-line:#b2c9f0; --acc-on-fill:#1a56c4;
  --teal-fill:#dff2f0; --teal-line:#9fd3ce;
  --neu-fill:#eef1f5; --neu-line:var(--line);
  --zebra:#f8fafc;

  --ctl:#ffffff; --ctl-hover:#f1f4f8; --ctl-active:#e5eaf1;
  --ctl-primary-fg:#12448f; --ctl-primary-hover:#d8e5fa;

  --code-fg:#1b4a9c;
  --skel-hi:#e9edf3;
  --scroll-thumb:#c2cad6; --scroll-thumb-hover:#a8b3c2;
  --overlay-shadow:#0f172a26;
}

:root.theme-switching *,:root.theme-switching *::before,:root.theme-switching *::after{
  transition:none !important;
}

/* Theme toggle: a status-rail pill that shows the mode you would switch *to*. */
.themebtn{width:var(--ctl-h);padding:0;font-size:13px;line-height:1}
.themebtn .t-dark{display:none}
:root[data-theme="light"] .themebtn .t-dark{display:inline}
:root[data-theme="light"] .themebtn .t-light{display:none}

/* ------------------------------------------------------------------ 2. base */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--txt);
  font:var(--t-body)/1.45 var(--font);
  font-variant-numeric:tabular-nums;
}
.hidden{display:none !important}
/* visually hidden, still announced */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
::selection{background:var(--acc-fill);color:var(--txt)}

/* thin dark scrollbars for the terminal's many scroll regions */
*{scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border:2px solid var(--bg);border-radius:var(--r-pill)}
::-webkit-scrollbar-thumb:hover{background:var(--scroll-thumb-hover)}

/* visible keyboard focus everywhere; mouse users keep the quiet hover */
:focus-visible{outline:2px solid var(--acc);outline-offset:2px;border-radius:var(--r-xs)}

/* ----------------------------------------------------------------- 3. shell */
/* Two rails instead of one. The old header packed brand + 12 tabs + 5 status
   pills into a single wrapping flex row: it wrapped to 3 lines by 1100px and
   stood 257px tall on a phone — a third of the viewport, permanently sticky.
   Splitting identity/status from navigation keeps both rails one line deep, and
   the nav rail scrolls sideways instead of wrapping. */
header{
  position:sticky;top:0;z-index:20;background:var(--bg);
  border-bottom:1px solid var(--line);
}
.rail{display:flex;align-items:center;gap:var(--s4);padding:0 var(--s5);min-height:38px}
.rail-top{gap:var(--s3);border-bottom:1px solid var(--line-soft)}
.brand{font-weight:700;letter-spacing:.3px;white-space:nowrap;font-size:var(--t-body)}
.brand span{color:var(--mut);font-weight:500}
.status{display:flex;gap:var(--s2);align-items:center;margin-left:auto;min-width:0;overflow-x:auto;
  scrollbar-width:none;padding:var(--s2) 0}
.status::-webkit-scrollbar{display:none}

/* nav rail — scrolls, never wraps; hairlines mark the four tab families */
.rail-nav{padding:0;min-height:36px;position:relative}
.tabs{
  display:flex;align-items:stretch;gap:2px;flex-wrap:nowrap;
  overflow-x:auto;scrollbar-width:none;padding:0 var(--s5);width:100%;
}
.tabs::-webkit-scrollbar{display:none}

/* Scroll affordance for the two hidden-scrollbar rails. The scrollbar stays
   hidden (it is DESIGN.md's nav rule); the cue is a short fade on whichever side
   still has content, driven by classes app.js sets on scroll/resize. A mask, not
   an overlay element: an absolutely-positioned fade inside a scroll container
   scrolls away with the content it is meant to be masking. No new hue and no
   chrome — a browser without mask support simply shows no fade. */
.tabs,.status{--fade:24px}
.tabs.has-end,.status.has-end{
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - var(--fade)),transparent);
          mask-image:linear-gradient(to right,#000 calc(100% - var(--fade)),transparent);
}
.tabs.has-start,.status.has-start{
  -webkit-mask-image:linear-gradient(to right,transparent,#000 var(--fade));
          mask-image:linear-gradient(to right,transparent,#000 var(--fade));
}
.tabs.has-start.has-end,.status.has-start.has-end{
  -webkit-mask-image:linear-gradient(to right,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
          mask-image:linear-gradient(to right,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
}

.tabgap{flex:0 0 1px;background:var(--line);margin:8px var(--s3);align-self:stretch}

.view{
  display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s4);
  padding:var(--s4) var(--s5) var(--s6);
  max-width:var(--shell);margin:0 auto;align-items:start;
}
/* A tab panel is focused programmatically after a tab activation; it is not a
   tab stop of its own (it contains focusable content), so no visible ring. */
.view:focus{outline:none}
/* Prose-led views read at a book measure; data views take the full shell. */
#view-start{max-width:var(--shell-narrow)}
.span2{grid-column:1/-1}

footer{color:var(--mut);font-size:var(--t-micro);text-align:center;padding:var(--s5);
  border-top:1px solid var(--line-soft);margin-top:var(--s4)}

/* ------------------------------------------------------------ 4. primitives */
button,.btn-confirm,.filterbar .fbtn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  height:var(--ctl-h);padding:0 11px;
  background:var(--ctl);border:1px solid var(--line);color:var(--txt);
  border-radius:var(--r-sm);font:600 var(--t-data)/1 var(--font);cursor:pointer;
  white-space:nowrap;
}
button:hover,.btn-confirm:hover,.filterbar .fbtn:hover{border-color:var(--acc);background:var(--ctl-hover)}
button:active,.btn-confirm:active,.filterbar .fbtn:active{background:var(--ctl-active);transform:translateY(.5px)}
button:disabled,.btn-confirm:disabled{opacity:.45;cursor:not-allowed;transform:none;background:var(--ctl);border-color:var(--line)}
button small{font-weight:400;color:var(--mut)}
/* the primary action on a toolbar earns the one interactive hue */
button.primary{border-color:var(--acc-line);color:var(--ctl-primary-fg);background:var(--acc-fill)}
button.primary:hover{border-color:var(--acc);background:var(--ctl-primary-hover)}

.pill{
  display:inline-flex;align-items:center;gap:var(--s1);
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:0 10px;height:var(--ctl-h);color:var(--mut);font-size:var(--t-data);white-space:nowrap;
}
.pill.ok{color:var(--grn);border-color:var(--grn-line)}
.pill.bad{color:var(--red);border-color:var(--red-line)}
.pill b{color:var(--txt);font-weight:600}
.pill input{margin:0 2px 0 0}
label.pill{cursor:pointer}
label.pill select{height:20px;padding:0 4px}

.chip{
  display:inline-flex;align-items:center;height:24px;padding:0 10px;
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-sm);
  cursor:pointer;color:var(--mut);font-size:var(--t-data);
}
.chip:hover{color:var(--txt);border-color:var(--acc)}
.chip[aria-pressed="true"],.chip.active{background:var(--card);color:var(--txt);border-color:var(--acc-line)}
/* A chip that is a label, not a control: same geometry, no affordance. */
.chip-static{cursor:default}
.chip-static:hover{color:var(--mut);border-color:var(--line)}

input[type=text],input[type=search],input:not([type]),select,.text-input,.filterbar .finput{
  height:var(--ctl-h);
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-sm);
  color:var(--txt);font:var(--t-data)/1 var(--font);padding:0 9px;
  caret-color:var(--acc);
}
select{padding-right:var(--s3);cursor:pointer}
input:hover,select:hover{border-color:var(--dim)}
/* Mouse focus gets the quiet border shift; keyboard focus keeps a real outline —
   suppressing it on every :focus (as the border-shift treatment did) left
   keyboard users with a 13%-alpha ring as their only position cue. */
input:focus,select:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb, var(--acc) 18%, transparent)}
input:focus:not(:focus-visible),select:focus:not(:focus-visible){outline:none}
input::placeholder{color:var(--dim)}

.tab{
  display:inline-flex;align-items:center;
  background:transparent;border:0;border-bottom:2px solid transparent;
  color:var(--mut);border-radius:0;height:36px;padding:0 var(--s4);
  font:500 var(--t-data)/1 var(--font);cursor:pointer;white-space:nowrap;
}
.tab:hover{color:var(--txt);background:var(--inset);border-color:transparent}
.tab:active{transform:none}
/* the active tab is marked by an underline, not a filled box — it keeps the
   rail visually flat so the eye lands on the data below, not the chrome. */
.tab.active{color:var(--txt);background:transparent;border-bottom-color:var(--acc);font-weight:600}

/* ------------------------------------------------------- 5. cards + sections */
.card{
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);
  padding:var(--s4) var(--s5) var(--s5);min-width:0;
}
h2{
  margin:0 0 var(--s4);font-size:var(--t-title);font-weight:600;letter-spacing:-.1px;
  display:flex;align-items:baseline;gap:var(--s2);flex-wrap:wrap;
}
h2 small,small{color:var(--mut);font-weight:400;font-size:var(--t-micro)}

/* A card body is a stack of sections. Replaces ~20 copies of
   `<h2 style="margin-top:14px">`: one rule now owns the rhythm, and each
   section is separated by a hairline so a 7-table analysis report reads as
   distinct blocks rather than one undifferentiated scroll. */
.section{margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--line-soft)}
.section:first-of-type{margin-top:var(--s4);padding-top:0;border-top:0}
.section>h2{margin-bottom:var(--s3)}
/* Sub-heading inside a card body. Replaces ~24 copies of
   `<h2 style="margin-top:14px">`: one rule owns the rhythm, and the hairline
   turns a 7-table analysis report into distinct blocks rather than one scroll. */
h2.subhead{
  margin:var(--s6) 0 var(--s3);padding-top:var(--s4);
  border-top:1px solid var(--line-soft);
}
.section-head{display:flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap;margin:0 0 var(--s3)}
.section-head h2{margin:0}
.section-head .spacer{flex:1}
.count-badge{
  font:700 var(--t-label)/1 var(--font);letter-spacing:.4px;color:var(--mut);
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-pill);padding:3px 7px;
}

/* -------------------------------------------------- 6. metric / stat surfaces */
/* Decision tiles (KPI cards). minmax raised 150→168px: at 150 the longer labels
   ("MISSED (NEVER FILLED)") wrapped and knocked tiles out of a shared baseline. */
.decision{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--s3);margin:0 0 var(--s4)}
.dtile{
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3) 10px;min-width:0;
}
.dtile.wide{grid-column:1/-1}
/* The verdict row. Twelve identically-sized tiles made "Entry Readiness" — the
   thing the panel exists to produce — the twelfth item, indistinguishable from
   "Session". Larger cells, fewer per row, and a hairline separating them from the
   diagnostics that support them. Same component, one size modifier. */
.decision-primary{grid-template-columns:repeat(auto-fit,minmax(232px,1fr));margin-bottom:var(--s3)}
.decision-primary .dtile{padding:10px var(--s4)}
.decision-primary .dtile .dv{font-size:18px;line-height:1.25}
.decision-primary+.decision{padding-top:var(--s4);border-top:1px solid var(--line-soft)}
/* A value that actually moved says so once. Interactive blue: this is attention,
   not market direction — the tile's own good/bad/warn rail still carries that. */
@media (prefers-reduced-motion:no-preference){
  .dtile.flash{animation:tileflash 900ms var(--ease)}
}
@keyframes tileflash{
  0%{background:var(--acc-fill);border-color:var(--acc-line)}
  100%{background:var(--inset)}
}
/* Stale live data. Amber is the hesitation colour and this is exactly that:
   the numbers are real, they are simply no longer current. */
.stalebar{
  display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s4);
  padding:var(--s2) var(--s4);border-radius:var(--r-md);
  background:var(--amb-fill);border:1px solid var(--amb-line);color:var(--amb);
  font-size:var(--t-data);font-weight:600;
}
.stalebar b{color:var(--txt);font-variant-numeric:tabular-nums}
.pill.stale{color:var(--amb);border-color:var(--amb-line)}
/* a state standing in for a whole tile grid takes the whole row */
.decision>.state-block{grid-column:1/-1}
.dtile .dk{
  color:var(--mut);font-size:var(--t-label);font-weight:700;
  text-transform:uppercase;letter-spacing:.4px;margin-bottom:var(--s1);
}
.dtile .dv{font-weight:600;font-size:var(--t-body);line-height:1.35;overflow-wrap:anywhere}
/* Signal state on a tile: a 2px left rail + coloured value. The border-only
   treatment it replaces was invisible at a glance on a 12-tile grid. */
.dtile.good{border-color:var(--grn-line);box-shadow:inset 2px 0 0 var(--grn)}   .dtile.good .dv{color:var(--grn)}
.dtile.bad{border-color:var(--red-line);box-shadow:inset 2px 0 0 var(--red)}    .dtile.bad .dv{color:var(--red)}
.dtile.warn{border-color:var(--amb-line);box-shadow:inset 2px 0 0 var(--amb)}   .dtile.warn .dv{color:var(--amb)}

/* Stat strip — the scan header line. Replaces the old `.metaline` tag soup
   (`universe 3150  NIFTY +0.38%  setups 31  to close 300m  updated 10:30`) where
   every fact carried identical weight. Here the label is 10px caps and the value
   is the hero, so "31 setups" reads before "universe 3150". */
.statstrip{
  display:flex;align-items:stretch;flex-wrap:wrap;
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-md);
  margin:0 0 var(--s4);overflow:hidden;
}
.statstrip .stat{
  display:flex;flex-direction:column;gap:2px;justify-content:center;
  padding:var(--s2) var(--s4);min-width:0;flex:0 0 auto;
  border-right:1px solid var(--line-soft);
}
.statstrip .stat:last-child{border-right:0}
.statstrip .stat.grow{flex:1 1 auto;align-items:flex-end;text-align:right}
.statstrip .sk{color:var(--mut);font-size:var(--t-label);font-weight:700;text-transform:uppercase;letter-spacing:.4px;white-space:nowrap}
.statstrip .sv{font-size:var(--t-body);font-weight:600;white-space:nowrap;font-variant-numeric:tabular-nums}
.statstrip .sv.up{color:var(--grn)} .statstrip .sv.down{color:var(--red)} .statstrip .sv.mut{color:var(--mut);font-weight:500}
/* Most stat values are short and must never wrap mid-number, which is why .sv is
   nowrap. A few migrated cells carry a sentence (a verdict reason, an invalidation
   caveat) — those opt in, and only ever in a .grow cell that can take the width. */
.statstrip .sv.wrap{white-space:normal;line-height:1.45;font-weight:500}
/* A cell whose label would be empty keeps the value on the strip's baseline
   instead of leaving a 10px gap where the overline would be. */
.statstrip .sk:empty{display:none}
/* .grow right-aligns, which is right for the trailing timestamp on a scan header
   but wrong for a strip that is one prose cell — that read as a stray right-aligned
   caption. .wide takes the row and stays left-aligned with the rest of the card. */
.statstrip .stat.wide{flex:1 1 100%;align-items:flex-start;text-align:left}
/* live/delayed/closed marker leads the strip */
.statstrip .stat.status-live .sv{color:var(--grn)}
.statstrip .stat.status-delayed .sv{color:var(--amb)}
.statstrip .stat.status-closed .sv{color:var(--mut)}
.statstrip .dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:var(--s2);background:currentColor;vertical-align:middle}
.statstrip .stat.status-live .dot{animation:pulse 2.4s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* Scan progress. A full-shortlist breakout pass is minutes of sequential provider
   calls, so it reports itself: a determinate bar over the shortlist, the counts that
   matter, and a way out. Occupies the same slot as the finished stat strip, so the
   panel does not jump when the scan completes. */
.scanprog{
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3) var(--s4);margin:0 0 var(--s4);
}
.scanprog .sp-head{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s2)}
.scanprog .sp-phase{
  color:var(--mut);font-size:var(--t-label);font-weight:700;
  text-transform:uppercase;letter-spacing:.4px;
}
.scanprog .sp-count{font-size:var(--t-body);font-weight:600;font-variant-numeric:tabular-nums}
.scanprog .sp-cancel{height:24px;padding:0 var(--s3);color:var(--mut);background:transparent}
.scanprog .sp-cancel:hover{color:var(--red);border-color:var(--red-line);background:var(--red-fill)}
.scanprog .sp-track{height:4px;border-radius:var(--r-pill);background:var(--well);overflow:hidden}
.scanprog .sp-bar{
  height:100%;width:0;border-radius:var(--r-pill);background:var(--acc);
  transition:width 240ms var(--ease);
}
.scanprog .sp-sub{
  margin-top:var(--s2);color:var(--mut);font-size:var(--t-micro);line-height:1.5;
  font-variant-numeric:tabular-nums;
}
.scanprog .sp-sub b{color:var(--txt)}
.scanprog .sp-sub i{font-style:normal;color:var(--dim)}
.scanprog .sp-sym{font-family:var(--mono);color:var(--txt)}
@media (prefers-reduced-motion: reduce){ .scanprog .sp-bar{transition:none} }

/* ----------------------------------------------------------- 7. toolbar rows */
/* One toolbar grammar for every tab: controls left, status right, all on the
   shared 28px control height so nothing sits half a pixel off the baseline. */
.toolbar{
  display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;margin:0 0 var(--s4);
}
.toolbar .grow,.grow{flex:1}
.toolbar .pill{cursor:default}
#tpo-auto-wrap,#utpo-auto-wrap{cursor:pointer}
#tpo-next,#utpo-next{font-variant-numeric:tabular-nums}
.toolbar .pill.paused{color:var(--amb);border-color:var(--amb-line)}

/* Filter bar. Was a wall of differently-sized pills that wrapped to 9 rows on a
   phone; now a fixed 3-column grid of label-over-control cells, so the controls
   align in columns and the bar collapses predictably instead of reflowing. */
.filterbar{
  display:flex;flex-wrap:wrap;gap:var(--s2) 10px;align-items:flex-end;
  background:var(--inset);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3) var(--s4);margin:0 0 var(--s4);
}
.filterbar .fpill{display:flex;flex-direction:column;gap:3px;flex:1 1 132px;max-width:200px;min-width:0;background:none;border:0;padding:0;height:auto}
.filterbar .fk{
  color:var(--mut);font-size:var(--t-label);font-weight:700;
  text-transform:uppercase;letter-spacing:.4px;white-space:nowrap;
}
.filterbar select,.filterbar .finput{width:100%;min-width:0;border-radius:var(--r-sm)}
.filterbar .grow{flex:1 1 auto;min-width:0}   /* pushes count + Reset to the trailing edge */
.filterbar .fcount{
  display:inline-flex;align-items:center;height:var(--ctl-h);padding:0;
  color:var(--mut);font-size:var(--t-data);font-variant-numeric:tabular-nums;
  background:none;border:0;border-radius:0;flex:0 0 auto;
}
/* A control that is away from its default says so. The aggregate count tells you
   something is filtered; this tells you which of ten controls is doing it — which
   matters because the selections persist across reloads. */
.filterbar .fpill.is-set .fk{color:var(--acc)}
.filterbar .fpill.is-set select,.filterbar .fpill.is-set .finput{border-color:var(--acc-line)}
.filterbar .fcount.filtered{color:var(--acc);font-weight:600}
.filterbar .fbtn{flex:0 0 auto;color:var(--mut);background:transparent}
.filterbar .fbtn:hover{color:var(--txt);background:var(--card)}

/* --------------------------------------------------------------- 8. tables */
.tablewrap{
  /* The wrapper is the scroll region, and the header pins to *it*. Previously
     `overflow-x:auto` silently made this a scroll container on both axes while
     the <th> was told `top:0` relative to the page — so the column headers
     scrolled away with the rows and the sticky rule did nothing at all.
     (`overflow-y:clip` is not an escape: beside a scrolling x-axis it computes
     to `hidden`, which still scrolls.) The cap only engages on tables that are
     actually long; a 6-row analysis table never reaches it and is unaffected. */
  overflow:auto;
  max-height:max(220px, calc(100vh - var(--stick-top) - 170px));
  max-height:max(220px, calc(100dvh - var(--stick-top) - 170px));
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--card);
  overscroll-behavior:contain;
}
.overflow-x-auto{overflow-x:auto}
.dtable{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--t-data)}
.dtable th{
  text-align:left;color:var(--mut);font-weight:600;font-size:var(--t-label);
  text-transform:uppercase;letter-spacing:.4px;
  padding:var(--s2) 10px;white-space:nowrap;
  background:var(--card);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:2;
}
.dtable td{
  padding:var(--s1) 10px;border-bottom:1px solid var(--line-soft);
  vertical-align:middle;height:var(--row-h);
}
.dtable tbody tr:last-child td{border-bottom:0}

/* Sortable headers. A 40-row, 13-column scan had exactly one order — the server's
   — and no way to ask "highest R:R first". The header is the control: the caret is
   dim until you hover, and takes the interactive hue only while a sort is active,
   so an unsorted grid gains no new colour. Blue here is an active affordance, which
   is what the Meaning-Only Color Rule reserves it for. */
.dtable th.sortable{cursor:pointer;padding-right:20px;position:sticky;user-select:none}
.dtable th.sortable::after{
  content:'';position:absolute;right:8px;top:50%;margin-top:-2px;
  border-left:3px solid transparent;border-right:3px solid transparent;
  border-top:4px solid var(--dim);opacity:0;
}
.dtable th.sortable:hover{color:var(--txt)}
.dtable th.sortable:hover::after{opacity:.7}
.dtable th.sortable.sorted{color:var(--txt)}
.dtable th.sortable.sorted::after{opacity:1;border-top-color:var(--acc)}
/* ascending flips the caret: same triangle, drawn from the bottom edge */
.dtable th.sortable.sorted.sort-asc::after{
  border-top:0;border-bottom:4px solid var(--acc);margin-top:-2px;
}
.dtable th.num,.dtable td.num{
  text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;
}
/* zebra + hover: on a 14-column, 30-row grid the eye loses its row without one */
.dtable tbody tr:nth-child(even) td{background:var(--zebra)}
.dtable tbody tr:hover td{background:var(--inset)}
/* "the row you are in" — a class, not aria-selected: aria-selected is only
   meaningful on a grid/listbox row, and these are plain data tables. */
.dtable tbody tr.is-current td{background:var(--acc-fill)}
.dtable tr.is-current td{box-shadow:inset 0 1px 0 var(--acc-line),inset 0 -1px 0 var(--acc-line)}
.dtable tr.is-current td:first-child{box-shadow:inset 2px 0 0 var(--acc),inset 0 1px 0 var(--acc-line),inset 0 -1px 0 var(--acc-line)}
.dtable .sym{font-weight:700;font-size:var(--t-body);white-space:nowrap;letter-spacing:-.1px}

/* The first column pins while the rest scrolls sideways. A 14-column scanner
   cannot fit a phone or a tablet, and without this you end up reading a row of
   entry/stop/target numbers with no idea whose they are. The hairline (not a
   shadow) marks the seam, per the Hairline Rule. */
.dtable th:first-child,.dtable td:first-child{
  position:sticky;left:0;z-index:1;
  background:var(--card);border-right:1px solid var(--line);
}
.dtable thead th:first-child{z-index:3}
/* a full-width state row is not a column and must not pin */
.dtable td.table-state{position:static;border-right:0;background:transparent}
.dtable .rr{font-weight:600}

/* The reason/why column was the single biggest density cost: min-width 220px,
   free wrapping, and 6–7 lines per row turned a 31-row scan into a ~2,500px
   scroll. Clamped to two lines with the full text on hover/title — the scan is
   for ranking, and the full rationale is one hover away. */
.dtable .reason{color:var(--mut);white-space:normal;line-height:1.4;font-size:var(--t-micro);min-width:200px;max-width:300px}
/* The clamp lives on an inner span: a <td> blockifies to flow-root, so
   `display:-webkit-box` on the cell itself degraded to a hard crop with no
   ellipsis. Two lines rank the setup; the full rationale is one hover away. */
.dtable .clamp{
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;overflow-wrap:anywhere;
}
.dtable .why{display:-webkit-box;font-size:var(--t-label);margin-top:2px;color:var(--dim)}
.dtable tr:hover .clamp{-webkit-line-clamp:8}
.ttime{color:var(--mut);font-size:var(--t-label);margin-top:1px}
.ezone{color:var(--mut);font-size:var(--t-label);font-weight:400;margin-top:1px;white-space:nowrap;font-family:var(--mono)}
.capped{color:var(--amb);cursor:help}

/* Breakdown: three small tables side by side on a wide shell, stacking as they
   run out of room. Uses the width the data grid already has rather than adding a
   fourth vertical scroll to the Testing tab. */
.bd-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:var(--s4)}
.bd-grid>div{min-width:0}
.bd-grid .scan-head{margin-top:0;padding-top:0;border-top:0}
.bd-grid .tablewrap{max-height:none}

/* key/value table (health, providers) */
table.kv{width:100%;border-collapse:collapse}
.kv td{padding:var(--s2) 0;border-bottom:1px solid var(--line-soft);font-size:var(--t-data)}
.kv tr:last-child td{border-bottom:0}
.kv td:first-child{color:var(--mut);width:42%}
.kv td:last-child{font-family:var(--mono);text-align:right}

/* ------------------------------------------------------------- 9. badges */
/* One badge geometry; the modifier only swaps the token pair. Signal, state,
   confidence, tier, setup and category badges previously each defined their own
   padding and radius, so a row carried five slightly different pill shapes. */
.sig,.conf,.st,.setup,.tier,.cat,.need,.bias{
  display:inline-flex;align-items:center;
  border-radius:var(--r-xs);padding:2px 7px;
  font-size:var(--t-label);font-weight:700;letter-spacing:.3px;white-space:nowrap;
  border:1px solid transparent;
}
.conf,.sig,.st,.bias{font-size:var(--t-micro)}
.bias{font-size:var(--t-data);padding:3px 9px;border-radius:var(--r-sm)}

.sig.LONG{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.sig.SHORT{background:var(--red-fill);color:var(--red);border-color:var(--red-line)}
.conf.High{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.conf.Good{background:var(--acc-fill);color:var(--acc-on-fill);border-color:var(--acc-line)}
.conf.Fair{background:var(--amb-fill);color:var(--amb);border-color:var(--amb-line)}

.st-VALID{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.st-ARMED{background:var(--acc-fill);color:var(--acc-on-fill);border-color:var(--acc-line)}
.st-EXTENDED{background:var(--amb-fill);color:var(--amb);border-color:var(--amb-line)}
.st-TARGET{background:var(--teal-fill);color:var(--teal);border-color:var(--teal-line)}
.st-INVALID{background:var(--red-fill);color:var(--red);border-color:var(--red-line)}
.st-EXPIRED{background:var(--neu-fill);color:var(--mut);border-color:var(--line)}
.st.good{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.st.warn{background:var(--amb-fill);color:var(--amb);border-color:var(--amb-line)}
.st:not(.good):not(.warn):not([class*="st-"]){background:var(--neu-fill);color:var(--mut);border-color:var(--line)}

/* Setup archetype, tier, category and needs are taxonomy, not market state —
   they stay on the neutral chassis per the Meaning-Only Color Rule, except the
   two that genuinely encode readiness (confirmed / no-setup-required). */
.setup,.cat,.need{background:var(--inset);color:var(--mut);border-color:var(--line);font-weight:600}
.setup-OPEN-DRIVE{color:var(--grn);border-color:var(--grn-line)}
.setup-IB-COIL{color:var(--acc);border-color:var(--acc-line)}
.setup-VALUE-EDGE{color:var(--amb);border-color:var(--amb-line)}
.setup-EXPANSION{color:var(--mut)}
.tier{background:var(--neu-fill);color:var(--mut);border-color:var(--line)}
.tier-confirmed{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.tier-pre{background:var(--acc-fill);color:var(--acc-on-fill);border-color:var(--acc-line)}
.tier-watch{background:var(--neu-fill);color:var(--mut)}
.need-none{color:var(--grn);border-color:var(--grn-line)}

.bias.BULLISH{background:var(--grn-fill);color:var(--grn);border-color:var(--grn-line)}
.bias.BEARISH{background:var(--red-fill);color:var(--red);border-color:var(--red-line)}
.bias.NEUTRAL{background:var(--neu-fill);color:var(--amb);border-color:var(--amb-line)}
.eq{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:600}
.eq.good{color:var(--grn)} .eq.low{color:var(--red)}

/* ------------------------------------------- 10. empty / loading / error states */
/* Every one of these used to be a bare line of dim 12px text (`Loading…`,
   `Failed: <msg>`) indistinguishable from a caption. A state is a surface: it
   fills the space the data would have taken so the layout does not jump. */
.state-block{
  display:flex;flex-direction:column;align-items:center;gap:var(--s2);
  padding:var(--s6) var(--s5);text-align:center;
  border:1px dashed var(--line);border-radius:var(--r-md);background:var(--inset);
  color:var(--mut);font-size:var(--t-data);line-height:1.55;
}
.state-block .sb-icon{font-size:18px;line-height:1;opacity:.75}
.state-block .sb-title{color:var(--txt);font-size:var(--t-body);font-weight:600}
.state-block p{margin:0;max-width:52ch}
.state-block .sb-actions{display:flex;gap:var(--s2);margin-top:var(--s1)}
.state-block.err{border-style:solid;border-color:var(--red-line);background:var(--red-fill)}
.state-block.err .sb-title{color:var(--red)}
.state-block.warn{border-style:solid;border-color:var(--amb-line);background:var(--amb-fill)}
.state-block.warn .sb-title{color:var(--amb)}
/* inside a table body */
td.table-state{padding:0;border-bottom:0}
td.table-state .state-block{border:0;border-radius:0;background:transparent}
.empty,.hint{color:var(--mut);font-size:var(--t-data);padding:var(--s2) 0;line-height:1.55}
.empty:has(.state-block),.hint:has(.state-block){padding:0}

/* Skeleton rows: a scan takes seconds against a 3,000-name universe. Showing the
   shape of the answer beats showing the word "Loading". */
.skel{background:linear-gradient(90deg,var(--inset) 25%,var(--skel-hi) 50%,var(--inset) 75%);
  background-size:200% 100%;border-radius:var(--r-xs);height:10px;display:block}
@media (prefers-reduced-motion:no-preference){.skel{animation:shimmer 1.3s linear infinite}}
@keyframes shimmer{to{background-position:-200% 0}}
.skelrow td{height:var(--row-h)}
.skelrow .skel{margin:2px 0}
.is-busy{opacity:.55;pointer-events:none;transition:opacity var(--dur) var(--ease)}

/* --------------------------------------------------------- 11. disclosures */
.disclosure{
  margin:0 0 var(--s3);border:1px solid var(--line);border-radius:var(--r-md);background:var(--inset);
}
.disclosure>summary{
  cursor:pointer;padding:var(--s2) var(--s4);font-weight:600;font-size:var(--t-data);
  color:var(--mut);list-style:none;user-select:none;display:flex;align-items:center;gap:var(--s2);
  min-height:var(--ctl-h);
}
.disclosure>summary:hover{color:var(--txt)}
.disclosure>summary::-webkit-details-marker{display:none}
.disclosure>summary::before{content:'▸';color:var(--dim);font-size:10px;transition:transform var(--dur) var(--ease)}
.disclosure[open]>summary{color:var(--txt);border-bottom:1px solid var(--line)}
.disclosure[open]>summary::before{transform:rotate(90deg)}
/* Disclosure prose was running the full 1,700px shell width — a 200-character
   measure. Capped to a readable column. */
.disclosure-body{padding:var(--s2) var(--s4) var(--s4)}
.disclosure-body p{margin:var(--s3) 0;color:var(--mut);font-size:var(--t-data);line-height:1.6;max-width:88ch}
.disclosure-body b{color:var(--txt);font-weight:600}
.disclosure-body code,.start-steps code{
  background:var(--well);border:1px solid var(--line);border-radius:4px;padding:1px 5px;
  font:var(--t-micro)/1 var(--mono);color:var(--code-fg);
}
.disclosure-note{border-top:1px dashed var(--line);padding-top:var(--s3);margin-top:var(--s3)}
.disclosure-trade .disclosure-body b{color:var(--txt)}
/* the two disclosures sit side by side instead of stacking two full-width bars
   above the data they explain */
.how-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);margin:0 0 var(--s4)}
.how-row .disclosure{margin:0}
@media(max-width:820px){.how-row{grid-template-columns:1fr}}

/* Stage-2 rejection log: capped and collapsed. Rendered inline it was a
   twelve-sentence grey wall directly under the table it explained. */
.skiplist{margin:var(--s4) 0 0}
.skiplist .disclosure-body{max-height:180px;overflow:auto;padding-top:var(--s3)}
.skiplist ul{margin:0;padding-left:18px;color:var(--mut);font-size:var(--t-micro);line-height:1.6}
.skiplist li{margin:2px 0}

/* legacy meta line, still used for secondary notes under tables */
.metaline{color:var(--mut);font-size:var(--t-data);margin:var(--s3) 0 0;display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;line-height:1.6}
.metaline .tag{background:var(--inset);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px 8px}
.metaline .tag.status-live{color:var(--grn);border-color:var(--grn-line);font-weight:600}
.metaline .tag.status-delayed{color:var(--amb);border-color:var(--amb-line);font-weight:600}
.metaline .tag.status-closed{color:var(--mut)}

.readout{
  background:var(--well);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s4);margin-top:var(--s4);font-size:var(--t-data);
}
.readout h3{margin:0 0 var(--s2);font-size:var(--t-body)}
.readout .prow{color:var(--mut);font:var(--t-data)/1.6 var(--mono)}

/* --------------------------------------------------------- 12. tab specifics */
/* --- Signal Summary --- */
.sigtop{display:flex;align-items:baseline;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s3);
  padding-bottom:var(--s3);border-bottom:1px solid var(--line-soft)}
/* Scoped: the bare `.sym` rule also matched every symbol cell in every scanner
   table, rendering 20px symbols inside a 12px grid — the loudest thing on the
   page was a column of tickers. */
.sigtop .sym{font-size:var(--t-symbol);font-weight:700;letter-spacing:-.3px}
.tf{color:var(--mut)}
.px{margin-left:auto;font-size:16px;font-family:var(--mono)} .chg.up{color:var(--grn)} .chg.down{color:var(--red)}
.verdict{
  margin:0 0 var(--s4);padding:var(--s3) var(--s4);border-radius:var(--r-md);
  font-weight:700;font-size:var(--t-body);background:var(--inset);border:1px solid var(--line);
}
.verdict.good{color:var(--grn);border-color:var(--grn-line);background:var(--grn-fill)}
.verdict.bad{color:var(--red);border-color:var(--red-line);background:var(--red-fill)}
.verdict.warn{color:var(--amb);border-color:var(--amb-line);background:var(--amb-fill)}
.metrics{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s2) 0}
.metrics .m{background:var(--inset);border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s1) var(--s3);font-size:var(--t-data)}
.metrics .m b{color:var(--mut);font-weight:500;margin-right:var(--s1)}
.signals{list-style:none;margin:var(--s2) 0 0;padding:0;display:flex;gap:var(--s2);flex-wrap:wrap}
.signals li{background:var(--inset);border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s1) var(--s3);font-size:var(--t-data);line-height:1.5}

/* --- market panel --- */
.tfs{display:flex;gap:var(--s2);flex-wrap:wrap;margin-bottom:var(--s4)}
.scan-head{
  color:var(--mut);font-size:var(--t-label);font-weight:700;text-transform:uppercase;
  letter-spacing:.5px;margin:var(--s4) 0 var(--s2);padding-top:var(--s3);border-top:1px solid var(--line-soft);
}
.scan-head:first-child{margin-top:0;padding-top:0;border-top:0}
.symlist{list-style:none;margin:0;padding:0;max-height:190px;overflow:auto}
.symlist li{display:flex;justify-content:space-between;align-items:center;padding:var(--s1) var(--s2);border-radius:var(--r-sm);cursor:pointer;font-size:var(--t-data)}
.symlist li:hover{background:var(--inset);color:var(--txt)}
.symlist .ex{color:var(--mut);font-size:var(--t-micro)}
.checklist ul{list-style:none;margin:0;padding:0}
.checklist li{display:flex;gap:var(--s2);align-items:flex-start;padding:var(--s1) 0;font-size:var(--t-data);line-height:1.5}
.checklist input{margin-top:2px;flex:none}

/* --- symbol combobox --- */
.combo{position:relative;flex:0 1 340px;min-width:220px}
.text-input{width:100%;min-width:0}
.suggest{
  position:absolute;z-index:30;top:calc(100% + 4px);left:0;right:0;margin:0;padding:var(--s1);
  list-style:none;max-height:290px;overflow-y:auto;
  background:var(--well);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:0 12px 32px var(--overlay-shadow);
}
.suggest li{display:flex;gap:var(--s3);align-items:baseline;padding:var(--s2) var(--s3);border-radius:var(--r-sm);cursor:pointer;font-size:var(--t-data)}
.suggest li:hover,.suggest li.active{background:var(--acc-fill)}
.suggest .s-sym{font-weight:600;color:var(--txt)}   /* was var(--fg): never defined */
.suggest .s-ex{color:var(--mut);font-size:var(--t-micro);border:1px solid var(--line);border-radius:4px;padding:0 5px}
.suggest .s-desc{color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:right}
.suggest .s-none{color:var(--mut);cursor:default}

/* --- analysis tables: value colouring --- */
#pat-card .dtable td.good,#vcp-card .dtable td.good,#ew-card .dtable td.good,
#ew-card .dtable .good,#cache-card .dtable td.good{color:var(--grn)}
#pat-card .dtable td.bad,#vcp-card .dtable td.bad,#ew-card .dtable td.bad,
#ew-card .dtable .bad,#cache-card .dtable td.bad{color:var(--red)}
#pat-card .dtable td.warn,#vcp-card .dtable td.warn,#ew-card .dtable td.warn,
#ew-card .dtable .warn,#cache-card .dtable td.warn{color:var(--amb)}
#pat-card .dtable td,#vcp-card .dtable td,#ew-card .dtable td,#cache-card .dtable td{vertical-align:top}

/* --- circuit / risk callout --- */
.circ{margin-top:var(--s3);padding:var(--s2) 10px;border-radius:var(--r-sm);font-size:var(--t-data);line-height:1.5}
.circ.ok{background:var(--grn-fill);border:1px solid var(--grn-line);color:var(--txt)}
.circ.warn{background:var(--amb-fill);border:1px solid var(--amb-line);color:var(--amb)}

/* --- charts --- */
.chartbox{
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--inset);
  padding:var(--s4);margin:0 0 var(--s4);overflow:hidden;
}
.chartbox svg{display:block;width:100%;height:auto}

/* --- Start Here --- */
.start-lede{color:var(--mut);font-size:var(--t-body);line-height:1.65;margin:var(--s2) 0;max-width:92ch}
.start-lede b{color:var(--txt);font-weight:600}
.start-disc{font-size:var(--t-data);border-top:1px dashed var(--line);padding-top:var(--s3);margin-top:var(--s4)}
.start-rules{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);margin:var(--s4) 0 var(--s1)}
.start-rule{background:var(--inset);border:1px solid var(--line);border-radius:var(--r-md);padding:10px var(--s4);font-size:var(--t-data);color:var(--mut);line-height:1.6}
.start-rule b{color:var(--txt)}
.start-rule-k{display:block;color:var(--txt);font-weight:600;margin-bottom:3px}
.dtile .start-sub{color:var(--mut);font-size:var(--t-micro);line-height:1.5;margin-top:var(--s1);font-weight:400}
#start-map td{vertical-align:top;line-height:1.55}
#start-map td:nth-child(2),#start-map td:nth-child(4){white-space:normal}
#start-map td:nth-child(3){min-width:320px;white-space:normal}
#start-map .sym{font-size:var(--t-body)}
.start-steps{margin:var(--s1) 0;padding-left:20px;color:var(--mut);font-size:var(--t-body);line-height:1.8}
.start-steps b{color:var(--txt)}
.start-gloss{margin:0}
.start-gloss dt{color:var(--txt);font-weight:600;font-size:var(--t-data);margin-top:var(--s3)}
.start-gloss dd{color:var(--mut);font-size:var(--t-data);line-height:1.55;margin:2px 0 0}

/* --------------------------------------------------------- 13. responsive */
/* ≤1400: the shell is already fluid; drop to a single column earlier so the two
   side-by-side panels never squeeze a table below its own minimum. */
@media(max-width:1100px){
  .view{grid-template-columns:1fr}
  .span2{grid-column:auto}
}
@media(max-width:820px){
  .rail{padding:0 var(--s4)}
  .tabs{padding:0 var(--s4)}
  .view{padding:var(--s3) var(--s4) var(--s5);gap:var(--s3)}
  .card{padding:var(--s4)}
  .start-rules{grid-template-columns:1fr}
  .decision{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
}
@media(max-width:560px){
  :root{--shell-narrow:100%}
  .rail{padding:0 10px;min-height:34px}
  .rail-top{gap:var(--s2)}
  .tabs{padding:0 10px}
  .tab{padding:0 10px;height:34px}
  .tabgap{margin:7px var(--s2)}
  .view{padding:var(--s3) 10px var(--s5)}
  .card{padding:var(--s3) 10px var(--s4)}
  h2{font-size:var(--t-body)}
  .filterbar{padding:var(--s3)}
  .filterbar .fpill{flex:1 1 calc(50% - 10px);max-width:none}
  .filterbar .grow{flex-basis:100%}
  .statstrip .stat{flex:1 1 40%;border-bottom:1px solid var(--line-soft)}
  .statstrip .stat.grow{align-items:flex-start;text-align:left}
  .decision{grid-template-columns:1fr 1fr;gap:var(--s2)}
  .dtable .reason,.dtable .why{min-width:180px;max-width:220px}
  .state-block{padding:var(--s5) var(--s4)}
}

/* ------------------------------------------------------------- 14. motion */
@media (prefers-reduced-motion: no-preference){
  .tab,button,.btn-confirm,.chip,.pill,.filterbar .fbtn{
    transition:border-color var(--dur) var(--ease),color var(--dur) var(--ease),
               background-color var(--dur) var(--ease),transform var(--dur) var(--ease);
  }
  .dtable tbody tr td,.symlist li,.suggest li{transition:background-color var(--dur) var(--ease)}
  .dtile,input,select,.text-input{transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
  .disclosure>summary{transition:color var(--dur) var(--ease)}
}
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important}
}
