:root{
  --ink:#000000;--card:#22293B;--cardS:#22293B;--glass:rgba(34,41,59,.92);
  --line:rgba(255,255,255,.16);--lineS:rgba(255,255,255,.09);
  --text:#F2F4F8;--dim:#9AA3B7;--faint:#727B90;
  --up:#30D158;--down:#FF453A;--warn:#FFD60A;--act:#3B96FF;
  --inputbg:#121722;--inputLine:var(--line);--btn:rgba(255,255,255,.10);--btn2:rgba(255,255,255,.16);
  --shadow:0 1px 0 rgba(255,255,255,.06) inset,0 8px 24px rgba(0,0,0,.55);
  --shadowLg:0 18px 48px rgba(0,0,0,.7);
  --wash:radial-gradient(1200px 520px at 50% -10%,rgba(59,150,255,.13),transparent);
  /* A smooth two-stop alpha gradient over near-black has too few distinct
     8-bit steps for a real OLED panel to render cleanly -- confirmed live
     ("horizontal lines...seems like a bug", visible in the gaps between
     stacked cards on every tab, scrolls with the page, so it's this shared
     body wash, not any one screen's own markup). A faint tiled noise layer
     underneath breaks up the banding the same way film-grain overlays do on
     any other dark gradient UI; at this opacity it's not meant to be
     consciously visible, just enough dither that the gradient no longer
     quantises into visible rings. --wash is "none" in light mode (see
     below), where a two-stop gradient this faint never bands in the first
     place, so --noise stays "none" there too rather than adding texture
     nothing was fixing. */
  --noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch' result='t'/%3E%3CfeColorMatrix in='t' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.03 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --fabShadow:0 10px 26px rgba(59,150,255,.40),0 2px 6px rgba(0,0,0,.35);
  --fabRing:rgba(255,255,255,.14);
  --primaryTop:#30D158;--primaryBottom:#28B84D;--primaryText:#03150A;--primaryShadow:rgba(48,209,88,.25);
  --focusRing:rgba(10,102,214,.13);--modalScrim:rgba(0,0,0,.45);
  --accentTint:rgba(10,132,255,.14);--accentTintSoft:rgba(10,132,255,.15);--accentTintStrong:rgba(10,132,255,.18);--accentBorder:rgba(10,132,255,.4);
  --upTint:rgba(48,209,88,.10);--upTintStrong:rgba(48,209,88,.16);--upBorder:rgba(48,209,88,.4);--upCheckBorder:rgba(48,209,88,.27);
  --downTint:rgba(255,69,58,.10);--downTintCheck:rgba(255,69,58,.12);--downTintStrong:rgba(255,69,58,.14);--downBorder:rgba(255,69,58,.35);
  --warnTint:rgba(255,214,10,.12);--warnTintStrong:rgba(255,214,10,.16);--warnBorder:rgba(255,214,10,.35);--warnBorderStrong:rgba(255,214,10,.4);
  --chartRef:#535B70;--chartMid:rgba(255,255,255,.08);
  /* Update is one visual system: dark mode uses a luminous azure control
     with cooler cyan progress, while light mode below switches to deeper
     blue for contrast on white. These never use green/red, so refresh state
     cannot be mistaken for a trade verdict. */
  --syncButtonTop:#72B9FF;--syncButtonMid:#268BF0;--syncButtonBottom:#0D56A7;
  --syncButtonSpecular:rgba(255,255,255,.52);--syncButtonRim:rgba(255,255,255,.36);
  --syncButtonHighlight:rgba(255,255,255,.58);--syncButtonShade:rgba(0,24,67,.22);
  --syncButtonGlow:rgba(38,139,240,.32);--syncButtonContact:rgba(0,0,0,.30);
  --syncRingTrack:rgba(255,255,255,.22);--syncPrice:#F7FBFF;--syncBars:#A4EBFF;--syncFiling:#E5CCFF;
  --syncProgressStart:#5DAEFF;--syncProgressEnd:#A4EBFF;--syncProgressTrack:rgba(255,255,255,.12);
  --syncStatusDot:#5DAEFF;
  --sans:-apple-system,BlinkMacSystemFont,'SF Pro Text','Segoe UI',Roboto,sans-serif;
  --mono:ui-monospace,'SF Mono',Menlo,Consolas,monospace;
}
html[data-theme="light"]{
  --ink:#F5F6F8;--card:#FFFFFF;--cardS:#FBFCFD;--glass:rgba(255,255,255,.88);
  --line:rgba(15,23,42,.10);--lineS:rgba(15,23,42,.055);
  --text:#162033;--dim:#566276;--faint:#8A96A7;
  --up:#167A3D;--down:#C53B35;--warn:#8A6200;--act:#0B6FD3;
  --inputbg:#F7F8FA;--inputLine:rgba(15,23,42,.14);--btn:#EEF1F5;--btn2:#E1E7EF;
  --shadow:0 1px 2px rgba(15,23,42,.04),0 7px 20px rgba(15,23,42,.07);
  --shadowLg:0 14px 36px rgba(15,23,42,.14);
  --wash:none;
  --noise:none;
  --fabShadow:0 10px 24px rgba(11,111,211,.24),0 2px 6px rgba(15,23,42,.16);
  --fabRing:rgba(255,255,255,.90);
  --primaryTop:#2A9154;--primaryBottom:#167A3D;--primaryText:#FFFFFF;--primaryShadow:rgba(22,122,61,.22);
  --focusRing:rgba(11,111,211,.16);--modalScrim:rgba(15,23,42,.28);
  --accentTint:rgba(11,111,211,.10);--accentTintSoft:rgba(11,111,211,.12);--accentTintStrong:rgba(11,111,211,.15);--accentBorder:rgba(11,111,211,.32);
  --upTint:rgba(22,122,61,.09);--upTintStrong:rgba(22,122,61,.13);--upBorder:rgba(22,122,61,.34);--upCheckBorder:rgba(22,122,61,.22);
  --downTint:rgba(197,59,53,.09);--downTintCheck:rgba(197,59,53,.10);--downTintStrong:rgba(197,59,53,.13);--downBorder:rgba(197,59,53,.30);
  --warnTint:rgba(138,98,0,.10);--warnTintStrong:rgba(138,98,0,.13);--warnBorder:rgba(138,98,0,.30);--warnBorderStrong:rgba(138,98,0,.34);
  --chartRef:#A2ACBA;--chartMid:rgba(15,23,42,.08);
  --syncButtonTop:#3A9BEF;--syncButtonMid:#0A66D6;--syncButtonBottom:#084C9E;
  --syncButtonSpecular:rgba(255,255,255,.50);--syncButtonRim:rgba(255,255,255,.68);
  --syncButtonHighlight:rgba(255,255,255,.62);--syncButtonShade:rgba(0,35,90,.26);
  --syncButtonGlow:rgba(10,102,214,.26);--syncButtonContact:rgba(16,24,40,.20);
  --syncRingTrack:rgba(255,255,255,.30);--syncPrice:#FFFFFF;--syncBars:#C4F1FF;--syncFiling:#F0DCFF;
  --syncProgressStart:#0A66D6;--syncProgressEnd:#24B6D7;--syncProgressTrack:#D9E4F1;
  --syncStatusDot:#0A66D6;
}
html[data-theme="dark"]{
  --fabShadow:0 10px 26px rgba(59,150,255,.40),0 2px 6px rgba(0,0,0,.35);
  --fabRing:rgba(255,255,255,.14);
  --ink:#000000;--card:#22293B;--cardS:#22293B;--glass:rgba(34,41,59,.92);
  --line:rgba(255,255,255,.16);--lineS:rgba(255,255,255,.09);
  --text:#F2F4F8;--dim:#9AA3B7;--faint:#727B90;
  --up:#30D158;--down:#FF453A;--warn:#FFD60A;--act:#3B96FF;
  --inputbg:#121722;--btn:rgba(255,255,255,.10);--btn2:rgba(255,255,255,.16);
  --shadow:0 1px 0 rgba(255,255,255,.06) inset,0 8px 24px rgba(0,0,0,.55);
  --shadowLg:0 18px 48px rgba(0,0,0,.7);
  --wash:radial-gradient(1200px 520px at 50% -10%,rgba(59,150,255,.13),transparent);
  --noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch' result='t'/%3E%3CfeColorMatrix in='t' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.03 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme:light){
  html:not([data-theme]){
  --ink:#F5F6F8;--card:#FFFFFF;--cardS:#FBFCFD;--glass:rgba(255,255,255,.88);
  --line:rgba(15,23,42,.10);--lineS:rgba(15,23,42,.055);
  --text:#162033;--dim:#566276;--faint:#8A96A7;
  --up:#167A3D;--down:#C53B35;--warn:#8A6200;--act:#0B6FD3;
  --inputbg:#F7F8FA;--inputLine:rgba(15,23,42,.14);--btn:#EEF1F5;--btn2:#E1E7EF;
  --shadow:0 1px 2px rgba(15,23,42,.04),0 7px 20px rgba(15,23,42,.07);
  --shadowLg:0 14px 36px rgba(15,23,42,.14);
  --wash:none;
  --noise:none;
  --fabShadow:0 10px 24px rgba(11,111,211,.24),0 2px 6px rgba(15,23,42,.16);
  --fabRing:rgba(255,255,255,.90);
  --primaryTop:#2A9154;--primaryBottom:#167A3D;--primaryText:#FFFFFF;--primaryShadow:rgba(22,122,61,.22);
  --focusRing:rgba(11,111,211,.16);--modalScrim:rgba(15,23,42,.28);
  --accentTint:rgba(11,111,211,.10);--accentTintSoft:rgba(11,111,211,.12);--accentTintStrong:rgba(11,111,211,.15);--accentBorder:rgba(11,111,211,.32);
  --upTint:rgba(22,122,61,.09);--upTintStrong:rgba(22,122,61,.13);--upBorder:rgba(22,122,61,.34);--upCheckBorder:rgba(22,122,61,.22);
  --downTint:rgba(197,59,53,.09);--downTintCheck:rgba(197,59,53,.10);--downTintStrong:rgba(197,59,53,.13);--downBorder:rgba(197,59,53,.30);
  --warnTint:rgba(138,98,0,.10);--warnTintStrong:rgba(138,98,0,.13);--warnBorder:rgba(138,98,0,.30);--warnBorderStrong:rgba(138,98,0,.34);
  --chartRef:#A2ACBA;--chartMid:rgba(15,23,42,.08);
  --syncButtonTop:#3A9BEF;--syncButtonMid:#0A66D6;--syncButtonBottom:#084C9E;
  --syncButtonSpecular:rgba(255,255,255,.50);--syncButtonRim:rgba(255,255,255,.68);
  --syncButtonHighlight:rgba(255,255,255,.62);--syncButtonShade:rgba(0,35,90,.26);
  --syncButtonGlow:rgba(10,102,214,.26);--syncButtonContact:rgba(16,24,40,.20);
  --syncRingTrack:rgba(255,255,255,.30);--syncPrice:#FFFFFF;--syncBars:#C4F1FF;--syncFiling:#F0DCFF;
  --syncProgressStart:#0A66D6;--syncProgressEnd:#24B6D7;--syncProgressTrack:#D9E4F1;
  --syncStatusDot:#0A66D6;
  }
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
html{background-color:var(--ink)}
body{background-color:var(--ink);background-image:var(--wash),var(--noise);background-repeat:no-repeat,repeat;
  background-attachment:fixed,fixed;color:var(--text);font-family:var(--sans);min-height:100dvh;
  -webkit-text-size-adjust:100%;font-variant-numeric:tabular-nums;
  padding:calc(20px + env(safe-area-inset-top)) 16px calc(100px + env(safe-area-inset-bottom))}
/* iOS renders its status bar above page content. Keep a slim glass surface there
   while a list is scrolled so app values cannot visually run through the clock. */
body::before{content:"";position:fixed;z-index:51;top:0;right:0;left:0;
  height:calc(env(safe-area-inset-top) + 8px);pointer-events:none;
  background:color-mix(in srgb,var(--ink) 90%,transparent);
  backdrop-filter:blur(18px) saturate(1.15);-webkit-backdrop-filter:blur(18px) saturate(1.15)}
body.gate{padding-bottom:calc(24px + env(safe-area-inset-bottom));display:flex;align-items:center}
.wrap{max-width:620px;margin:0 auto;width:100%}
.card{background:var(--card);border:1px solid var(--line);border-radius:20px;padding:18px;
  margin-bottom:12px;box-shadow:var(--shadow)}
.card.flush{padding:0;overflow:hidden}
.title{font-size:30px;font-weight:800;letter-spacing:-.03em}
.sub{font-size:13px;color:var(--faint);margin-top:2px;font-weight:500}
.pill{display:flex;align-items:center;gap:6px;padding:6px 11px;background:var(--card);
  border:1px solid var(--line);border-radius:100px;font-size:11px;font-family:var(--mono);color:var(--dim)}
.dot{width:6px;height:6px;border-radius:50%;flex:0 0 auto}
.dot.auto-refreshing{animation:autoRefreshDot 1.2s cubic-bezier(.22,1,.36,1) infinite;will-change:opacity,box-shadow}
@keyframes autoRefreshDot{0%,100%{opacity:.62;box-shadow:0 0 0 0 color-mix(in srgb,var(--up) 0%,transparent)}
  45%{opacity:1;box-shadow:0 0 0 2px color-mix(in srgb,var(--up) 16%,transparent)}
  70%{opacity:.76;box-shadow:0 0 0 3px color-mix(in srgb,var(--up) 0%,transparent)}}
/* Scenario milestone status dots — five real states, one colour language,
   reused nowhere else so a Watch-For dot is never confused with a setup
   icon or the auto-refresh dot above. */
.dot.st-confirmed{background:var(--up)}
.dot.st-improving{background:var(--up);opacity:.55}
.dot.st-watching{background:var(--faint)}
.dot.st-deteriorating{background:var(--warn)}
.dot.st-failed{background:var(--down)}
.cap{font-size:10.5px;font-weight:600;letter-spacing:.06em;color:var(--faint);text-transform:uppercase}
.lab{font-size:12px;font-weight:600;color:var(--dim);display:block;margin-bottom:8px;letter-spacing:-.01em}
input,select,textarea{width:100%;background:var(--inputbg);border:1px solid var(--inputLine);color:var(--text);
  font-family:var(--mono);font-size:16px;padding:13px 14px;border-radius:13px;outline:none;
  -webkit-appearance:none;font-variant-numeric:tabular-nums}
input:focus,select:focus,textarea:focus{border-color:var(--act);box-shadow:0 0 0 3px var(--focusRing)}
textarea{resize:vertical;font-size:11px;line-height:1.5}
textarea.big{font-size:14.5px;line-height:1.6;min-height:104px}
.tag.conv{color:var(--warn);border-color:var(--warnBorderStrong)}
.vlist{font-size:12.5px;color:var(--dim);line-height:1.6;margin-top:9px}
.vlist div{padding:7px 0 7px 15px;position:relative}
.vlist div:before{content:"";position:absolute;left:0;top:14px;width:5px;height:5px;border-radius:50%;background:var(--down)}
button{font-family:var(--sans);cursor:pointer;border:none;background:none;color:inherit}
.btn{width:100%;padding:14px;border-radius:14px;font-size:14px;font-weight:600;letter-spacing:-.01em;
  background:var(--btn);color:var(--text);
  transition:background var(--d1) var(--e-out),color var(--d1) var(--e-out),
    opacity var(--d1) var(--e-out),box-shadow var(--d1) var(--e-out),transform .13s var(--e-out)}
.btn.primary{background:linear-gradient(180deg,var(--primaryTop),var(--primaryBottom));color:var(--primaryText);font-weight:700;
  box-shadow:0 6px 20px var(--primaryShadow)}
.btn.blue{background:var(--act);color:#fff}
.btn.ghost{background:var(--card);border:.5px solid var(--line);color:var(--act)}
.btn.danger{background:var(--downTint);color:var(--down);border:1px solid var(--downBorder)}
/* A .btn is width:100% by default so it fills a flex:1 slot with no extra
   markup -- but that same width becomes the used flex-basis for any sibling
   stuck at flex:0 0 auto (flex-basis:auto falls back to the width property
   when it isn't itself auto), so an unmodified .btn placed next to a flex:1
   one explodes to the row's full width instead of hugging its own text.
   .auto opts a button out of that by giving it a real auto width. */
.btn.auto{width:auto;flex:0 0 auto;padding-left:22px;padding-right:22px}
.btn:disabled{background:var(--btn);opacity:.5;color:var(--faint);cursor:not-allowed;box-shadow:none}
.kv{display:flex;justify-content:space-between;font-size:13.5px;margin-top:10px}
.kv span:first-child{color:var(--dim)}
.kv span:last-child{font-family:var(--mono)}
.hero{font-size:36px;font-weight:800;letter-spacing:-.04em;line-height:1.05;margin-top:7px}
.mut{font-family:var(--mono);font-size:12px;color:var(--dim);margin-top:4px}
/* The nav outranks the floating button on purpose. They are laid out not to
   overlap, but if anything ever changes the bar's height — a device's font
   metrics, a longer label — the tab underneath must still receive the tap.
   The button losing a corner is recoverable; an unreachable tab is not. */
.nav{position:fixed;left:16px;right:16px;bottom:calc(14px + env(safe-area-inset-bottom));z-index:50}
.navin{max-width:620px;margin:0 auto;display:flex;gap:4px;padding:5px;background:var(--glass);
  backdrop-filter:blur(28px);-webkit-backdrop-filter:blur(28px);border:1px solid var(--line);
  border-radius:100px;box-shadow:var(--shadowLg)}
/* Icon over label, not icon beside label — every tab is exactly the same
   footprint regardless of what it's showing, which is the actual fix for
   "alignment is worst": the old row layout put a variable-width count next
   to a variable-width word, so Ideas at "1" was a different width than a
   Setup with nothing next to its label, and four different widths in a row
   is what reads as misaligned even though every button was technically
   centered in its own flex:1 slot. Four identical rounded-square tiles in
   a floating glass pill is the minimal, current shape this was going for. */
.navin button{flex:1;padding:9px 4px 8px;min-height:0;border-radius:16px;font-size:10px;font-weight:700;
  letter-spacing:.01em;color:var(--faint);position:relative;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  transition:color var(--d1) var(--e-out),background var(--d2) var(--e),transform .13s var(--e-out)}
.navin .nl{position:relative;line-height:1}
.navin .ic{position:relative;display:flex;align-items:center;justify-content:center}
.navin .ic svg{width:20px;height:20px;display:block}
/* The count, now a corner badge instead of inline text — it can carry a
   number without ever changing how wide its tab is. */
.navin .ct{position:absolute;top:-6px;right:-10px;min-width:15px;height:15px;padding:0 3px;
  border-radius:100px;background:var(--act);color:#fff;font-family:var(--mono);
  font-size:9px;font-weight:800;line-height:15px;text-align:center;
  box-shadow:0 0 0 2px var(--glass)}
/* Ideas tab's own "something just became worth a look" dot -- opposite
   corner from .ct (which always shows the plain watchlist count) so the two
   never compete for the same pixel, no number, just presence. See
   highPriorityIdeaChanges() in js/idea-priority.js for what turns it on. */
.navin .nd{position:absolute;top:-4px;left:-6px;width:9px;height:9px;border-radius:50%;
  background:var(--warn);box-shadow:0 0 0 2px var(--glass)}
/* Add is an action, not a destination — so it stays a button that floats
   over Desk rather than a tab of its own, even now that Ideas is one. */
/* A flat fill read fine on dark but went flat against a white Setup/Desk
   background in light mode — same blue, far less separation. The gradient
   and ring give it a consistent edge in both themes instead of leaning on
   contrast that only really existed in one of them. */
.fab{position:fixed;right:18px;bottom:calc(96px + env(safe-area-inset-bottom));z-index:45;
  width:54px;height:54px;border-radius:50%;
  background:linear-gradient(155deg,color-mix(in srgb,var(--act) 100%,#fff 14%),var(--act));color:#fff;
  display:flex;align-items:center;justify-content:center;
  box-shadow:var(--fabShadow);border:1.5px solid var(--fabRing);
  transition:transform .16s var(--e-out),opacity .18s var(--e)}
.fab svg{width:24px;height:24px;display:block}
.fab:active{transform:scale(.9)}
body.hasfab{padding-bottom:calc(172px + env(safe-area-inset-bottom))}
.navin button.on{background:var(--btn2);color:var(--act)}
/* The one button in the tab bar that isn't a destination — its own circle,
   larger and coloured, so it still reads as "the action" without needing to
   float above the bar to prove it. Earlier versions raised it (first by a
   guessed margin-top, then by centring it on the bar's TOP EDGE) and both
   read as "too high" — reasonably: half a button hanging off the top of a
   pill isn't centred in that pill, it's centred on its rim. This version
   centres it on the bar's actual vertical middle instead, the same cross-
   axis centre every other tab gets from the row's own alignment — no
   transform, no offset to keep in sync with anything. Horizontally it's
   simply the middle child of five, flanked by two tabs on each side.

   The material is glass, not a flat gradient disc — a soft specular
   highlight top-left, a paler rim light, and a shadow stack (inner top
   highlight, inner bottom shade, coloured ambient glow, a crisp contact
   shadow) rather than one flat drop shadow. That combination — thin
   precise ring, translucent white borders, restraint instead of a looping
   idle animation — is what was actually meant by "Apple-like": the
   material communicates depth on its own, so nothing needs to keep moving
   to prove the button is alive. */
.navin .navsync{flex:0 0 auto;width:54px;height:54px;min-height:0;padding:0;
  align-self:center;
  border-radius:50%;position:relative;
  /* The hold gesture sits right on top of iOS's own long-press trigger — the
     text-selection magnifier and callout menu popping up over "SYNC" mid-hold
     was that default behaviour firing on top of ours, not the ring itself
     doing anything wrong. touch-action stops the browser treating this as a
     scroll/zoom/select surface at all, so only the pointerdown/up handlers
     in events.js ever see the gesture. */
  touch-action:manipulation;-webkit-touch-callout:none;
  -webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;
  background:
    radial-gradient(120% 130% at 32% 18%,var(--syncButtonSpecular),transparent 42%),
    linear-gradient(165deg,var(--syncButtonTop),var(--syncButtonMid) 60%,var(--syncButtonBottom));
  border:1px solid var(--syncButtonRim);
  box-shadow:
    inset 0 1px 1px var(--syncButtonHighlight),
    inset 0 -7px 11px var(--syncButtonShade),
    0 6px 14px var(--syncButtonGlow),
    0 1px 3px var(--syncButtonContact);
  transition:transform .16s var(--e-out),opacity .18s var(--e),box-shadow .18s var(--e)}
.navin .navsync:active{transform:scale(.93)}
/* The hold gesture's only other tell besides the ring itself — a soft green
   glow that grows in over the same HOLD_MS the ring takes to fill, so the
   two read as one cue building together rather than the ring alone.
   color-mix(var(--up)) rather than a literal rgba: the ring stroke itself
   (set inline, in events.js) already resolves to this theme's --up, and a
   fixed dark-theme green here made the halo visibly mismatch the ring's own
   colour in light mode — one glow, one variable, in both themes now. */
.navin .navsync.charging{box-shadow:
    inset 0 1px 1px var(--syncButtonHighlight),
    inset 0 -7px 11px var(--syncButtonShade),
    0 0 0 7px color-mix(in srgb,var(--up) 22%,transparent),
    0 6px 14px var(--syncButtonGlow),
    0 1px 3px var(--syncButtonContact);
  transition:box-shadow .55s linear}
/* Busy is also marked disabled (a second tap mid-sweep is a no-op, guarded in
   updatePrices() itself) but must stay at full strength — the ring is doing
   its most important work right there, and dimming it would read as broken
   rather than working. Only genuinely idle-with-nothing-to-do dims. */
.navin .navsync:disabled:not(.busy):not(.auto-shining){opacity:.5}
.navin .navsync.empty:not(.busy):not(.auto-shining){opacity:.4}
.navsyncring{position:absolute;inset:0;transition:opacity .18s var(--e)}
.navsyncring svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.navsyncbg{fill:none;stroke:var(--syncRingTrack);stroke-width:1.75}
.navsyncarc{fill:none;stroke:var(--syncPrice);stroke-width:1.75;stroke-linecap:round;transition:stroke-dashoffset .1s linear}
/* Automatic refresh is deliberately quieter than an update the person
   started -- the dot at the top already tells the truth about background
   work, so this button doesn't try to reproduce the ring's exact-progress
   read for a run nobody's actively watching. It used to answer with a
   diagonal shine sweeping across the whole button; now it fills the icon
   itself left-to-right instead (see .navsyncmarkfill below, driven by
   animateProgress() in prices.js), a real percent-complete rather than an
   ambient loop with no relationship to actual progress. */
.navin .navsync.auto-shining .navsyncring{opacity:0}
/* The mark at rest, the word while busy — same slot, swapped by the one
   .busy class shell.js already toggles on the button for the ring/disabled
   state. Nothing about the word's own logic changes: whichever ticker is
   being read while busy, one flip-in per name so a run of AAPL → MSFT →
   NVDA reads as a rotation rather than a flicker. Restarting a CSS
   animation from JS needs the class removed, a reflow forced, then
   re-added — see the wordEl handling in shell.js. */
.navsyncmarkwrap{position:relative;width:27px;height:27px;flex:0 0 auto}
.navsync.busy .navsyncmarkwrap{display:none}
.navsyncmark{position:absolute;inset:0;display:block;width:27px;height:27px}
/* The fill copy sits on top of the dim track copy, invisible and fully
   clipped away until a quiet pull is actually running -- at rest (and
   during a MANUAL update, which keeps the ring instead) it never shows,
   so it can safely just always exist in the DOM rather than being
   mounted/unmounted per state. */
.navsyncmarkfill{clip-path:inset(0 100% 0 0);opacity:0}
.navin .navsync.auto-shining .navsyncmarkfill{opacity:1;transition:clip-path .1s linear}
.navin .navsync.auto-shining .navsyncmark:not(.navsyncmarkfill){opacity:.3}
.navsyncword{position:relative;display:none;color:#fff;font-family:var(--mono);font-weight:800;
  font-size:10px;letter-spacing:.02em;text-align:center;line-height:1;
  text-shadow:0 1px 2px rgba(0,0,0,.18);
  max-width:44px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.navsync.busy .navsyncword{display:block}
.navsyncword.flip{animation:navwordFlip .32s var(--e-out) both}
@keyframes navwordFlip{from{opacity:0;transform:translateY(6px) rotateX(35deg)}to{opacity:1;transform:none}}
/* The ring and its glow both sit directly under a real thumb the whole time
   it's held — the one place on this button a press can never actually see.
   This floats clear of that: a pill above the button, same visual language
   as toast(), that a thumb pressing straight down never covers. The bar
   inside mirrors the ring's own fill so there are two independent reads of
   "how far along", and the label itself flips at the exact instant a
   release would trigger the full sweep — the one moment worth calling out
   loudest, since the vibration events.js also fires there is silent on
   iOS Safari (the Vibration API was never implemented in WebKit), so this
   cannot be the only cue that moment gets. */
.navsynchint{position:absolute;left:50%;bottom:calc(100% + 12px);
  transform:translateX(-50%) translateY(6px) scale(.94);opacity:0;pointer-events:none;
  transition:opacity .15s var(--e),transform .15s var(--e);
  background:var(--glass);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border:.5px solid var(--line);border-radius:100px;padding:8px 14px 8px 12px;
  box-shadow:var(--shadowLg);white-space:nowrap;display:flex;align-items:center;gap:8px;z-index:5}
.navsync.charging .navsynchint{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
.navsynchintbar{display:block;width:34px;height:5px;border-radius:100px;background:var(--line);overflow:hidden;flex:0 0 auto}
.navsynchintbar em{display:block;height:100%;width:0;border-radius:100px;background:var(--up)}
.navsynchint b{font-size:12px;font-weight:700;color:var(--dim);white-space:nowrap}
.navsynchint.ready{animation:navHintReady .32s var(--e-out);
  background:color-mix(in srgb,var(--up) 18%,var(--glass));border-color:color-mix(in srgb,var(--up) 45%,var(--line))}
.navsynchint.ready b{color:var(--up)}
@keyframes navHintReady{0%{transform:translateX(-50%) translateY(0) scale(1)}45%{transform:translateX(-50%) translateY(0) scale(1.09)}100%{transform:translateX(-50%) translateY(0) scale(1)}}
.seg{display:flex;gap:3px;padding:3px;background:var(--inputbg);border-radius:12px;margin-bottom:16px}
/* white-space:nowrap is the actual guarantee — a label that's back to
   wrapping (a translation, a future fifth mode) shrinks its own text via
   the browser's natural overflow before it ever throws the row's height
   off again, rather than silently wrapping and un-aligning every sibling. */
.seg button{flex:1;padding:10px 6px;border-radius:10px;font-size:13.5px;font-weight:600;
  color:var(--dim);letter-spacing:-.01em;transition:all .2s;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.seg button.on{background:var(--btn2);color:var(--text)}
.tag{font-size:9px;padding:2px 7px;border-radius:100px;border:.5px solid var(--line);color:var(--faint)}
.tag.br{color:var(--down);border-color:var(--downBorder)}
.tag.held{color:var(--act);border-color:var(--accentBorder)}
.tag.hit{color:var(--up);border-color:var(--upBorder)}
.tag.reason{color:var(--warn);border-color:var(--warnBorder);text-transform:none;font-size:9.5px}
.session-mini{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  padding:2px 5px;border:1px solid var(--accentBorder);border-radius:100px;background:var(--accentTint);
  color:var(--act);font-size:8.5px;font-weight:800;line-height:1.1;letter-spacing:.04em}
/* This is an information strip, not another card inside a position card. Its
   two columns make the comparison readable at a glance without suggesting an
   extended-hours print is the same thing as a regular-session close. */
.session-price{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);margin-top:12px;
  padding:11px 0;border-top:.5px solid var(--lineS);border-bottom:.5px solid var(--lineS)}
.session-price-cell{min-width:0;padding-right:12px}
.session-price-cell+.session-price-cell{padding:0 0 0 14px;border-left:.5px solid var(--lineS)}
.session-price-kicker{display:flex;align-items:center;gap:6px;min-height:14px;color:var(--faint);
  font-size:9px;font-weight:800;letter-spacing:.065em;line-height:1.2}
.session-price-tag{display:inline-flex;align-items:center;justify-content:center;height:15px;min-width:24px;
  padding:0 5px;border:1px solid var(--accentBorder);border-radius:100px;background:var(--accentTint);
  color:var(--act);font-family:var(--sans);font-size:8px;font-style:normal;font-weight:800;letter-spacing:.04em}
.session-price-value{display:block;margin-top:4px;color:var(--text);font-family:var(--mono);font-size:17px;
  font-weight:800;line-height:1.15;white-space:nowrap}
.session-price-reading{display:flex;align-items:baseline;gap:7px;min-width:0}
.session-price-change{font-family:var(--mono);font-size:10.5px;font-weight:800;line-height:1;white-space:nowrap;color:var(--faint)}
.session-price-change.is-up{color:var(--up)}
.session-price-change.is-down{color:var(--down)}
.session-price small{display:block;overflow:hidden;margin-top:4px;color:var(--faint);font-family:var(--mono);
  font-size:9.5px;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}
.session-price-note{margin:7px 0 0;color:var(--faint);font-size:10.5px;line-height:1.4}
@media (max-width:360px){
  .session-price{grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr)}
  .session-price-value{font-size:15px}
  .session-price-change{font-size:9.5px}
}
.chip{display:inline-flex;align-items:center;gap:7px;padding:8px 12px;background:var(--btn);
  border-radius:100px;font-size:13px;font-weight:500;margin:0 6px 8px 0}
.chip button{color:var(--faint);font-size:15px;line-height:1;padding:0 2px}
.rail{position:relative;height:10px;margin-top:16px}
.railbar{position:absolute;top:4px;left:0;right:0;height:2.5px;border-radius:2px}
.railentry{position:absolute;top:1px;width:1.5px;height:9px;border-radius:1px;background:var(--faint)}
.raildot{position:absolute;top:0;width:10px;height:10px;border-radius:50%;transform:translateX(-5px);
  transition:left .25s cubic-bezier(.25,1,.5,1)}
.bar{height:3.5px;background:var(--inputbg);border-radius:2px;overflow:hidden;flex:1}
.bar>i{display:block;height:100%;border-radius:2px}
.chk{display:flex;gap:12px;align-items:center;margin-bottom:9px;padding:14px;border-radius:14px;
  background:var(--inputbg);border:.5px solid transparent;font-size:14px;font-weight:500;
  color:var(--dim);letter-spacing:-.01em;transition:background .2s;cursor:pointer}
.chk.on{background:var(--upTint);border-color:var(--upCheckBorder);color:var(--text)}
/* A real native checkbox, but drawn by hand rather than left to
   accent-color -- confirmed live ("although it tick, but it doesnt feel
   like ticking the box"): native checkbox rendering with accent-color
   varies enough across browsers/WebViews that a checked state can read as
   a plain filled dot with no clear tick, not the deliberate green-card-plus-
   checkmark this app's own .cbx component already draws by hand elsewhere
   (see js/views/new.js's fEarnOv). Same fix, applied to the CSS only --
   every .chk input stays a real <input type="checkbox"> (unchanged
   markup, unchanged :checked/onchange wiring everywhere it's used), just
   painted consistently instead of however the platform feels like that day. */
.chk input{appearance:none;-webkit-appearance:none;width:19px;height:19px;flex:0 0 auto;
  border-radius:6px;border:1.5px solid var(--faint);background:var(--inputbg);
  padding:0;margin:0;cursor:pointer;position:relative;transition:background .15s,border-color .15s}
.chk input:checked{background:var(--up);border-color:var(--up)}
.chk input:checked::after{content:"";position:absolute;left:6px;top:2px;width:5px;height:9px;
  border:solid var(--primaryText);border-width:0 2px 2px 0;transform:rotate(45deg)}
.chk.warn.on{background:var(--downTintCheck);border-color:var(--downBorder);color:var(--down)}
.chk.warn input:checked{background:var(--down);border-color:var(--down)}
.chk.warn input:checked::after{border-color:#fff}
.cbx{width:22px;height:22px;border-radius:7px;border:1.5px solid var(--faint);flex:0 0 auto;
  display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;
  color:transparent;transition:all .18s;background:transparent}
.cbx.on{background:var(--down);border-color:var(--down);color:#fff}
.sug{position:absolute;top:100%;left:0;right:0;z-index:20;margin-top:5px;background:var(--glass);
  backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border:.5px solid var(--line);
  border-radius:14px;overflow:hidden;box-shadow:var(--shadowLg)}
/* touch-action:manipulation -- same fix, same reasoning as .navin .navsync
   above: without it the browser can spend a moment deciding whether a tap
   here is the start of a scroll before ever dispatching pointerdown,
   which is what an intermittently-unresponsive suggestion tap looks like
   on a real device. */
.sug div{padding:13px 15px;display:flex;align-items:baseline;gap:10px;border-top:.5px solid var(--lineS);
  touch-action:manipulation}
.sug div:first-child{border-top:none}
.sug b{font-family:var(--mono);font-size:14px;min-width:56px}
.sug s{font-size:13px;color:var(--dim);text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.modal{position:fixed;inset:0;background:var(--modalScrim);backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:18px;z-index:56}
.modal .card{background:var(--glass);max-width:380px;width:100%;margin:0}
.chart-modal-panel{width:100%;height:100%;min-height:0;display:flex;flex-direction:column;background:var(--ink)}
.chart-modal-head{flex:0 0 auto;position:relative;z-index:2}
.chart-modal-host{flex:1 1 auto;min-height:0;position:relative;z-index:1}
.chart-modal-host #tvFall{z-index:2}
.chart-lens{flex:0 0 auto;padding:10px 16px 9px;background:var(--cardS);border-bottom:.5px solid var(--lineS)}
.chart-lens-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.chart-lens-head span{font-size:9.5px;font-weight:700;letter-spacing:.07em;color:var(--faint)}
.chart-lens-head b{font-size:11px;font-weight:600;color:var(--dim)}
.chart-lens-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:9px}
.chart-lens-cell{min-width:0;padding:0 9px;border-left:.5px solid var(--lineS)}
.chart-lens-cell:first-child{padding-left:0;border-left:none}
.chart-lens-cell:last-child{padding-right:0}
.chart-lens-cell span{display:block;font-size:9px;font-weight:700;letter-spacing:.06em;color:var(--faint);white-space:nowrap}
.chart-lens-cell b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--mono);font-size:11.5px;font-weight:700;margin-top:3px}
.chart-lens-note{font-size:10.5px;color:var(--faint);line-height:1.45;margin-top:7px}
@media (max-width:390px){
  .chart-lens-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 0}
  .chart-lens-cell:nth-child(3){padding-left:0;border-left:none}
}
.desk-card,.compact-card,.hist-compact-card,.idea-card{cursor:pointer}
@media (min-width:860px){
  body{padding:36px 24px 120px}
  .wrap{max-width:760px}
  .nav{left:50%;right:auto;width:min(760px,calc(100% - 48px));transform:translateX(-50%)}
  .navin,.syncbar-row{max-width:760px}
  .modal .card{max-width:520px}
}
.toast{position:fixed;left:50%;bottom:calc(84px + env(safe-area-inset-bottom));transform:translateX(-50%);
  z-index:60;background:var(--glass);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border:.5px solid var(--line);border-radius:100px;padding:11px 20px;font-size:13px;font-weight:600;
  box-shadow:var(--shadowLg);max-width:90vw;text-align:center}
/* The plain .toast pill (border-radius:100px) reads fine for a short,
   single-line message, but a toastUndo/toastAction can carry a much
   longer sentence plus a button -- a full capsule shape around wrapped,
   multi-element content looked visibly lopsided (caught live: the
   rounded caps didn't match the flex layout once the message wrapped to
   two lines). A card-style radius reads as a rounded rectangle instead,
   which suits variable-length wrapped content the way a stretched pill
   doesn't. */
.toast.toast-undo{display:flex;align-items:center;gap:16px;text-align:left;border-radius:20px;max-width:min(92vw,420px)}
.toast.toast-undo button{flex:0 0 auto;background:transparent;color:var(--act);font-weight:800;
  font-size:13px;padding:0;letter-spacing:.01em}
/* Update everything's status. This used to be a floating rounded pill with a
   heavy drop shadow (var(--shadowLg), an 18-48px blur) — against a dark
   theme's near-black page that shadow read as its own dark halo sitting
   behind the pill, which is what looked like "a black background" rather
   than a status readout. It's a flush, edge-to-edge strip now instead: no
   card, no shadow, no blur, no rounded corners floating in the middle of
   the screen — a thin bar docked to the true top edge is the whole idea,
   the same visual language a browser's own tab-loading indicator uses.
   Hiding is still opacity + transform together (a translateY alone would
   have the exact same "technically off-screen, visually not" problem a
   flat background strip doesn't have shadow bleed to worry about, but
   there is no reason to reintroduce the risk). */
.syncbar{position:fixed;top:0;left:0;right:0;z-index:65;
  padding-top:env(safe-area-inset-top);
  background:var(--card);border-bottom:.5px solid var(--line);
  transform:translateY(-100%);opacity:0;pointer-events:none;
  transition:transform var(--d2) var(--e),opacity var(--d2) var(--e)}
.syncbar.show{transform:translateY(0);opacity:1}
/* The page's own content used to sit directly behind wherever the old pill
   landed, which is exactly why a wide "TICKER · phase" line ran under the
   cash pill and menu button — no amount of clever width-capping fixes an
   element sharing a header's real estate on purpose. Content gets pushed
   down instead, the same padding-swap trick body.hasfab already uses for
   the FAB. Shorter now than the old card needed, since a flush strip has no
   padding or shadow margin of its own to clear. */
body.syncing{padding-top:calc(50px + env(safe-area-inset-top))}
/* One row, not two — a full-width strip has the room a 240px pill didn't,
   so the ticker, the phase, and the count all sit on the same line instead
   of stacking. max-width mirrors .wrap so the text column lines up with
   every card underneath it once the page settles into place; the coloured
   track below stays genuinely edge-to-edge, unconstrained, because a
   progress indicator reads as more purposeful spanning the whole screen
   than boxed in with the text. */
.syncbar-row{display:flex;align-items:baseline;gap:8px;max-width:620px;margin:0 auto;
  padding:8px 18px 7px}
.syncbar-dot{width:6px;height:6px;border-radius:50%;background:var(--syncStatusDot);flex:0 0 auto;align-self:center}
.syncbar-text{font-size:12px;font-weight:700;color:var(--text);font-family:var(--mono);
  letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  flex:0 1 auto;max-width:96px}
/* The phase line — which exact step, and how long it's been on it. Turns
   amber past 15s on the SAME step: still moving, just slow, is what a filing
   fetch genuinely looks like (see FILING_TIMEOUT_MS's own comment in
   prices.js) — the colour is the difference between "working" and "stuck"
   that a static ticker name alone could never communicate. */
.syncbar-phase{font-size:11px;color:var(--faint);font-family:var(--mono);flex:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.syncbar-phase.slow{color:var(--warn)}
.syncbar-count{font-size:11px;color:var(--faint);font-family:var(--mono);flex:0 0 auto;white-space:nowrap}
.syncbar-track{height:2px;background:var(--syncProgressTrack);overflow:hidden}
.syncbar-track>i{display:block;height:100%;width:0%;background:linear-gradient(90deg,var(--syncProgressStart),var(--syncProgressEnd))}
.row{display:flex;gap:10px}
.row>*{flex:1}
.exit-reason-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;margin-bottom:8px}
.exit-reason-auto{padding:3px 6px;border-radius:6px;background:var(--accentTint);color:var(--act);font-size:9px;font-weight:800;letter-spacing:.07em;line-height:1.1}
.exit-reason-reset{padding:2px 0;color:var(--act);font-size:10.5px;font-weight:750;line-height:1.2}
.exit-reason-note{display:flex;align-items:flex-start;gap:7px;margin-top:8px;color:var(--faint);font-size:11px;line-height:1.45}
.exit-reason-note .dot{flex:0 0 auto;margin-top:5px}
.hair{border-top:1px solid var(--lineS)}
a{color:var(--act);text-decoration:none}
.prog{position:absolute;left:0;top:0;bottom:0;background:var(--accentTintStrong);overflow:hidden;will-change:width}
.prog.indet{width:100%}
.prog::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.16),transparent);animation:sheen 1.5s infinite}
@keyframes sheen{100%{transform:translateX(100%)}}
@keyframes pulseDot{0%,100%{opacity:.35}50%{opacity:1}}
.pulse{animation:pulseDot 1.1s ease-in-out infinite}
.eye{position:absolute;right:6px;top:50%;transform:translateY(-50%);padding:10px 12px;
  font-size:11px;font-weight:700;letter-spacing:.04em;color:var(--act)}
.tools{display:flex;gap:8px;margin-bottom:12px}
.tools select{flex:1;padding:10px 12px;font-size:13px;font-family:var(--sans);border-radius:12px}
.tools button{flex:0 0 auto;padding:10px 15px;border-radius:12px;background:var(--btn);
  font-size:13px;font-weight:600;color:var(--text);transition:background .15s,color .15s;
  display:flex;align-items:center;gap:6px}
.tools button svg{width:14px;height:14px;flex:0 0 auto}
/* A synchronous full re-render on tap (S.ideaManageOpen flips, render() runs
   in the same click handler) rebuilds this button before the browser ever
   paints the global button:active tap-scale, so relying on that alone left
   Manage looking unresponsive -- caught live ("doesn't show I'm clicking").
   A persistent color change survives the re-render because it's driven by
   state (the .on class), not a transient pseudo-class on a soon-replaced node. */
.tools button.on{background:var(--act);color:#fff}
.crow{display:flex;align-items:center;gap:11px;padding:13px 14px;border-radius:14px;
  background:var(--card);border:1px solid var(--line);margin-bottom:7px;cursor:pointer;box-shadow:var(--shadow)}
.crow .tk{font-size:15px;font-weight:800;letter-spacing:-.01em;min-width:56px}
.crow .mid{flex:1;min-width:0;font-size:11.5px;color:var(--faint);font-family:var(--mono)}
.crow .rr{font-size:15px;font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
/* Compact mode is one line by design: the trade's operational readings stay
   visible, while the longer explanation remains in the expanded card. Every
   compact value is whole and unellipsized; the small-screen tune keeps the
   realistic longest ticker and price on the same line without overlap. */
.crow.desk-compact-row,.crow.idea-compact-row,.crow.hist-compact-row{display:flex;align-items:center;gap:6px;padding:8px 12px;min-height:48px;white-space:nowrap}
.crow.desk-compact-row>.dot,.crow.idea-compact-row>.dot,.crow.hist-compact-row>.dot{flex:0 0 auto}
.desk-compact-ticker,.idea-compact-ticker,.hist-compact-ticker{flex:0 0 auto;min-width:0!important;max-width:110px;overflow:hidden;text-overflow:ellipsis;font-size:14px!important;letter-spacing:0!important;white-space:nowrap}
.desk-compact-row .session-mini,.idea-compact-row .session-mini{flex:0 0 auto}
.desk-compact-bucket,.idea-compact-bucket,.hist-compact-bucket{flex:0 0 auto;font-size:8.5px;font-weight:800;letter-spacing:.06em;line-height:1.2;white-space:nowrap}
.compact-action-tag{display:inline-flex;flex:0 0 auto;align-items:center;min-height:18px;padding:2px 6px;border-radius:6px;background:var(--btn);
  font-size:8.5px;font-weight:800;letter-spacing:.055em;line-height:1.15;white-space:nowrap}
.desk-compact-price,.idea-compact-price,.hist-compact-price{flex:0 0 auto;min-width:0;margin-left:auto;color:var(--faint);font-family:var(--mono);font-size:11.5px;font-weight:750;line-height:1.15;white-space:nowrap}
.desk-compact-result,.idea-compact-result,.hist-compact-result{flex:0 0 auto;min-width:0;font-family:var(--mono);font-size:13px;font-weight:800;line-height:1.15;letter-spacing:0;white-space:nowrap}
@media (max-width:360px){
  .crow.desk-compact-row,.crow.idea-compact-row,.crow.hist-compact-row{gap:3px;padding:8px 8px;min-height:44px}
  .desk-compact-ticker,.idea-compact-ticker,.hist-compact-ticker{font-size:12px!important;max-width:76px}
  .desk-compact-row .session-mini,.idea-compact-row .session-mini{padding:1px 3px;font-size:7px}
  .compact-action-tag{min-height:17px;padding:2px 3px;font-size:7.5px}
  .desk-compact-bucket,.idea-compact-bucket,.hist-compact-bucket{font-size:7px}
  .desk-compact-price,.idea-compact-price,.hist-compact-price{font-size:9px}
  .desk-compact-result,.idea-compact-result,.hist-compact-result{font-size:10.5px}
}
/* Swipe-to-delete — one shell shared by every Desk/Ideas/History compact row
   (see swipeWrap() in utils.js and bindSwipeDelete() in events.js). The red
   action sits underneath at a fixed width; the row itself is the layer that
   actually moves, so it always fully occludes the action until dragged. */
.swipe-wrap{position:relative;margin-bottom:7px;border-radius:14px;overflow:hidden}
.swipe-wrap .crow{margin-bottom:0}
/* Sized to exactly the revealed sliver (SWIPE_MAX in events.js), not the
   whole row — the row itself sits on top and occludes the rest, so a wider
   hit target would only ever catch a tap the row's own content already
   claimed underneath it. Rounded on every corner, matching the row's own
   14px — the wrap's overflow:hidden already rounds the outer (right) edge,
   this is what makes the inner (left) edge read as a matching rounded shape
   once revealed, instead of a slab with one sharp straight side.
   Invisible until events.js adds .active to the wrap for an actual drag —
   a row's own open/close and tab-change animations briefly drop its
   opacity, which would otherwise let this always-present red layer show
   through for a frame on interactions that have nothing to do with it. */
.swipe-action{position:absolute;top:0;right:0;bottom:0;width:84px;display:flex;align-items:center;
  justify-content:center;background:var(--down);border:none;border-radius:14px;
  opacity:0;transition:opacity var(--d1) var(--e-out)}
.swipe-wrap.active .swipe-action{opacity:1}
.swipe-action-label{color:#fff;font-size:12.5px;font-weight:800;letter-spacing:.02em}
.swipe-surface{position:relative;touch-action:pan-y;transform:translate3d(0,0,0);
  transition:transform var(--d1) var(--e-out);will-change:transform}
.swipe-surface.dragging{transition:none}
/* History is an overview first: three views share one short top surface, while
   the old detailed breakdown remains one deliberate tap away. These are
   work surfaces, not nested cards, so the screen stays dense and scanable. */
.hist-view-seg{margin-bottom:14px}
.hist-view-seg button{min-width:0;font-size:11.5px;padding-left:7px;padding-right:7px}
.hist-timeline-seg{max-width:250px;margin:0 auto 12px}
.hist-timeline-toggle{display:grid;grid-template-columns:repeat(2,42px);gap:3px;width:max-content;margin:0 auto 13px;padding:3px;
  background:var(--inputbg);border:1px solid var(--lineS);border-radius:12px}
.hist-timeline-toggle button{position:relative;width:42px;height:38px;border-radius:9px;color:var(--faint);display:flex;
  align-items:center;justify-content:center;transition:background-color var(--d2) var(--e-out),color var(--d2) var(--e-out),box-shadow var(--d2) var(--e-out)}
.hist-timeline-toggle button svg{width:19px;height:19px}
.hist-timeline-toggle button.on{background:var(--accentTint);color:var(--act);box-shadow:inset 0 0 0 1px var(--accentBorder),0 3px 12px var(--accentTintStrong)}
.hist-timeline-toggle button.on::after{content:"";position:absolute;right:7px;top:7px;width:4px;height:4px;border-radius:50%;background:currentColor;box-shadow:0 0 7px currentColor}
.hist-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.hist-section-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}
.hist-section-head b{font-size:15px;font-weight:760;line-height:1.25;overflow-wrap:anywhere}
.hist-section-head strong{font-family:var(--mono);font-size:15px;color:var(--dim);white-space:nowrap}
.hist-hero{padding-bottom:14px}
.hist-verified{font-family:var(--mono);font-size:10.5px;color:var(--act);background:var(--accentTint);border:1px solid var(--accentBorder);border-radius:100px;padding:5px 8px;white-space:nowrap}
.hist-hero-chart{margin:12px -5px 2px}
.hist-hero-empty{height:78px;display:flex;align-items:center;color:var(--faint);font-size:12px;line-height:1.5}
.hist-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-top:1px solid var(--lineS);margin-top:10px;padding-top:12px}
.hist-metrics>div{min-width:0;padding:0 9px;border-left:1px solid var(--lineS)}
.hist-metrics>div:first-child{padding-left:0;border-left:0}
.hist-metrics>div:last-child{padding-right:0}
.hist-metrics span{display:block;font-size:9.5px;font-weight:750;color:var(--faint);line-height:1.2}
.hist-metrics b{display:block;margin-top:5px;font-family:var(--mono);font-size:15px;font-weight:800;white-space:nowrap}
.hist-confidence{padding-bottom:15px}
.hist-confidence-track{height:7px;position:relative;margin:13px 0 10px;border-radius:100px;background:var(--btn);overflow:visible}
.hist-confidence-track i{display:block;height:100%;border-radius:100px;background:var(--up);min-width:2px}
.hist-confidence-track em{position:absolute;top:-4px;width:1px;height:15px;background:var(--warn);font-style:normal}
.hist-confidence p,.hist-window p,.hist-compare p,.hist-earnings p,.hist-upcoming p,.hist-graph p{margin:0;color:var(--dim);font-size:11.5px;line-height:1.55}
.hist-confidence small{display:block;margin-top:7px;color:var(--faint);font-size:10.5px;line-height:1.45}
.hist-window{padding-top:16px;padding-bottom:16px}
.hist-compare-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0;margin:13px 0 11px}
.hist-compare-grid>div{min-width:0;padding:0 12px;border-left:1px solid var(--lineS)}
.hist-compare-grid>div:first-child{padding-left:0;border-left:0}
.hist-compare-grid>div:last-child{padding-right:0}
.hist-compare-grid small{display:block;color:var(--faint);font-size:10.5px;line-height:1.35;min-height:28px}
.hist-compare-grid b{display:block;margin-top:7px;color:var(--text);font-family:var(--mono);font-size:12.5px;line-height:1.4;overflow-wrap:anywhere}
.hist-earnings,.hist-upcoming{padding-top:16px;padding-bottom:15px}
.hist-event-badge{width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:50%;font-family:var(--mono);font-size:11px;font-weight:800}
.hist-event-badge.is-earnings{background:var(--warnTint);color:var(--warn);border:1px solid var(--warnBorder)}
.hist-earnings-row{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:10px 0;border-top:1px solid var(--lineS)}
.hist-earnings-row:first-of-type{margin-top:10px}
.hist-earnings-row b{font-size:13px;font-family:var(--mono);font-weight:800}
.hist-earnings-row span{font-size:11.5px;color:var(--warn);font-family:var(--mono);text-align:right;white-space:nowrap}
.hist-upcoming-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:10px 0;border-top:1px solid var(--lineS)}
.hist-upcoming-row:first-of-type{margin-top:10px}
.hist-upcoming-row>div{min-width:0;display:flex;flex-direction:column;gap:4px}
.hist-upcoming-row>div>b{font-family:var(--mono);font-size:13px;font-weight:800}
.hist-upcoming-row>div>small{display:flex;align-items:center;gap:5px;color:var(--faint);font-size:10.5px;line-height:1.35;overflow-wrap:anywhere}
.hist-upcoming-row>div>small i{display:inline-block;flex:0 0 auto;width:6px;height:6px;border-radius:50%}
.hist-upcoming-row>span{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-end;gap:3px;text-align:right;font-family:var(--mono)}
.hist-upcoming-row>span>b{font-size:11.5px;font-weight:800;color:var(--warn)}
.hist-upcoming-row>span>small{font-size:10px;color:var(--faint)}
.hist-analysis-toggle{width:100%;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:14px 4px 15px;margin:1px 0 4px;border-top:1px solid var(--lineS);color:var(--dim);text-align:left}
.hist-analysis-toggle span{display:flex;flex-direction:column;gap:3px;min-width:0}
.hist-analysis-toggle b{font-size:13px;font-weight:720;color:var(--text)}
.hist-analysis-toggle small{font-size:11px;color:var(--faint);line-height:1.35}
.hist-analysis-toggle i{flex:0 0 auto;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--btn);color:var(--act);font-size:18px;font-style:normal}
.hist-deep{margin-top:2px}
.hist-calendar{padding:15px}
.hist-calendar-head{display:grid;grid-template-columns:34px minmax(0,1fr) 34px;align-items:center;gap:8px;text-align:center}
.hist-calendar-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}
.hist-calendar-head b{font-size:15px;font-weight:760;line-height:1.25}
.hist-cal-nav{width:34px;height:34px;border-radius:50%;background:var(--btn);border:1px solid var(--line);color:var(--act);display:flex;align-items:center;justify-content:center}
.hist-cal-nav svg{width:17px;height:17px}
.hist-cal-week,.hist-cal-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px}
.hist-cal-week{margin:15px 0 5px}
.hist-cal-week span{text-align:center;color:var(--faint);font-size:10px;font-weight:750}
.hist-cal-grid{grid-auto-rows:42px}
.hist-cal-blank{display:block}
.hist-cal-day{position:relative;min-width:0;height:42px;padding:0;border:0;border-radius:9px;background:transparent;color:var(--dim);font-family:var(--mono);font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center}
.hist-cal-day.is-today{box-shadow:inset 0 0 0 1px var(--accentBorder);color:var(--act)}
.hist-cal-day.has-events{background:var(--btn);color:var(--text)}
.hist-cal-day.is-selected{background:var(--accentTint);color:var(--act);box-shadow:inset 0 0 0 1px var(--accentBorder),0 2px 10px var(--accentTintStrong)}
.hist-cal-day:hover{background:var(--btn2)}
.hist-cal-day.is-selected:hover{background:var(--accentTint)}
.hist-cal-dots{position:absolute;bottom:5px;left:50%;transform:translateX(-50%);display:flex;align-items:center;justify-content:center;gap:2px;max-width:26px}
.hist-cal-dots i{display:block;flex:0 0 auto;width:5px;height:5px;border-radius:50%}
.hist-cal-key,.hist-graph-key{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:7px 12px;margin:13px 0 12px;color:var(--faint);font-size:10.5px}
.hist-cal-key span,.hist-graph-key span{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.hist-cal-key i,.hist-graph-key i{width:6px;height:6px;border-radius:50%;background:var(--act)}
.hist-cal-key i.is-win,.hist-graph-key i.is-win{background:var(--up)}
.hist-cal-key i.is-loss,.hist-graph-key i.is-loss{background:var(--down)}
.hist-cal-key i.is-earnings,.hist-graph-key i.is-earnings{background:var(--warn)}
.hist-cal-key i.is-dividend,.hist-graph-key i.is-dividend{background:var(--up)}
.hist-date-list{margin-top:4px;padding-top:13px;border-top:1px solid var(--lineS)}
.hist-date-list-head{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin-bottom:9px}
.hist-date-list-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}
.hist-date-list-head>div b{font-family:var(--mono);font-size:13px;font-weight:800;color:var(--text);line-height:1.2;overflow-wrap:anywhere}
.hist-date-list-head>strong{flex:0 0 auto;font-family:var(--mono);font-size:10.5px;font-weight:800;color:var(--faint);white-space:nowrap}
.hist-date-empty{padding:13px 0 3px;color:var(--faint);font-size:12px;line-height:1.5}
.hist-date-group{display:grid;grid-template-columns:38px minmax(0,1fr);gap:10px;padding:10px 0;border-top:1px solid var(--lineS)}
.hist-date-group time{display:flex;flex-direction:column;align-items:center;padding-top:1px;font-family:var(--mono);line-height:1}
.hist-date-group time b{font-size:16px;font-weight:800;color:var(--text)}
.hist-date-group time small{margin-top:3px;font-size:9px;font-weight:800;letter-spacing:.06em;color:var(--faint)}
.hist-date-events{min-width:0}
.hist-date-row{display:grid;grid-template-columns:6px minmax(0,1fr) auto;align-items:start;column-gap:8px;padding:8px 0;min-height:31px}
.hist-date-row+ .hist-date-row{margin-top:5px}
.hist-date-row>i{width:6px;height:6px;margin-top:5px;border-radius:50%}
.hist-date-row>div{min-width:0;display:flex;flex-direction:column;gap:3px}
.hist-date-row b{min-width:0;font-size:12px;font-weight:720;line-height:1.35;overflow-wrap:anywhere}
.hist-date-row small{min-width:0;color:var(--faint);font-family:var(--mono);font-size:10px;line-height:1.4;overflow-wrap:anywhere}
.hist-date-row>span{font-family:var(--mono);font-size:9px;font-weight:800;letter-spacing:.05em;white-space:nowrap}
.hist-graph{padding-bottom:15px}
.hist-equity-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.hist-equity-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}
.hist-equity-head>div:last-child{align-items:flex-end;text-align:right}
.hist-equity-head b{font-family:var(--mono);font-size:24px;font-weight:800;line-height:1.1;letter-spacing:0}
.hist-equity-head strong{font-family:var(--mono);font-size:14px;font-weight:800;white-space:nowrap}
.hist-equity-head small{font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap}
.hist-equity-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin-top:14px;padding-top:12px;border-top:1px solid var(--lineS)}
.hist-equity-stats span{min-width:0;padding:0 8px;border-left:1px solid var(--lineS)}
.hist-equity-stats span:first-child{padding-left:0;border-left:0}
.hist-equity-stats span:last-child{padding-right:0}
.hist-equity-stats small{display:block;color:var(--faint);font-size:9px;font-weight:800;letter-spacing:.06em;line-height:1.2}
.hist-equity-stats b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:5px;font-family:var(--mono);font-size:11px;font-weight:750}
.hist-graph-svg{display:block;width:100%;height:224px;margin:10px 0 0;overflow:visible}
.hist-graph-svg text{fill:var(--faint);font-family:var(--mono);font-size:16px}
.hist-graph-grid{stroke:var(--lineS);stroke-width:1}
.hist-graph-line{fill:none;stroke:var(--act);stroke-width:2.4;stroke-linejoin:round;stroke-linecap:round}
.hist-graph-marker-line{stroke:var(--line);stroke-width:1;stroke-dasharray:3 4;opacity:.7}
.hist-graph-marker-dot,.hist-graph-money-dot{stroke:var(--card);stroke-width:1.5}
.hist-equity-note{padding-top:1px}
@media(max-width:430px){
  .hist-metrics{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 0}
  .hist-metrics>div:nth-child(3){padding-left:0;border-left:0}
  .hist-metrics>div:nth-child(2){padding-right:0}
  .hist-metrics>div:nth-child(4){padding-right:0}
  .hist-compare-grid>div{padding:0 9px}
  .hist-compare-grid>div:first-child{padding-left:0}
  .hist-compare-grid>div:last-child{padding-right:0}
  .hist-cal-grid{grid-auto-rows:38px}
  .hist-cal-day{height:38px;font-size:11px}
  .hist-date-row{min-height:38px}
  .hist-equity-stats span{padding:0 6px}
}
/* Motion tokens. One easing curve everywhere so the whole app feels like one object. */
:root{--e:cubic-bezier(.22,1,.36,1);--e-out:cubic-bezier(.33,0,.2,1);
  --d1:.26s;--d2:.42s;--d3:.60s}
