:root {
  --mono: 'IBM Plex Mono', ui-monospace, 'Courier New', monospace;
  --doc: 'Newsreader', Georgia, 'Times New Roman', serif;
  /* Instrument register is dense; document register is for reading. */
  --t-micro: 10px; --t-data: 12px; --t-body: 13px; --t-lead: 15px;
  --t-doc: 16px; --t-doc-h: 20px; --t-title: 22px;
  --lh-tight: 1.35; --lh-doc: 1.6; --measure: 66ch;
  /* Restrained on purpose (CLAUDE.md "UI / design guidelines": no excessive rounding).
     A 12px panel radius read as a SaaS template, not an instrument. --radius-xs keeps
     micro-chips (counts, badges, code spans) and segment buttons on the scale; a segment
     inside a --radius-sm track with 2px padding needs xs so the corners stay concentric.
     Pill shapes (99px) are reserved for bars, tracks and grips, never controls. */
  --radius: 8px; --radius-sm: 6px; --radius-md: 7px; --radius-xs: 4px;
  /* Not a fourth surface — a state derived from the third. Collapsing the old five-step
     neutral ramp to three left hover states and skeleton bars resolving to the same value
     as the surface they sit on, so they vanished. This nudges --chrome-2 toward the text
     color, which lightens in dark themes and darkens in light ones without a new literal
     per theme. --rule is too near --chrome-2 in the dark palettes to do the job. */
  --chrome-hi: color-mix(in srgb, var(--chrome-2) 84%, var(--ink));
  /* Every pixel here is spent three times over: the header is one of only two fixed bands
     left above the workspace, and the machine this is used on has a 585px viewport. */
  /* 130px is not arbitrary: "Fundamentals" is one unbreakable 94px word at this mono size,
     and a rail narrower than its widest label breaks it across two lines. Width is the
     axis this layout can afford — height is the one it cannot. */
  --header-h: 52px; --ctl-h: 34px; --rail-w: 130px;
  --ease: cubic-bezier(.22,.9,.32,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-spring: cubic-bezier(.34,1.4,.44,1);
  --shadow: 0 1px 2px rgba(5,12,20,.25), 0 8px 28px -12px rgba(5,12,20,.35);
}
:root[data-theme="dark"] {
  --accent:#3fd0b6; --accent-ink:#06251f; --accent-soft:rgba(63,208,182,.10);
  --chart-fill-top:rgba(63,208,182,.22); --chart-fill-bot:rgba(63,208,182,0);
  --skeleton-shine:rgba(255,255,255,.06);
  color-scheme: dark;
  --chrome-0: #0a0e14; --chrome-1: #101723; --chrome-2: #18212f;
  --rule: #1a2535;
  --ink: #8c9db1; --ink-dim: #7a8ca1; --ink-bright: #eaf0f7;
}
:root[data-theme="light"] {
  --accent:#0a8372; --accent-ink:#ffffff; --accent-soft:rgba(10,131,114,.09);
  --chart-fill-top:rgba(10,131,114,.16); --chart-fill-bot:rgba(10,131,114,0);
  --shadow:0 1px 2px rgba(19,32,46,.06), 0 8px 24px -12px rgba(19,32,46,.14);
  --skeleton-shine:rgba(255,255,255,.65);
  color-scheme: light;
  --chrome-0: #eef2f5; --chrome-1: #ffffff; --chrome-2: #f2f5f8;
  --rule: #e0e7ed;
  --ink: #54677b; --ink-dim: #5c6e80; --ink-bright: #13202e;
}
/* A theme owns exactly two things: the neutral ramp (surfaces + rule + ink) and the single
   accent. Nothing else. --up/--down/--warn are NOT theme-owned — they live in the
   [data-mode] blocks below, so green means the same green in all six palettes. Themes used
   to restate them, which is why Noir read as "greyscaling the whole app": picking it
   repainted the semantic colors too, not just the chrome. Adding a palette means editing
   this list and THEMES in app.js (which mirrors each id plus its bg/accent swatch). */
:root[data-theme="noir"] {            /* black + white */
  --accent:#ffffff; --accent-ink:#000000; --accent-soft:rgba(255,255,255,.12);
  --chart-fill-top:rgba(255,255,255,.16); --chart-fill-bot:rgba(255,255,255,0);
  --shadow:0 1px 2px rgba(0,0,0,.5), 0 8px 28px -12px rgba(0,0,0,.7);
  --skeleton-shine:rgba(255,255,255,.08);
  color-scheme: dark;
  --chrome-0: #000000; --chrome-1: #0c0c0c; --chrome-2: #161616;
  --rule: #1c1c1c;
  --ink: #a8a8a8; --ink-dim: #8a8a8a; --ink-bright: #ffffff;
}
:root[data-theme="paper"] {           /* white + black */
  --accent:#000000; --accent-ink:#ffffff; --accent-soft:rgba(0,0,0,.08);
  --chart-fill-top:rgba(0,0,0,.13); --chart-fill-bot:rgba(0,0,0,0);
  --shadow:0 1px 2px rgba(0,0,0,.07), 0 8px 24px -12px rgba(0,0,0,.18);
  --skeleton-shine:rgba(255,255,255,.7);
  color-scheme: light;
  --chrome-0: #ffffff; --chrome-1: #ffffff; --chrome-2: #f4f4f4;
  --rule: #e4e4e4;
  --ink: #4a4a4a; --ink-dim: #5c5c5c; --ink-bright: #000000;
}
:root[data-theme="lagoon"] {          /* turquoise + pink */
  --accent:#ff4fa3; --accent-ink:#2c0418; --accent-soft:rgba(255,79,163,.14);
  --chart-fill-top:rgba(255,79,163,.22); --chart-fill-bot:rgba(255,79,163,0);
  --shadow:0 1px 2px rgba(1,17,17,.4), 0 8px 28px -12px rgba(1,17,17,.6);
  --skeleton-shine:rgba(255,255,255,.09);
  color-scheme: dark;
  --chrome-0: #0b1a1a; --chrome-1: #122525; --chrome-2: #1a3130;
  --rule: #1f3b3a;
  --ink: #a8c4c1; --ink-dim: #8fadaa; --ink-bright: #f0fffb;
}
:root[data-theme="matrix"] {          /* black + lime */
  --accent:#8dff3a; --accent-ink:#061a00; --accent-soft:rgba(141,255,58,.12);
  --chart-fill-top:rgba(141,255,58,.20); --chart-fill-bot:rgba(141,255,58,0);
  --shadow:0 1px 2px rgba(0,0,0,.6), 0 8px 28px -12px rgba(0,20,0,.8);
  --skeleton-shine:rgba(141,255,58,.10);
  color-scheme: dark;
  --chrome-0: #000000; --chrome-1: #080c08; --chrome-2: #0e150e;
  --rule: #162b19;
  --ink: #83c98b; --ink-dim: #6dab74; --ink-bright: #d8ffd8;
}
/* Fixed instrument colors — direction and warning, keyed to the light/dark family only.
   Switching palette must never restate what green and red mean, and these are the values
   every theme is contrast-checked against (worst cases: #000 in Noir/Terminal, #fff in
   Paper). Same specificity as the [data-theme] blocks, declared after them, so a theme
   that tries to override one loses on purpose. */
:root[data-mode="dark"] {
  --up: #46c95d; --down: #f0716b; --warn: #e9b44c;
  --up-soft: rgba(70,201,93,.10); --down-soft: rgba(240,113,107,.10); --warn-soft: rgba(233,180,76,.10);
}
/* Darker than the values the two light palettes used to carry separately: one shared set
   has to clear AA against the *worst* light surface, which is Daylight's #eef2f5 page and
   not Paper's white. The old #cf3f38 / #a2740e measured 4.22 and 3.70 there. */
:root[data-mode="light"] {
  --up: #14702e; --down: #bd332b; --warn: #8a5f0a;
  --up-soft: rgba(20,112,46,.09); --down-soft: rgba(189,51,43,.08); --warn-soft: rgba(138,95,10,.10);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
@supports (height: 100dvh) { body { height: 100dvh; } }
body { background: var(--chrome-0); color: var(--ink); font-family: var(--mono); font-size: 14px; line-height: 1.55;
  overflow: hidden; display: flex; flex-direction: column; transition: background .35s var(--ease), color .35s var(--ease); }
::selection { background: var(--accent); color: var(--accent-ink); }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb { background: var(--rule); border-radius: var(--radius-sm); border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background-color: var(--ink-dim); }
* { scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
button { font-family: inherit; }
/* Keyboard focus — one consistent ring everywhere (mouse clicks don't trigger :focus-visible) */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration:.01ms !important; transition-duration:.01ms !important; } }
/* Theme crossfade — done by the compositor, not by the style engine.
   The old approach put a color transition on `*` + both pseudo-elements for the duration
   of a switch: ~3.5k elements each running six interpolated properties, re-laying out and
   repainting every frame, which is what made switching lag on a rendered analysis. A view
   transition instead snapshots the page as two textures and cross-fades them on the GPU —
   one animating layer regardless of how much is on screen — while the real DOM switches
   instantly underneath. `.theme-swap` suppresses per-element transitions for the duration
   so nothing double-animates beneath the crossfade (and so browsers without view
   transitions get a clean instant switch rather than a few stragglers fading alone). */
:root.theme-swap *, :root.theme-swap *::before, :root.theme-swap *::after {
  transition: none !important;
}
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: .34s; animation-timing-function: var(--ease);
}

/* HEADER */
header { flex-shrink: 0; display: flex; align-items: center; gap: 10px; padding: 0 16px; height: var(--header-h);
  background: var(--chrome-1); border-bottom: 1px solid var(--rule); position: relative; z-index: 30; transition: background .35s var(--ease); }
.wordmark { display: flex; align-items: center; gap: 11px; user-select: none; cursor: pointer; background: none; border: none;
  padding: 4px 8px; margin-left: -8px; border-radius: var(--radius-sm); transition: background .2s var(--ease); }
.wordmark:hover { background: var(--chrome-2); }
.wordmark:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wordmark:active { transform: scale(.985); }
.wind { width: 30px; height: 26px; overflow: visible; }
.wind path { fill: none; stroke: var(--accent); stroke-width: 2.4; stroke-linecap: round; stroke-dasharray: 60; }
.wordmark:hover .wind path { animation: gust 1.4s var(--ease); }
.wind path:nth-child(2) { animation-delay: .08s !important; } .wind path:nth-child(3) { animation-delay: .16s !important; }
@keyframes gust { 0%{stroke-dashoffset:0;} 45%{stroke-dashoffset:60;opacity:0;} 46%{stroke-dashoffset:-60;} 100%{stroke-dashoffset:0;opacity:1;} }
.wordmark .wm-title { font-family: var(--mono); font-size: 18px; font-weight: 700; letter-spacing: .16em; color: var(--ink-bright); text-transform: uppercase; }
.wordmark small { font-size: 11px; color: var(--ink-dim); letter-spacing: .02em; font-weight: 500; margin-left: 2px; transform: translateY(2px); }
/* Search instrument — the field and its own submit share one housing, so Analyze reads
   as part of searching rather than as another peer button in the bar. The housing takes
   the header's leftover width (to a cap) instead of sitting at a fixed size, which is
   what keeps the placeholder from truncating as the bar narrows. */
#searchForm { display: flex; align-items: center; flex: 1 1 auto; max-width: 420px; margin-left: auto; min-width: 0; }
/* Only one search field is live at a time: the hero's while the landing page is up, the
   header's everywhere else. Both on screen at once is what made the header one easy to miss. */
body.hero-up #searchForm { display: none; }
/* With the header search hidden, nothing is left to absorb the free width, so the rail
   collapsed back against the wordmark and read as part of it. The auto margin is on this
   rule rather than on .util-rail itself: everywhere else #searchForm already carries the
   auto margin, and a second one would split the leftover space and drag the search field
   in off the right edge. */
body.hero-up .util-rail { margin-left: auto; }
.ticker-wrap, .hero-search { position: relative; display: flex; align-items: center; width: 100%; height: var(--ctl-h); min-width: 0;
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-md);
  transition: border-color .22s var(--ease), background .22s var(--ease), box-shadow .22s var(--ease); }
.ticker-wrap:focus-within, .hero-search:focus-within { border-color: var(--accent); background: var(--chrome-1); box-shadow: 0 0 0 3px var(--accent-soft); }
.search-ico { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px;
  fill: none; stroke: var(--ink-dim); pointer-events: none; transition: stroke .22s var(--ease); }
.ticker-wrap:focus-within .search-ico, .hero-search:focus-within .search-ico { stroke: var(--accent); }
/* Flexes instead of animating its own width — the old focus expansion shoved every
   control to its right sideways on every click into the field. */
#ticker, #heroTicker { flex: 1 1 0; min-width: 0; height: 100%; background: none; border: none; outline: none;
  color: var(--ink-bright); font-family: var(--mono); font-size: 13px; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; padding: 0 6px 0 31px; }
#ticker::placeholder, #heroTicker::placeholder { color: var(--ink-dim); font-family: var(--mono); text-transform: none; letter-spacing: 0; }
/* Search typeahead — in-site dropdown (replaces the native datalist) */
#tickerSuggest, #heroSuggest { position: absolute; top: calc(100% + 6px); right: 0; min-width: 100%; width: 300px; max-width: calc(100vw - 24px);
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm); box-shadow: var(--shadow);
  z-index: 60; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  max-height: calc(100dvh - var(--header-h) - 24px); display: none; }
#tickerSuggest.open, #heroSuggest.open { display: block; animation: sugIn .14s var(--ease-out); }
@keyframes sugIn { from { opacity: 0; transform: translateY(-4px); } }
.sug { display: flex; align-items: baseline; gap: 10px; padding: 8px 12px; cursor: pointer; }
.sug + .sug { border-top: 1px solid var(--rule); }
.sug .sym { font-family: var(--mono); font-size: 12.5px; font-weight: 600; letter-spacing: .04em; color: var(--ink-bright); min-width: 56px; flex-shrink: 0; }
.sug .nm { font-size: 12px; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sug mark { background: none; color: var(--accent); font-weight: 700; }
.sug:hover, .sug.active { background: var(--chrome-2); }
.sug.active .sym { color: var(--accent); }
#analyzeBtn, #heroAnalyzeBtn { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; height: calc(var(--ctl-h) - 10px);
  margin: 0 4px 0 2px; padding: 0 12px; background: var(--accent); color: var(--accent-ink); border: none; border-radius: var(--radius-xs);
  cursor: pointer; font-size: 12.5px; font-weight: 700; letter-spacing: .02em;
  transition: filter .18s var(--ease), transform .16s var(--ease), opacity .2s var(--ease); }
#analyzeBtn:hover, #heroAnalyzeBtn:hover { filter: brightness(1.08); }
#analyzeBtn:active, #heroAnalyzeBtn:active { transform: scale(.94); }
#analyzeBtn:disabled, #heroAnalyzeBtn:disabled { opacity: .5; cursor: progress; }
#analyzeBtn svg, #heroAnalyzeBtn svg { width: 12px; height: 12px; flex-shrink: 0; }

/* Utility rail — screener, profile and theme share one housing with hairline
   separators, so three unrelated-looking buttons read as a single object.
   position:relative anchors the theme menu; #themeMenu is absolutely positioned so it
   stays out of the flex flow and :last-of-type still resolves to #themeBtn. */
.util-rail { position: relative; display: flex; align-items: center; flex-shrink: 0; height: var(--ctl-h);
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-md);
  transition: border-color .22s var(--ease), background .22s var(--ease); }
.util-rail > button { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 100%; padding: 0 12px; background: none; border: none; cursor: pointer; color: var(--ink);
  font-size: 12px; font-weight: 650; white-space: nowrap;
  transition: color .2s var(--ease), background .2s var(--ease); }
.util-rail > button:first-of-type { border-radius: calc(var(--radius-md) - 1px) 0 0 calc(var(--radius-md) - 1px); }
.util-rail > button:last-of-type { border-radius: 0 calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0; }
.util-rail > button:hover { color: var(--ink-bright); background: var(--chrome-hi); }
/* Now that the rail links across pages, the current page needs to say so. Weight and ink,
   not a second hue — and the accent hairline rather than a fill, so it stays legible against
   the hover state sitting on the same --chrome-hi. */
.util-rail > button[aria-current="page"] { color: var(--ink-bright); background: var(--chrome-hi); box-shadow: inset 0 -2px 0 var(--accent); }
.util-rail > button svg { width: 15px; height: 15px; flex-shrink: 0; transition: transform .3s var(--ease-spring); }
.util-rail > button:active svg { transform: scale(.85); }
.rail-div { width: 1px; height: 17px; flex-shrink: 0; background: var(--rule); transition: background .22s var(--ease); }
/* `hidden` is a UA display:none, and the rules above set display:inline-flex on these —
   which wins. Without restating it the Saved button would sit in the rail with an empty
   label whenever there is nothing saved. */
.util-rail > button[hidden], .rail-div[hidden] { display: none; }
/* :first-of-type is positional and still resolves to #savedBtn while it is hidden, so the
   rail's left cap has to be handed over explicitly rather than inferred. */
#savedBtn[aria-expanded="true"], #watchBtn[aria-expanded="true"], #accountBtn[aria-expanded="true"] { color: var(--ink-bright); background: var(--chrome-hi); }
#screenerNav:hover svg { transform: translateX(2px); }
#themeBtn { width: 38px; padding: 0; }
#themeBtn svg { width: 16px; height: 16px; }
/* The palette's largest well carries the live accent, so the button previews the
   active theme instead of just naming the menu. */
#themeBtn .well { fill: var(--accent); stroke: none; transition: fill .35s var(--ease);
  transform-box: fill-box; transform-origin: center; }
#themeBtn[aria-expanded="true"] { color: var(--ink-bright); background: var(--chrome-hi); }
#themeBtn[aria-expanded="true"] svg { transform: rotate(-14deg) scale(1.06); }

/* Theme menu — one popover of full-palette choices, replacing the old binary toggle */
#themeMenu { position: absolute; top: calc(100% + 8px); right: 0; width: 244px; max-width: calc(100vw - 20px);
  padding: 6px; background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-md);
  box-shadow: var(--shadow); z-index: 70; transform-origin: top right;
  /* Opacity + transform rather than display:none/block + a keyframe: the display swap gave
     the menu no exit at all (it vanished mid-fade on close) and re-ran a keyframe on a
     freshly laid-out box each open. These two properties animate on the compositor, and
     visibility flips at the end of the close so the menu stays untabbable while hidden. */
  opacity: 0; visibility: hidden; transform: translateY(-6px) scale(.97);
  transition: opacity .13s var(--ease-out), transform .16s var(--ease-out), visibility 0s .16s; }