/* One shimmer, one shape language, used everywhere a real value is on its way
   in rather than genuinely unknown — Desk/Ideas compact rows waiting on a
   sweep, History's MFE/MAE and community cards waiting on a shared-cache
   read. Deliberately never shown for data that's just missing/unmeasured
   (a "—" stays a "—" for that); it only ever replaces a spot that is about
   to become a real number, so it never promises something isn't coming. */
@keyframes skelShimmer{0%{background-position:-135% 0}100%{background-position:135% 0}}
.skel{display:inline-block;border-radius:6px;background:linear-gradient(90deg,var(--btn) 25%,var(--btn2) 50%,var(--btn) 75%);
  background-size:200% 100%;animation:skelShimmer 1.3s ease-in-out infinite;vertical-align:middle}
@media (prefers-reduced-motion:reduce){.skel{animation:none;background:var(--btn)}}
/* AI-card loading state — block-shaped placeholder lines standing in for
   the paragraph that's about to arrive, each line's shimmer staggered
   slightly so it reads as one soft wave moving down the card rather than
   every line flashing in lockstep. Replaces the old flat .prog.indet bar
   for the two AI cards specifically; .prog.indet stays as-is everywhere
   else it's already used. */
.ai-load-line{height:12px;border-radius:6px;margin-top:9px;
  background:linear-gradient(90deg,var(--btn) 25%,var(--btn2) 50%,var(--btn) 75%);
  background-size:200% 100%;animation:skelShimmer 1.6s ease-in-out infinite}
.ai-load-line:first-child{margin-top:0}
.ai-load-line:nth-child(1){animation-delay:0ms}
.ai-load-line:nth-child(2){animation-delay:90ms}
.ai-load-line:nth-child(3){animation-delay:180ms}
@media (prefers-reduced-motion:reduce){.ai-load-line{animation:none;background:var(--btn)}}

/* Apple Intelligence/Siri-style animated gradient ring, used ONLY on the
   combined News/Scenarios/Filings card (see aiResearchCardHTML in
   ideas.js) -- a slow-rotating conic gradient traced along a 1.5px ring
   just outside the card, the same visual language iOS uses to mark "an
   AI did something here" without a badge or a label competing with the
   card's own content. A ::before pseudo-element carries the gradient so
   the card's own background/border/padding are completely untouched;
   mask-composite:exclude keeps only the ring itself visible, not a solid
   gradient fill. @property is what makes the angle animatable at all
   (a plain custom property can't be transitioned) -- unsupported browsers
   just see a static gradient, not a broken one. */
@property --ai-angle{syntax:'<angle>';inherits:false;initial-value:0deg}
.ai-glow-card{position:relative;isolation:isolate}
.ai-glow-card::before{content:"";position:absolute;inset:-1.5px;border-radius:calc(20px + 1.5px);
  padding:1.5px;z-index:-1;pointer-events:none;
  background:conic-gradient(from var(--ai-angle),#0A84FF,#BF5AF2,#FF375F,#FF9F0A,#30D158,#0A84FF);
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:aiGlowRotate 7s linear infinite;opacity:.85;
  transition:opacity .3s var(--e-out)}
@keyframes aiGlowRotate{to{--ai-angle:360deg}}
/* While a News/Scenarios generation is actually in flight (see the .busy
   class toggled in aiResearchCardHTML), the ring spins faster and pulses
   brighter -- the same "idle glow" vs "actively thinking" distinction
   Siri's own animation draws, so a card that's genuinely waiting on a
   provider reads differently from one just sitting there finished. */
.ai-glow-card.busy::before{animation:aiGlowRotate 2.2s linear infinite,aiGlowPulse 1.3s ease-in-out infinite}
@keyframes aiGlowPulse{0%,100%{opacity:.7}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .ai-glow-card::before{animation:none}
  .ai-glow-card.busy::before{animation:none;opacity:1}
}

/* pressable feedback on anything tappable */
html{transition:background-color var(--d2) var(--e-out)}
body{transition:background-color var(--d2) var(--e-out),color var(--d2) var(--e-out),padding-top .42s var(--e)}
/* A saved Light preference should paint as Light on first render, not crossfade
   from the default Dark canvas. Once shell.js marks the initial theme ready,
   normal in-app theme changes keep their existing smooth transition. */
html:not(.theme-ready),html:not(.theme-ready) body{transition:none!important}
.card,.crow,.navin,input,select,textarea,.pill{
  transition:background-color var(--d2) var(--e-out),border-color var(--d2) var(--e-out),
    color var(--d2) var(--e-out),box-shadow var(--d2) var(--e-out)}
button,.crow,.setup-nav,.chip,label.chk,.sug div{
  transition:transform .13s var(--e-out),background .18s var(--e-out),opacity .18s var(--e-out)}
button:active,.crow:active,.setup-nav:active,label.chk:active,.sug div:active{transform:scale(.975)}
.btn:active{transform:scale(.985)}
.navin button:active{transform:scale(.94)}

/* modals: backdrop fades, sheet lifts */
@keyframes bgIn{from{opacity:0}to{opacity:1}}
@keyframes bgOut{from{opacity:1}to{opacity:0}}
@keyframes popIn{from{opacity:0;transform:translate3d(0,14px,0) scale(.965)}to{opacity:1;transform:none}}
@keyframes popOut{from{opacity:1;transform:none}to{opacity:0;transform:translate3d(0,10px,0) scale(.975)}}
.modal{animation:bgIn var(--d1) var(--e-out) both}
.modal>.card{animation:popIn var(--d2) var(--e) both}
.modal.closing{animation:bgOut var(--d1) var(--e-out) both}
.modal.closing>.card{animation:popOut var(--d1) var(--e-out) both}

/* full-screen sheets slide up from the bottom, iOS style */
@keyframes sheetIn{from{transform:translate3d(0,100%,0)}to{transform:none}}
@keyframes sheetOut{from{transform:none}to{transform:translate3d(0,100%,0)}}
.sheet{animation:sheetIn var(--d3) var(--e) both}
.sheet.closing{animation:sheetOut var(--d2) var(--e-out) both}
/* A re-render replaces the overlay's markup, which re-creates these elements
   and replays their opening animation. Opening should animate; redrawing the
   same sheet you are already looking at should not — without this, typing a
   ticker made the whole sheet slide up from the bottom once per keystroke.
   .nosheet is the softer case: the sheet itself holds still while its contents
   crossfade, which is what switching Add mode should feel like. */
.noanim,.noanim>.card,.noanim .pg-fade{animation:none!important}
.nosheet{animation:none!important}
/* Switching Add modes should feel like one sheet changing its mind, not a
   new sheet arriving. The header and form take one short shared beat while
   the fixed sheet surface stays under the finger. */
@keyframes addModeSwap{from{opacity:0;transform:translate3d(8px,0,0)}to{opacity:1;transform:none}}
.add-mode-swap{animation:addModeSwap .24s var(--e-out) both}

/* a card expanding out of a compact row grows into place */
@keyframes cardOpen{from{opacity:0;transform:translate3d(0,-6px,0) scale(.97)}to{opacity:1;transform:none}}
@keyframes cardShut{from{opacity:1;transform:none}to{opacity:0;transform:translate3d(0,-6px,0) scale(.97)}}
.card.opening{animation:cardOpen var(--d3) var(--e) both}
.card.opening>*{animation:pgFade var(--d2) var(--e) both;animation-delay:.06s}
.card.closing{animation:cardShut var(--d1) var(--e-out) both}

/* rows animate only when the page itself changed, or when one is freshly collapsed */
@keyframes rowIn{from{opacity:0;transform:translate3d(0,-6px,0) scale(.985)}to{opacity:1;transform:none}}
.pg-fwd .crow,.pg-back .crow,.pg-fade .crow{animation:rowIn var(--d2) var(--e) both}
.crow.settling{animation:rowIn var(--d2) var(--e) both}

/* suggestions dropdown */
@keyframes sugIn{from{opacity:0;transform:translate3d(0,-8px,0) scale(.98)}to{opacity:1;transform:none}}
.sug{animation:sugIn var(--d1) var(--e) both;transform-origin:top center}

/* toast */
@keyframes toastIn{from{opacity:0;transform:translate3d(-50%,18px,0) scale(.94)}
  to{opacity:1;transform:translate3d(-50%,0,0) scale(1)}}
.toast{animation:toastIn var(--d2) var(--e) both}

/* numbers and bars ease rather than snap */
.hero,.rr{transition:color var(--d1) var(--e-out)}
.bar>i{transition:width var(--d3) var(--e)}
.tag{transition:all var(--d1) var(--e-out)}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important}
}