#themeMenu.open { opacity: 1; visibility: visible; transform: none;
  transition: opacity .12s var(--ease-out), transform .17s var(--ease-out), visibility 0s; }
.theme-menu-head { padding: 5px 8px 7px; font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-dim); }
.theme-opt { display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 8px; text-align: left;
  background: none; border: none; border-radius: var(--radius-sm); cursor: pointer; color: var(--ink);
  transition: background .16s var(--ease), color .16s var(--ease); }
.theme-opt:hover, .theme-opt.cursor { background: var(--chrome-2); color: var(--ink-bright); }
.theme-opt[aria-checked="true"] { color: var(--ink-bright); }
/* Swatches are literal hex from the THEMES table, not vars — each row has to show its
   own palette, not the one currently applied. */
.theme-swatch { width: 22px; height: 22px; flex-shrink: 0; border-radius: var(--radius-sm); border: 1px solid var(--rule);
  background: linear-gradient(135deg, var(--sw-bg) 0 50%, var(--sw-accent) 50% 100%); }
.theme-opt .tl { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.theme-opt .tl b { font-size: 12.5px; font-weight: 650; letter-spacing: .01em; }
.theme-opt .tl span { font-size: 10.5px; color: var(--ink-dim); }
.theme-opt .tick { margin-left: auto; width: 14px; height: 14px; flex-shrink: 0; fill: none; stroke: var(--accent);
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; opacity: 0; transform: scale(.6);
  transition: opacity .18s var(--ease), transform .22s var(--ease-spring); }
.theme-opt[aria-checked="true"] .tick { opacity: 1; transform: none; }

/* MySquall — compact profile control + locally saved preference editor */
#profileBtn svg { stroke: var(--accent); }
#profileBtn.configured { color: var(--ink-bright); }
#profileDot { position: absolute; top: 7px; right: 7px; width: 6px; height: 6px; border-radius: 50%;
  background: transparent; transition: background .25s var(--ease), box-shadow .25s var(--ease); }
#profileBtn.configured #profileDot { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
#profileModal { position: fixed; inset: 0; z-index: 100; display: none; place-items: center; padding: 20px;
  background: rgba(4,8,13,.68); backdrop-filter: blur(7px); }
#profileModal.open { display: grid; animation: profileFade .18s ease-out; }
@keyframes profileFade { from { opacity: 0; } }
#profilePanel { width: min(720px, 100%); max-height: min(820px, calc(100dvh - 40px)); overflow: auto;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius); box-shadow: var(--shadow); }
.profile-head { position: sticky; top: 0; z-index: 2; padding: 20px 22px 16px;
  background: var(--chrome-1); border-bottom: 1px solid var(--rule); }
.profile-head-top { display: flex; align-items: flex-start; gap: 18px; }
.profile-head h2 { font-family: var(--mono); color: var(--ink-bright); font-size: var(--t-doc-h); letter-spacing: .05em; }
.profile-head p { color: var(--ink-dim); font-size: 12px; margin-top: 3px; }
.profile-close { margin-left: auto; width: 34px; height: 34px; display: grid; place-items: center; flex-shrink: 0; border-radius: var(--radius-sm);
  border: 1px solid var(--rule); background: var(--chrome-2); color: var(--ink); cursor: pointer;
  transition: color .2s var(--ease), border-color .2s var(--ease), background .2s var(--ease); }
.profile-close:hover { color: var(--ink-bright); border-color: var(--ink-dim); background: var(--chrome-2); }
.profile-close svg { width: 15px; height: 15px; }
#profileForm { padding: 20px 22px 22px; }
.profile-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.profile-field { padding: 14px; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-2); }
.profile-field > label, .profile-field legend { display: flex; justify-content: space-between; gap: 12px; color: var(--ink-bright);
  font-size: 12px; font-weight: 700; margin-bottom: 10px; }
.profile-value { color: var(--accent); font-family: var(--mono); font-size: 11px; font-weight: 600; text-align: right; }
.profile-field input[type="range"] { width: 100%; accent-color: var(--accent); cursor: pointer; }
.profile-scale { display: flex; justify-content: space-between; margin-top: 5px; color: var(--ink-dim); font-family: var(--mono); font-size: var(--t-micro); }
.profile-field select, .profile-field textarea { width: 100%; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--chrome-1);
  color: var(--ink-bright); font: inherit; outline: none; }
.profile-field select { height: 38px; padding: 0 10px; }
.profile-field textarea { min-height: 100px; resize: vertical; padding: 10px 11px; line-height: 1.45; }
.profile-wide { grid-column: 1 / -1; }
.profile-priorities { display: flex; flex-wrap: wrap; gap: 8px; }
.profile-priorities label { position: relative; cursor: pointer; }
.profile-priorities input { position: absolute; opacity: 0; pointer-events: none; }
.profile-priorities span, .prompt-chip { display: inline-flex; align-items: center; border: 1px solid var(--rule); border-radius: var(--radius-xs);
  background: var(--chrome-1); color: var(--ink); padding: 6px 10px; font-size: 11px; transition: color .15s, border-color .15s, background .15s; }
.profile-priorities input:checked + span { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.profile-priorities input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.prompt-suggestions { display: flex; flex-wrap: wrap; gap: 6px; margin: 9px 0; }
.prompt-chip { cursor: pointer; font-family: inherit; }
.prompt-chip:hover { color: var(--accent); border-color: var(--accent); }
.profile-note { margin-top: 8px; color: var(--ink-dim); font-size: 11px; }
.profile-actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.profile-actions button { height: 38px; border-radius: var(--radius-sm); padding: 0 15px; cursor: pointer; font-weight: 700; }
#profileReset { border: 1px solid var(--rule); background: transparent; color: var(--ink); }
#profileSave { margin-left: auto; border: none; background: var(--accent); color: var(--accent-ink); }
#profileSaveStatus { color: var(--accent); font-size: 11px; }

/* PROGRESS BAR — driven by market-data, rendering, and AI stream events */
#progressWrap { flex-shrink: 0; height: 0; overflow: hidden; background: var(--chrome-0); border-bottom: 0 solid var(--rule); transition: height .35s var(--ease-out); }
#progressWrap.show { height: 46px; border-bottom-width: 1px; }
#progressInner { display: flex; align-items: center; gap: 14px; padding: 0 22px; height: 100%; }
#progressTrack { flex: 1; height: 6px; border-radius: 99px; background: var(--chrome-2); overflow: hidden; position: relative; }
#progressFill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%; border-radius: 99px;
  background: var(--accent); transition: width .5s var(--ease-out); }
#progressFill.error { background: var(--down); }
#progressWrap.done #progressFill { background: var(--accent); }
#progressMeta { font-family: var(--mono); font-size: 12px; color: var(--ink); white-space: nowrap; display: flex; align-items: center; gap: 8px; }
#progressMeta b { color: var(--ink-bright); font-weight: 600; }
#progressPct { color: var(--accent); font-weight: 600; min-width: 38px; text-align: right; font-family: var(--mono); font-size: 12px; }
#progressMeta.error { color: var(--down); } #progressMeta.error b { color: var(--down); }

/* HERO */
#hero { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px; padding: 24px; text-align: center; animation: heroIn .6s var(--ease-out); position: relative; overflow: hidden; }
#hero > * { position: relative; }
@keyframes heroIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
#hero .wind { width: 64px; height: 52px; } #hero .wind path { stroke-width: 3; animation: gust 4.5s var(--ease) infinite; }
#hero h1 { font-family: var(--mono); font-size: clamp(32px, 6vw, 52px); font-weight: 700; letter-spacing: .2em; color: var(--ink-bright); text-transform: uppercase; line-height: 1; margin-right: -.2em; }
#hero p { max-width: 500px; font-size: 15px; color: var(--ink); } #hero p b { color: var(--ink-bright); font-weight: 600; }
/* Wider than the pitch so the source list and the disclaimer stay on one line together. */
#hero .hero-fine { max-width: 640px; font-size: 12px; color: var(--ink-dim); margin-top: -8px; animation: riseIn .6s var(--ease-out) backwards .36s; }
#hero .hero-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
/* Hero search — inherits the header instrument's housing, focus ring and submit above;
   only the scale and the dropdown's anchor differ. The dropdown opens left-aligned and
   full width here because the field is centred, not pinned to the right edge. */
#heroSearchForm { width: min(560px, 100%); }
/* A hairline is enough in a dense header bar; alone on the hero the same --rule edge
   measures 1.1:1 against the page and the field reads as empty space — which is the
   problem this field exists to fix. Deepen it with ink, not accent: the accent belongs
   to the focus ring and the submit, and it stays one accent. */
.hero-search { height: 52px; border-color: color-mix(in srgb, var(--ink-dim) 75%, var(--rule)); }
#heroTicker { font-size: var(--t-lead); padding: 0 8px 0 44px; }
.hero-search .search-ico { left: 17px; width: 17px; height: 17px; }
#heroAnalyzeBtn { height: 40px; margin: 0 6px 0 4px; padding: 0 16px; font-size: 13px; }
#heroAnalyzeBtn svg { width: 13px; height: 13px; }
#heroSuggest { left: 0; right: auto; width: 100%; text-align: left; }
.chip { background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm); color: var(--ink); font-family: var(--mono);
  font-size: 12px; font-weight: 550; padding: 7px 12px; cursor: pointer; transition: color .16s, border-color .16s, background .16s; }
.chip:hover { color: var(--ink-bright); border-color: var(--accent); background: var(--accent-soft); }
.chip.primary-link { font-family: var(--mono); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--rule)); }
.chip-rand { display: inline-flex; align-items: center; gap: 6px; color: var(--accent); }
.chip-rand:hover { border-color: var(--accent); }
.chip.resume { display: none; color: var(--accent); border-color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.chip.resume.show { display: inline-flex; align-items: center; gap: 6px; animation: heroIn .4s var(--ease-out); }

/* Snapshot age. --warn is a marker here, never the text colour: it measures 3.7:1 on
   Daylight's surface, below what 10–12px text needs. The text stays ink (brighter and
   heavier as it ages) and the warn role carries the dot, the edge and the tint. */
/* Written against the three places a date appears, whose own colour rules come later. */
.stale, .as-of.stale, .analysis-tab-main span.stale, .resume-row .resume-when.stale { font-weight: 650; color: var(--ink-bright); }
.stale::before { content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 5px; border-radius: 50%;
  vertical-align: .08em; background: var(--warn); }
.stale.aging::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--warn); }
.stale.old { background: var(--warn-soft); border-radius: var(--radius-xs); padding: 1px 6px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 45%, transparent); }
.metric .v.stale-val { color: var(--ink-bright); font-weight: 650; }
.stale-note { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; padding: 8px 8px 8px 12px;
  border: 1px solid var(--rule); border-left: 3px solid var(--warn); border-radius: var(--radius-sm); background: var(--chrome-2);
  font-family: var(--mono); font-size: var(--t-data); line-height: 1.45; color: var(--ink); }
.stale-note.old { background: var(--warn-soft); }
.stale-note b { color: var(--ink-bright); font-weight: 700; }
.stale-note > span { flex: 1; min-width: 0; }
.stale-note .chip { flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--rule)); }
.stale-note .chip svg { width: 12px; height: 12px; }
.prose .stale-note { font-family: var(--mono); margin-bottom: 16px; }

/* Saved-research picker. Same backdrop and close button as MySquall; the panel is a
   dense list, not a form, so it is narrower and the list is the only thing that scrolls. */
#resumeModal { position: fixed; inset: 0; z-index: 100; display: none; place-items: start center; padding: 12vh 20px 20px;
  background: color-mix(in srgb, var(--chrome-0) 72%, transparent); backdrop-filter: blur(7px); }
#resumeModal.open { display: grid; animation: profileFade .18s ease-out; }
#resumePanel { width: min(560px, 100%); max-height: min(620px, 76vh); display: flex; flex-direction: column; overflow: hidden;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius); box-shadow: var(--shadow); text-align: left; }
.resume-head { display: flex; align-items: center; gap: 10px; padding: 12px 12px 10px 16px; }
.resume-head h2 { font-family: var(--mono); font-size: var(--t-lead); letter-spacing: .05em; color: var(--ink-bright); }
#resumeMeta { font-family: var(--mono); font-size: var(--t-micro); color: var(--ink-dim); }
.resume-head .profile-close { width: 30px; height: 30px; }
#resumeFilter { margin: 0 12px 8px; height: 36px; padding: 0 12px; border: 1px solid var(--rule); border-radius: var(--radius-sm);
  background: var(--chrome-0); color: var(--ink-bright); font-family: var(--mono); font-size: var(--t-body); outline: none; }
#resumeFilter:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
#resumeList { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 6px 6px; border-top: 1px solid var(--rule); }
.resume-group { position: sticky; top: 0; z-index: 1; padding: 8px 10px 4px; background: var(--chrome-1);
  font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim); }
.resume-row { display: grid; grid-template-columns: 52px minmax(0, 1fr) auto auto; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border: 0; border-radius: var(--radius-xs); background: none; color: var(--ink); cursor: pointer; text-align: left;
  font-family: var(--mono); transition: background .14s; }
.resume-row:hover, .resume-row:focus-visible { background: var(--chrome-hi); outline: none; }
.resume-row:focus-visible { box-shadow: inset 2px 0 0 var(--accent); }
.resume-kind { font-size: var(--t-micro); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim); }
.resume-name { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.resume-name b { flex-shrink: 0; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-data); color: var(--ink-bright); }
.resume-name small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-micro); color: var(--ink-dim); }
.resume-price { font-size: var(--t-data); font-variant-numeric: tabular-nums; color: var(--ink); }
.resume-when { font-size: var(--t-micro); color: var(--ink-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.resume-empty { padding: 18px 10px; font-family: var(--mono); font-size: var(--t-data); color: var(--ink-dim); }
.resume-foot { padding: 8px 16px 10px; border-top: 1px solid var(--rule); font-size: var(--t-micro); color: var(--ink-dim); }
@media (max-width: 620px) {
  #resumeModal { padding: 10px; place-items: stretch; }
  #resumePanel { max-height: calc(100dvh - 20px); }
  .resume-row { grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .resume-kind, .resume-price { display: none; }
}

/* TICKER BAR — identity, inside the header.
   Replaces #summaryStrip, which was a 100px band. Sizes are the header's register, not a
   banner's: the price is the largest thing in the bar but it still sits inside 52px. */
#tickerBar { display: none; align-items: baseline; gap: 10px; min-width: 0; flex-shrink: 1; overflow: hidden;
  margin-left: 16px; padding-left: 16px; border-left: 1px solid var(--rule); }
#tickerBar.show { display: flex; animation: heroIn .35s var(--ease); }
#sTicker { flex-shrink: 0; font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .08em; background: var(--accent-soft);
  color: var(--accent); border-radius: var(--radius-xs); padding: 3px 8px; transform: translateY(-1px); }
#sCompany { min-width: 0; flex: 0 1 auto; max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--t-data); font-weight: 500; color: var(--ink-dim); letter-spacing: 0; }
#sPrice { flex-shrink: 0; font-family: var(--mono); font-size: var(--t-lead); font-weight: 600; color: var(--ink-bright); }
.pill { flex-shrink: 0; font-family: var(--mono); font-size: 11px; font-weight: 600; border-radius: var(--radius-xs); padding: 2px 9px; white-space: nowrap; }
.pill.up { color: var(--up); background: var(--up-soft); } .pill.down { color: var(--down); background: var(--down-soft); }
.meta-dot { font-size: 12px; color: var(--ink-dim); } .meta-dot b { color: var(--ink); font-weight: 600; }
/* Plain labels, not boxed pills: the bar already has a ticker chip and a change pill, and
   three more tinted boxes beside them read as decoration and cost 18px of padding each. */
.sector-badge, .regime-badge { flex-shrink: 0; font-family: var(--mono); font-size: var(--t-micro); font-weight: 650; letter-spacing: .04em; white-space: nowrap; }
/* Regime labels vary from "RANGE" to "RANGE / TRANSITION", so the bar cannot be tuned to
   one width. They never shrink: fitTickerBar() in app.js drops whole labels instead. */
.as-of { flex-shrink: 0; font-size: var(--t-micro); color: var(--ink-dim); white-space: nowrap; letter-spacing: .02em; }
.sector-badge { color: var(--ink); }
.regime-badge { color: var(--warn); }
/* The shed ladder. The bar gives up its own content — right to left, least glanceable
   first — before the wordmark or the search field lose any width, because searching and
   getting home are what the header is FOR. The tagline goes as soon as identity arrives. */
body.has-analysis .wordmark small { display: none; }
/* The field still holds a ticker and a placeholder at 280px; the bar's identity block
   does not fit at all if the search keeps its full 420px landing-page width. */
body.has-analysis #searchForm { max-width: 280px; }
/* The company name goes FIRST, not last. It is the widest variable item in the bar and
   the most redundant one — the ticker is already there, the full name is on the chip's
   title and in the Live Snapshot card. Kept to the end of the ladder it did not disappear,
   it ellipsed: at 1280px it rendered as 32px of "AAP…", which is worse than absent and
   costs the sector and regime badges the room they need. */
@media (max-width: 1400px) { #sCompany { display: none; } }
/* Then the rail's labels, before any chip ellipses. At 1280px the labels cost ~190px that
   the bar needed: every chip rendered as "Tec…" / "TR…" / "Resul…", which carries nothing.
   The rail's icons are learned after one visit and each button keeps its title; the chips
   are the analysis. Same collapse the rail already does at 720px, keyed to has-analysis so
   the landing page keeps its labels. */
@media (max-width: 1400px) {
  body.has-analysis #profileBtn span, body.has-analysis #screenerNav span, body.has-analysis #portfolioNav span, body.has-analysis #savedBtn span, body.has-analysis #watchBtn span, body.has-analysis #accountBtn span { display: none; }
  body.has-analysis .util-rail > button { width: 38px; padding: 0; }
  body.has-analysis #savedBtn { width: auto; padding: 0 9px; }
  /* The watchlist gave the ticker bar's badges ~80px less room here. Its count is the
     part that can go — the popover shows the rows — so it drops to a bare 38px icon. */
  body.has-analysis #watchCount { display: none; }
}
@media (max-width: 1180px) { #tickerBar .regime-badge:not(.event-badge) { display: none; } }
@media (max-width: 1060px) { #tickerBar .sector-badge { display: none; } }
@media (max-width:  620px) { #tickerBar { margin-left: 10px; padding-left: 10px; gap: 7px; } #sTicker { display: none; } }

/* Saved analyses persist locally and live in a popover off the utility rail. They were a
   permanent 37px strip under the header; switching between saved work is on-demand, so it
   is on-demand chrome. The rows keep their markup (renderTickerPills is unchanged) — only
   the axis changed, from a row of browser-style tabs to a vertical menu. */
#savedMenu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; display: none;
  width: min(320px, calc(100vw - 24px)); max-height: calc(100dvh - var(--header-h) - 24px); overflow-y: auto; overscroll-behavior: contain;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: 6px; }
#savedMenu.open { display: block; animation: sugIn .14s var(--ease-out); }
.menu-head { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-dim); padding: 6px 8px 8px; }
#savedCount { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--radius-xs); padding: 1px 5px; margin-left: 1px; }
#savedCount:empty { display: none; }
#analysisTabs { display: flex; flex-direction: column; gap: 2px; }
/* Watchlist popover. Same housing as #savedMenu. A row is two lines — ticker, price and
   the day's move on a shared axis, then what changed since the saved snapshot — because
   the second line is the reason to keep a watchlist rather than a bookmark. */
#watchMenu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; display: none;
  width: min(360px, calc(100vw - 24px)); max-height: calc(100dvh - var(--header-h) - 24px); overflow-y: auto; overscroll-behavior: contain;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: 6px; }
#watchMenu.open { display: block; animation: sugIn .14s var(--ease-out); }
#watchCount { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--radius-xs); padding: 1px 5px; margin-left: 1px; }
#watchCount:empty { display: none; }
.watch-head { display: flex; align-items: center; gap: 8px; }
.watch-head .menu-head { flex: 1; }
#watchRefresh { appearance: none; border: 0; background: none; color: var(--ink-dim); cursor: pointer;
  width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--radius-xs); }