@keyframes pgFwd{from{opacity:0;transform:translate3d(26px,0,0)}to{opacity:1;transform:none}}
@keyframes pgBack{from{opacity:0;transform:translate3d(-26px,0,0)}to{opacity:1;transform:none}}
@keyframes pgFade{from{opacity:0;transform:translate3d(0,12px,0) scale(.995)}to{opacity:1;transform:none}}
.onboarding-screen{animation:pgFade var(--d2) var(--e) both}
/* Onboarding-specific pieces only — cards, buttons and inputs all reuse the
   app's normal .card/.btn/input styling so the flow feels like the rest of
   Pantau, not a bolted-on wizard. */
.ob-hero{text-align:center;padding:6px 4px 2px}
.ob-mark{width:64px;height:64px;border-radius:50%;background:var(--accentTint);color:var(--act);
  display:inline-flex;align-items:center;justify-content:center;box-shadow:0 0 0 1px var(--accentBorder) inset}
.ob-steps{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11px;color:var(--faint)}
.ob-dot{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;
  background:var(--btn);color:var(--faint);font-size:11px;font-weight:800;flex:0 0 auto;
  transition:background var(--d1) var(--e-out),color var(--d1) var(--e-out)}
.ob-step.on .ob-dot{background:var(--act);color:#fff}
.ob-line{flex:1;height:1px;background:var(--line);min-width:10px;transition:background var(--d1) var(--e-out)}
.ob-line.on{background:var(--act)}
.ob-drawback{display:flex;gap:12px;align-items:flex-start;padding:11px 2px}
.ob-drawback+.ob-drawback{border-top:.5px solid var(--lineS)}
.ob-drawback-ic{flex:0 0 auto;width:22px;height:22px;margin-top:1px;border-radius:50%;background:var(--warnTint);
  color:var(--warn);display:grid;place-items:center;font-size:12px;font-weight:800;font-style:normal}
.ob-drawback>span:last-child{font-size:12.5px;color:var(--dim);line-height:1.55}
.ob-drawback b{color:var(--text)}
.ob-hrow{display:flex;align-items:center;gap:10px;padding:10px 8px}
.ob-hrow+.ob-hrow{border-top:.5px solid var(--lineS)}
.ob-hrow-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.ob-hrow-main b{font-size:14px;font-weight:800}
.ob-hrow-main span{font-size:11.5px;color:var(--faint);font-family:var(--mono)}
.ob-hrow-val{flex:0 0 auto;font-family:var(--mono);font-size:13px;font-weight:700;color:var(--dim)}
.ob-hrow-rm{flex:0 0 auto;width:26px;height:26px;border-radius:50%;background:var(--btn);
  color:var(--faint);font-size:11px;padding:0}
.ob-intro-item{display:flex;gap:14px;align-items:flex-start}
.ob-intro-ic{flex:0 0 auto;width:38px;height:38px;border-radius:11px;background:var(--accentTint);
  color:var(--act);display:grid;place-items:center}
.ob-intro-ic svg{width:19px;height:19px}
.pg-fwd{animation:pgFwd var(--d3) var(--e) both}
.pg-back{animation:pgBack var(--d3) var(--e) both}
.pg-fade{animation:pgFade var(--d2) var(--e) both}
.pg-fwd .card,.pg-back .card{animation:pgFade var(--d2) var(--e) both}
.pg-fwd .card:nth-of-type(2),.pg-back .card:nth-of-type(2){animation-delay:.06s}
.pg-fwd .card:nth-of-type(3),.pg-back .card:nth-of-type(3){animation-delay:.12s}
.pg-fwd .card:nth-of-type(n+4),.pg-back .card:nth-of-type(n+4){animation-delay:.17s}
.setup-nav{transition:background var(--d1) var(--e-out)}
.setup-nav:active{background:var(--btn)}
.setup-group-hd{transition:background var(--d1) var(--e-out)}
.setup-group-hd:active{background:var(--btn)}
.setup-group.open .setup-group-hd{background:var(--btn)}
.pg-none{animation:none}
@media (prefers-reduced-motion:reduce){.onboarding-screen,.pg-fwd,.pg-back,.pg-fade,.add-mode-swap{animation:none}}
.empty{text-align:center;padding:34px 18px;color:var(--dim);font-size:13.5px;line-height:1.65}
.sheet{position:fixed;inset:0;z-index:55;background:var(--ink);display:flex;flex-direction:column}
.sheethd{display:flex;align-items:center;justify-content:space-between;padding:calc(12px + env(safe-area-inset-top)) 16px 12px;border-bottom:.5px solid var(--line);transition:padding-top var(--d2) var(--e)}
/* The syncbar docks to the true top edge at z-index:65 -- above every sheet
   -- on purpose, so background-sync progress is never hidden by whatever's
   open. body.syncing already reserves 50px so ordinary page content clears
   it, but a sheet is position:fixed/inset:0, its own layer on top of body,
   so that padding never reaches it -- its header rendered right under the
   bar's live text with no room of its own. Same 50px, applied here too, for
   exactly as long as the bar is actually showing. */
body.syncing .sheethd{padding-top:calc(12px + env(safe-area-inset-top) + 50px)}
.sheetbody{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:18px 16px calc(30px + env(safe-area-inset-bottom))}
.sheetbody h3{font-size:17px;font-weight:800;letter-spacing:-.02em;margin:26px 0 8px}
.sheetbody h3:first-child{margin-top:0}
.sheetbody p{font-size:14px;line-height:1.65;color:var(--dim);margin:0 0 11px}
.sheetbody b{color:var(--text)}
/* Share performance sheet — controls grouped into one card (segmented
   period picker, @-prefixed username field, toggle row), and a soft glow
   behind the generated canvas preview so it reads as the featured object
   rather than a plain image sitting on the sheet background. */
.share-controls .seg{margin-bottom:0}
.share-username-field{position:relative}
.share-at{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--faint);font-size:14px;font-weight:600;pointer-events:none}
.share-username-field input{padding-left:27px}
.share-toggle-row{display:flex;gap:11px;align-items:center;margin-top:16px;padding-top:16px;border-top:.5px solid var(--lineS);cursor:pointer}
.share-toggle-row b{display:block;font-size:13px;font-weight:700;letter-spacing:-.01em}
.share-toggle-row small{display:block;margin-top:1px;font-size:11px;color:var(--faint);line-height:1.4;font-weight:500}
.share-preview-wrap{position:relative;margin-top:20px;display:flex;justify-content:center}
.share-preview-glow{position:absolute;inset:-10% -6%;background:radial-gradient(closest-side,var(--accentTint),transparent 72%);filter:blur(28px);z-index:0;opacity:.85;pointer-events:none}
.share-preview-wrap canvas{position:relative;z-index:1}
.share-trade-note{display:flex;align-items:center;justify-content:space-between;gap:12px}
.share-trade-note b{font-size:15px;font-weight:700}
.seg.icon-seg button{display:flex;align-items:center;justify-content:center;gap:5px;padding:9px 4px;font-size:12px}
.seg.icon-seg svg{width:14px;height:14px;flex:0 0 auto;opacity:.7}
.seg.icon-seg button.on svg{opacity:1}
/* Compact trade picker -- a closed-by-default button + row list, standing
   in for a native <select> so each row can carry a colored win/loss
   triangle plus a short date instead of one long plain-text line (a
   native list is OS-rendered on mobile and can't be styled at all). */