#watchRefresh:hover { color: var(--ink-bright); background: var(--chrome-hi); }
#watchRefresh svg { width: 14px; height: 14px; }
#watchRefresh.spin svg { animation: watchSpin .8s linear infinite; }
@keyframes watchSpin { to { transform: rotate(360deg); } }
#watchAdd { display: flex; gap: 6px; padding: 0 4px 8px; }
#watchInput { flex: 1; min-width: 0; height: 28px; padding: 0 9px; font-family: var(--mono); font-size: var(--t-data);
  text-transform: uppercase; color: var(--ink-bright); background: var(--chrome-0); border: 1px solid var(--rule); border-radius: var(--radius-xs); }
#watchInput::placeholder { text-transform: none; color: var(--ink-dim); }
#watchInput:focus { outline: none; border-color: var(--accent); }
#watchAdd button { appearance: none; height: 28px; padding: 0 11px; font-family: var(--mono); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--accent); background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); border-radius: var(--radius-xs); cursor: pointer; }
#watchAdd button:hover { background: var(--chrome-hi); }
#watchRows { display: flex; flex-direction: column; gap: 2px; }
.watch-row { display: flex; align-items: stretch; border-radius: var(--radius-xs); color: var(--ink); }
.watch-row:hover { background: var(--chrome-hi); }
.watch-open, .watch-del { appearance: none; border: 0; background: none; color: inherit; cursor: pointer; }
.watch-open { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; padding: 7px 5px 7px 10px; text-align: left; }
.watch-line { display: flex; align-items: baseline; gap: 8px; }
.watch-line b { flex: 1; min-width: 0; font-family: var(--mono); font-size: 11px; letter-spacing: .02em; color: var(--ink-bright); }
.watch-px { font-family: var(--mono); font-size: var(--t-data); font-weight: 600; color: var(--ink-bright); }
.watch-line .pill { font-size: var(--t-micro); padding: 1px 7px; }
.watch-sub { display: flex; flex-wrap: wrap; gap: 3px 6px; font-family: var(--mono); font-size: var(--t-micro); color: var(--ink-dim); }
.watch-sub i { font-style: normal; }
.watch-sub .green { color: var(--up); } .watch-sub .red { color: var(--down); }
.watch-sub .watch-event { color: var(--warn); }
.watch-del { width: 30px; border-radius: var(--radius-sm); font-size: 17px; color: var(--ink-dim); opacity: .65; }
.watch-del:hover { opacity: 1; color: var(--down); background: var(--down-soft); }
.watch-empty { margin: 0; padding: 4px 8px 10px; font-size: var(--t-data); line-height: var(--lh-tight); color: var(--ink-dim); }
.watch-note { margin: 0; padding: 0 8px; font-family: var(--mono); font-size: var(--t-micro); color: var(--warn); }
.watch-note:not(:empty) { padding: 6px 8px 4px; }
/* Account (optional sign-in). Same housing as #savedMenu. Signed in, the user's initial
   replaces the glyph, in the same badge the Saved and Watch counts use, so the button stays
   38px wide wherever the rail collapses to icons. */
#accountInitial { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--radius-xs); padding: 1px 5px; }
#accountInitial[hidden] { display: none; }
#accountBtn.signed-in svg { display: none; }
#accountBtn.sync-warn span { color: var(--warn); }
#accountMenu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; display: none;
  width: min(280px, calc(100vw - 24px));
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: 6px; }
#accountMenu.open { display: block; animation: sugIn .14s var(--ease-out); }
.acct-line { margin: 0; padding: 0 8px 4px; font-family: var(--mono); font-size: var(--t-data); color: var(--ink-bright); overflow-wrap: anywhere; }
.acct-note { margin: 0; padding: 0 8px 8px; font-family: var(--mono); font-size: var(--t-micro); line-height: var(--lh-tight); color: var(--ink-dim); }
.acct-note:empty { display: none; }
.acct-note.warn { color: var(--warn); }
.acct-actions { display: flex; flex-direction: column; gap: 2px; padding-top: 4px; border-top: 1px solid var(--rule); }
.acct-actions[hidden] { display: none; }
.acct-actions .acct-note { padding-top: 4px; }
.acct-actions button { appearance: none; border: 0; background: none; cursor: pointer; text-align: left;
  padding: 7px 8px; border-radius: var(--radius-xs); font-family: var(--mono); font-size: var(--t-data); color: var(--ink); }
.acct-actions button:hover, .acct-actions button:focus-visible { color: var(--ink-bright); background: var(--chrome-hi); }
/* The ticker bar's star. Direction colors are reserved, so "watched" is the accent. */
#sWatch { flex-shrink: 0; appearance: none; border: 0; background: none; cursor: pointer; padding: 0 2px; margin-left: -4px;
  font-size: var(--t-lead); line-height: 1; color: var(--ink-dim); }
#sWatch:hover { color: var(--ink-bright); }
#sWatch[aria-pressed="true"] { color: var(--accent); }
#analysisTabs:empty::after { content: "Nothing saved yet."; display: block; padding: 4px 8px 10px; font-size: var(--t-data); color: var(--ink-dim); }
/* Saved tabs can fail for one reason the user can act on — a full origin storage budget —
   and that failure used to be entirely silent. Warn role, not a new hue: --warn already
   means "state you should look at" and is declared per [data-mode], so this reads the same
   in all six palettes. Dismissed by the user, never on a timer; it reports dropped data. */
#storageNotice { display: flex; align-items: center; gap: 10px; padding: 8px 14px;
  font-family: var(--mono); font-size: var(--t-data); line-height: var(--lh-tight);
  color: var(--warn); background: var(--warn-soft); border-bottom: 1px solid var(--rule); }
#storageNotice[hidden] { display: none; }
#storageNoticeText { flex: 1; min-width: 0; }
#storageNotice button { appearance: none; border: 0; background: none; color: inherit; cursor: pointer;
  flex-shrink: 0; font-family: var(--mono); font-size: var(--t-body); line-height: 1;
  padding: 3px 6px; border-radius: var(--radius-xs); }
#storageNotice button:hover { background: var(--chrome-hi); }
/* A menu row, not a tab. The selected one is carried by an accent inset rule and bright
   ink — never by a fill alone, because in Daylight --chrome-1 and --chrome-2 sit close
   enough that a tint reads as nothing at all. */
.analysis-tab { display: flex; align-items: stretch; border: 0; border-radius: var(--radius-xs); background: none; color: var(--ink-dim); }
.analysis-tab:hover { background: var(--chrome-hi); }
.analysis-tab.active { color: var(--ink-bright); background: var(--chrome-2); box-shadow: inset 2px 0 0 var(--accent); }
.analysis-tab-main, .analysis-tab-close { appearance: none; border: 0; background: none; color: inherit; cursor: pointer; }
.analysis-tab-main { display: flex; flex: 1; min-width: 0; align-items: baseline; gap: 8px; padding: 8px 5px 8px 11px; text-align: left; }
.analysis-tab-main b { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); font-size: 11px; letter-spacing: .02em; }
.analysis-tab-main span { flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-micro); color: var(--ink-dim); }
.analysis-tab-close { width: 30px; border-radius: var(--radius-sm); font-size: 17px; opacity: .65; }
.analysis-tab-close:hover { opacity: 1; color: var(--down); background: var(--down-soft); }
.analysis-tab.screener-tab:not(.active) .analysis-tab-main b { color: var(--ink); }

/* Natural-language multi-index screener */
#screenerView { flex: 1; min-height: 0; display: none; overflow-y: auto; padding: 26px clamp(16px, 4vw, 54px) 70px; background: var(--chrome-0); }
#screenerView.show { display: block; animation: wsIn .35s var(--ease-out); }
/* On /screener the screener IS the page, not a view being revealed into an existing one.
   Visible from first paint so it does not depend on JS, and no wsIn: that animation encoded
   an in-app transition from another view, and replaying it on navigation would be exactly
   the arrival-announcing motion the design direction removed everywhere else. */
body[data-page="screener"] #screenerView { display: block; animation: none; }

/* ── Portfolio (/portfolio) ─────────────────────────────────────────────────
   Borrows the screener's shell and head. Numbers are right-aligned tabular mono so a
   column of values reads down by magnitude; green/red only on changes and P&L. */
#portfolioView { flex: 1; min-height: 0; overflow-y: auto; padding: 26px clamp(16px, 4vw, 54px) 70px; background: var(--chrome-0); }
#pfSummary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0 0 18px; border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-1); }
#pfSummary:empty { display: none; }
.pf-stat { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; min-width: 0; }
.pf-stat + .pf-stat { border-left: 1px solid var(--rule); }
.pf-stat small { color: var(--ink-dim); font-size: var(--t-micro); letter-spacing: .04em; text-transform: uppercase; }
.pf-stat b { color: var(--ink-bright); font-size: var(--t-lead); font-weight: 600; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-stat span { font-size: var(--t-data); font-variant-numeric: tabular-nums; color: var(--ink-dim); }
.pf-stat .green, .pf-table .green { color: var(--up); } .pf-stat .red, .pf-table .red { color: var(--down); }
#pfAdd { display: flex; margin-top: 18px; align-items: flex-end; flex-wrap: wrap; gap: 10px; padding: 10px; border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-1); }
.pf-field { display: flex; flex-direction: column; gap: 5px; flex: 1 1 140px; min-width: 0; }
.pf-field.pf-ticker { flex: 0 1 120px; }
.pf-field span { color: var(--ink-dim); font-size: var(--t-micro); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.pf-field em { font-style: normal; font-weight: 400; text-transform: none; letter-spacing: 0; }
.pf-field input, #pfImportText { width: 100%; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--chrome-0); color: var(--ink-bright); padding: 8px 10px; font: var(--t-body) var(--mono); }
.pf-field input:focus, #pfImportText:focus { outline: none; border-color: var(--accent); }
#pfTicker { text-transform: uppercase; }
#pfSubmit, #pfCancel, #pfImportBtn { height: 36px; min-width: 88px; padding: 0 16px; border-radius: var(--radius-sm); cursor: pointer; font: 600 var(--t-data) var(--mono); }
#pfSubmit, #pfImportBtn { border: 0; background: var(--accent); color: var(--accent-ink); }
#pfCancel { border: 1px solid var(--rule); background: transparent; color: var(--ink); }
#pfCancel:hover { color: var(--ink-bright); background: var(--chrome-hi); }
#pfCancel[hidden] { display: none; }
.pf-note { margin: 6px 2px 0; min-height: 1em; font-size: var(--t-data); color: var(--warn); }
.pf-note.ok { color: var(--ink-dim); }
#pfImport { margin: 8px 0 0; }
#pfImport summary { cursor: pointer; color: var(--ink-dim); font-size: var(--t-data); }
#pfImport summary:hover { color: var(--ink-bright); }
#pfImportText { display: block; margin-top: 8px; resize: vertical; line-height: var(--lh-tight); }
.pf-import-row { display: flex; align-items: center; gap: 12px; margin-top: 8px; color: var(--ink-dim); font-size: var(--t-micro); }
.pf-table-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.pf-table-head .menu-head { flex: 1; padding: 0; }
#pfRefresh { display: inline-grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--radius-xs); background: none; color: var(--ink-dim); cursor: pointer; }
#pfRefresh:hover { color: var(--ink-bright); background: var(--chrome-hi); }
#pfRefresh svg { width: 14px; height: 14px; }
#pfRefresh.spin svg { animation: spin 1s linear infinite; }
.pf-scroll { overflow-x: auto; border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-1); }
.pf-table { width: 100%; border-collapse: collapse; font-size: var(--t-data); font-variant-numeric: tabular-nums; }
.pf-table th { padding: 9px 12px; border-bottom: 1px solid var(--rule); color: var(--ink-dim); font-size: var(--t-micro); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; text-align: right; white-space: nowrap; }
.pf-table td { padding: 9px 12px; border-bottom: 1px solid var(--rule); color: var(--ink); text-align: right; white-space: nowrap; }
.pf-table th:first-child, .pf-table td:first-child { text-align: left; }
.pf-table tbody tr:hover { background: var(--chrome-hi); }
.pf-table tfoot td { border-bottom: 0; color: var(--ink-bright); font-weight: 600; }
.pf-table td.pf-val { color: var(--ink-bright); font-weight: 600; }
.pf-sym { border: 0; background: none; padding: 0; cursor: pointer; color: var(--ink-bright); font: 600 var(--t-data) var(--mono); letter-spacing: .02em; }
.pf-sym:hover { color: var(--accent); text-decoration: underline; }
.pf-sub { display: block; color: var(--ink-dim); font-size: var(--t-micro); }
td:first-child > .pf-sub { max-width: 26ch; overflow: hidden; text-overflow: ellipsis; }
.pf-weight { display: inline-flex; align-items: center; gap: 8px; }
.pf-weight i { display: block; width: 48px; height: 4px; border-radius: 2px; background: var(--chrome-2); overflow: hidden; }
.pf-weight i::before { content: ""; display: block; width: var(--w, 0%); height: 100%; background: var(--accent); }
.pf-act { display: inline-flex; gap: 2px; }
.pf-act button { width: 28px; height: 26px; border: 0; border-radius: var(--radius-xs); background: none; color: var(--ink-dim); cursor: pointer; font-size: var(--t-body); }
.pf-act button:hover { color: var(--ink-bright); background: var(--chrome-2); }
.pf-act .pf-del:hover { color: var(--down); background: var(--down-soft); }
.pf-table tr.editing td { background: var(--accent-soft); }
.pf-empty { padding: 26px 14px; text-align: center; color: var(--ink-dim); font-size: var(--t-body); }
@media (max-width: 720px) {
  #portfolioView { padding: 20px 12px calc(40px + env(safe-area-inset-bottom, 0px)); }
  #pfSummary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pf-stat:nth-child(3) { border-left: 0; }
  .pf-stat:nth-child(n+3) { border-top: 1px solid var(--rule); }
  /* Today's move is in the summary; value and P&L are what a phone row has room for. */
  .pf-col-cost, .pf-col-weight, .pf-col-shares, .pf-col-day { display: none; }
  .pf-table th, .pf-table td { padding: 9px 6px; }
  .pf-act { flex-direction: column; }
  td:first-child > .pf-sub { max-width: 12ch; }
}

/* ── Private point-in-time analyzer (/ilgar) ──────────────────────────────── */
body[data-page="backtester"] { overflow: auto; }
body[data-page="backtester"] header { flex: 0 0 auto; }
#backtestView { flex: 1 0 auto; min-height: calc(100vh - var(--header-h)); background: var(--bg); }
.backtest-shell { width: min(1120px, calc(100% - 36px)); margin: 0 auto; padding: 48px 0 80px; }
.backtest-intro { position: static; height: auto; padding: 0 0 25px; border: 0; background: transparent; display: flex; align-items: flex-end; justify-content: space-between; gap: 28px; }
.backtest-intro h1 { margin: 6px 0 8px; color: var(--ink-bright); font: 500 clamp(32px, 5vw, 54px)/1.03 var(--doc); letter-spacing: -.025em; }
.backtest-intro p { max-width: 700px; color: var(--ink-dim); font-size: 14px; line-height: 1.6; }
.backtest-private { flex: 0 0 auto; padding: 6px 9px; border: 1px solid var(--rule); border-radius: var(--radius-xs); color: var(--ink-dim); font: 10px var(--mono); text-transform: uppercase; letter-spacing: .08em; }
.backtest-method { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.backtest-method > div { display: grid; grid-template-columns: 28px 1fr; gap: 8px; padding: 15px 18px 15px 0; color: var(--ink-dim); font-size: 11px; line-height: 1.45; }
.backtest-method > div + div { border-left: 1px solid var(--rule); padding-left: 18px; }
.backtest-method b { color: var(--accent); font: 600 11px var(--mono); }
.backtest-form { display: grid; grid-template-columns: minmax(220px, 1.3fr) minmax(190px, .8fr) auto; align-items: end; gap: 10px; margin-top: 24px; }
.backtest-form label { display: flex; flex-direction: column; gap: 6px; color: var(--ink-dim); font: 10px var(--mono); text-transform: uppercase; letter-spacing: .05em; }
.backtest-form input { width: 100%; min-height: 43px; border: 1px solid var(--rule); border-radius: var(--radius-sm); outline: 0; background: var(--chrome-1); color: var(--ink-bright); padding: 10px 12px; font: 13px var(--mono); }
.backtest-form input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent); }
#backtestDate { color-scheme: dark; }
[data-mode="light"] #backtestDate { color-scheme: light; }
#backtestRun { min-height: 43px; padding: 0 17px; border: 0; border-radius: var(--radius-sm); background: var(--accent); color: var(--accent-ink); font-weight: 700; cursor: pointer; }
#backtestRun:disabled { opacity: .55; cursor: progress; }
.backtest-caveat { margin: 9px 0 0; color: var(--ink-dim); font: 10px/1.5 var(--mono); }
/* The valuation caveat has to carry more weight than a footnote without becoming a
   second accent: --up/--down are direction only and there is exactly one accent, so
   emphasis here is weight and ink, never hue. */
.backtest-caveat b { color: var(--ink-bright); font-weight: 600; }
.backtest-caveat details { margin-top: 6px; }
.backtest-caveat summary { cursor: pointer; }
#backtestProgress { display: none; margin-top: 20px; }
#backtestProgress.show { display: block; }
.backtest-progress-row { display: flex; justify-content: space-between; gap: 14px; margin-bottom: 7px; color: var(--ink-dim); font: 10px var(--mono); }
.backtest-progress-row b { color: var(--ink); }
.backtest-progress-track { height: 3px; overflow: hidden; background: var(--chrome-2); }
#backtestProgressFill { width: 0; height: 100%; background: var(--accent); transition: width .35s ease; }
#backtestProgress.error .backtest-progress-row, #backtestProgress.error .backtest-progress-row b { color: var(--down); }
#backtestProgress.error #backtestProgressFill { background: var(--down); }
.backtest-error { display: flex; flex-direction: column; gap: 5px; margin-top: 18px; padding: 13px 0; border-top: 1px solid var(--down); border-bottom: 1px solid var(--down); color: var(--down); font-size: 12px; }
.backtest-error span { color: var(--ink-dim); }
#backtestSnapshot, #backtestAi, #backtestOutcomes { margin-top: 38px; }
.backtest-section-head { display: flex; align-items: end; justify-content: space-between; gap: 18px; margin-bottom: 11px; }
.backtest-section-head > div > span { color: var(--accent); font: 10px var(--mono); text-transform: uppercase; letter-spacing: .08em; }
.backtest-section-head h2 { margin-top: 3px; color: var(--ink-bright); font: 500 var(--t-title)/1.15 var(--doc); }
.backtest-section-head h2 small { margin-left: 6px; color: var(--ink-dim); font: 500 11px var(--mono); }
.backtest-section-head > b { color: var(--ink-dim); font: 500 10px var(--mono); text-align: right; }
.backtest-integrity { margin: 0 0 12px; padding: 10px 12px; border-left: 2px solid var(--accent); background: var(--chrome-1); color: var(--ink-dim); font: 10px/1.55 var(--mono); }
.backtest-integrity b { color: var(--ink-bright); }
.backtest-panel { margin-top: 10px; padding: 17px; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); }
/* #backtestSetup is emitted by the snapshot template but only filled once there are
   enough pre-cutoff bars to chart. Without this, a short history leaves a bordered,
   padded, empty box where a chart belongs, which reads as a broken render. */
.backtest-panel:empty { display: none; }
.backtest-panel h3 { margin-bottom: 13px; color: var(--ink-bright); font: 600 11px var(--mono); text-transform: uppercase; letter-spacing: .055em; }
.backtest-stats { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--rule); border-left: 1px solid var(--rule); }
.backtest-stat { padding: 10px; border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.backtest-stat span, .backtest-fact > span { display: block; color: var(--ink-dim); font-size: 10px; }
.backtest-stat b { display: block; margin-top: 4px; color: var(--ink-bright); font: 600 13px var(--mono); }
.backtest-scores { display: grid; grid-template-columns: repeat(3, 1fr); gap: 13px 20px; margin-top: 18px; }
.backtest-score > div:first-child { display: flex; justify-content: space-between; gap: 8px; color: var(--ink-dim); font-size: 10px; }
.backtest-score b { color: var(--ink-bright); font: 600 10px var(--mono); }
.backtest-score > div:last-child { height: 2px; margin-top: 6px; background: var(--chrome-2); }
.backtest-score i { display: block; height: 100%; background: var(--accent); }
.backtest-facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.backtest-fact { min-width: 0; padding: 11px; border: 1px solid var(--rule); border-radius: var(--radius-sm); }
.backtest-fact > b { display: block; margin: 5px 0; color: var(--ink-bright); font: 600 14px var(--mono); }
.backtest-fact small { display: block; color: var(--ink-dim); font-size: 10px; line-height: 1.4; }
.backtest-fact details { margin-top: 8px; color: var(--ink-dim); font-size: 10px; }
.backtest-fact summary { cursor: pointer; }
.backtest-fact details span { display: block; margin-top: 4px; font-family: var(--mono); }
.backtest-filings { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px 20px; }
.backtest-filing { display: flex; align-items: baseline; gap: 9px; padding: 7px 0; border-bottom: 1px solid var(--rule); }
.backtest-filing b { min-width: 54px; color: var(--ink-bright); font: 600 10px var(--mono); }
.backtest-filing span, .backtest-empty { color: var(--ink-dim); font-size: 10px; }
.backtest-ai-panel { min-height: 230px; }
.backtest-ai-panel > details { float: right; width: min(320px, 40%); margin: 0 0 15px 22px; color: var(--ink-dim); font-size: 10px; }
.backtest-ai-panel > details summary { cursor: pointer; font-family: var(--mono); }
.backtest-ai-panel pre { max-height: 210px; overflow: auto; margin-top: 8px; white-space: pre-wrap; font: 10px/1.45 var(--mono); }
.backtest-ai-wait { display: flex; gap: 5px; align-items: center; min-height: 80px; }
.backtest-ai-wait span { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); animation: bt-pulse 1.2s infinite ease-in-out; }
.backtest-ai-wait span:nth-child(2) { animation-delay: .15s; }
.backtest-ai-wait span:nth-child(3) { animation-delay: .3s; }
@keyframes bt-pulse { 0%,80%,100% { opacity:.25; transform:translateY(0); } 40% { opacity:1; transform:translateY(-3px); } }
.backtest-outcome-entry { margin-bottom: 10px; color: var(--ink-dim); font: 10px var(--mono); }
.backtest-outcome-entry b { color: var(--ink-bright); }
.backtest-outcomes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-bottom: 12px; }
.backtest-outcome { padding: 16px; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); }
.backtest-outcome > span { display: block; color: var(--ink-dim); font: 10px var(--mono); text-transform: uppercase; }
.backtest-outcome > b { display: block; margin: 7px 0 5px; color: var(--ink-bright); font: 600 24px var(--mono); }
.backtest-outcome.positive > b { color: var(--up); }
.backtest-outcome.negative > b { color: var(--down); }
.backtest-outcome small, .backtest-outcome em { display: block; color: var(--ink-dim); font-size: 10px; font-style: normal; line-height: 1.45; }
.backtest-outcome em { margin-top: 5px; font-family: var(--mono); }

@media (max-width: 760px) {
  .backtest-shell { width: min(100% - 24px, 1120px); padding-top: 30px; }
  .backtest-intro { display: block; }
  .backtest-private { display: inline-block; margin-top: 12px; }
  .backtest-method, .backtest-form { grid-template-columns: 1fr; }
  .backtest-method > div + div { border-left: 0; border-top: 1px solid var(--rule); padding-left: 0; }
  .backtest-stats { grid-template-columns: repeat(2, 1fr); }
  .backtest-scores, .backtest-facts, .backtest-filings, .backtest-outcomes { grid-template-columns: 1fr; }
  .backtest-section-head { display: block; }
  .backtest-section-head > b { display: block; margin-top: 6px; text-align: left; }
  .backtest-ai-panel > details { float: none; width: 100%; margin: 0 0 16px; }
}
.backtest-chart-wrap { position: relative; width: 100%; height: 320px; margin-bottom: 10px; }
.backtest-chart-wrap canvas { width: 100%; height: 100%; display: block; }
.backtest-legend { display: flex; flex-wrap: wrap; gap: 16px; color: var(--ink-dim); font-size: 10px; font-family: var(--mono); }
/* Wide enough to show a full dash cycle — the swatch has to carry the pattern,
   because the three series are separated by weight and dash rather than hue. */
.backtest-legend i { display: inline-block; width: 24px; height: 2px; vertical-align: middle; margin-right: 5px; }
.backtest-curve-stats { margin-top: 14px; }
/* Mirrors .backtest-outcome.positive/.negative so a return means the same thing
   wherever it appears. Direction only — never used to make a value stand out. */
.backtest-stat.positive b { color: var(--up); }
.backtest-stat.negative b { color: var(--down); }
.backtest-curve-note { margin-top: 9px; color: var(--ink-dim); font: 10px var(--mono); line-height: 1.5; }
.backtest-curve-note b { color: var(--ink-bright); font-weight: 600; }
.backtest-thesis { margin-top: 12px; color: var(--ink); font-family: var(--doc); font-size: 13px; line-height: 1.6; }
@media (max-width: 640px) { .backtest-chart-wrap { height: 240px; } }
.screen-shell { width: min(1120px, 100%); margin: 0 auto; }
.screen-kicker { color: var(--ink-dim); font-size: var(--t-micro); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.screen-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 18px; }
.screen-head h1 { margin: 4px 0 6px; color: var(--ink-bright); font-family: var(--mono); font-size: var(--t-title); font-weight: 600; letter-spacing: -.01em; line-height: var(--lh-tight); }
.screen-head p { max-width: 680px; color: var(--ink-dim); font-size: 13px; line-height: 1.55; }
#screenForm { position: relative; display: flex; gap: 10px; padding: 8px; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); }
#screenQuery { flex: 1; min-height: 54px; max-height: 150px; resize: vertical; border: 0; outline: 0; background: transparent; color: var(--ink-bright); font: 14px/1.5 var(--mono); padding: 8px 10px; }
#screenQuery::placeholder { color: var(--ink-dim); }
#screenRun { align-self: stretch; min-width: 118px; border: 0; border-radius: var(--radius-sm); background: var(--accent); color: var(--accent-ink); cursor: pointer; font-weight: 700; }
#screenRun:disabled { opacity: .5; cursor: progress; }
.screen-controls { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.screen-controls label { color: var(--ink-dim); font-size: 11px; font-weight: 650; }
#screenUniverse { min-width: 250px; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--chrome-1); color: var(--ink-bright); padding: 8px 30px 8px 10px; font: 12px var(--mono); }
.screen-examples { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 7px; margin: 10px 0 22px; }
.screen-example { border: 1px solid var(--rule); border-radius: var(--radius-sm); background: transparent; color: var(--ink-dim); padding: 8px 10px; cursor: pointer; font-size: 11px; text-align: left; }
.screen-example:hover { color: var(--ink-bright); border-color: var(--accent); background: var(--chrome-2); }
#screenProgress { display: none; margin: 18px 0; padding: 13px 14px; border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-1); }
#screenProgress.show { display: block; animation: riseIn .2s var(--ease-out); }
.screen-progress-row { display: flex; align-items: center; gap: 12px; }
#screenProgressText { flex: 1; color: var(--ink); font-size: 12px; }
#screenProgressPct { color: var(--accent); font: 600 11px var(--mono); min-width: 38px; text-align: right; }
.screen-progress-track { height: 6px; margin-top: 10px; overflow: hidden; border-radius: 4px; background: var(--chrome-2); }
#screenProgressFill { width: 0%; height: 100%; background: var(--accent); transition: width .35s var(--ease-out); }
#screenProgress.error #screenProgressText, #screenProgress.error #screenProgressPct { color: var(--down); }
#screenProgress.error #screenProgressFill { background: var(--down); }
#screenInterpretation:empty, #screenResults:empty { display: none; }
.recipe { margin: 18px 0; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); padding: 16px; }
.recipe-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.recipe h2 { color: var(--ink-bright); font-family: var(--mono); font-size: 15px; }
.recipe p { margin-top: 4px; color: var(--ink-dim); font-size: 12px; line-height: 1.55; }
.recipe-source { flex-shrink: 0; color: var(--ink); background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-xs); padding: 4px 7px; font: var(--t-micro) var(--mono); }
.recipe-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.recipe-chip { border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-2); padding: 7px 9px; }
.recipe-chip b { display: block; color: var(--ink-bright); font-size: 11px; }
.recipe-chip span { color: var(--ink-dim); font: var(--t-micro) var(--mono); }
.recipe-adjust { margin-top: 10px; color: var(--accent); font-size: 10.5px; }
/* A theme chip is fuzzy evidence (word matching over profile text), not a measured
   concept. The dashed rule says "approximate" and the dimmer label says "weaker evidence" —
   the hairline alone was too faint to carry the distinction on its own. */
.recipe-chip.theme-chip { border-style: dashed; }
.recipe-chip.theme-chip b { color: var(--ink); font-weight: 500; }
.recipe-adjust.theme-note { color: var(--ink-dim); }
.recipe-adjust.recipe-scorenote { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule); color: var(--ink-dim); }
.recipe-adjust.recipe-scorenote b { color: var(--ink); } .recipe-adjust.recipe-scorenote em { font-style: normal; color: var(--ink); }
.recipe-definitions { display: grid; grid-template-columns: repeat(auto-fit,minmax(240px,1fr)); gap: 6px; margin-top: 12px; }
.recipe-definition { border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--chrome-2); }
.recipe-definition summary { display: flex; justify-content: space-between; gap: 8px; padding: 8px 10px; color: var(--ink); cursor: pointer; font-size: 10.5px; }
.recipe-definition summary span { color: var(--ink-dim); font: var(--t-micro) var(--mono); text-transform: uppercase; }
.recipe-definition p { margin: 0; padding: 0 10px 10px; color: var(--ink-dim); font-family: var(--doc); font-size: var(--t-body); line-height: var(--lh-doc); }
.screen-results-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 24px 0 10px; }
.screen-results-head h2 { color: var(--ink-bright); font-family: var(--mono); font-size: 16px; }
.screen-results-head span { color: var(--ink-dim); font: 10px var(--mono); }
.screen-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 11px; }
.screen-result { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); padding: 14px; }
.screen-result-top { display: flex; align-items: flex-start; gap: 10px; }
.screen-symbol { border: 0; border-bottom: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); background: none; color: var(--accent); border-radius: 0; padding: 4px 0; cursor: pointer; font: 700 12px var(--mono); }
.screen-symbol:hover { color: var(--ink-bright); border-bottom-color: var(--ink-bright); }
.screen-name { min-width: 0; flex: 1; }
.screen-name b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-bright); font-size: 12.5px; }
.screen-name span { color: var(--ink-dim); font-size: 10px; }
/* Match ring — conic arc fills 0→score on entrance (app.js drives --ring + a count-up) */
.match-score { display: grid; place-items: center; width: 46px; height: 46px; flex-shrink: 0; border-radius: 50%;
  background: radial-gradient(closest-side, var(--chrome-1) calc(100% - 4px), transparent calc(100% - 3px)),
              conic-gradient(var(--accent) var(--ring, 100%), var(--chrome-2) 0);
  color: var(--ink-bright); font: 700 12px var(--mono); }
.screen-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; margin: 11px 0; }
.screen-metric { padding: 6px; border-radius: var(--radius-sm); background: var(--chrome-2); }
.screen-metric small { display: block; color: var(--ink-dim); font-size: var(--t-micro); text-transform: uppercase; }
.screen-metric b { color: var(--ink); font: 10.5px var(--mono); }
.screen-reasons { display: flex; flex-direction: column; gap: 5px; }
.screen-reason { color: var(--ink-dim); font-size: 10.5px; line-height: 1.4; }
.screen-reason::before { content: "✓"; color: var(--up); margin-right: 6px; }
/* Per-card match-score breakdown — theme + concept sub-scores as mini bars */
.score-breakdown { display: flex; flex-direction: column; gap: 4px; margin: 2px 0 11px; }
.sb-head { color: var(--ink-dim); font: 700 8px var(--mono); letter-spacing: .1em; text-transform: uppercase; margin-bottom: 2px; cursor: help; }
.sb-row { display: grid; grid-template-columns: 78px 1fr 22px; align-items: center; gap: 8px; }
.sb-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-dim); font-size: var(--t-micro); }
.sb-bar { height: 4px; border-radius: 99px; background: var(--chrome-2); overflow: hidden; }
.sb-bar b { display: block; height: 100%; border-radius: inherit; background: var(--accent); }
.sb-val { text-align: right; color: var(--ink); font: 10px var(--mono); }
.sb-row.theme .sb-label { color: var(--ink-bright); font-weight: 600; }
.sb-row.theme .sb-bar b { background: var(--ink-dim); }
.screen-empty { padding: 34px; text-align: center; color: var(--ink-dim); border: 1px dashed var(--rule); border-radius: var(--radius); }
.screen-empty b, .screen-empty span { display: block; }.screen-empty span { margin-top: 6px; font-size: 11px; }
.screen-refine { margin-top: 24px; border: 1px solid var(--rule); border-radius: var(--radius); background: var(--chrome-1); overflow: hidden; }
.screen-refine-head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; padding: 15px 16px; border-bottom: 1px solid var(--rule); }
.screen-refine-head h2 { color: var(--ink-bright); font-family: var(--mono); font-size: 15px; }.screen-refine-head p { margin-top: 3px; color: var(--ink-dim); font-size: 10.5px; }
.screen-refine-head > span { flex-shrink: 0; color: var(--ink-dim); font: var(--t-micro) var(--mono); }
.screen-chat-messages { max-height: 290px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; }
.screen-chat-msg { max-width: min(720px,88%); padding: 9px 11px; border-radius: var(--radius); background: var(--chrome-2); }
/* Same idiom as the main chat: the user's turn is the accent block, the model's is the
   plain surface. Violet used to carry this; without it the two bubbles were identical. */