.share-trade-btn{display:flex;align-items:center;gap:9px;width:100%;background:var(--inputbg);
  border:1px solid var(--inputLine);border-radius:12px;padding:12px 14px;font-size:14px;color:var(--text)}
.share-trade-chev{margin-left:auto;color:var(--faint);font-size:10px}
.share-trade-list{margin-top:6px;max-height:264px;overflow-y:auto;background:var(--inputbg);
  border:1px solid var(--inputLine);border-radius:12px}
.share-trade-list div{display:flex;align-items:center;gap:9px;padding:11px 14px;
  border-top:.5px solid var(--lineS);touch-action:manipulation}
.share-trade-list div:first-child{border-top:none}
.share-trade-list div.sel{background:var(--btn)}
.share-trade-dir{width:14px;flex:0 0 auto;text-align:center;font-size:10px;font-weight:800}
.share-trade-dir.up{color:var(--up)}
.share-trade-dir.down{color:var(--down)}
.share-trade-tk{font-weight:700;font-size:13.5px;letter-spacing:-.01em}
.share-trade-date{margin-left:auto;color:var(--faint);font-size:12px;font-family:var(--mono)}
/* Trades tab's screen/outcome filters -- compact auto-width pills instead
   of two full-width selects, so a secondary "narrow this down" control
   doesn't carry the same visual weight as the sort/density row above it. */