.screen-chat-msg.user { align-self: flex-end; background: var(--accent); }
.screen-chat-msg.user span, .screen-chat-msg.user p { color: var(--accent-ink); }
.screen-chat-msg.assistant { align-self: flex-start; }
.screen-chat-msg span { display: block; margin-bottom: 3px; color: var(--ink-dim); font: var(--t-micro) var(--mono); text-transform: uppercase; }.screen-chat-msg p { color: var(--ink); font-size: 11px; line-height: 1.5; }
.screen-refine-prompts { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 16px 11px; }
.screen-refine-prompts button { border: 1px solid var(--rule); border-radius: var(--radius-xs); background: transparent; color: var(--ink-dim); padding: 6px 9px; cursor: pointer; font-size: 10px; }.screen-refine-prompts button:hover { color: var(--ink-bright); border-color: var(--accent); background: var(--chrome-2); }
.screen-refine-form { display: flex; gap: 8px; padding: 11px; border-top: 1px solid var(--rule); background: var(--chrome-2); }.screen-refine-form input { flex: 1; min-width: 0; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--chrome-0); color: var(--ink-bright); padding: 9px 10px; outline: 0; font-size: 11px; transition: border-color .2s, box-shadow .2s; }.screen-refine-form input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }.screen-refine-form button { border: 0; border-radius: var(--radius-sm); background: var(--accent); color: var(--accent-ink); padding: 0 13px; cursor: pointer; font-weight: 700; font-size: 10.5px; transition: filter .15s; }.screen-refine-form button:hover:not(:disabled) { filter: brightness(1.08); }.screen-refine-form button:disabled { opacity: .5; }

/* ═══ SCREENER MOTION ═══ */
/* View entrance */
.screen-example { transition: color .15s, border-color .15s, background .15s; }
#screenForm { transition: border-color .25s, box-shadow .25s; }
#screenForm:focus-within { border-color: var(--accent); }
#screenRun { transition: filter .15s, background .15s; }
#screenRun:hover:not(:disabled) { filter: brightness(1.08); }

/* Results */
.screen-result { position: relative; overflow: hidden; transition: border-color .2s; }
.screen-result:focus-within { border-color: var(--accent); }
.screen-symbol { transition: color .15s, border-color .15s; }
/* Breakdown bars grow from zero after the card lands (from-only keyframe → animates to inline width) */
.sb-bar b { animation: sbGrow .7s var(--ease-out) backwards; animation-delay: calc(min(var(--i, 0), 14) * .05s + .25s); }
@keyframes sbGrow { from { width: 0; } }

/* Scroll reveal — a screen can return 50+ matches, so most of the render-time cascade
   above used to play (and count its score ring up) far below the fold, where nobody saw
   it. app.js tags anything past the first screenful with .screen-reveal instead; it holds
   still until scrolled to, then settles. Deliberately understated: one short fade and
   rise, no movement large enough to read as an effect. */
.screen-reveal { opacity: 0; transform: translateY(14px);
  transition: opacity .55s var(--ease-out), transform .55s var(--ease-out); }
.screen-reveal.in { opacity: 1; transform: none; }
/* The reveal supersedes the render-time entrance for these elements — running both
   would animate the card twice. */
.screen-result.screen-reveal, .screen-refine.screen-reveal { animation: none; }
/* Breakdown bars hold at zero (over the inline width) until the card lands, then grow. */
.screen-result.screen-reveal .sb-bar b { animation: none; transition: width .7s var(--ease-out) .14s; }
.screen-result.screen-reveal:not(.in) .sb-bar b { width: 0 !important; }

/* Refine panel: chat messages share the analyzer's msgIn; prompt chips lift */
.screen-chat-msg { animation: msgIn .3s var(--ease) backwards; }
.screen-refine-prompts button { transition: color .15s, border-color .15s, background .15s; }

/* MAIN SPLIT */
#workspace { flex: 1; display: none; min-height: 0; }
#workspace.show { display: block; animation: wsIn .4s var(--ease-out); }
#workspace.leaving { animation: wsOut .3s var(--ease) forwards; }
@keyframes wsIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes wsOut { to { opacity: 0; transform: translateY(8px) scale(.997); } }
#hero.leaving { animation: wsOut .28s var(--ease) forwards; }
/* The rail is a real grid column, so it costs width and no height. The old .pane-head
   bands are gone entirely: the rail already names what the left pane is showing, and the
   right pane's "AI ANALYSIS · <model>" is now a tag riding the top of the prose itself. */
#split { display: grid; height: 100%; grid-template-columns: var(--rail-w) minmax(300px, var(--left-w, 50%)) 10px minmax(340px, 1fr); }
.pane { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.pane-body { flex: 1; overflow-y: auto; padding: 16px 18px 40px; scroll-behavior: smooth; }

/* WORKSPACE RAIL */
#viewRail { display: flex; flex-direction: column; gap: 1px; min-width: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 10px 8px; background: var(--chrome-1); border-right: 1px solid var(--rule); }
#viewRail::-webkit-scrollbar { display: none; }
#viewRail button { position: relative; flex: 0 0 auto; text-align: left; background: none; border: 0; cursor: pointer;
  padding: 8px 10px; border-radius: var(--radius-xs); font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .04em; line-height: 1.35; overflow-wrap: break-word; color: var(--ink-dim);
  transition: color .15s var(--ease), background .15s var(--ease); }
#viewRail button:hover { color: var(--ink-bright); background: var(--chrome-hi); }
/* Selected state is the accent rule plus bright ink, never a fill on its own: in Daylight
   --chrome-1 and --chrome-2 sit within ~1.2:1 and a tint carries no affordance at all. */
#viewRail button.active { color: var(--ink-bright); background: var(--chrome-2); box-shadow: inset 2px 0 0 var(--accent); }
#viewRail button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#viewRail button[data-group-start] { margin-top: 9px; padding-top: 15px; border-top: 1px solid var(--rule); }
/* Kept from #mobileTabs: while the write-up streams and you are reading numbers, this is
   the only thing that says so. */
#viewRail .tab-dot { display: none; width: 6px; height: 6px; margin-left: 6px; border-radius: 50%;
  background: var(--accent); animation: dotPulse 1.2s ease-in-out infinite; vertical-align: middle; }
#viewRail button.streaming .tab-dot { display: inline-block; }

/* FOCUS MODE — one destination, whole workspace. Triggered by either axis, because a wide
   but short window (1280×720 is the common one) was the case the old width-only breakpoint
   missed: it kept the split and handed the analysis a 228px reading slot. */
body.focus-mode #split { grid-template-columns: var(--rail-w) 1fr; }
body.focus-mode #resizer { display: none; }
body.focus-mode .pane { grid-column: 2; grid-row: 1; height: 100%; }
body.focus-mode .pane[data-hidden] { display: none; }
body.focus-mode .pane:not([data-hidden]) { animation: paneIn .24s var(--ease-out); }
@keyframes paneIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* Split resizer — wide hit area, rAF-driven, live % badge, touch-friendly */
#resizer { cursor: col-resize; position: relative; background: transparent; touch-action: none;
  z-index: 20; margin: 0 -7px; padding: 0 7px; }
#resizer::before { content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--rule); transform: translateX(-50%); transition: background .2s, width .2s; }
#resizer:hover::before, #resizer.dragging::before { background: var(--accent); width: 3px; border-radius: 2px; }
#resizer::after { content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 4px; height: 44px; border-radius: 99px; background: var(--rule); transition: background .2s, height .25s var(--ease-spring); }
#resizer:hover::after, #resizer.dragging::after { background: var(--accent); height: 58px; }
.rz-badge { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -160%); z-index: 30;
  background: var(--chrome-2); border: 1px solid var(--rule); color: var(--ink-bright); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 11px; font-weight: 600; padding: 3px 9px; pointer-events: none;
  opacity: 0; transition: opacity .15s; white-space: nowrap; box-shadow: var(--shadow); }
#resizer.dragging .rz-badge { opacity: 1; }
body.resizing, body.resizing * { cursor: col-resize !important; user-select: none !important; }
body.resizing-y, body.resizing-y * { cursor: row-resize !important; user-select: none !important; }
body.resizing .pane *, body.resizing-y #chatDock * { transition: none !important; }

@keyframes dotPulse { 0%,100% { opacity: .35; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.15); } }
@media (max-width: 960px) {
  /* Pane switching is no longer a breakpoint concern — body.focus-mode owns it, and it
     watches height too. What is left here is genuinely about a narrow window. */
  header { padding: 0 14px; } #ticker { font-size: 16px; } .wordmark small { display: none; }
  #chatInput { font-size: 16px; }
  #chatRow { padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
  #chatGrip { height: 20px; } #chatGrip::before { width: 56px; height: 5px; }
  .pane-body { padding: 14px 14px 40px; }
  #rangeSel button { padding: 6px 11px; }
  .toggle { padding: 6px 12px 6px 10px; }
}
/* The header sheds labels one at a time as it narrows, rather than all at once. Each
   step hands its width to the search field, which is the bar's real job — dropping them
   together left a band around 560px where the field collapsed to ~120px. */
@media (max-width: 900px) { #analyzeBtn { width: 30px; padding: 0; justify-content: center; } #analyzeBtn .btn-label { display: none; } }
@media (max-width: 720px) {
  /* Rail collapses to icons — the labels go, the controls stay. */
  #profileBtn span, #screenerNav span, #portfolioNav span, #savedBtn span, #watchBtn span, #accountBtn span { display: none; }
  .util-rail > button { width: 38px; padding: 0; }
  /* The search box sits far right in a crowded header, so a dropdown anchored to it
     overhangs the left edge of the screen. Anchor to the viewport instead. */
  #tickerSuggest { position: fixed; top: calc(var(--header-h) + 6px); right: 12px; left: auto;
    width: min(340px, calc(100vw - 24px)); min-width: 0; max-width: none; }
}
@media (max-width: 600px) { .wordmark .wm-title { display: none; } }
@media (max-width: 520px) {
  /* 16px for the same reason as #ticker: anything smaller makes iOS zoom the page on focus. */
  #heroTicker { font-size: 16px; padding-left: 40px; } .hero-search { height: 48px; }
  .hero-search .search-ico { left: 15px; }
  #profileModal { padding: 0; place-items: stretch; } #profilePanel { max-height: 100dvh; height: 100dvh; width: 100%; border-radius: 0; }
  .profile-grid { grid-template-columns: 1fr; } .profile-wide { grid-column: auto; }
  #screenForm { flex-direction: column; } #screenRun { min-height: 44px; }
  .screen-head { display: block; } .screen-grid { grid-template-columns: 1fr; }
  .screen-examples { grid-template-columns: 1fr 1fr; }
  .screen-controls { align-items: stretch; flex-direction: column; gap: 4px; }
  #screenUniverse { min-width: 0; width: 100%; }
}
/* Smallest phones (320–400px): the header used to overflow and scroll the page
   sideways. Tighten the housings until the whole bar fits. */
@media (max-width: 400px) {
  header { gap: 7px; padding: 0 10px; }
  #ticker { padding-left: 27px; letter-spacing: .02em; }
  .search-ico { left: 9px; width: 13px; height: 13px; }
  .util-rail > button { width: 33px; }
  .util-rail > button svg { width: 14px; height: 14px; }
  #themeBtn { width: 33px; }
}

/* PHONE LAYOUT
   Phones are not a narrower desktop. The desktop rail spends width because height is the
   scarce axis; on a phone that trade is backwards. The same destinations become a bottom
   dock, the active pane gets the full width, and the crowded header becomes two deliberate
   rows once there is work on screen. No information or action is removed. */
@media (max-width: 600px) {
  :root { --header-h: 100px; --mobile-nav-h: 56px; --rail-w: 0px; }

  body > header {
    height: var(--header-h); padding: 7px 10px 8px; gap: 7px;
    display: grid; grid-template-columns: 34px minmax(0, 1fr) auto;
    grid-template-rows: 36px 42px;
  }
  body > header .wordmark { grid-column: 1; grid-row: 1; width: 34px; height: 34px; margin: 0; padding: 3px; justify-content: center; }
  body > header .wordmark .wind { width: 27px; height: 24px; }
  body > header .wordmark .wm-title, body > header .wordmark small { display: none; }
  body > header #tickerBar {
    grid-column: 2; grid-row: 1; margin: 0; padding: 0; border: 0; gap: 7px;
    align-items: center; justify-self: start;
  }
  body > header #searchForm { grid-column: 1 / -1; grid-row: 2; width: 100%; max-width: none; margin: 0; }
  body > header .ticker-wrap { height: 42px; }
  body > header .util-rail { grid-column: 3; grid-row: 1; height: 34px; justify-self: end; }
  body > header .util-rail > button { min-width: 36px; width: 36px; }
  body > header #themeBtn { width: 36px; }

  /* The landing page has no header search or ticker identity. Keep that state to one calm
     row and give the recovered width back to the wordmark. */
  body.hero-up > header { height: 52px; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: 36px; padding-block: 8px; }
  body.hero-up > header .wordmark { grid-column: 1; width: auto; justify-content: flex-start; padding-inline: 5px; }
  body.hero-up > header .wordmark .wm-title { display: inline; font-size: 16px; }
  body.hero-up > header .util-rail { grid-column: 2; }

  /* The standalone 404 has no utility rail or global search. It keeps the simple one-row
     identity header instead of reserving an empty second row for controls it does not load. */
  body[data-page="notfound"] > header { height: 52px; display: flex; padding: 8px 10px; }
  body[data-page="notfound"] > header .wordmark { width: auto; height: 34px; justify-content: flex-start; padding-inline: 5px; }
  body[data-page="notfound"] > header .wordmark .wm-title { display: inline; font-size: 16px; }

  #tickerSuggest { top: calc(var(--header-h) + 6px); right: 10px; width: calc(100vw - 20px); }
  #savedMenu, #themeMenu, #watchMenu, #accountMenu { position: fixed; top: calc(var(--header-h) + 6px); right: 10px; max-width: calc(100vw - 20px); }
  body.hero-up #savedMenu, body.hero-up #themeMenu, body.hero-up #watchMenu, body.hero-up #accountMenu { top: 58px; }

  #progressWrap.show { height: 42px; }
  #progressInner { gap: 9px; padding: 0 12px; }
  #progressMeta { min-width: 0; overflow: hidden; }
  #progressMeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #progressTrack { min-width: 48px; }
  #progressPct { min-width: 34px; }

  #hero { justify-content: flex-start; gap: 16px; padding: max(42px, 9dvh) 16px 28px; overflow-y: auto; }
  #hero .wind { width: 54px; height: 44px; }
  #hero h1 { font-size: clamp(31px, 12vw, 42px); }
  #hero p { font-size: var(--t-lead); }
  #hero .hero-fine { font-size: var(--t-data); line-height: 1.5; }
  #heroAnalyzeBtn { min-width: 44px; padding-inline: 13px; }
  .hero-actions { width: 100%; }
  .hero-actions .chip { min-height: 40px; display: inline-flex; align-items: center; justify-content: center; }

  #workspace { min-height: 0; }
  body.focus-mode #split { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  body.focus-mode .pane { grid-column: 1; grid-row: 1; min-height: 0; }
  body.focus-mode #resizer { display: none; }
  #viewRail {
    grid-column: 1; grid-row: 2; min-width: 0; min-height: var(--mobile-nav-h);
    flex-direction: row; align-items: stretch; gap: 2px; overflow-x: auto; overflow-y: hidden;
    padding: 6px max(8px, env(safe-area-inset-right, 0px)) calc(6px + env(safe-area-inset-bottom, 0px)) max(8px, env(safe-area-inset-left, 0px));
    border-right: 0; border-top: 1px solid var(--rule); scroll-snap-type: x proximity;
  }
  #viewRail button {
    min-width: max-content; min-height: 42px; padding: 0 14px; text-align: center;
    white-space: nowrap; overflow-wrap: normal; font-size: var(--t-data); scroll-snap-align: center;
  }
  #viewRail button.active { box-shadow: inset 0 2px 0 var(--accent); }
  #viewRail button[data-group-start] { margin: 0 0 0 6px; padding: 0 14px 0 20px; border-top: 0; border-left: 1px solid var(--rule); }

  .pane-body { padding: 12px 12px 22px; }

  #screenerView { padding: 20px 12px calc(40px + env(safe-area-inset-bottom, 0px)); }
  .screen-head { margin-bottom: 15px; }
  .screen-head h1 { font-size: var(--t-title); }
  #screenForm { padding: 8px; border-radius: var(--radius); }
  #screenQuery { min-height: 96px; padding: 8px; font-size: 16px; }
  #screenRun { min-height: 46px; }
  .screen-examples { display: flex; grid-template-columns: none; gap: 8px; overflow-x: auto; padding-bottom: 4px; scroll-snap-type: x proximity; }
  .screen-example { flex: 0 0 min(76vw, 270px); min-height: 44px; scroll-snap-align: start; }
  .recipe, .screen-result { padding: 13px 12px; }
  .recipe-top, .screen-results-head, .screen-refine-head { display: block; }
  .recipe-source, .screen-results-head span, .screen-refine-head > span { display: inline-block; margin-top: 7px; }
  .recipe-definitions { grid-template-columns: 1fr; }
  .screen-metrics { grid-template-columns: 1fr; gap: 0; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
  .screen-metric { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 7px 0; border-radius: 0; background: transparent; }
  .screen-metric + .screen-metric { border-top: 1px solid var(--rule); }
  .screen-metric small { text-transform: none; }
  .screen-metric b { text-align: right; }
  .screen-chat-msg { max-width: 94%; }
  .screen-refine-form input { min-height: 42px; font-size: 16px; }
  .screen-refine-form button { min-height: 42px; }

  #profilePanel { padding-bottom: env(safe-area-inset-bottom, 0px); }
  .profile-head { padding: calc(14px + env(safe-area-inset-top, 0px)) 14px 12px; }
  #profileForm { padding: 14px; }
  .profile-field { padding: 12px; }
  .profile-actions button { min-height: 42px; }

  .backtest-shell { width: calc(100% - 24px); padding: 26px 0 calc(48px + env(safe-area-inset-bottom, 0px)); }
  .backtest-intro h1 { font-size: clamp(31px, 10vw, 42px); }
  .backtest-form input, #backtestRun { min-height: 46px; font-size: 16px; }
  .backtest-panel { padding: 13px 12px; }
}

@media (max-width: 380px) {
  body > header { grid-template-columns: 32px minmax(0, 1fr) auto; padding-inline: 8px; }
  body > header .wordmark { width: 32px; }
  body > header .util-rail > button, body > header #themeBtn { min-width: 33px; width: 33px; }
  #sPrice { font-size: var(--t-body); }
  .pill { font-size: var(--t-micro); padding-inline: 6px; }
  .screen-refine-form { flex-direction: column; }
  .screen-refine-form button { width: 100%; }
}

/* CARDS */
/* The horizontal #dataSections tablist is gone — its job moved to #viewRail, which is a
   vertical desktop rail and a bottom phone dock. What remains here is the panel itself. */
.data-section { display: none; }
/* A metric row is a label and a value sharing an axis; at the ~1150px a focus-mode pane
   now offers, that axis is far enough apart that the eye stops connecting the two. The
   extra width is a gain for the chart and for prose, and a loss here, so the cards keep a
   measure. Left-aligned, not centred: the rail is the left edge of the reading column. */
.data-section.show { display: block; max-width: 960px; }
/* The chart destination is alone in its panel and takes the region's height rather than a
   constant. #dataBody has to stop being a scroll container for it, or the fluid #chartBox
   inside resolves against a scrollHeight that grows to fit it — a feedback loop. */
#dataBody:has(> .chart-section.show) { display: flex; flex-direction: column; overflow: hidden; padding-bottom: 16px; }
/* The chart is the exception — it is a picture, and a picture wants the width. */
.data-section.chart-section.show { display: flex; flex-direction: column; flex: 1; min-height: 0; max-width: none; }

.card { background: none; border: 0; border-top: 1px solid var(--rule); border-radius: 0; box-shadow: none;
  margin-bottom: 0; overflow: hidden; transition: none; }
.card:first-of-type { border-top: 0; }
.card > summary { list-style: none; cursor: pointer; user-select: none; display: flex; align-items: center; gap: 9px; padding: 12px 16px;
  font-family: var(--mono); font-size: var(--t-body); font-weight: 500; letter-spacing: 0; text-transform: lowercase; color: var(--ink-bright); transition: background .15s; }
.card > summary::-webkit-details-marker { display: none; }
/* Cards are transparent now, so the hover sits on the page (--chrome-0), not on a
   card fill. --chrome-2 against --chrome-0 measures 1.03 in Daylight, i.e. invisible. */
.card > summary:hover { background: var(--chrome-hi); }
/* Daylight's surfaces sit within ~1.2:1 of each other, so a background tint alone is a weak
   affordance there. The chevron brightening is the signal that survives in every theme. */
.card > summary:hover .chev { stroke: var(--ink-bright); }
.card > summary .chev { margin-left: auto; width: 14px; height: 14px; stroke: var(--ink-dim); transition: transform .25s var(--ease); }
.card[open] > summary .chev { transform: rotate(180deg); }
.card > summary .count { font-family: var(--mono); font-size: 10px; font-weight: 500; color: var(--ink-dim); background: var(--chrome-2); border-radius: var(--radius-xs); padding: 1px 7px; letter-spacing: 0; }
/* Extraction provenance — names the pipeline a card's numbers came out of (SEC EDGAR's
   filing read vs the yahooquery market pull). Small and low-contrast on purpose: it is a
   footnote, not a heading. */
.card > summary .src { font-family: var(--mono); font-size: var(--t-micro); font-weight: 500; letter-spacing: .03em;
  text-transform: none; padding: 1px 6px; border-radius: var(--radius-xs); border: 1px solid transparent;
  white-space: nowrap; flex-shrink: 0; }                     /* a narrow pane wraps the title, not this */
/* The two pipelines are told apart by treatment, not hue: the market pull is accent-tinted,
   the filing read is a plain ruled chip. One accent, two readable states. */
.card > summary .src[data-src="sec"] { color: var(--ink-dim);
  border-color: var(--rule); background: var(--chrome-2); }
.card > summary .src[data-src="market"] { color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 32%, transparent); background: var(--accent-soft); }
@media (max-width: 560px) { .card > summary .src { display: none; } }   /* title needs the room */
.card-body { padding: 4px 16px 16px; }
.card[open] .card-body { animation: bodyIn .3s var(--ease); }
@keyframes bodyIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* Rows, not tiles: values share a right-hand axis so a reader can compare down a
   column. Tiles made that impossible, which is the whole reason for the change. */
.mgrid { display: block; }
.metric { display: flex; align-items: baseline; gap: 10px; min-height: 22px; padding: 2px 0; }
.metric + .metric { border-top: 1px solid var(--rule); }
.metric .k { flex: 1; min-width: 0; font-size: var(--t-data); font-weight: 400; color: var(--ink-dim);
  text-transform: lowercase; letter-spacing: 0; }
.metric .v { flex-shrink: 0; font-family: var(--mono); font-size: var(--t-data); font-weight: 500;
  color: var(--ink-bright); font-variant-numeric: tabular-nums; text-align: right; }
.metric .v.green { color: var(--up); } .metric .v.red { color: var(--down); } .metric .v.amber { color: var(--warn); }
.metric .v small { font-size: var(--t-micro); color: var(--ink-dim); font-weight: 400; }
/* Narrow panes: the left pane drags to 360px, where a shared axis stops working. */
@media (max-width: 460px) { .metric { flex-direction: column; gap: 0; align-items: stretch; }
  .metric .v { text-align: left; } }
#dataPane.stack-metrics .metric { flex-direction: column; gap: 0; align-items: stretch; }
#dataPane.stack-metrics .metric .v { text-align: left; }
.regime-summary { border: 1px solid var(--rule); border-left: 3px solid var(--warn); background: var(--chrome-2); border-radius: var(--radius-sm); padding: 12px 14px; }
.regime-summary.green { border-left-color: var(--up); } .regime-summary.red { border-left-color: var(--down); }
.regime-summary > div:first-child { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.regime-summary span { font-family: var(--mono); font-size: 10px; color: var(--ink-dim); text-transform: uppercase; letter-spacing: .07em; }
.regime-summary strong { color: var(--ink-bright); font-family: var(--mono); font-size: 14px; }
.regime-summary p { margin: 8px 0 0; color: var(--ink); font-size: 12.5px; line-height: 1.55; }
.regime-confidence { display: flex; align-items: center; gap: 9px; margin-top: 9px; }
.regime-confidence i { display: block; flex: 1; height: 5px; overflow: hidden; border-radius: 99px; background: var(--rule); }
.regime-confidence i b { display: block; height: 100%; background: var(--warn); border-radius: inherit; }
.regime-evidence { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.regime-evidence span { padding: 5px 8px; border-radius: var(--radius-sm); background: var(--chrome-2); color: var(--ink-dim); font-size: 11px; }
.learn-note { margin: 10px 2px 0; color: var(--ink-dim); font-size: 11.5px; line-height: 1.55; }
.news-list { display: grid; gap: 8px; }
.news-item { padding: 11px 12px; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--chrome-2); }
.news-meta { display: flex; justify-content: space-between; gap: 10px; margin-bottom: 5px; color: var(--ink-dim); font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }
.news-item h4 { font-size: 13px; line-height: 1.4; font-weight: 650; }
.news-item h4 a, .news-item h4 span { color: var(--ink-bright); text-decoration: none; }
.news-item h4 a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.news-item p { margin-top: 5px; color: var(--ink); font-size: 12px; line-height: 1.5; }
/* Web-search digest extras. Tags are neutral chips; only direction takes --up/--down,
   because green and red mean direction and nothing else. */
.news-overview { margin: 0 2px 10px; color: var(--ink); font-family: var(--doc); font-size: var(--t-body); line-height: 1.55; }
.news-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.news-tag { padding: 2px 6px; border: 1px solid var(--rule); border-radius: var(--radius-sm); color: var(--ink-dim); font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }
.news-tag.up { color: var(--up); background: var(--up-soft); border-color: transparent; }
.news-tag.down { color: var(--down); background: var(--down-soft); border-color: transparent; }
.news-upcoming { display: grid; gap: 4px; margin-top: 10px; padding: 9px 12px; border: 1px dashed var(--rule); border-radius: var(--radius-sm); font-size: 12px; color: var(--ink); }
.news-upcoming > span { color: var(--ink-dim); font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }
.news-upcoming a { color: var(--ink); text-decoration: none; }
.news-upcoming a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

.news-loading-head { display: flex; align-items: center; gap: 10px; margin: 0 2px 10px; color: var(--ink-dim); font-family: var(--mono); font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .05em; }
.news-loading-head .wind { width: 22px; height: 19px; flex-shrink: 0; }
.news-loading-head time { margin-left: auto; font-variant-numeric: tabular-nums; }
.news-ghost { display: grid; gap: 7px; }
.news-ghost .skeleton::after { animation-delay: calc(var(--i) * .18s); }
.news-ghost-meta { width: 28%; height: 8px; }
.news-ghost-h { width: 78%; height: 11px; }
.news-ghost:nth-child(2) .news-ghost-h { width: 64%; }
.news-ghost-short { width: 52%; }
.news-fresh .news-list, .news-fresh .news-overview { animation: paneIn .35s var(--ease-out) backwards; }

.signal { display: block; font-family: var(--mono); font-size: 12px; line-height: 1.55; padding: 8px 12px; border-radius: var(--radius-sm); margin-bottom: 6px; border-left: 3px solid; transition: transform .15s var(--ease); }
.signal:hover { transform: translateX(2px); } .signal b { font-weight: 600; }
.signal.green { color: var(--up); background: var(--up-soft); border-color: var(--up); }
.signal.red { color: var(--down); background: var(--down-soft); border-color: var(--down); }
.signal.amber { color: var(--warn); background: var(--warn-soft); border-color: var(--warn); }
.signal.neutral { color: var(--ink); background: var(--chrome-2); border-color: var(--rule); }

.rangebar { margin: 6px 0 14px; }
.rangebar .rb-labels { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11px; color: var(--ink-dim); margin-bottom: 5px; }
.rangebar .rb-labels b { color: var(--ink); font-weight: 600; }
.rb-track { position: relative; height: 6px; border-radius: 99px; background: var(--chrome-2); }
.rb-fill { position: absolute; top: 0; bottom: 0; border-radius: 99px; background: var(--accent); opacity: .35; }
.rb-marker { position: absolute; top: 50%; width: 13px; height: 13px; border-radius: 50%; background: var(--accent); border: 2.5px solid var(--chrome-1); transform: translate(-50%,-50%); box-shadow: 0 0 0 3px var(--accent-soft); transition: left .8s var(--ease); }
.rb-marker.alt { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); width: 10px; height: 10px; }
.rb-title { font-size: 10px; font-weight: 600; color: var(--ink-dim); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 4px; }

.levels { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.lvl { font-family: var(--mono); font-size: 12px; font-weight: 500; border-radius: var(--radius-xs); padding: 4px 10px; }
.lvl.res { color: var(--down); background: var(--down-soft); } .lvl.sup { color: var(--up); background: var(--up-soft); }
.lvl-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-dim); margin: 8px 0 4px; }

.tbl-wrap { overflow-x: auto; border: 1px solid var(--rule); border-radius: var(--radius-sm); }
table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12px; white-space: nowrap; }
th { text-align: right; font-weight: 600; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim); background: var(--chrome-2); padding: 7px 11px; position: sticky; top: 0; }
th:first-child, td:first-child { text-align: left; }
td { text-align: right; padding: 6px 11px; border-top: 1px solid var(--rule); color: var(--ink); }
td.hi { color: var(--ink-bright); font-weight: 500; }
tr.itm td { background: var(--accent-soft); } tr:hover td { background: var(--chrome-2); }
td.pos { color: var(--up); } td.neg { color: var(--down); }
.itm-badge { font-size: var(--t-micro); font-weight: 600; color: var(--accent); background: var(--accent-soft); border-radius: 4px; padding: 1px 5px; margin-left: 6px; }

.opt-exp { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 14px 0 8px; font-family: var(--mono); font-size: 12px; }
.opt-exp:first-child { margin-top: 4px; } .opt-exp b { color: var(--ink-bright); font-size: 13px; font-weight: 600; } .opt-exp span { color: var(--ink-dim); }
.opt-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 1400px) { .opt-pair { grid-template-columns: 1fr; } }
.opt-side-label { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; margin: 0 0 5px 2px; }
.opt-side-label.calls { color: var(--up); } .opt-side-label.puts { color: var(--down); }

.prose { color: var(--ink); font-size: 13.5px; line-height: 1.75; }
.prose, .learn-note, .screen-chat-msg p, #hero p, .prose blockquote {
  font-family: var(--doc); font-size: var(--t-doc); line-height: var(--lh-doc); }
.prose { max-width: var(--measure); overflow-wrap: anywhere; }
.prose h1, .prose h2, .prose h3 { font-family: var(--doc); font-size: var(--t-doc-h); line-height: 1.3; }
.prose h2 { font-family: var(--doc); font-size: var(--t-doc-h); font-weight: 600; color: var(--ink-bright); margin: 22px 0 8px; padding-bottom: 6px; border-bottom: 1px solid var(--rule); }
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-family: var(--doc); font-size: var(--t-doc); font-weight: 700; color: var(--accent); margin: 16px 0 6px; }
.prose p { margin-bottom: 11px; } .prose ul, .prose ol { padding-left: 20px; margin-bottom: 11px; } .prose li { margin-bottom: 5px; }
.prose strong { color: var(--ink-bright); font-weight: 600; }
.prose code { font-family: var(--mono); font-size: 12px; background: var(--chrome-2); border-radius: var(--radius-xs); padding: 1px 5px; }
.prose hr { border: none; border-top: 1px solid var(--rule); margin: 16px 0; }
.prose table { font-family: var(--mono); font-size: 12px; margin-bottom: 12px; }
.prose blockquote { border-left: 3px solid var(--accent); padding: 2px 0 2px 12px; color: var(--ink-dim); margin-bottom: 11px; font-style: italic; }

pre.raw { font-family: var(--mono); font-size: 11.5px; line-height: 1.6; background: var(--chrome-0); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); padding: 14px; overflow: auto; max-height: 420px; white-space: pre-wrap; word-break: break-word; color: var(--ink); }
.copy-btn { display: inline-flex; align-items: center; gap: 6px; background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  color: var(--ink); font-size: 11px; font-weight: 600; cursor: pointer; padding: 5px 12px; margin-bottom: 8px; transition: color .2s, border-color .2s; }
.copy-btn:hover { color: var(--accent); border-color: var(--accent); }

/* CHART */
/* One row, never two. `flex-wrap: wrap` plus eight inline toggles is what turned this into
   93px above a 360px canvas; the toggles moved into #overlayMenu and the row is now the
   things you act on while reading the chart, plus the two labels that say what it is. */
/* No overflow on the row itself: a scroll container on x forces y to compute to auto as
   well, which would clip #overlayMenu — a popover anchored inside it. The elastic part is
   the range selector, so that is where the sideways scroll goes. */
#chartControls { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; margin-bottom: 10px; flex-shrink: 0; min-width: 0; }
.overlay-wrap { position: relative; flex-shrink: 0; }
#overlayBtn { display: inline-flex; align-items: center; gap: 5px; }
#overlayBtn .chev { width: 11px; height: 11px; transition: transform .25s var(--ease); }
#overlayBtn[aria-expanded="true"] { color: var(--ink-bright); border-color: var(--ink-dim); }
#overlayBtn[aria-expanded="true"] .chev { transform: rotate(180deg); }
/* Reports the chart's state now that the switches are behind a door. */
.overlay-count { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--radius-xs); padding: 0 5px; }
.overlay-count:empty { display: none; }
#overlayMenu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60; display: none;
  width: 232px; max-width: calc(100vw - 24px); max-height: 62dvh; overflow-y: auto; overscroll-behavior: contain;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); padding: 6px; }
#overlayMenu.open { display: flex; flex-direction: column; align-items: stretch; gap: 3px; animation: sugIn .14s var(--ease-out); }
/* Inside the menu the toggles are rows, not pills — a stack of pill-shaped chips reads as
   a filter bar rather than a settings list. */
#overlayMenu .toggle { justify-content: flex-start; border: 0; background: none; border-radius: var(--radius-xs); padding: 6px 9px; }
#overlayMenu .toggle:hover { background: var(--chrome-hi); }
#overlayMenu .toggle.on { background: var(--chrome-2); }
#overlayMenu .menu-head { padding: 7px 9px 3px; }
#overlayMenu .chart-guide { margin-top: 6px; padding: 8px 9px 4px; }
/* COMPARE — same disclosure shape as Overlays, addressed by data attribute rather than id
   because expandChart clones this control into the modal bar and a cloned id is a dead
   duplicate. */
.compare-wrap { position: relative; flex-shrink: 0; }
[data-compare-btn] { display: inline-flex; align-items: center; gap: 5px; }
[data-compare-btn] .chev { width: 11px; height: 11px; transition: transform .25s var(--ease); }
[data-compare-btn][aria-expanded="true"] .chev { transform: rotate(180deg); }
.compare-cur { font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--radius-xs); padding: 0 5px; }
.compare-cur:empty { display: none; }
/* A quiet pill fills with the accent on hover (from .chart-tool-btn:hover, which .quiet
   only overrides the border and text of), so the accent-tinted chip inside it has to step
   off the accent or it reads as a hole punched in the button. */
[data-compare-btn]:hover .compare-cur { background: var(--chrome-1); }
.compare-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60; display: none;
  width: 190px; max-width: calc(100vw - 24px); max-height: 46dvh; overflow-y: auto; overscroll-behavior: contain;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); padding: 6px; }
.compare-menu.open { display: block; animation: sugIn .14s var(--ease-out); }
.compare-menu .menu-head { padding: 7px 9px 3px; }
.compare-menu button { display: block; width: 100%; text-align: left; background: none; border: 0;
  border-radius: var(--radius-xs); padding: 6px 9px; cursor: pointer;
  font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--ink); }
/* Measured across all six themes, --chrome-hi against the menu's --chrome-1 surface is as
   little as 1.07:1 (Terminal, Noir) — a background tint alone cannot carry either of these
   states, so both also move the ink. This is the same reason #overlayMenu's rows change
   their text and border on hover rather than only their fill. */
.compare-menu button:hover { background: var(--chrome-hi); color: var(--ink-bright); }
.compare-menu button.on { background: var(--chrome-2); color: var(--accent); }
.compare-menu .menu-clear { color: var(--ink-dim); border-top: 1px solid var(--rule); border-radius: 0; margin-top: 4px; padding-top: 8px; }
.compare-menu .menu-empty { padding: 6px 9px; color: var(--ink-dim); font-family: var(--mono); font-size: var(--t-micro); line-height: 1.5; }
/* nowrap: in a nowrap row a button that wraps its own label is what turns a 28px control
   strip back into a 44px one. */