.hist-filter-row{display:flex;align-items:center;gap:8px;margin-bottom:12px;flex-wrap:wrap}
.hist-filter-ic{flex:0 0 auto;color:var(--faint)}
.hist-filter-row select.chip-select{flex:0 0 auto;width:auto;max-width:46%;padding:7px 10px;
  font-size:12.5px;font-weight:600;border-radius:100px;background:var(--inputbg);
  border:1px solid var(--inputLine);color:var(--dim)}
/* Fixed-width icon-button group replacing the old "All/Trades/Dividends"
   text select -- see HIST_TYPE_ICONS' own comment in js/views/history.js
   for why: a predictable ~90px total instead of a variable, text-sized
   select is what keeps .hist-filter-row on one line. */
.hist-type-seg{display:flex;gap:2px;padding:2px;flex:0 0 auto;margin-left:auto;
  background:var(--inputbg);border:1px solid var(--inputLine);border-radius:100px}
.hist-type-seg button{width:27px;height:27px;display:flex;align-items:center;justify-content:center;
  border-radius:100px;color:var(--faint)}
.hist-type-seg button.on{background:var(--btn2);color:var(--act)}
.hist-share-open-btn{width:100%;margin-top:14px;padding:13px;border-radius:14px;
  background:var(--accentTint);color:var(--act);border:1px solid var(--accentBorder);
  font-size:13.5px;font-weight:700;display:flex;align-items:center;justify-content:center;gap:8px}