.chart-tool-btn { flex-shrink: 0; white-space: nowrap; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent); border-radius: var(--radius-sm); padding: 5px 11px; cursor: pointer; font-family: var(--mono); font-size: 11px; font-weight: 650; }
.chart-tool-btn:hover, .chart-tool-btn.active { background: var(--accent); color: var(--accent-ink); }
.chart-tool-btn.quiet { border-color: var(--rule); background: var(--chrome-2); color: var(--ink-dim); }
.chart-tool-btn.quiet:hover { border-color: var(--down); color: var(--down); }
.chart-tool-btn:disabled { opacity: .35; cursor: default; }
.fib-status { color: var(--ink-dim); font-family: var(--mono); font-size: 10px; min-width: 0; }
/* Names the bar interval the selected range draws. Not decoration: 78 five-minute candles
   and a zoomed daily chart are indistinguishable by eye, so without this the short tiers
   read as the same view at a different scale. It takes the auto margin so it sits with the
   range selector it describes; #rangeSel then follows immediately rather than drifting. */
.interval-chip { flex-shrink: 0; margin-left: auto; color: var(--ink-dim); font-family: var(--mono);
  font-size: var(--t-micro); letter-spacing: .03em; white-space: nowrap; }
.interval-chip:empty { display: none; }
.interval-chip + #rangeSel { margin-left: 8px; }
.toggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; font-family: var(--mono); font-size: 11px;
  color: var(--ink); background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm); padding: 4px 11px 4px 9px; transition: all .15s var(--ease); }
.toggle:hover { border-color: var(--ink-dim); color: var(--ink-bright); }
/* Kept off --radius-xs: at 13px square that token reads as a radio button, not a checkbox. */
.toggle input { appearance: none; -webkit-appearance: none; width: 13px; height: 13px; border: 1.5px solid var(--ink-dim); border-radius: 4px; position: relative; cursor: pointer; flex-shrink: 0; transition: all .15s; }
.toggle input:checked { background: var(--swatch, var(--accent)); border-color: var(--swatch, var(--accent)); }
.toggle input:checked::after { content: ""; position: absolute; left: 3.5px; top: 1px; width: 3px; height: 6px; border: solid var(--accent-ink); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.toggle.on { color: var(--ink-bright); border-color: var(--swatch, var(--accent)); }
.toggle .dash { width: 13px; height: 0; border-top: 2px solid var(--swatch, var(--accent)); border-radius: 2px; }
.toggle .dash.dotted { border-top-style: dashed; }
#rangeSel { display: inline-flex; margin-left: auto; min-width: 0; overflow-x: auto; scrollbar-width: none;
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm); padding: 2px; }
#rangeSel::-webkit-scrollbar { display: none; }
#rangeSel button { flex: 0 0 auto; }
/* The interval chip explains the range selector; when the pane is too narrow for both, the
   selector is the one you act on. */
.fib-status { flex-shrink: 1; overflow: hidden; }
@media (max-width: 1180px) { body:not(.focus-mode) .interval-chip { display: none; } }
/* The control row is nowrap, so something has to give before the range selector does — and
   what gives is set by the PANE's width, which syncMetricDensity measures, not by the
   viewport's. The newest control gives up its word first and keeps a two-letter one: a bare
   chevron would be a button with nothing to read. */
.cmp-label-short { display: none; }
.tight-controls .interval-chip { display: none; }
.tight-controls .cmp-label { display: none; }
.tight-controls .cmp-label-short { display: inline; }
#rangeSel button { background: none; border: none; cursor: pointer; font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--ink-dim); padding: 4px 10px; border-radius: var(--radius-xs); transition: all .15s; }
#rangeSel button.active { background: var(--accent); color: var(--accent-ink); }
/* The canvas takes the region rather than a constant. 360px was a number chosen against a
   pane that has 312px of window on a 720p display, so the chart and its controls could
   never be on screen together no matter how much room the browser actually had. drawChart
   already measures clientWidth/clientHeight and bails on zero, so nothing there changes —
   observeChartBox() just has to tell it when the box resizes. */
#chartBox { position: relative; flex: 1; min-height: 240px; display: flex; gap: 20px; }
/* Outside the chart destination (the expand modal reuses these ids) the box still needs a
   height to resolve against, since there is no flex parent to stretch it. */
.card #chartBox { flex: none; height: clamp(240px, 42vh, 460px); }
/* COMPARE SPLIT — two panes, one viewport.
   The split is a row and stays a row at every width. Height is the binding constraint on
   this page (see the height budget in CLAUDE.md) and there is no second 200px to hand to a
   stacked chart; width is the axis that can be spent. The expand button is absolute, so it
   is out of this flow and does not take a column. */
.chart-pane { position: relative; flex: 1 1 0; min-width: 0; }
.chart-pane[hidden] { display: none; }
/* Separated with `gap` rather than a padding on the second pane: the pane tag is placed
   against the canvas's own left gutter, and a padding only one of the two panes carries
   would put that tag in a different place on each side. */
.chart-pane + .chart-pane { border-left: 1px solid var(--rule); }
/* touch-action: one finger still scrolls the page vertically, so the chart never traps a
   scroll; horizontal drags and two-finger pinches are ours. */
.chart-pane canvas { width: 100%; height: 100%; display: block; cursor: crosshair; touch-action: pan-y; }
.chart-pane.panning canvas { cursor: grabbing; }
/* Which pane is which. Only rendered while comparing — with one chart on screen the header
   already says whose it is, and a label that repeats it is chrome for nothing.
   Sits at 57px because that is the canvas's left padding: anywhere left of it is the price
   gutter, where it lands on top of the topmost axis label. */
.pane-tag { position: absolute; top: 1px; left: 57px; z-index: 4; pointer-events: none;
  font-family: var(--mono); font-size: var(--t-micro); font-weight: 700; letter-spacing: .09em; color: var(--ink-dim); }
.pane-tag:empty { display: none; }
.chart-tip { position: absolute; pointer-events: none; display: none; background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 11px; line-height: 1.7; padding: 8px 11px; color: var(--ink-bright); box-shadow: var(--shadow); z-index: 5; white-space: nowrap; }
.chart-tip .tg { color: var(--up); } .chart-tip .tr { color: var(--down); } .chart-tip b { color: var(--accent); }
.chart-guide { margin-top: 10px; border-top: 1px solid var(--rule); padding-top: 8px; color: var(--ink-dim); }
.chart-guide summary { cursor: pointer; width: fit-content; font-size: 11px; font-weight: 650; color: var(--accent); }
.chart-guide p { margin: 7px 0 0; max-width: 760px; font-size: 11.5px; line-height: 1.55; }

/* CHART EXPAND BUTTON */
.chart-expand-btn {
  position: absolute; top: 8px; right: 8px; z-index: 6;
  display: grid; place-items: center; width: 28px; height: 28px;
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  color: var(--ink-dim); cursor: pointer;
  transition: opacity .15s, color .15s, border-color .15s, transform .12s var(--ease);
}
/* Hide-until-hover only where hover exists — touch devices keep it always visible */
@media (hover: hover) {
  .chart-expand-btn { opacity: 0; }
  #chartBox:hover .chart-expand-btn, .chart-expand-btn:focus-visible { opacity: 1; }
}
.chart-expand-btn:hover { color: var(--accent); border-color: var(--accent); }
.chart-expand-btn:active { transform: scale(.9); }
.chart-expand-btn svg { width: 13px; height: 13px; pointer-events: none; }

/* CHART EXPAND MODAL */
#chartModal {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(5,12,20,.85);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  display: none; flex-direction: column; padding: 20px;
}
#chartModal.open { display: flex; animation: modalIn .22s var(--ease); }
@keyframes modalIn { from { opacity: 0; transform: scale(.975); } to { opacity: 1; transform: none; } }
#chartModalInner {
  background: var(--chrome-1); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow);
  display: flex; flex-direction: column; flex: 1; overflow: hidden; min-height: 0;
}
#chartModalHead {
  flex-shrink: 0; display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; border-bottom: 1px solid var(--rule);
}
#chartModalTitle {
  font-size: 12px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-bright);
}
#chartModalTag {
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  color: var(--accent); background: var(--accent-soft); border-radius: var(--radius-xs); padding: 1px 7px;
}
#chartModalRangeSel {
  display: inline-flex; margin-left: auto;
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm); padding: 2px;
}
#chartModalRangeSel button {
  background: none; border: none; cursor: pointer;
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  color: var(--ink-dim); padding: 4px 10px; border-radius: var(--radius-xs); transition: all .15s;
}
#chartModalRangeSel button.active { background: var(--accent); color: var(--accent-ink); }
#chartModalClose {
  display: grid; place-items: center; width: 30px; height: 30px; flex-shrink: 0;
  background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  color: var(--ink-dim); cursor: pointer; transition: color .15s, border-color .15s, transform .15s var(--ease);
}
#chartModalClose:hover { color: var(--down); border-color: var(--down); }
#chartModalClose:active { transform: scale(.9); }
#chartModalClose svg { width: 14px; height: 14px; pointer-events: none; }
/* Same two .chart-pane children as the inline box — the canvas, tooltip and pane-tag rules
   are shared, so only the frame differs. */
#chartModalBody {
  flex: 1; position: relative; min-height: 0; padding: 14px 16px 16px; display: flex; gap: 20px;
}
/* overlay controls bar mirrors inline chart controls */
#chartModalControls {
  flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 8px 16px; border-top: 1px solid var(--rule);
}
/* This bar is at the BOTTOM of the modal and #chartModalInner clips its overflow, so a
   popover that drops down opens into nothing. */
#chartModalControls .compare-menu { top: auto; bottom: calc(100% + 6px); }
#chartModalControls .cmp-label { display: inline; }
#chartModalControls .cmp-label-short { display: none; }

/* AI PANE */
#aiPane .pane-body { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
#aiScroll { flex: 1; overflow-y: auto; padding: 14px 20px 24px; min-height: 0; }
/* What is left of the deleted .pane-head. The band said "AI ANALYSIS" beside a pane that
   was obviously the AI analysis; only the model name was information, so only the model
   name survived — and it scrolls with the prose instead of costing a fixed 38px. */
.ai-modelbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.ai-modelbar .tag { font-family: var(--mono); font-size: var(--t-micro); font-weight: 500; letter-spacing: .04em;
  color: var(--accent); background: var(--accent-soft); border-radius: var(--radius-xs); padding: 2px 8px; }
#aiSummary.thinking { display: flex; align-items: center; gap: 10px; color: var(--ink-dim); font-size: 13px; }
.ai-warn { background: var(--warn-soft); color: var(--warn); border-radius: var(--radius-sm); padding: 8px 12px; font-size: 12px; margin-bottom: 12px; font-family: var(--mono); }
/* VERDICT + COLLAPSIBLE ANALYSIS SECTIONS
   The rating is the thing a reader is looking for and it used to be a bold phrase inside
   the first paragraph. Same ruled-and-left-bordered treatment as .regime-summary — no
   shadow, no icons, no second accent. */
.verdict-block { border: 1px solid var(--rule); border-left: 3px solid var(--ink-dim); background: var(--chrome-2);
  border-radius: var(--radius-sm); padding: 13px 15px; margin-bottom: 18px; }
/* Direction only. Hold takes neither colour: --warn is a distinct role and would read as
   a caution the model never expressed. */
.verdict-block:has(.verdict-rating[data-tone="up"])   { border-left-color: var(--up); }
.verdict-block:has(.verdict-rating[data-tone="down"]) { border-left-color: var(--down); }
.verdict-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.verdict-rating { font-family: var(--mono); font-size: var(--t-lead); font-weight: 600; letter-spacing: .02em;
  text-transform: uppercase; color: var(--ink-bright); }
.verdict-rating[data-tone="up"]   { color: var(--up); }
.verdict-rating[data-tone="down"] { color: var(--down); }
.verdict-label { font-family: var(--mono); font-size: var(--t-micro); letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-dim); }
.verdict-body > :last-child { margin-bottom: 0; }
.verdict-body p { margin-bottom: 8px; }

.ai-section { border-top: 1px solid var(--rule); }
.ai-section > summary { list-style: none; cursor: pointer; user-select: none; display: flex; align-items: center; gap: 9px;
  padding: 11px 2px; font-family: var(--doc); font-size: var(--t-doc); font-weight: 600; color: var(--ink-bright);
  transition: color .15s; }
.ai-section > summary::-webkit-details-marker { display: none; }
.ai-section > summary:hover { color: var(--accent); }
.ai-section > summary:hover .chev { stroke: var(--ink-bright); }
.ai-section > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-xs); }
.ai-section > summary .chev { margin-left: auto; width: 14px; height: 14px; flex-shrink: 0;
  stroke: var(--ink-dim); transition: transform .25s var(--ease); }
.ai-section[open] > summary .chev { transform: rotate(180deg); }
.ai-section-body { padding: 0 2px 14px; }
/* The prose renderer emits <h2> for any stray heading inside a section body; it has already
   been titled by the summary, so a second rule under it is noise. */
.ai-section-body h2 { border-bottom: 0; padding-bottom: 0; margin-top: 12px; }
.ai-section-body > :last-child { margin-bottom: 0; }

.ai-disclaimer { margin-top: 18px; padding-top: 10px; border-top: 1px solid var(--rule); font-size: 11.5px; color: var(--ink-dim); }

/* LIVE GENERATION */
#genIndicator { display: flex; align-items: center; gap: 10px; padding: 10px 0 16px; }
#genIndicator .wind { width: 26px; height: 22px; flex-shrink: 0; }
#genIndicator .wind path, .news-loading .wind path { fill: none; stroke: var(--accent); stroke-width: 2.4; stroke-linecap: round; stroke-dasharray: 60; animation: gust 1.8s var(--ease) infinite; }
#genIndicator .wind path:nth-child(2), .news-loading .wind path:nth-child(2) { animation-delay: .12s; }
#genIndicator .wind path:nth-child(3), .news-loading .wind path:nth-child(3) { animation-delay: .24s; }
#genIndicator span { font-family: var(--mono); font-size: 12px; font-weight: 500; color: var(--ink-dim);
  animation: genPulse 1.8s ease-in-out infinite; }
@keyframes genPulse { 50% { color: var(--ink-bright); } }
/* blinking caret rides the last streamed element */
#aiSummary.streaming #answerStream > :last-child::after { content: ""; display: inline-block; width: 3px; height: 1.05em;
  background: var(--accent); margin-left: 3px; vertical-align: -0.15em; border-radius: 1px; animation: caretBlink 1s steps(2) infinite; }
@keyframes caretBlink { 50% { opacity: 0; } }

/* SHOW THINKING */
#thinkingWrap { margin-bottom: 14px; }
#thinkingToggle { display: inline-flex; align-items: center; gap: 7px; background: var(--chrome-2); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); color: var(--ink); font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .03em;
  padding: 5px 12px 5px 10px; cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
#thinkingToggle:hover { color: var(--ink-bright); border-color: var(--ink-dim); }
#thinkingToggle.open { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
#thinkingToggle .live-dot { display: none; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: dotPulse 1.2s ease-in-out infinite; }
#thinkingToggle.live .live-dot { display: inline-block; }
#thinkingToggle svg.brain { width: 13px; height: 13px; stroke: var(--accent); }
#thinkingToggle svg.chev { width: 12px; height: 12px; stroke: currentColor; transition: transform .25s var(--ease); }
#thinkingToggle.open svg.chev { transform: rotate(180deg); }
#thinkingPanel { display: none; margin-top: 9px; border: 1px solid var(--rule); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); background: var(--chrome-0); padding: 12px 14px; max-height: 340px; overflow-y: auto;
  font-family: var(--mono); font-size: 11.5px; line-height: 1.7; color: var(--ink-dim); white-space: pre-wrap; word-break: break-word; }
#thinkingPanel.show { display: block; animation: bodyIn .25s var(--ease); }
#thinkingPanel .tk-label { display: block; font-size: var(--t-micro); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 7px; }

/* CHAT — collapsed until used, resizable, per-ticker.
   It used to hold a fixed 280px band (min 120) inside a pane that has 390px in total, so
   the written analysis was read through 72px on a first visit. Empty, it is now just its
   input row: the affordance is still visible and still says what it is for, and it costs
   44px instead of 280. It opens on the first message or when the field takes focus. */
#chatDock { flex: 0 0 auto; border-top: 1px solid var(--rule); background: var(--chrome-1); display: flex; flex-direction: column;
  height: auto; }
#chatDock > #chatGrip, #chatDock > #chatMessages { display: none; }
#chatDock.expanded { height: var(--chat-h, 46%); min-height: 150px; max-height: 70%; }
#chatDock.expanded > #chatGrip { display: grid; }
#chatDock.expanded > #chatMessages { display: flex; }
/* Chat as a focus-mode destination — it is the whole region, so there is nothing to
   resize against and the grip would be a control with no meaning. */
body.chat-view #chatDock { height: 100%; min-height: 0; max-height: none; }
body.chat-view #chatDock > #chatGrip { display: none; }
body.chat-view #chatDock > #chatMessages { display: flex; }
body.chat-view #aiScroll { display: none; }
#chatDock.animate { transition: height .3s var(--ease-out); }
#chatGrip { height: 14px; flex-shrink: 0; cursor: row-resize; display: grid; place-items: center; position: relative; touch-action: none; }
#chatGrip::before { content: ""; width: 44px; height: 4px; border-radius: 99px; background: var(--rule); transition: background .2s, width .25s var(--ease-spring); }
#chatGrip:hover::before, #chatGrip.dragging::before { background: var(--accent); width: 60px; }
#chatMessages { flex: 1; overflow-y: auto; padding: 4px 18px 12px; display: flex; flex-direction: column; gap: 9px; min-height: 0; }
.chat-empty { margin: auto; color: var(--ink-dim); font-size: 12.5px; text-align: center; padding: 16px; max-width: 320px; }
.msg { max-width: 86%; padding: 9px 13px; border-radius: var(--radius); font-size: 13px; line-height: 1.6; animation: msgIn .3s var(--ease); }
@keyframes msgIn { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
.msg.user { align-self: flex-end; background: var(--accent); color: var(--accent-ink); font-weight: 500; border-bottom-right-radius: 4px; }
.msg.ai { align-self: flex-start; background: var(--chrome-2); color: var(--ink); border: 1px solid var(--rule); border-bottom-left-radius: 4px; }
.msg.ai .prose { font-size: 13px; }
/* Collapsed, this row IS the dock, so its padding is a fixed band rather than breathing
   room inside one. Tightened from 10/14 — eight pixels that the analysis above it needs
   more than the input does. It reopens to comfortable spacing once the dock expands. */
#chatRow { display: flex; gap: 8px; padding: 7px 16px 10px; flex-shrink: 0; }
#chatDock.expanded > #chatRow, body.chat-view #chatRow { padding: 10px 16px 14px; }
#chatInput { flex: 1; background: var(--chrome-2); border: 1px solid var(--rule); border-radius: var(--radius-sm); color: var(--ink-bright);
  font-family: var(--mono); font-size: 13px; padding: 9px 13px; outline: none; transition: border-color .2s, box-shadow .2s; }