.hist-share-open-btn svg{width:17px;height:17px;flex:0 0 auto}
/* Phase 4A cohort explorer — dimension-picker pills (up to two "on" at
   once), the per-row confidence-tier badge, and the inline show-raw/hide
   toggle a shrunk "emerging" cohort exposes. */
.cohort-dim-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.cohort-dim-chip{width:auto;padding:6px 12px;font-size:12px;font-weight:600;border-radius:100px;
  background:var(--inputbg);border:1px solid var(--inputLine);color:var(--dim)}
.cohort-dim-chip.on{background:var(--accentTint);border-color:var(--accentBorder);color:var(--act)}
.cohort-tier-badge{font-size:9.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  border:1px solid;border-radius:100px;padding:2px 8px;flex:0 0 auto}
.cohort-expand-btn{width:auto;display:inline;padding:0;color:var(--act);font-weight:600;font-size:11px}
.step{display:flex;gap:12px;margin-bottom:14px}
.stepn{flex:0 0 24px;height:24px;border-radius:50%;background:var(--accentTintSoft);color:var(--act);
  font-size:12px;font-weight:800;display:flex;align-items:center;justify-content:center}
.gloss{padding:12px 0;border-top:.5px solid var(--lineS)}
.gloss b{display:block;font-size:13.5px;margin-bottom:3px}
.gloss span{font-size:13px;color:var(--dim);line-height:1.6}
/* Small visual maps keep the guide useful to a new reader without turning
   each new product behavior into another wall of prose. They are diagrams,
   not interactive cards, and collapse to a clear vertical reading order. */