#chatInput:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
#chatSend { display: grid; place-items: center; width: 38px; background: var(--accent); border: none; border-radius: var(--radius-sm); color: var(--accent-ink); cursor: pointer; transition: filter .15s, transform .12s; }
#chatSend:hover { filter: brightness(1.08); } #chatSend:active { transform: scale(.93); } #chatSend:disabled { opacity: .45; cursor: progress; }
#chatSend svg { width: 15px; height: 15px; }
/* stop mode — send button turns into a stop control while a reply streams */
#chatSend.stopping { background: var(--down); animation: stopPulse 1.6s ease-in-out infinite; }
#chatSend.stopping:hover { filter: brightness(1.06); }
@keyframes stopPulse { 0%, 100% { box-shadow: 0 0 0 0 var(--down-soft); } 55% { box-shadow: 0 0 0 5px rgba(240,113,107,0); } }

/* retry affordance — appears under a failed analysis or chat reply */
.retry-btn { display: inline-flex; align-items: center; gap: 6px; background: var(--chrome-2); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); color: var(--ink); font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  padding: 5px 12px; margin-top: 10px; cursor: pointer; transition: color .15s, border-color .15s, background .15s, transform .12s var(--ease); }
.retry-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.retry-btn:active { transform: scale(.95); }
.retry-btn svg { width: 13px; height: 13px; }
.ai-warn .retry-btn { margin-top: 8px; }
.ai-warn { display: flex; flex-direction: column; align-items: flex-start; }

/* Reasoning toggle in the chat row */
.chat-think-btn { display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; background: var(--chrome-2); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); color: var(--ink-dim); cursor: pointer; font-family: var(--mono); font-size: 11px; font-weight: 600;
  padding: 0 10px; transition: color .15s, border-color .15s, background .15s; }
.chat-think-btn svg { width: 13px; height: 13px; stroke: currentColor; }
.chat-think-btn:hover { color: var(--ink-bright); border-color: var(--ink-dim); }
.chat-think-btn.on { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
@media (max-width: 520px) { .chat-think-btn span { display: none; } .chat-think-btn { padding: 0 9px; } }

/* Streaming chat: typing indicator, error text, collapsible reasoning */
.chat-typing { display: flex; align-items: center; gap: 8px; color: var(--ink-dim); font-size: 13px; }
.msg.ai .prose.chat-err { color: var(--down); white-space: pre-wrap; }
.chat-think { margin-bottom: 8px; }
.chat-think-toggle { display: inline-flex; align-items: center; gap: 6px; background: var(--chrome-2); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); color: var(--ink-dim); font-family: var(--mono); font-size: 10.5px; font-weight: 600; letter-spacing: .03em;
  padding: 3px 10px 3px 8px; cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
.chat-think-toggle:hover { color: var(--ink-bright); border-color: var(--ink-dim); }
.chat-think.open .chat-think-toggle { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.chat-think-toggle .brain { width: 12px; height: 12px; stroke: var(--accent); }
.chat-think-toggle .chev { width: 11px; height: 11px; stroke: currentColor; transition: transform .25s var(--ease); }
.chat-think.open .chat-think-toggle .chev { transform: rotate(180deg); }
.chat-think-toggle .live-dot { display: none; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: dotPulse 1.2s ease-in-out infinite; }
.chat-think.live .chat-think-toggle .live-dot { display: inline-block; }
.chat-think-panel { display: none; margin-top: 7px; border: 1px solid var(--rule); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); background: var(--chrome-0); padding: 10px 12px; max-height: 240px; overflow-y: auto;
  font-family: var(--mono); font-size: 11px; line-height: 1.65; color: var(--ink-dim); white-space: pre-wrap; word-break: break-word; }
.chat-think.open .chat-think-panel { display: block; animation: bodyIn .25s var(--ease); }

.spinner { width: 15px; height: 15px; flex-shrink: 0; border: 2px solid var(--rule); border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.placeholder { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 60px 20px; color: var(--ink-dim); font-size: 13px; text-align: center; }
.placeholder svg { width: 34px; height: 34px; stroke: var(--rule); }

/* SKELETON LOADING */
.skeleton { position: relative; overflow: hidden; background: var(--chrome-2); border-radius: var(--radius-sm); }
.skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--skeleton-shine), transparent); animation: shimmer 1.5s ease-in-out infinite; }
@keyframes shimmer { to { transform: translateX(100%); } }
.sk-card { pointer-events: none; }
.sk-summary { display: flex; align-items: center; gap: 9px; padding: 12px 16px; }
.sk-line { height: 10px; border-radius: var(--radius-xs); }
.sk-title { width: 42%; max-width: 200px; }
/* Skeleton mirrors the row shape it resolves into: a key bar and a value bar sharing
   the same hairline and row height as the real .metric rows, not a filled tile. */
.sk-metric { display: flex; align-items: center; gap: 10px; min-height: 22px; padding: 2px 0; }
.sk-metric + .sk-metric { border-top: 1px solid var(--rule); }
.sk-metric .sk-k { flex: 1; max-width: 100px; height: 8px; background: var(--chrome-hi); }
.sk-metric .sk-v { flex-shrink: 0; width: 46px; height: 8px; background: var(--chrome-hi); }
.sk-chart { height: 220px; width: 100%; border-radius: var(--radius-sm); }
.sk-prose { padding: 4px 2px; }
.sk-prose .sk-h { width: 34%; height: 12px; margin: 20px 0 12px; background: var(--chrome-2); }
.sk-prose .sk-h:first-child { margin-top: 4px; }
.sk-para { margin-bottom: 16px; }
.sk-para .sk-line { height: 11px; margin-bottom: 10px; }

/* ═══ MOTION SYSTEM — entrance choreography, ambient wind, micro-interactions ═══ */
@keyframes riseIn { from { opacity: 0; transform: translateY(8px); } }

/* Hero — orchestrated page-load sequence: icon → title → copy → chips */
#hero > .wind { animation: riseIn .6s var(--ease-out) backwards .05s; }
#hero h1 { animation: heroTitle .8s var(--ease-out) backwards .14s; }
@keyframes heroTitle { from { opacity: 0; letter-spacing: .4em; filter: blur(5px); } }
#hero p { animation: riseIn .6s var(--ease-out) backwards .3s; }
#hero #heroSearchForm { animation: riseIn .6s var(--ease-out) backwards .4s; }
#hero .hero-actions { animation: riseIn .6s var(--ease-out) backwards .48s; }

/* Cursor-reactive wind field (canvas, injected by app.js). Ambient streaks drift and
   part around the cursor as it moves. Below the copy. */
#hero .wind-field { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }

/* Header controls */
#analyzeBtn { transition: filter .15s, transform .18s var(--ease), opacity .2s; }
#analyzeBtn:hover { filter: brightness(1.08); }
#analyzeBtn:active { transform: scale(.96); }
#themeBtn svg { transition: transform .3s var(--ease-spring); }
/* Applying a theme pops the button's accent well, so the click has a visible receipt
   even when the menu closes over the change. */
#themeBtn.picked .well { animation: themePop .5s var(--ease-spring); }
@keyframes themePop { 0% { transform: scale(.2); } 60% { transform: scale(1.5); } 100% { transform: scale(1); } }

/* Progress — sheen sweeps the fill while the pipeline runs */
#progressFill { overflow: hidden; }
#progressFill::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.28), transparent); animation: fillSheen 1.5s ease-in-out infinite; }
@keyframes fillSheen { to { transform: translateX(100%); } }

/* Data pane — .rb-fill keeps its transform-origin for the risk-bar fill's inline scaleX */
#dataBody .rb-fill { transform-origin: left center; }

/* Card micro-interactions */
.card > summary .chev { transition: transform .4s var(--ease-spring); }

/* Live model tag pulses while streaming */
.ai-modelbar .tag.live { animation: livePulse 1.8s ease-in-out infinite; }
@keyframes livePulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 40%, transparent); } 55% { box-shadow: 0 0 0 6px transparent; } }

/* Chat — wind-dot typing indicator + empty-state entrance */
.chat-empty { animation: riseIn .5s var(--ease-out) backwards .05s; }
.gust-dots { display: inline-flex; gap: 4px; align-items: center; }
.gust-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); animation: gustDot 1.1s ease-in-out infinite; }
.gust-dots i:nth-child(2) { animation-delay: .14s; }
.gust-dots i:nth-child(3) { animation-delay: .28s; }
@keyframes gustDot { 0%, 100% { opacity: .25; transform: translateX(-2px); } 50% { opacity: 1; transform: translateX(2px); } }

/* ═══ TOOLTIPS — one site-styled bubble replaces every native title balloon.
   app.js migrates [title] → data-tip on first hover and positions #tip. ═══ */
#tip { position: fixed; z-index: 300; max-width: 300px; padding: 7px 11px; border-radius: var(--radius-sm);
  background: var(--chrome-2); border: 1px solid var(--rule); color: var(--ink-bright);
  font: 500 11.5px/1.55 var(--mono); box-shadow: var(--shadow); pointer-events: none;
  opacity: 0; transform: translateY(5px) scale(.97); transition: opacity .16s var(--ease), transform .2s var(--ease-spring); }
#tip.show { opacity: 1; transform: none; }
#tip.below { transform: translateY(-5px) scale(.97); } #tip.below.show { transform: none; }
#tip::before { content: ""; position: absolute; left: var(--ax, 50%); top: 100%; width: 8px; height: 8px;
  background: var(--chrome-2); border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  transform: translate(-50%, -55%) rotate(45deg); }
#tip.below::before { top: 0; border: 0; border-left: 1px solid var(--rule); border-top: 1px solid var(--rule); transform: translate(-50%, -45%) rotate(45deg); }

.analysis-tab { transition: background .18s, color .18s, border-color .18s; }
.analysis-tab:not(.active):hover { color: var(--ink); background: var(--chrome-hi); }

/* ═══ 404 — the only page that stands alone (no app.js, no shared chrome partial).
   Everything here is token-driven like the rest of the app, so the palette a visitor
   picked still applies on the page that tells them they took a wrong turn. `body` is a
   flex column with overflow:hidden, so the scroll has to live on .nf, not the page. ═══ */
/* `margin: auto` on the shell rather than `align-items: center` on the flex parent: a
   centered flex child that outgrows its container overflows *both* ways and the top of it
   becomes unreachable by scrolling. This centers when there is room and pins to the top
   when there isn't, which at 1280×720 — the display this layout is budgeted against — is
   the case that actually happens. */
/* Padding is tuned so the whole page clears the 533px region a maximized 1280×720 window
   leaves under the header — the display this layout is budgeted against. It still scrolls,
   because a long enough path wraps the readout value and grows the shell. */
.nf { flex: 1; min-height: 0; overflow-y: auto; display: flex; justify-content: center; padding: 20px 20px 20px; }
.nf-shell { width: 100%; max-width: 640px; margin: auto; }
/* preserveAspectRatio="none" stretches the box to the shell's width, so every stroke here
   carries vector-effect: non-scaling-stroke or the hairlines thicken with the viewport —
   and every mark is a straight line, because a circle drawn in a stretched box is an
   ellipse whose eccentricity changes as you resize the window. */
.nf-tape { display: block; width: 100%; height: 40px; margin-bottom: 12px; opacity: .85; }
.nf-tape-line { fill: none; stroke: var(--ink-dim); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.nf-tape-gap { fill: none; stroke: var(--down); stroke-width: 1.4; stroke-dasharray: 3 6; stroke-linecap: round; opacity: .75; vector-effect: non-scaling-stroke; }
.nf-tape-stop { fill: none; stroke: var(--down); stroke-width: 1; stroke-dasharray: 2 4; opacity: .45; vector-effect: non-scaling-stroke; }
.nf-kicker { font-size: var(--t-micro); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim); font-weight: 600; }
.nf-title { font-family: var(--mono); font-size: var(--t-title); font-weight: 600; letter-spacing: -.01em;
  color: var(--ink-bright); line-height: var(--lh-tight); margin: 8px 0 10px; }
/* The one stretch of prose on the page, so it takes the document register like every other
   explanation in the app — the readout below it stays in the instrument register. */
.nf-lede { font-family: var(--doc); font-size: var(--t-doc); line-height: var(--lh-doc); color: var(--ink); max-width: var(--measure); }

.nf-readout { margin: 22px 0; border-top: 1px solid var(--rule); }
.nf-row { display: flex; align-items: baseline; gap: 16px; padding: 7px 0; border-bottom: 1px solid var(--rule); font-size: var(--t-data); }
.nf-row span { color: var(--ink-dim); letter-spacing: .04em; text-transform: uppercase; font-size: var(--t-micro); }
/* min-width: 0 so a long path wraps inside the row instead of forcing it wider than the
   shell — a 68-character URL is the point of the row and the common case on a bad link. */
.nf-row b { margin-left: auto; min-width: 0; font-weight: 500; color: var(--ink-bright); text-align: right;
  overflow-wrap: anywhere; }
.nf-row .nf-bad { color: var(--down); }

.nf-search label { display: block; font-size: var(--t-micro); letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-dim); font-weight: 600; margin-bottom: 8px; }
/* Same housing idiom as the header search: field and submit share one border, so the two
   read as one instrument rather than a box next to a button. */
.nf-search-housing { display: flex; align-items: center; gap: 8px; height: 42px; padding-left: 11px;
  background: var(--chrome-1); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  transition: border-color .18s var(--ease); }
.nf-search-housing:focus-within { border-color: var(--accent); }
.nf-ico { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: var(--ink-dim); transition: stroke .18s var(--ease); }
.nf-search-housing:focus-within .nf-ico { stroke: var(--accent); }
.nf-search-housing input { flex: 1; min-width: 0; height: 100%; background: none; border: 0; outline: none;
  font-family: var(--mono); font-size: var(--t-body); color: var(--ink-bright); letter-spacing: .02em; }
.nf-search-housing input::placeholder { color: var(--ink-dim); }
.nf-search-housing button { height: 100%; padding: 0 18px; border: 0; border-radius: 0 7px 7px 0; cursor: pointer;
  background: var(--accent); color: var(--accent-ink); font-size: var(--t-data); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; transition: filter .18s var(--ease); }
.nf-search-housing button:hover { filter: brightness(1.08); }

.nf-links { display: grid; gap: 10px; margin-top: 22px; }
@media (min-width: 620px) { .nf-links { grid-template-columns: 1fr 1fr; } }
.nf-links a { display: block; padding: 12px 14px; border: 1px solid var(--rule); border-radius: var(--radius-sm);
  text-decoration: none; background: none; transition: background .18s var(--ease), border-color .18s var(--ease); }
.nf-links a:hover { background: var(--chrome-hi); border-color: var(--ink-dim); }
.nf-links a b { display: block; font-size: var(--t-body); font-weight: 600; color: var(--ink-bright); letter-spacing: .02em; }
.nf-links a span { display: block; margin-top: 3px; font-family: var(--doc); font-size: var(--t-body); color: var(--ink-dim); line-height: 1.45; }
/* The wordmark is a link here, not the button app.js wires up — reset the anchor bits the
   shared .wordmark rule has no reason to carry. */
header .wordmark[href] { text-decoration: none; color: inherit; }
/* Professional polish preview: calmer chrome, clearer hierarchy, same data layout. */
body { letter-spacing: 0; }
#hero { gap: 18px; background: none; }
#hero h1 { letter-spacing: .14em; }
#hero p { font-family: var(--doc); font-size: 17px; line-height: 1.45; }
#hero .hero-fine { font-family: var(--mono); font-size: 11px; letter-spacing: .04em; }
.chip, .hero-search, #analyzeBtn, #heroAnalyzeBtn { box-shadow: none; }
.chip { border-radius: var(--radius-sm); }
.view-tab, #viewRail button, .analysis-tab-main, .analysis-tab-close { font-family: ui-sans-serif, system-ui, sans-serif; letter-spacing: .01em; }
#sTicker { font-size: 12px; letter-spacing: .12em; }
#sCompany { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 13px; font-weight: 600; }
.prose, .verdict-body, .ai-section-body { font-family: var(--doc); font-size: 18px; line-height: 1.62; }
.verdict-block { border-left-width: 3px; box-shadow: none; }
.verdict-head { font-family: ui-sans-serif, system-ui, sans-serif; letter-spacing: .04em; }
.ai-section summary { font-family: ui-sans-serif, system-ui, sans-serif; letter-spacing: .01em; }
.metric-label, .metric-value, .card > summary { font-family: var(--mono); }
.card > summary { font-weight: 600; }

/* These components are declared after the main phone layout block, so their mobile
   adjustments live at the end of the cascade rather than depending on selector weight. */
@media (max-width: 600px) {
  #hero p { font-size: var(--t-lead); }
  #aiScroll { padding: 12px 14px 22px; }
  #chatRow, #chatDock.expanded > #chatRow, body.chat-view #chatRow { padding: 9px 12px 10px; }
  #chatInput { min-width: 0; }
  #chatSend { width: 42px; min-height: 42px; flex-shrink: 0; }
  .chat-think-btn { min-height: 42px; }

  #dataBody:has(> .chart-section.show) { padding-bottom: 10px; }
  #chartControls { gap: 4px; margin-bottom: 7px; }
  .chart-tool-btn { min-height: 36px; padding-inline: 10px; }
  #rangeSel button { min-height: 30px; padding-inline: 10px; }
  #chartBox { min-height: 220px; gap: 8px; }
  .chart-expand-btn { width: 36px; height: 36px; }
  .card > summary { min-height: 48px; padding-inline: 12px; }
  .card-body { padding-inline: 12px; }

  #chartModal { padding: 0; }
  #chartModalInner { border: 0; border-radius: 0; }
  #chartModalHead { flex-wrap: wrap; gap: 8px; padding: 9px 10px; }
  #chartModalTitle { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #chartModalTag { display: none; }
  #chartModalRangeSel { order: 3; width: 100%; margin: 0; overflow-x: auto; }
  #chartModalRangeSel button { flex: 1 0 auto; min-height: 36px; }
  #chartModalClose { width: 38px; height: 38px; }
  #chartModalBody { padding: 8px 10px 10px; gap: 8px; }
  #chartModalControls { padding: 7px 10px calc(7px + env(safe-area-inset-bottom, 0px)); flex-wrap: nowrap; overflow-x: auto; }
}