.gflow{display:grid;gap:8px;margin:11px 0 16px}
.gflow-node{min-width:0;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--inputbg)}
.gflow-node span{display:block;font-size:9px;font-weight:800;letter-spacing:.07em;color:var(--faint);line-height:1.15}
.gflow-node b{display:block;font-size:12px;font-weight:750;line-height:1.3;margin-top:4px;color:var(--text)}
.gflow-node small{display:block;font-size:10.5px;line-height:1.4;color:var(--dim);margin-top:3px}
.gflow-node.is-act{background:var(--accentTint);border-color:var(--accentBorder)}
.gflow-node.is-up{background:var(--upTint);border-color:var(--upBorder)}
.gflow-node.is-warn{background:var(--warnTint);border-color:var(--warnBorder)}
.gflow-node.is-down{background:var(--downTint);border-color:var(--downBorder)}
.gflow-node.is-act span,.gflow-node.is-act b{color:var(--act)}
.gflow-node.is-up span,.gflow-node.is-up b{color:var(--up)}
.gflow-node.is-warn span,.gflow-node.is-warn b{color:var(--warn)}
.gflow-node.is-down span,.gflow-node.is-down b{color:var(--down)}
.gflow-chain{display:flex;align-items:stretch;gap:6px}
.gflow-chain .gflow-node{flex:1 1 0}
.gflow-arrow{display:flex;align-items:center;justify-content:center;color:var(--faint);font-size:16px;font-weight:700;flex:0 0 12px}
.gflow-branch{grid-template-columns:repeat(2,minmax(0,1fr))}
.gflow-steps{grid-template-columns:repeat(4,minmax(0,1fr))}
.gflow-steps .gflow-node{position:relative;padding-left:29px}
.gflow-steps .gflow-node:before{content:attr(data-step);position:absolute;top:10px;left:10px;width:14px;height:14px;border-radius:50%;background:var(--btn2);color:var(--dim);font-family:var(--mono);font-size:9px;font-weight:800;line-height:14px;text-align:center}
.gflow-caption{font-size:11.5px;line-height:1.5;color:var(--dim);margin:-4px 0 3px}
/* A visual primer appears before the detailed copy in every guide module.
   The diagrams share a clear left-to-right reading order, while their short
   labels wrap safely on a narrow phone instead of forcing a horizontal scroll. */
.gprimer{margin:3px 0 18px;padding:11px 0 14px;border-bottom:.5px solid var(--lineS)}
.gprimer-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.gprimer-head span{font-size:9px;font-weight:800;letter-spacing:.08em;line-height:1.2;color:var(--faint)}
.gprimer-head b{font-size:13px;font-weight:760;line-height:1.3;text-align:right}
.gprimer-flow{display:flex;align-items:stretch;gap:5px;margin-top:10px}
.gprimer-step{position:relative;flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;
  text-align:center;padding:10px 5px 9px;border:1px solid var(--line);border-radius:8px;background:var(--inputbg)}
.gprimer-step.is-act{background:var(--accentTint);border-color:var(--accentBorder);color:var(--act)}
.gprimer-step.is-up{background:var(--upTint);border-color:var(--upBorder);color:var(--up)}
.gprimer-step.is-warn{background:var(--warnTint);border-color:var(--warnBorder);color:var(--warn)}
.gprimer-step.is-down{background:var(--downTint);border-color:var(--downBorder);color:var(--down)}
.gprimer-num{position:absolute;top:7px;left:7px;width:13px;height:13px;border-radius:50%;background:var(--btn2);
  color:var(--dim);font-family:var(--mono);font-size:8px;font-weight:800;line-height:13px;text-align:center}
.gprimer-icon{width:29px;height:29px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:var(--btn2);color:var(--dim);font-style:normal}
.gprimer-step.is-act .gprimer-icon{background:var(--accentTintSoft);color:var(--act)}
.gprimer-step.is-up .gprimer-icon{background:var(--upTintStrong);color:var(--up)}
.gprimer-step.is-warn .gprimer-icon{background:var(--warnTintStrong);color:var(--warn)}
.gprimer-step.is-down .gprimer-icon{background:var(--downTintStrong);color:var(--down)}
.gdet .gdetbody .gprimer-icon svg{display:block;width:16px;height:16px;margin:0}
.gprimer-step b{margin-top:7px;font-size:11.5px;font-weight:800;line-height:1.2;overflow-wrap:anywhere}
.gprimer-step small{display:block;margin-top:3px;font-size:9.5px;line-height:1.3;color:var(--dim);overflow-wrap:anywhere}
.gprimer-arrow{display:flex;align-items:center;justify-content:center;flex:0 0 10px;color:var(--faint);font-size:14px;font-style:normal}
.gprimer figcaption{margin-top:9px;font-size:11.5px;line-height:1.45;color:var(--dim)}
@media(max-width:560px){
  .gflow-chain{display:grid;grid-template-columns:1fr}
  .gflow-chain .gflow-arrow{width:auto;height:10px;flex:auto;transform:rotate(90deg)}
  .gflow-steps{grid-template-columns:repeat(2,minmax(0,1fr))}
  .gprimer{margin-bottom:16px}
  .gprimer-head{align-items:flex-start;flex-direction:column;gap:3px}
  .gprimer-head b{text-align:left}
  .gprimer-flow{gap:4px}
  .gprimer-step{padding:9px 4px 8px}
  .gprimer-step b{font-size:10.5px}
  .gprimer-step small{font-size:9px}
  .gprimer-arrow{flex-basis:8px;font-size:12px}
}
.gjump{display:flex;gap:7px;overflow-x:auto;padding-bottom:14px;margin-bottom:6px;
  border-bottom:.5px solid var(--lineS);-webkit-overflow-scrolling:touch;scrollbar-width:none}
.gjump::-webkit-scrollbar{display:none}
.gjumpbtn{flex:0 0 auto;padding:8px 13px;border-radius:100px;background:var(--btn);
  color:var(--dim);font-size:12px;font-weight:600;white-space:nowrap}
.gdet{border-bottom:.5px solid var(--lineS)}
.gdet:last-child{border-bottom:none}
.gdet summary{display:flex;align-items:center;justify-content:space-between;gap:10px;
  list-style:none;cursor:pointer;padding:15px 2px;font-size:15px;font-weight:700;letter-spacing:-.01em}
.gdet summary::-webkit-details-marker{display:none}
.gchev{color:var(--faint);font-size:17px;transition:transform .18s ease;flex:0 0 auto}
.gdet[open] .gchev{transform:rotate(90deg)}
.gdet .gdetbody{padding:0 2px 18px}
.gdet .gdetbody svg{margin:4px 0 12px}
.chk2{display:flex;gap:11px;align-items:flex-start;padding:13px 0;border-top:.5px solid var(--lineS)}
.chk2 i{flex:0 0 20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:800;font-style:normal;margin-top:1px}
.chk2 b{display:block;font-size:13.5px;font-weight:600;margin-bottom:2px}
.chk2 span{font-size:12.5px;color:var(--dim);line-height:1.5}
.tally{display:flex;align-items:baseline;gap:8px;margin:4px 0 2px}
.tally b{font-size:34px;font-weight:800;letter-spacing:-.03em}
.qbtn{width:30px;height:30px;border-radius:50%;background:var(--btn);border:.5px solid var(--line);
  color:var(--dim);font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center}
/* The header's Explain/Minimal switch. Icon only — three lines shrinking to
   one reads as "how much text" without needing a label, and the same shape
   the toggle already uses in Setup means it's recognizable either place.
   Filled when Explain is on, outline otherwise, so the state reads even
   before the icon registers. */
.modebtn{position:relative;width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:var(--btn);border:.5px solid var(--line);color:var(--dim)}
.modebtn.on{background:var(--accentTint);border-color:transparent;color:var(--act)}
.modebtn .notifbadge{position:absolute;top:-4px;right:-4px;min-width:15px;height:15px;padding:0 3px;
  border-radius:100px;background:var(--warn);color:#1a1300;font-family:var(--mono);
  font-size:9px;font-weight:800;line-height:15px;text-align:center;box-shadow:0 0 0 2px var(--glass)}
/* A side drawer, not a fifth overlay family cloned from .modal/.sheet -- it
   is meant to survive a tab switch (the whole point: notifications aren't
   part of any one tab's content), so it slides in from the edge over
   whatever tab is showing instead of taking the full screen like .sheet or
   centring like .modal. Same bgIn/bgOut scrim timing as .modal for a
   consistent feel, its own slide for the panel itself.

   The panel itself floats, inset from every edge, the same idea as macOS's
   own Notification Center rather than a flush industrial side-panel -- a
   full-bleed rectangle with one rounded seam read as sharp/dated next to
   everything else in this app. Inset lets every corner round, .glass (the
   same translucent material .modal's own card already uses, defined for
   both themes -- rgba navy in dark, rgba white in light, see :root) gives
   it real depth instead of a flat fill, and --shadowLg (already theme-
   tuned: a soft dark spread in dark mode, a tighter neutral one in light)
   makes it read as a floating layer rather than a hard-edged rectangle
   pasted onto the scrim. */
.drawer-scrim{position:fixed;inset:0;background:var(--modalScrim);backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);z-index:56;display:flex;justify-content:flex-end;
  padding:calc(10px + env(safe-area-inset-top)) 10px calc(10px + env(safe-area-inset-bottom))}
/* The syncbar docks at z-index:65 -- above this drawer's own 56 -- so it
   was drawing straight over the drawer's rounded top edge during an active
   sweep, caught live as the two visibly overlapping. .sheethd already
   solves this exact problem the same way (see its own comment just above):
   reserve the syncbar's real height only while it's actually showing. */
body.syncing .drawer-scrim{padding-top:calc(10px + env(safe-area-inset-top) + 50px)}
.drawer{width:min(380px,92vw);max-height:100%;background:var(--glass);
  backdrop-filter:blur(28px);-webkit-backdrop-filter:blur(28px);
  display:flex;flex-direction:column;border:1px solid var(--line);border-radius:24px;
  box-shadow:var(--shadowLg);overflow:hidden}
.drawer-hd{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:16px 18px 12px;border-bottom:.5px solid var(--lineS)}
.drawer-body{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:12px 14px 18px}
/* One soft-edged box per category ("Decisions needed", "Book risk", ...)
   instead of every notice lumped into one undifferentiated scroll -- a
   tonal fill (--btn, the same subtle raised surface buttons already use)
   reads as a distinct layer against .drawer's own glass without a second
   heavy border competing with the panel's own, at a slightly smaller
   radius to suit the drawer's narrower width. */
.notif-group{background:var(--btn);border-radius:16px;padding:4px 14px;margin-bottom:10px}
.notif-group-hd{display:flex;align-items:center;gap:8px;padding:10px 0;
  font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.notif-group-ct{min-width:16px;height:16px;padding:0 4px;border-radius:100px;background:var(--card);
  color:var(--dim);font-family:var(--mono);font-size:9.5px;font-weight:800;line-height:16px;text-align:center}
.notif-row{padding:10px 0;display:flex;justify-content:space-between;gap:10px;align-items:baseline;
  border-top:.5px solid var(--lineS)}
.notif-row:first-of-type{border-top:none}
@keyframes drawerIn{from{transform:translate3d(100%,0,0)}to{transform:none}}
@keyframes drawerOut{from{transform:none}to{transform:translate3d(100%,0,0)}}
.drawer-scrim{animation:bgIn var(--d1) var(--e-out) both}
.drawer-scrim>.drawer{animation:drawerIn var(--d2) var(--e) both}
.drawer-scrim.closing{animation:bgOut var(--d1) var(--e-out) both}
.drawer-scrim.closing>.drawer{animation:drawerOut var(--d1) var(--e-out) both}
/* Autopilot: four scores, one row. Deliberately the same visual weight as the
   existing tally so nothing here reads as more authoritative than the filing.
   .cols6 is the same grid with Sector and Insider added, wrapped to 3 across
   over 2 rows rather than 6 crammed into one — six equal 1fr tracks left
   each cell too narrow for "DURABILITY" to fit, and a plain 1fr track
   doesn't shrink below its content's own min-content width by default, so
   the row pushed past the card's edge instead of wrapping. minmax(0,1fr) on
   every track is the actual fix for that (belt-and-suspenders here, since
   3-across gives each cell room to spare regardless), and it's applied to
   the base rule too so a future column count doesn't reopen the same bug. */
.afgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-top:12px}
.afgrid.cols6{grid-template-columns:repeat(3,minmax(0,1fr))}
.afm{background:var(--inputbg);border-radius:12px;padding:8px 9px 9px}
.afm span{display:block;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
.afm b{font-size:15px;font-weight:800;font-family:var(--mono);letter-spacing:-.02em}
.afbar{height:5px;border-radius:100px;background:var(--line);overflow:hidden;margin-top:5px}
.afbar i{display:block;height:100%;border-radius:100px;transition:width .5s cubic-bezier(.22,1,.36,1)}
.afnotes div:before{background:var(--warn)!important}
.afsrc{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:10px}
