/* ===== Ascend — net worth, calmly tracked. Cream + forest-green aesthetic ===== */
/* Base theme is a warm dark; the light theme below is the brand's primary look
   (cream paper + forest green), matching the app icon. */
:root {
  /* Dark mode is the brand in a different lighting — a warm forest near-black, not
     a cool neutral charcoal. Surfaces lean green-brown so the cream/forest identity
     survives at night. */
  --bg: #14160e;
  --surface: #1c1f15;
  --surface-2: #262a1d;
  --hover: rgba(255,255,255,.045);
  --active: rgba(255,255,255,.09);
  --shadow-color: 0,0,0;    /* dark mode: neutral black shadows */
  --line: rgba(255,255,255,.085);
  --line-strong: rgba(255,255,255,.15);
  --text: #f0efe6;
  --muted: #9ba293;
  --muted-2: #8b9080;       /* ~5:1 on the dark bg — WCAG AA (was #767d73, ~4.3:1) */
  --accent: #49a679;        /* forest green, lifted for dark legibility (was minty #4caf82) */
  --accent-soft: rgba(73,166,121,.16);
  --positive: #5cc78d;      /* gains/up — brighter than brand so directional change pops */
  --red: #e8675a;
  --red-soft: rgba(232,103,90,.16);
  --warn: #d99a2b;          /* caution/amber (e.g. mid Monte-Carlo success) — ~7.6:1 on dark */
  --skel: #262a1d;
  --skel-hi: #333829;
  --radius: 22px;
  --radius-sm: 14px;
  --ease: cubic-bezier(.22,.61,.36,1);
  /* Motion vocabulary (feel pass): --ease-out decelerates into place (entrances),
     --ease-spring adds a whisper of overshoot (sheet/toast arrivals, pops).
     Keep durations inside 150–300ms so feedback reads as response, not delay. */
  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-spring: cubic-bezier(.34,1.35,.64,1);
  --dur-view: .24s;
  /* Clearance above the fixed tab bar — one source for the FAB / coachmark / toast
     bottom offsets so they no longer drift as three hand-tuned magic numbers. */
  --tabbar-h: 64px;
  /* ===== Dynamic Type — a semantic rem type-scale that respects iOS Dynamic Type
     (the -apple-system-body probe in app.js sets --dynamic-type-scale) AND browser/OS
     text-size (rem responds to the root). --dynamic-type-scale defaults to 1 so the app
     is never un-styled if the probe doesn't run; the chrome/dense tiers CAP their
     response so the fixed tab bar and dense rows don't blow out. Each value equals the
     px it replaces at the 16px default root, so default rendering is unchanged. ===== */
  --dynamic-type-scale: 1;
  --fs-nano:    calc(0.625rem  * min(var(--dynamic-type-scale, 1), 1.25)); /* 10 — chrome (tabbar) */
  --fs-micro:   calc(0.6875rem * min(var(--dynamic-type-scale, 1), 1.35)); /* 11 — dense labels / chart axis */
  --fs-small:   calc(0.75rem   * var(--dynamic-type-scale, 1));  /* 12 */
  --fs-caption: calc(0.8125rem * var(--dynamic-type-scale, 1));  /* 13 (also 13.5) */
  --fs-label:   calc(0.875rem  * var(--dynamic-type-scale, 1));  /* 14 */
  --fs-sub:     calc(0.9375rem * var(--dynamic-type-scale, 1));  /* 15 */
  --fs-body:    calc(1rem      * var(--dynamic-type-scale, 1));  /* 16 */
  --fs-lg:      calc(1.125rem  * var(--dynamic-type-scale, 1));  /* 18 (also 17) */
  --fs-h2:      calc(1.25rem   * var(--dynamic-type-scale, 1));  /* 20 (also 19) */
  --fs-title:   calc(1.375rem  * var(--dynamic-type-scale, 1));  /* 22 (also 21) */
  --fs-display: calc(1.5rem    * var(--dynamic-type-scale, 1));  /* 24 (also 26) */
}
/* Root at 100% = 16px, user-adjustable; the rem tokens above ride it. text-size-adjust
   :100% stops mobile browsers from auto-inflating text and fighting the scale. */
html { font-size:100%; -webkit-text-size-adjust:100%; }
[data-theme="light"] {
  --bg: #f3eee3;            /* warm cream paper */
  --surface: #fbf9f2;       /* ivory cards */
  --surface-2: #eae3d4;     /* warm tan tiles / tracks */
  --hover: rgba(40,52,40,.045);
  --active: rgba(40,52,40,.08);
  --shadow-color: 31,42,35;  /* light mode: forest-ink shadows, never pure black on cream */
  --line: rgba(40,52,40,.10);
  --line-strong: rgba(40,52,40,.17);
  --text: #1f2a23;          /* deep forest charcoal */
  /* Muted/semantic tokens are deepened so they clear WCAG AA (4.5:1) on the TAN
     --surface-2 tiles (.stat/.kstat/.rd-stat), not just on the lighter cream --bg —
     the tan tiles dropped the prior values to ~4.1:1. */
  --muted: #5d6753;         /* ~4.7:1 on the tan tiles, ~5.1:1 on cream */
  --muted-2: #5a6450;       /* ~4.9:1 on tan tiles */
  --accent: #2a6f53;        /* forest green — ~4.7:1 on tan tiles, AA for text */
  --accent-soft: rgba(42,111,83,.14);
  --positive: #237046;      /* gains/up — distinct from brand; ~4.8:1 on tan tiles */
  --red: #af4029;           /* warm terracotta-red — ~4.6:1 on tan tiles */
  --red-soft: rgba(175,64,41,.10);
  --warn: #855c0f;          /* caution/amber — deep ochre, ~4.6:1 on the tan tiles */
  --skel: #e7e0d2;
  --skel-hi: #f5f1e8;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-feature-settings: "ss01", "cv01";
  /* Tabular figures app-wide so every stacked number column (stats, returns, P/E grid,
     reconciliation, holdings) aligns digit-for-digit instead of wobbling. */
  font-variant-numeric: tabular-nums;
}
::selection { background: var(--accent-soft); }
/* Visually hidden but available to assistive tech (offscreen live regions, etc.). */
.sr-only { position:absolute !important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* Horizontal insets keep content clear of the notch in landscape; the large
   bottom inset clears the fixed tab bar + floating Sync pill + home indicator. */
/* Bottom padding reserves a FAB exclusion zone so that at scroll-end the last row (often
   a right-aligned section-head action like "Follow shows") clears the fixed Ask-Ascend
   FAB instead of sitting under it (design review #3). Mid-scroll the FAB legitimately
   floats over reachable content and hides while scrolling; docking it into the tab bar is
   the fuller fix (structural). */
.wrap { max-width: 880px; margin: 0 auto; padding: 22px max(20px, env(safe-area-inset-right)) calc(148px + env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }

/* ---------- top bar ---------- */
/* padding-top absorbs the iOS status bar / Dynamic Island so the brand row
   never sits under it (the bar's var(--bg) fills the inset area). */
/* Pin to its OWN compositor layer (same trick the .tabbar uses). Without it, iOS
   caches this sticky bar's painted background in the main scroll layer and does NOT
   invalidate it when --bg flips on a light/dark toggle — so the bar keeps the OLD
   theme colour as a strip across the top (incl. the safe-area inset) until a scroll
   forces a repaint. On its own layer, the theme-flip style recalc repaints it at once. */
header.topbar { display:flex; align-items:center; justify-content:space-between; gap:14px; margin-bottom:30px; position:sticky; top:0; z-index:40; background:var(--bg); padding:calc(12px + env(safe-area-inset-top)) 0 12px; transform:translate3d(0,0,0); will-change:transform; -webkit-backface-visibility:hidden; }
header.topbar.scrolled { border-bottom:1px solid var(--line); }
.brand { display:flex; align-items:center; gap:11px; min-width:0; background:none; border:none; padding:0; margin:0; font:inherit; color:inherit; cursor:pointer; text-align:left; }
.brand:active { transform:scale(.98); }
/* Net worth slides into the header once the hero scrolls out of view. */
.nav-nw { font-size:var(--fs-body); font-weight:600; letter-spacing:-.01em; font-variant-numeric:tabular-nums; opacity:0; max-width:0; overflow:hidden; white-space:nowrap; transition:opacity .2s var(--ease); }
header.topbar.scrolled .nav-nw { opacity:1; max-width:200px; }
header.topbar.scrolled .brand h1 { display:none; }
.logo {
  width:34px; height:34px; border-radius:10px; overflow:hidden;
  display:grid; place-items:center; flex:none;
  /* Forest tile + cream "summit" mark — a brand constant in both themes, matching
     the home-screen icon (so the in-app mark and the app icon are the same mark). */
  background:#2c7457; color:#f3eee3;
}
.logo img { width:100%; height:100%; object-fit:cover; display:block; }
.logo svg { width:58%; height:58%; display:block; }
.brand h1 { font-size:var(--fs-body); margin:0; font-weight:600; letter-spacing:-.01em; }
.brand p { display:none; }
.topbar-actions { display:flex; gap:6px; align-items:center; }

/* buttons */
/* Inherit the theme text color: <button> defaults to the UA color (near-black),
   which reads fine on cream but is unreadable on dark surfaces. Buttons that need
   a specific color (.btn-primary, .seg button, …) set their own and override this.
   Without it, content-style buttons (.rundown-card, .rd-hold) go black in dark mode. */
button { font:inherit; color:inherit; cursor:pointer;
  /* Scope the transition to the properties that animate, not `all` — animating `all`
     eased JS-toggled state classes (hidden, active) in unintended ways. */
  transition-property: color, background-color, border-color, transform, filter, box-shadow, opacity;
  transition-duration:.18s; transition-timing-function:var(--ease); }
.btn-primary {
  background:var(--accent); color:#00210f; border:none; font-weight:600; font-size:var(--fs-label);
  padding:11px 18px; border-radius:999px; display:inline-flex; align-items:center; gap:7px;
}
[data-theme="light"] .btn-primary { color:#fff; }
.btn-primary:hover { filter:brightness(1.08); }
.btn-primary:disabled { opacity:.4; cursor:default; filter:none; }
.btn-ghost {
  background:transparent; color:var(--text); border:1px solid var(--line-strong);
  padding:9px 14px; border-radius:999px; font-size:var(--fs-caption); display:inline-flex; align-items:center; gap:7px;
}
/* display:inline-flex above outranks the UA [hidden]{display:none}, so the hidden
   attribute alone wouldn't hide a ghost button (e.g. #podcastManageBtn when there are
   no shows, #rundownRefreshBtn by default). Restore it. */
.btn-ghost[hidden] { display:none; }
.btn-ghost:hover { background:var(--hover); border-color:var(--text); }
.icon-only { width:38px; height:38px; padding:0; justify-content:center; border-radius:999px; }
.btn-danger { color:var(--red); border:1px solid transparent; background:transparent; padding:9px 14px; border-radius:999px; }
.btn-danger:hover { border-color:var(--red); }

/* Unified connect picker */
.connect-list { display:flex; flex-direction:column; gap:6px; max-height:46vh; overflow:auto; margin:4px 0 2px; }
.connect-row { display:flex; align-items:center; gap:12px; width:100%; text-align:left; background:var(--surface-2); border:1px solid transparent; border-radius:var(--radius-sm); padding:10px 12px; cursor:pointer; font-size:var(--fs-label); color:var(--text); }
.connect-row:hover { background:var(--hover); border-color:var(--line); }
.connect-row .clogo { width:30px; height:30px; border-radius:8px; object-fit:contain; background:#fff; flex:none; }
.connect-row .cfallback { width:30px; height:30px; border-radius:8px; flex:none; display:flex; align-items:center; justify-content:center; background:var(--surface); color:var(--muted); font-size:var(--fs-label); }
.connect-row .cname { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.connect-row .cvia { font-size:var(--fs-micro); color:var(--muted-2); flex:none; }
.connect-row.cother { background:transparent; border:1px dashed var(--line); margin-top:2px; }
.connect-row.cother:hover { background:var(--hover); }
.connect-row.cother .cfallback { background:transparent; }
/* Pro-locked upsell row in the connect picker (free users). */
.connect-row.cupsell { background:var(--accent-soft); border:1px solid transparent; margin-top:2px; }
.connect-row.cupsell:hover { border-color:var(--accent); }
.connect-row.cupsell .cvia { color:var(--accent); font-weight:600; }
.connect-row.cupsell .cfallback { background:transparent; }

.inv-code { font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:var(--fs-sub); font-weight:600; letter-spacing:.04em; color:var(--accent); }

/* RentCast property list (inside the Properties modal) */
.prop-list { display:flex; flex-direction:column; gap:8px; margin:4px 0 6px; }
.prop-list:empty { display:none; }
.prop-row { display:flex; align-items:center; gap:10px; background:var(--surface-2); border-radius:var(--radius-sm); padding:10px 12px; }
.prop-row .prop-addr { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; cursor:pointer; }
.prop-row .prop-addr:hover { opacity:.8; }
.prop-row .prop-addr > :first-child, .prop-row .prop-addr { font-size:var(--fs-label); }
.prop-meta { font-size:var(--fs-small); color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.prop-meta.dim { font-style:italic; opacity:.85; }
.prop-row .prop-del { color:var(--muted); border:none; background:transparent; font-size:var(--fs-lg); line-height:1; padding:2px 6px; border-radius:8px; align-self:flex-start; }
.prop-row .prop-del:hover { color:var(--red); background:var(--hover); }
.prop-row .prop-reconnect { flex:none; align-self:center; white-space:nowrap; border:none; background:var(--accent); color:#00210f; font:inherit; font-size:var(--fs-caption); font-weight:600; padding:7px 12px; border-radius:9px; cursor:pointer; }
[data-theme="light"] .prop-row .prop-reconnect { color:#fff; }
.prop-row .prop-reconnect:hover { filter:brightness(1.05); }
.prop-row .prop-retry { flex:none; align-self:center; white-space:nowrap; border:1px solid var(--line-strong);
  background:transparent; color:var(--text); font:inherit; font-size:var(--fs-small); font-weight:600; padding:6px 9px; border-radius:9px; cursor:pointer; }
.manage-subtitle { margin:18px 0 8px; font-size:var(--fs-small); font-weight:650; color:var(--muted); text-transform:uppercase; letter-spacing:.04em; }
.reconciliation-list { display:flex; flex-direction:column; gap:7px; }
.recon-row { display:grid; grid-template-columns:1fr auto; gap:3px 12px; padding:10px 12px; border-radius:var(--radius-sm); background:var(--surface-2); }
.recon-name { font-size:var(--fs-caption); font-weight:600; }
.recon-net { font-size:var(--fs-label); font-weight:650; }
.recon-meta { grid-column:1 / -1; font-size:var(--fs-micro); color:var(--muted); }
.history-import-actions, .scenario-actions { display:flex; gap:7px; flex-wrap:wrap; }
.prop-refresh-bar { display:flex; align-items:center; gap:10px; margin-top:10px; }
.prop-refresh-bar .btn-ghost:disabled { opacity:.5; cursor:not-allowed; }
.prop-refresh-note { font-size:var(--fs-small); color:var(--muted); }

/* ---------- hero ---------- */
.hero { text-align:left; margin-bottom:30px; }
.metric-label { color:var(--muted); font-size:var(--fs-label); font-weight:500; letter-spacing:-.01em; }
.metric-value { font-size:clamp(2.75rem, max(9vw, calc(2.75rem * var(--dynamic-type-scale, 1))), 4.25rem); font-weight:700; line-height:1.02; letter-spacing:-.03em; margin:6px 0 12px; }
.metric-sub { font-size:var(--fs-sub); color:var(--muted); display:flex; align-items:center; gap:9px; flex-wrap:wrap; }
.pill { display:inline-flex; align-items:center; gap:5px; font-size:var(--fs-sub); font-weight:600; }
.pill.up { color:var(--positive); }
.pill.down { color:var(--red); }
.pill .dim { color:var(--muted); font-weight:500; }
/* Grace-in-the-red context line under a down hero delta. flex-basis:100% drops it to its
   own row in the flex-wrap subtitle; muted + calm, never colored like a gain. */
.hero-resilience { flex-basis:100%; color:var(--muted); font-size:var(--fs-caption); margin-top:2px; }

/* small stat tiles */
.stats { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:30px; }
.stat {
  background:var(--surface-2); border-radius:var(--radius-sm); padding:16px 18px;
}
.stat-label { font-size:var(--fs-caption); color:var(--muted); }
.stat-value { font-size:var(--fs-title); font-weight:650; margin-top:4px; letter-spacing:-.02em; }
.stat-value.green { color:var(--positive); }
.stat-value.red { color:var(--red); }
.stat-sub { font-size:var(--fs-small); color:var(--muted-2); margin-top:3px; }

/* ---------- sections ---------- */
.section { margin-bottom:34px; }
.section-head { display:flex; align-items:center; justify-content:space-between; margin:0 0 14px; gap:12px; }
.section-head h2 { font-size:var(--fs-h2); font-weight:600; margin:0; letter-spacing:-.02em; }
.section-head .hint { color:var(--muted); font-size:var(--fs-caption); }
.trend-meta { display:inline-flex; align-items:center; gap:12px; }

.chartbox { width:100%; height:200px; }
svg#lineChart { display:block; width:100%; height:100%; overflow:visible; }
.axis-label { fill:var(--muted-2); font-size:var(--fs-micro); }

.empty { color:var(--muted); font-size:var(--fs-label); text-align:center; padding:34px 16px; }

/* Boot load-failure / retry state — never strand the user on infinite skeletons. */
.load-error { min-height:62vh; display:flex; align-items:center; justify-content:center; padding:32px 24px; }
.load-error[hidden] { display:none; }
.load-error-card { text-align:center; max-width:320px; }
.load-error-ic { width:56px; height:56px; border-radius:16px; margin:0 auto 16px; display:grid; place-items:center; background:var(--red-soft); color:var(--red); }
.load-error-ic svg { width:26px; height:26px; }
.load-error-card strong { display:block; font-size:var(--fs-lg); font-weight:600; letter-spacing:-.02em; margin-bottom:6px; }
.load-error-card p { font-size:var(--fs-label); line-height:1.5; color:var(--muted); margin:0 0 18px; }

/* Markets orientation/empty state — a primary tab must never paint blank. */
.markets-empty { text-align:center; padding:72px 24px; max-width:340px; margin:0 auto; }
.markets-empty[hidden] { display:none; }
.markets-empty-ic { width:58px; height:58px; border-radius:17px; margin:0 auto 18px; display:grid; place-items:center; background:var(--accent-soft); color:var(--accent); }
.markets-empty-ic svg { width:27px; height:27px; }
.markets-empty h2 { font-size:var(--fs-h2); font-weight:600; letter-spacing:-.02em; margin:0 0 8px; }
.markets-empty p { font-size:var(--fs-label); line-height:1.5; color:var(--muted); margin:0; }

/* ---------- touch feedback + hit targets ---------- */
/* iOS has no :hover, so every tappable needs a pressed state. Buttons/pills compress;
   list rows highlight. (.tabbar-btn/.settings-row/.ai-fab/.brand already had theirs.) */
.btn-primary:active, .btn-ghost:active, .btn-danger:active, .icon-only:active,
.copilot-chip:active, .seg button:active, .connect-row:active, .earn-more:active,
.digest-cat-chip:active, .pill-btn:active,
.research-shelf-toggle:active, .proj-adjust-btn:active, .wrapped-nudge:active,
.btn-share:active, .research-chip:active, .scenario-del:active, .row-info:active { transform: scale(.97); }
.acct-row:active, .h-row:active, .earn-row.tappable:active, .news-item:active,
.prop-row:active, .rundown-card:active, .insight-card:active,
.defi-bk-row:active, .spend-month:active,
.rd-hold:active, .signal-sg-row:active, .ai-flag-row:active, .proj-acct:active,
.defi-pos:active, .nw-review:active, .prop-addr:active { background: var(--active); }
/* Small round controls (close X, per-row actions, chips): a .97 squeeze is invisible
   at 30px — they compress harder so the press actually registers under a thumb. */
.overlay-x:active, .digest-x:active, .insight-close:active, .rundown-card-dismiss:active,
.install-hint-close:active, .prop-del:active, .prop-reconnect:active, .prop-retry:active,
.signal-share:active, .signal-remove:active, .kstat-info:active,
.signal-explore-chip:active, .podcast-chip:active, .podcast-jump:active,
.podcast-showmore:active, .research-dismiss:active { transform: scale(.9); }
/* Press-in is near-instant so the button feels attached to the finger; the release
   springs back on each element's own (slower) base transition. */
button:active { transition-duration: .07s; }
/* Expand small close/delete controls to a 44pt touch target (HIG min) without changing
   their look — an invisible centered ::before, same trick as .kstat-info.
   (.scenario-del is already position:absolute, so it's its own positioning context.) */
.overlay-x, .rundown-card-dismiss, .install-hint-close, .prop-row .prop-del { position: relative; }
.overlay-x::before, .rundown-card-dismiss::before, .install-hint-close::before,
.digest-x::before, .insight-close::before, .prop-row .prop-del::before, .scenario-del::before,
.research-dismiss::before {
  content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:44px; height:44px;
}

/* ---------- holdings ---------- */
.h-list { display:flex; flex-direction:column; }
.h-row {
  display:flex; align-items:center; gap:14px; padding:13px 4px;
  border-top:1px solid var(--line); transition:.15s var(--ease);
}
.h-row:first-child { border-top:none; }
.h-row:hover { background:var(--hover); }
/* Reserve a right gutter so the fixed icon FAB (right:16px, ~68px arc) never clips a
   right-aligned holding value AT REST — scroll-to-hide only covers the in-motion case.
   Scoped to holdings so Overview/Accounts rows are untouched. */
#holdingsList .h-row { padding-right:60px; }
.logo-wrap { position:relative; width:40px; height:40px; flex:none; border-radius:50%; overflow:hidden; }
.logo-fallback {
  position:absolute; inset:0; display:grid; place-items:center;
  color:#fff; font-size:var(--fs-label); font-weight:700; letter-spacing:.2px;
}
.logo-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; background:#fff; }
/* Stock/ticker brand logos (NOT institution logos) are square 100x100 images,
   and many are full-bleed wordmarks (SANDISK, LUMENTUM, Micron, AOI) whose text
   spans edge to edge. A circular mask clips those ends — the "too zoomed in /
   messed up" look. Render them as rounded squares instead so the whole mark
   shows; object-fit:contain keeps any rare non-square logo from cropping. */
.logo-wrap:not(.acct-logo) { border-radius:11px; }
.logo-wrap:not(.acct-logo) .logo-img { object-fit:contain; }
.cash-logo { background:var(--accent); color:#00210f; font-size:var(--fs-h2); font-weight:800; }
[data-theme="light"] .cash-logo { color:#fff; }
.h-main { flex:1; min-width:0; }
/* Ticker + company name share one line; the name takes the slack and truncates. */
.h-id { display:flex; align-items:baseline; gap:7px; min-width:0; }
.h-ticker { font-size:var(--fs-sub); font-weight:600; letter-spacing:-.01em; flex:none; }
.h-name { font-size:var(--fs-caption); color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; flex:1 1 auto; }
.h-right { text-align:right; flex:none; }
.h-value { font-size:var(--fs-sub); font-weight:600; letter-spacing:-.01em; }
.h-shares { font-size:var(--fs-caption); color:var(--muted); margin-top:2px; }
/* The live per-share price — the figure members watch closest. Full-contrast and
   tabular so it reads at a glance against the muted share count beside it. */
.h-price { color:var(--text); font-weight:600; font-variant-numeric:tabular-nums; }
/* Position concentration (this holding's % of the book). Quiet by default; amber +
   weighted when a single name dominates (>=25%), so a concentrated portfolio reads
   as a caution at a glance. Tabular so the % aligns across rows. */
.h-weight { font-variant-numeric:tabular-nums; white-space:nowrap; }
.h-weight.hot { color:var(--warn); font-weight:600; }

/* ---------- breakdown ---------- */
.bar-row { margin-bottom:15px; }
.bar-top { display:flex; justify-content:space-between; font-size:var(--fs-label); margin-bottom:7px; }
.bar-top .name { display:flex; align-items:center; gap:9px; color:var(--text); }
.dot { width:9px; height:9px; border-radius:50%; }
.bar-track { height:6px; background:var(--surface-2); border-radius:999px; overflow:hidden; }
.bar-fill { height:100%; border-radius:999px; transition:width .5s var(--ease); }

/* segmented control */
.seg { display:inline-flex; background:var(--surface-2); border-radius:999px; padding:3px; gap:2px; }
.seg button { border:none; background:transparent; padding:7px 15px; border-radius:999px; color:var(--muted); font-size:var(--fs-caption); font-weight:500; transition:.15s var(--ease); position:relative; }
/* 44pt tap target (HIG) without inflating the compact pill height — invisible centered expander. */
.seg button::before { content:""; position:absolute; left:0; right:0; top:50%; transform:translateY(-50%); height:44px; }
.seg button.active { background:var(--surface); color:var(--text); box-shadow:0 1px 4px rgba(0,0,0,.3); }
[data-theme="light"] .seg button.active { background:#fff; box-shadow:0 1px 3px rgba(var(--shadow-color),.12); }

/* ---------- accounts ---------- */
.acct-toolbar { display:flex; gap:12px; align-items:center; margin-bottom:8px; flex-wrap:wrap; }
.acct-toolbar h2 { font-size:var(--fs-h2); font-weight:600; margin:0; letter-spacing:-.02em; }
.acct-toolbar .spacer { flex:1; }
.acct-logo .logo-img { background:#fff; }
.acct-row {
  display:flex; align-items:flex-start; gap:14px; padding:14px 4px;
  border-top:1px solid var(--line); transition:.15s var(--ease); cursor:pointer;
}
.acct-row:hover { background:var(--hover); }
.acct-badge { width:40px; height:40px; border-radius:11px; flex:none; display:grid; place-items:center; font-size:var(--fs-sub); font-weight:700; color:#fff; }
.acct-main { flex:1; min-width:0; }
/* Name truncates; the SYNCED/DUPLICATE chips never shrink. */
.acct-name { font-size:var(--fs-sub); font-weight:600; display:flex; align-items:center; gap:8px; letter-spacing:-.01em; min-width:0; }
.acct-name-text { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.src-chip, .owner-chip { flex:none; }
/* Single, truncating line — freshness lives on .acct-sub below, so meta can't wrap. */
.acct-meta { font-size:var(--fs-caption); color:var(--muted); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.acct-sub { display:flex; align-items:center; gap:7px; font-size:var(--fs-small); color:var(--muted-2); margin-top:4px; min-width:0; }
.acct-sub .acct-fresh { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.acct-right { text-align:right; flex:none; padding-top:1px; }
.acct-bal { font-size:var(--fs-sub); font-weight:600; letter-spacing:-.01em; }
.acct-bal.liability { color:var(--red); }
.src-chip { font-size:var(--fs-micro); padding:2px 7px; border-radius:999px; font-weight:700; letter-spacing:.05em; background:var(--accent-soft); color:var(--accent); }
.owner-chip { font-size:var(--fs-small); font-weight:500; }
.acct-cat { font-size:var(--fs-small); font-weight:600; color:var(--muted); }
.glyph-logo { display:grid; place-items:center; }
.glyph-logo svg { width:21px; height:21px; }
.veh-est-note { font-size:var(--fs-caption); color:var(--muted); margin-top:10px; line-height:1.45; min-height:1px; }
.veh-est-note b { color:var(--text); }
.veh-est-dim { color:var(--muted-2); font-style:italic; }

/* ---------- per-account freshness + cross-connector duplicate notice ---------- */
.acct-fresh { color:var(--muted-2); font-variant-numeric:tabular-nums; }
.acct-fresh.stale { color:var(--red); font-weight:600; }
.src-chip.dup { background:var(--red-soft); color:var(--red); }
.acct-row.is-dup { background:var(--red-soft); }
.dup-banner { display:flex; flex-direction:column; gap:8px; margin:2px 0 12px; }
.dup-row {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  background:var(--red-soft); border:1px solid var(--line);
  border-left:3px solid var(--red); border-radius:var(--radius-sm); padding:12px 14px;
}
.dup-text { flex:1; min-width:200px; font-size:var(--fs-caption); line-height:1.45; }
.dup-text b { color:var(--text); }
.dup-sub { font-size:var(--fs-small); color:var(--muted); margin-top:3px; }
.dup-actions { display:flex; gap:8px; flex-wrap:wrap; }
.dup-actions .btn-ghost { padding:7px 12px; font-size:var(--fs-caption); }
.dup-hide { color:var(--red); }
/* Headline "needs review" flag — shown under the net-worth number while a likely
   duplicate is unresolved, so the big number isn't silently trusted while inflated. */
.nw-review {
  display:inline-block; margin-top:8px; padding:6px 12px; border-radius:999px;
  background:var(--red-soft); border:1px solid var(--red); color:var(--text);
  font-size:var(--fs-small); line-height:1.4; text-align:left; cursor:pointer; max-width:100%;
}
.nw-review[hidden] { display:none; }
.nw-review b { color:var(--red); }
.nw-review .nw-review-cta { color:var(--red); font-weight:600; text-decoration:underline; margin-left:2px; }
/* Small "high est." disclosure chip on RentCast real-estate rows. */
.acct-est { color:var(--muted); }
/* Admin funnel + cron heartbeat view. */
.funnel-cron { display:flex; align-items:center; gap:8px; font-size:var(--fs-caption); color:var(--muted); margin:4px 0 12px; }
.funnel-cron-dot { width:9px; height:9px; border-radius:50%; flex:none; background:var(--accent); }
.funnel-cron-dot.stale { background:var(--red); }
.funnel-list { display:flex; flex-direction:column; gap:2px; }
.funnel-row { display:flex; flex-wrap:wrap; justify-content:space-between; gap:12px; padding:7px 10px; border-radius:var(--radius-sm); }
.funnel-row:nth-child(odd) { background:var(--surface-2); }
.funnel-name { color:var(--muted); font-size:var(--fs-caption); }
.funnel-n { font-variant-numeric:tabular-nums; font-weight:600; }
/* Events that haven't fired this month still show (a 0 is informative) — just dimmed. */
.funnel-row.is-zero .funnel-name, .funnel-row.is-zero .funnel-n { opacity:.5; }
/* Per-event breakdown rows, indented under their parent (e.g. research_viewed → held/nonheld). */
.funnel-sub .funnel-name { padding-left:16px; font-size:var(--fs-small); opacity:.75; }
.funnel-sub .funnel-name::before { content:"↳ "; opacity:.5; }
.funnel-sub .funnel-n { font-weight:500; opacity:.75; }

/* Shared per-row "what this tracks" affordance (funnel events + AI-spend features):
   a tap/hover (i) that reveals a one-line description. The hint sits inside the row
   at flex-basis:100% so it drops to its own line without disturbing nth-child stripes. */
.row-info { flex:none; appearance:none; -webkit-appearance:none; width:15px; height:15px; margin-left:6px;
  padding:0; border:1px solid var(--line); border-radius:50%; background:transparent; color:var(--muted-2);
  font:italic 700 10px/13px Georgia, "Times New Roman", serif; cursor:pointer; vertical-align:middle; }
.row-info:hover { border-color:var(--muted); color:var(--text); }
.row-hint { flex-basis:100%; margin-top:5px; padding-top:5px; border-top:1px dashed var(--line);
  font-size:var(--fs-small); line-height:1.4; color:var(--muted); }
.row-hint[hidden] { display:none; }

/* ---------- admin: AI spend ---------- */
.spend-total { font-size:clamp(1.375rem, max(7vw, calc(1.375rem * var(--dynamic-type-scale, 1))), 1.75rem); font-weight:700; letter-spacing:-.02em; margin-bottom:4px; }
.spend-total .spend-sub { font-size:var(--fs-small); font-weight:500; color:var(--muted); letter-spacing:0; }
.spend-peruser { font-size:var(--fs-body); font-weight:650; letter-spacing:-.01em; margin-bottom:6px; color:var(--text); font-variant-numeric:tabular-nums; }
.spend-peruser .spend-sub { font-size:var(--fs-small); font-weight:500; color:var(--muted); letter-spacing:0; }
.spend-month { margin-left:auto; font:inherit; font-size:var(--fs-caption); color:var(--text); background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-sm); padding:6px 10px; cursor:pointer; }
.spend-h { margin:16px 0 6px; font-size:var(--fs-micro); font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--muted-2); }
.spend-rows { display:flex; flex-direction:column; gap:2px; }
.spend-row { display:flex; flex-wrap:wrap; align-items:baseline; gap:12px; padding:7px 10px; border-radius:var(--radius-sm); }
.spend-row:nth-child(odd) { background:var(--surface-2); }
.spend-name { flex:1; min-width:0; font-size:var(--fs-caption); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spend-meta { flex:none; color:var(--muted-2); font-size:var(--fs-small); font-variant-numeric:tabular-nums; }
.spend-usd { flex:none; font-weight:700; font-variant-numeric:tabular-nums; min-width:64px; text-align:right; }

/* ---------- admin: decision-grade report panels ---------- */
/* Cron 3-state: base dot = healthy (accent), .warn = balances-only, .stale = stalled. */
.funnel-cron-dot.warn { background:var(--warn); }
/* Funnel v2 rows gained conv/delta chips — the name flexes so extras cluster right. */
.funnel-row .funnel-name { flex:1 1 auto; min-width:0; }
.funnel-ev { font-weight:500; font-size:var(--fs-small); color:var(--muted-2); }
.funnel-conv { flex:none; font-size:var(--fs-small); color:var(--muted-2); font-variant-numeric:tabular-nums; }
.funnel-delta { flex:none; font-size:var(--fs-micro); border-radius:999px; padding:1px 7px; border:1px solid var(--line); color:var(--muted); font-variant-numeric:tabular-nums; }
.funnel-delta.up { color:var(--positive); border-color:var(--positive); }
.funnel-delta.down { color:var(--red); border-color:var(--red); }
/* Pre-instrumentation months: counts are n/a (not measured), never "zero activity". */
.funnel-floor { font-size:var(--fs-small); color:var(--warn); margin:2px 0 8px; line-height:1.4; }
/* Row accents (both themes via vars): danger = red regression/failure, warn = amber caution. */
.spend-row.danger { background:var(--red-soft); box-shadow:inset 3px 0 0 var(--red); }
.spend-row.danger .spend-name { color:var(--red); font-weight:600; }
.spend-row.warn { box-shadow:inset 3px 0 0 var(--warn); }
/* Indented sub-rows (operator per-module split), mirroring .funnel-sub. */
.spend-row.sub .spend-name { padding-left:16px; font-size:var(--fs-small); opacity:.8; }
.spend-row.sub .spend-name::before { content:"↳ "; opacity:.5; }
/* Section headings host the block-level (i) hint — flex so the hint can drop to
   its own full-width line; the hint resets the heading's uppercase styling. */
.spend-h { display:flex; align-items:center; flex-wrap:wrap; }
.spend-h .row-hint { text-transform:none; letter-spacing:0; font-weight:500; }
.spend-note { font-size:var(--fs-small); color:var(--muted-2); margin:6px 0; line-height:1.4; }
.spend-clean, .health-clear { font-size:var(--fs-caption); color:var(--positive); margin:8px 0; line-height:1.4; }
.billing-note { font-size:var(--fs-small); color:var(--muted); font-style:italic; margin:0 0 8px; }
/* Member dormancy badges: quiet (>7d) amber, dormant (>30d / never) red. */
.member-badge.quiet { color:var(--warn); border-color:var(--warn); }
.member-badge.dormant { color:var(--red); border-color:var(--red); }
/* Daily-spend sparkline: full-width inline SVG, no chart lib. */
.spend-spark { margin:4px 0 2px; }
.spend-spark svg { display:block; width:100%; height:44px; }
/* Growth panel: waitlist source chips + tally lines. */
.growth-chips { display:flex; flex-wrap:wrap; gap:6px; margin:6px 0 10px; }
.growth-chip { font-size:var(--fs-small); color:var(--muted); border:1px solid var(--line); border-radius:999px; padding:3px 9px; }
.growth-total, .inv-tally { font-size:var(--fs-caption); color:var(--muted); margin:2px 0 8px; }
/* Perk league-table sub-line (generated-vs-seen etc.) — its own full-width line. */
.perk-sub { flex-basis:100%; font-size:var(--fs-small); color:var(--muted-2); line-height:1.4; }

/* ---------- admin: status cockpit + question-labeled group cards ----------
   The redesigned admin surface: one verdict + a unified needs-attention list + a
   money/growth ticker at the top, then four collapsible question cards. All colors
   come from theme tokens so light (cream) + dark both read correctly. */
.admin-status { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
  padding:16px 18px; margin-bottom:20px; box-shadow:0 1px 3px rgba(var(--shadow-color),.06); }
/* Verdict: a bold colored dot + one line. The three states map to the theme's
   positive/warn/red so a glance reads the whole app's health. */
.admin-verdict { display:flex; align-items:center; gap:10px; margin-bottom:12px; }
.admin-verdict-dot { width:12px; height:12px; border-radius:50%; flex:none; background:var(--muted-2); }
.admin-verdict-dot.ok { background:var(--positive); }
.admin-verdict-dot.amber { background:var(--warn); }
.admin-verdict-dot.red { background:var(--red); }
.admin-verdict-text { font-size:var(--fs-lg); font-weight:700; letter-spacing:-.01em; }
/* Unified to-do list: one line per firing item, a small severity dot, most-severe
   first. The empty (green) line reads "Nothing needs you today." */
.admin-todo { list-style:none; margin:0 0 12px; padding:0; display:flex; flex-direction:column; gap:2px; }
.admin-todo-item { display:flex; align-items:baseline; gap:9px; padding:7px 10px; border-radius:var(--radius-sm);
  font-size:var(--fs-caption); line-height:1.4; }
.admin-todo-item:nth-child(odd) { background:var(--surface-2); }
.admin-todo-dot { width:7px; height:7px; border-radius:50%; flex:none; transform:translateY(-1px); background:var(--muted-2); }
.admin-todo-item.red { background:var(--red-soft); box-shadow:inset 3px 0 0 var(--red); }
.admin-todo-item.red .admin-todo-dot { background:var(--red); border-radius:1px; } /* square dot = critical, apart from amber by SHAPE not colour alone (WCAG 1.4.1) */
.admin-todo-item.amber { box-shadow:inset 3px 0 0 var(--warn); }
.admin-todo-item.amber .admin-todo-dot { background:var(--warn); }
.admin-todo-item.ok .admin-todo-dot { background:var(--positive); }
.admin-todo-item.ok { color:var(--positive); }
/* Money + growth ticker — always visible, never behind a click. */
.admin-ticker { display:flex; flex-wrap:wrap; gap:6px 14px; margin-bottom:8px; }
.admin-ticker:empty, .admin-week:empty { display:none; }
.admin-tick { font-size:var(--fs-caption); font-weight:650; color:var(--text); font-variant-numeric:tabular-nums; }
.admin-tick + .admin-tick::before { content:"·"; color:var(--muted-2); margin-right:14px; margin-left:-10px; }
/* Weekly-snapshot chips. */
.admin-week { display:flex; flex-wrap:wrap; gap:6px; }
.admin-chip { font-size:var(--fs-small); color:var(--muted); border:1px solid var(--line); border-radius:999px;
  padding:3px 10px; font-variant-numeric:tabular-nums; }
.admin-chip.warn { color:var(--warn); border-color:var(--warn); }

/* Group card: header (question + status pill), always-visible summary, then a
   "Show detail" toggle that reveals the moved report sections. */
.admin-group { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
  padding:16px 18px; margin-bottom:16px; box-shadow:0 1px 3px rgba(var(--shadow-color),.06); }
.admin-group-head { margin-bottom:4px; }
.admin-group-q { display:flex; align-items:center; gap:10px; margin-bottom:10px; }
.admin-group-q h2 { margin:0; font-size:var(--fs-h2); font-weight:700; letter-spacing:-.01em; }
/* Status pill on the group header — green/amber/red via data-state. */
.admin-pill { flex:none; font-size:var(--fs-micro); font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  border-radius:999px; padding:3px 9px; border:1px solid var(--positive); color:var(--positive); }
.admin-pill[data-state="amber"] { border-color:var(--warn); color:var(--warn); }
.admin-pill[data-state="red"] { border-color:var(--red); color:var(--red); background:var(--red-soft); }
/* Always-visible plain-language summary rows: a label + a good/bad-tinted value. */
.admin-group-summary { display:flex; flex-direction:column; gap:2px; }
.admin-sum-row { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  padding:6px 10px; border-radius:var(--radius-sm); }
.admin-sum-row:nth-child(odd) { background:var(--surface-2); }
.admin-sum-label { font-size:var(--fs-caption); color:var(--muted); flex:none; }
.admin-sum-val { font-size:var(--fs-caption); font-weight:600; text-align:right; min-width:0; }
.admin-sum-row.good .admin-sum-val { color:var(--positive); }
.admin-sum-row.bad .admin-sum-val { color:var(--red); }
/* "Show detail" toggle — reuses .planning-toggle's look but slimmer, holding just a
   label + chevron. The moved sections live in .admin-detail below it. */
.admin-detail-toggle { min-height:44px; margin-top:12px; padding:10px 14px; justify-content:space-between; }
.admin-detail-label { font-size:var(--fs-caption); font-weight:600; color:var(--muted); }
.admin-detail-toggle.open .planning-toggle-chev { transform:rotate(180deg); }
.admin-detail { margin-top:12px; }
/* The moved report sections keep their own styling; just tighten the top margin so
   the first one doesn't double-space under the toggle. */
.admin-detail .section:first-child { margin-top:0; }

/* ---------- brokerage sync ---------- */
.sync-card { background:var(--surface-2); border-radius:var(--radius-sm); padding:18px 20px; display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.sync-card .grow { flex:1; min-width:200px; }
.sync-card .status { font-size:var(--fs-caption); color:var(--muted); }
.sync-dot { display:inline-block; width:7px; height:7px; border-radius:50%; margin-right:7px; vertical-align:middle; }
.badge { font-size:var(--fs-small); font-weight:600; color:var(--muted); }
.badge.on { color:var(--accent); }
.badge.on .sync-dot, .sync-dot.on { background:var(--accent); }
.badge.off .sync-dot, .sync-dot.off { background:var(--muted-2); }

/* ---------- modal → iOS bottom sheet on phones, centered card on wide screens ----------
   Open/close is still driven ONLY by toggling the "open" class on .overlay. */
.overlay{
  position:fixed; inset:0;
  display:flex;                 /* always flex; visibility toggled below */
  z-index:70;                   /* above the tab bar (60) + Sync pill (61): a sheet covers them, iOS-style */
  align-items:flex-end;         /* phone: dock the sheet to the bottom */
  justify-content:center;
  pointer-events:none; visibility:hidden;
  transition:visibility 0s linear .32s;
}
/* The scrim + blur live on ::before so the sliding sheet is IN FRONT of the filter, not
   INSIDE it. A compositor-promoted descendant MOVING inside a backdrop-filter forces WebKit
   to re-blur the whole viewport every frame (the "not smooth" open/close); as a sibling
   behind the sheet the blurred backdrop stays static, so it's just cross-faded (cheap). */
.overlay::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:rgba(0,0,0,.6);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  opacity:0; transition:opacity .32s var(--ease); will-change:opacity;
}
[data-theme="light"] .overlay::before{ background:rgba(0,0,0,.3); }
.overlay.open{ pointer-events:auto; visibility:visible; transition:visibility 0s linear 0s; }
.overlay.open::before{ opacity:1; }

.modal{
  width:100%; max-width:none;
  background:var(--surface);
  border:1px solid var(--line); border-bottom:none;
  border-radius:24px 24px 0 0;
  padding:30px 26px calc(26px + env(safe-area-inset-bottom));
  overflow:auto; -webkit-overflow-scrolling:touch;
  position:relative; z-index:1;  /* above the ::before scrim */
  max-height:min(92vh, 92dvh);  /* stays scrollable when the keyboard shows */
  transform:translateY(100%);
  transition:transform .32s var(--ease-out); will-change:transform;
  box-shadow:0 -10px 40px rgba(var(--shadow-color),.45);
}
/* Both arrival AND exit decelerate on --ease-out for a consistent, crisp landing that
   matches the scrim's .32s fade — NOT a spring: a bottom-docked sheet with overshoot would
   lift its bottom edge off the viewport and flash the backdrop underneath. */
.overlay.open .modal{ transform:translateY(0); }
/* iOS-style grab handle (generated, so no markup changes) */
.modal::before{ content:""; position:absolute; top:9px; left:50%; width:38px; height:5px; margin-left:-19px; border-radius:999px; background:var(--line-strong); pointer-events:none; }
/* While dragging, JS sets an inline transform — suppress the transition so it tracks the finger 1:1. */
.modal.dragging{ transition:none; }
/* Top "grab" strip for the swipe-to-close drag. touch-action:none so a downward
   drag here tracks the finger instead of scrolling the sheet. Phones only. */
.sheet-grip{ position:absolute; top:0; left:0; right:0; height:46px; z-index:3; touch-action:none; cursor:grab; }
.sheet-grip:active{ cursor:grabbing; }
@media (min-width:560px){ .sheet-grip{ display:none; } }

@media (min-width:560px){
  .overlay{ align-items:center; padding:20px; }
  .modal{
    max-width:440px; border:1px solid var(--line); border-radius:26px;
    padding:26px; max-height:92vh; box-shadow:none;
    transform:none; transition:opacity .25s var(--ease-out); animation:pop .25s var(--ease);
  }
  /* pop handles the entry; on close fade the centered card out (opacity) in step with the
     ::before scrim — otherwise it hangs fully opaque for the scrim's fade then hard-cuts. */
  .overlay:not(.open) .modal{ animation:none; opacity:0; }
  .overlay.open .modal{ transform:none; }
  .modal::before{ display:none; }
  [data-theme="light"] .modal{ box-shadow:0 24px 60px rgba(40,52,40,.18); }
}
@keyframes pop { from { opacity:0; transform:scale(.96) translateY(8px);} to { opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){
  .overlay, .overlay::before, .modal{ transition:none !important; animation:none !important; }
  .sync-top.spinning svg, .reval-dot, .skel, .ptr.spinning .ptr-spinner, .copilot-typing{ animation:none !important; }
  /* Feel-pass motion: views/sections/pops appear in place. (JS side: the count-up
     and enter classes are also gated on prefersReducedMotion().) */
  .tab-panel.view-enter-r, .tab-panel.view-enter-l, .boot-stagger > *,
  .sheet-in > *, .sheet-in .rd-alloc-bar > span,
  .tabbar-btn.active .tabbar-icon, .metric-value.nw-settle, .sync-top.done::after{ animation:none !important; }
  /* Belt-and-suspenders: confettiBurst() already no-ops under reduced motion,
     but kill any bits that slip through (e.g. a mid-flight preference change). */
  .confetti-bit{ display:none !important; }
}
/* Match the team's reduced-motion discipline for transparency: swap every blur for a
   solid surface so the UI stays legible when the OS asks to reduce transparency. */
@media (prefers-reduced-transparency:reduce){
  .overlay::before{ -webkit-backdrop-filter:none; backdrop-filter:none; background:rgba(0,0,0,.78); }
  .tabbar{ -webkit-backdrop-filter:none; backdrop-filter:none; background:var(--bg); }
  .pro-lock{ -webkit-backdrop-filter:none; backdrop-filter:none; background:var(--surface); }
  [data-theme="light"] .pro-lock{ background:var(--surface); }
  .pro-teaser-blur{ filter:none; -webkit-filter:none; }
}
/* Strengthen separators and the focus ring when the OS asks for more contrast. */
@media (prefers-contrast:more){
  :root, [data-theme="light"]{ --line:var(--line-strong); }
  :focus-visible{ outline-width:3px; }
}
.modal h3 { margin:0 0 4px; font-size:var(--fs-title); font-weight:600; letter-spacing:-.02em; }
.modal .sub { color:var(--muted); font-size:var(--fs-caption); margin-bottom:20px; line-height:1.4; }
.field { margin-bottom:15px; }
.field label { display:block; font-size:var(--fs-caption); color:var(--muted); margin-bottom:7px; }
/* font-size MUST be >=16px: iOS Safari auto-zooms the page when focusing any
   input smaller than that. */
input, select { width:100%; font:inherit; font-size:var(--fs-body); padding:13px 14px; border-radius:13px; border:1px solid var(--line-strong); background:var(--surface-2); color:var(--text); transition:.15s var(--ease); }
input:focus, select:focus { outline:none; border-color:var(--accent); }
/* Global keyboard-focus ring: only on :focus-visible, so mouse/touch focus stays
   ring-free while keyboard (and a11y-tool) users get a clear, consistent target.
   The outline follows each element's own border-radius. Inputs get an accent
   border on :focus already; this adds a matching ring under keyboard nav. */
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.field-row { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.modal-actions { display:flex; gap:10px; justify-content:flex-end; margin-top:24px; align-items:center; }

/* ---------- first-run onboarding ---------- */
.onboard-list { list-style:none; margin:18px 0 4px; padding:0; display:flex; flex-direction:column; gap:16px; }
.onboard-list li { display:flex; gap:14px; align-items:flex-start; }
.onboard-ic { font-size:var(--fs-h2); line-height:1.2; flex:none; width:34px; height:34px; display:grid; place-items:center; background:var(--accent-soft); border-radius:11px; }
.onboard-list b { display:block; font-size:var(--fs-label); font-weight:600; letter-spacing:-.01em; }
.onboard-list span { display:block; color:var(--muted); font-size:var(--fs-caption); line-height:1.45; margin-top:2px; }
#onboardOverlay .modal-actions .btn-primary { width:100%; justify-content:center; padding:14px; }

/* ---------- login ---------- */
.login-screen { position:fixed; inset:0; display:none; align-items:center; justify-content:center; padding:20px; z-index:80; background:var(--bg); }
.login-screen.open { display:flex; }
.login-card { width:100%; max-width:340px; text-align:center; }
.login-card .logo { margin:0 auto 20px; width:54px; height:54px; border-radius:16px; font-size:var(--fs-display); }
.login-card h1 { margin:0 0 8px; font-size:var(--fs-display); font-weight:600; letter-spacing:-.02em; }
.login-card p { color:var(--muted); font-size:var(--fs-label); margin:0 0 22px; }
.login-card input { display:block; width:100%; text-align:center; padding:14px; border-radius:14px; margin-bottom:10px; }
.login-card .err { color:var(--red); font-size:var(--fs-caption); min-height:18px; margin-top:6px; }
.login-card .btn-primary { width:100%; justify-content:center; margin-top:8px; padding:14px; }
.login-card .auth-toggle { font-size:var(--fs-caption); color:var(--muted); margin:16px 0 0; }
.login-card .auth-toggle a { color:var(--accent); text-decoration:none; font-weight:600; }
.login-card .auth-toggle a:hover { text-decoration:underline; }
.auth-legal { font-size:var(--fs-small); color:var(--muted); margin:14px 0 0; line-height:1.5; }
.auth-legal a { color:var(--accent); text-decoration:none; font-weight:600; }
.auth-legal a:hover { text-decoration:underline; }
.auth-consent { display:flex; width:100%; box-sizing:border-box; align-items:flex-start; gap:8px; font-size:var(--fs-caption); color:var(--muted); margin:4px 2px 2px; padding:0 2px; text-align:left; line-height:1.45; cursor:pointer; }
.auth-consent input { margin:2px 0 0; flex:0 0 auto; width:16px; height:16px; accent-color:var(--accent); }
.auth-consent span { flex:1 1 auto; }
.auth-consent a { color:var(--accent); text-decoration:none; font-weight:600; }
.auth-consent a:hover { text-decoration:underline; }

/* ---------- toast ---------- */
/* Sits above the bottom tab bar + floating Sync pill so it never hides behind them. */
.toast { position:fixed; bottom:calc(var(--tabbar-h) + 22px + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%) translateY(20px); background:var(--surface-2); border:1px solid var(--line-strong); padding:13px 20px; border-radius:999px; opacity:0; pointer-events:none; transition:.28s var(--ease); font-size:var(--fs-label); z-index:90; box-shadow:0 8px 30px rgba(var(--shadow-color),.4); }
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); transition-timing-function:var(--ease-spring); }
[data-theme="light"] .toast { box-shadow:0 10px 30px rgba(40,52,40,.16); }

/* ---------- privacy mode (blur all amounts) ---------- */
#privacyBtn svg { width:18px; height:18px; display:block; }
body.private .metric-value,
body.private .stat-value,
body.private .pill,
body.private .h-value,
body.private .h-price,
body.private .acct-bal,
body.private .grp-sub,
body.private .amt {
  filter: blur(9px); -webkit-filter: blur(9px);
  user-select:none; pointer-events:none;
  transition: filter .2s var(--ease);
}
/* The whole trend chart (line + dollar axis labels) blurs as one unit. */
body.private .chartbox {
  filter: blur(10px); -webkit-filter: blur(10px);
  user-select:none; pointer-events:none;
  transition: filter .2s var(--ease);
}

/* ---------- grouped accounts (category subtotals) ---------- */
.grp-head { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:18px 4px 7px; }
.grp-head:first-child { padding-top:4px; }
.grp-name { display:flex; align-items:center; gap:8px; font-size:var(--fs-small); font-weight:600; letter-spacing:.05em; text-transform:uppercase; color:var(--muted); }
.grp-name .dot { width:8px; height:8px; border-radius:50%; }
.grp-sub { font-size:var(--fs-caption); font-weight:600; color:var(--muted); font-variant-numeric:tabular-nums; }
.grp-head + .acct-row { border-top:none; }

/* ---------- skeleton shimmer loaders ---------- */
.skel { display:inline-block; background:linear-gradient(90deg, var(--skel) 25%, var(--skel-hi) 37%, var(--skel) 63%); background-size:400% 100%; animation:shimmer 1.4s ease infinite; border-radius:8px; }
@keyframes shimmer { 0% { background-position:100% 0; } 100% { background-position:0 0; } }
.skel-hero { height:clamp(40px,8vw,58px); width:min(72%,260px); border-radius:14px; vertical-align:middle; }
.skel-line { height:14px; width:170px; border-radius:7px; }
.skel-stat { height:22px; width:96px; }
.skel-row { display:flex; align-items:center; gap:14px; padding:14px 4px; border-top:1px solid var(--line); }
.skel-row:first-child { border-top:none; }
.skel-circle { width:40px; height:40px; border-radius:50%; flex:none; }
.skel-grow { height:14px; flex:1; max-width:210px; }
.skel-amt { height:14px; width:72px; margin-left:auto; }
/* Loading skeletons for the Signal / Podcast reading sheets (16:9 player + text lines). */
.skel-video { display:block; width:100%; aspect-ratio:16/9; border-radius:12px; }
.skel-para { display:block; height:13px; width:100%; margin:0 0 10px; border-radius:7px; }
.skel-para-short { width:60%; }

.footnote { display:none; }

@media (max-width:560px) {
  .stats { grid-template-columns:1fr; }
  .topbar-actions .label { display:none; }
  .hide-sm { display:none; }
  /* Toolbar action buttons (Connect account / Add manually / Add crypto /
     Property) are too chunky on phones. Slim them down, and break the heading
     onto its own row so the buttons sit together neatly below it (instead of
     wrapping unevenly around the title). */
  .acct-toolbar { gap:8px; row-gap:10px; }
  .acct-toolbar .spacer { flex:1 0 100%; height:0; order:1; }
  .acct-toolbar .btn-ghost { order:2; min-height:34px; padding:6px 12px; font-size:var(--fs-small); gap:5px; }
  .acct-toolbar .btn-ghost > svg { width:14px; height:14px; }
}

/* ---- account recovery / authenticator (TOTP) ---- */
.recovery-row { display:flex; align-items:center; justify-content:space-between; gap:12px;
  margin-top:18px; padding-top:16px; border-top:1px solid var(--line-strong); }
.recovery-title { font-size:var(--fs-caption); font-weight:600; }
.recovery-status { font-size:var(--fs-small); color:var(--muted); margin-top:2px; max-width:340px; }
.recovery-row .btn-ghost { flex:none; }
.totp-qr { display:flex; justify-content:center; margin:6px 0 14px; }
.totp-qr > * { background:#fff; padding:12px; border-radius:12px; width:180px; height:180px; }
.totp-qr img, .totp-qr svg { width:100%; height:100%; display:block; }
.totp-key { display:flex; align-items:center; gap:10px; background:var(--surface-2);
  border-radius:var(--radius-sm); padding:10px 12px; margin-bottom:6px; flex-wrap:wrap; }
.totp-key-label { font-size:var(--fs-micro); text-transform:uppercase; letter-spacing:.05em; color:var(--muted); }
.totp-key code { flex:1; min-width:120px; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:var(--fs-caption); letter-spacing:.06em; word-break:break-all; color:var(--accent); }
.btn-tiny { padding:5px 10px; font-size:var(--fs-small); }
.totp-backup { display:grid; grid-template-columns:repeat(2,1fr); gap:8px; margin:6px 0 14px; }
.totp-backup code { background:var(--surface-2); border-radius:10px; padding:9px 10px; text-align:center;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:var(--fs-label); letter-spacing:.04em; }
.member-badge { font-size:var(--fs-micro); color:var(--accent); border:1px solid var(--accent); border-radius:999px; padding:1px 7px; flex:none; }
.member-badge.pro { color:#00210f; background:var(--accent); border-color:var(--accent); font-weight:650; }
[data-theme="light"] .member-badge.pro { color:#fff; }
.mbr-actions { display:flex; gap:6px; flex:none; align-items:center; }
.mbr-pro.on { color:var(--accent); border-color:var(--accent); }
/* Member row: name truncates, the admin badge keeps its size, and the Reset
   button never gets squeezed/overlapped (it wraps to its own line if tight). */
.prop-name-row { display:flex; align-items:center; gap:6px; min-width:0; }
.prop-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.prop-row .mbr-reset { flex:none; white-space:nowrap; align-self:center; }
#memberList .prop-row { flex-wrap:wrap; row-gap:8px; }
#memberList .prop-addr { flex:1 1 56%; }

/* ===== Offline banner — shown when rendering cached data with no network ===== */
.offline-banner { display:flex; align-items:center; gap:9px; margin:0 0 4px; padding:9px 14px;
  background:var(--accent-soft); color:var(--text); border-radius:var(--radius-sm);
  font-size:var(--fs-caption); line-height:1.3; }
.offline-banner[hidden] { display:none; }
.offline-banner b { font-weight:600; }
.offline-dot { width:8px; height:8px; border-radius:50%; background:var(--accent); flex:none;
  box-shadow:0 0 0 3px var(--accent-soft); }

/* ===== iOS "Add to Home Screen" hint card ===== */
.install-hint { display:flex; align-items:center; gap:12px; margin:0 0 14px; padding:12px 14px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm);
  box-shadow:0 6px 20px rgba(var(--shadow-color),.06); }
.install-hint[hidden] { display:none; }
.install-hint-icon { width:38px; height:38px; border-radius:10px; flex:none; }
.install-hint-text { display:flex; flex-direction:column; gap:2px; font-size:var(--fs-caption); line-height:1.35; }
.install-hint-text strong { font-size:var(--fs-label); font-weight:650; }
.install-hint-text span { color:var(--muted); }
.install-hint-text b { color:var(--text); font-weight:600; }
.ios-share { width:15px; height:15px; vertical-align:-3px; color:var(--accent); }
.install-hint-close { margin-left:auto; flex:none; width:28px; height:28px; border:none;
  background:transparent; color:var(--muted); font-size:var(--fs-sub); cursor:pointer; border-radius:8px; }
.install-hint-close:hover { background:var(--hover); color:var(--text); }

/* "updating…" marker while cached data is being revalidated in the background */
.reval-dot { font-size:var(--fs-micro); font-weight:500; letter-spacing:0; text-transform:none;
  color:var(--accent); margin-left:6px; animation:reval-pulse 1.1s ease-in-out infinite; }
.reval-dot[hidden] { display:none; }
@keyframes reval-pulse { 0%,100% { opacity:.45; } 50% { opacity:1; } }

/* ============================================================
   iOS-NATIVE CHROME — tab bar, floating Sync, tab panels,
   settings rows, tap targets, in-button icons. (2026 redesign)
   ============================================================ */

/* ---- Tap targets: Apple HIG 44pt minimum on primary controls ---- */
.btn-primary, .btn-ghost, .btn-danger { min-height:44px; }
.btn-tiny { min-height:0; }                 /* tiny copy buttons stay compact */
.icon-only { width:44px; height:44px; }

/* ---- Crisp inline icons inside buttons ---- */
.btn-primary > svg, .btn-ghost > svg, .btn-danger > svg { width:17px; height:17px; flex:none; }
.icon-only svg { width:20px; height:20px; display:block; }
.install-hint-close svg { width:16px; height:16px; display:block; margin:0 auto; }

/* ---- Tab panels: only the active one is shown ---- */
.tab-panel[hidden] { display:none; }

/* ---- View + first-paint motion ---- */
/* Tab switches travel: the incoming panel slides from the direction you're heading
   (activateTab picks -r/-l from tab order). Transform/opacity ONLY — the panel's
   clientWidth never changes, so the trend chart's measure-on-show and its
   ResizeObserver stay accurate, and animating the panel (not #app/main#tabs) never
   creates a containing block that would detach the fixed tab bar / FAB. */
@keyframes view-in-r { from { opacity:0; transform:translateX(26px); } to { opacity:1; transform:none; } }
@keyframes view-in-l { from { opacity:0; transform:translateX(-26px); } to { opacity:1; transform:none; } }
.tab-panel.view-enter-r { animation:view-in-r var(--dur-view) var(--ease-out); }
.tab-panel.view-enter-l { animation:view-in-l var(--dur-view) var(--ease-out); }
/* First paint: Overview's sections rise in once (boot adds .boot-stagger for ~1s).
   `backwards` keeps delayed children invisible until their turn. Delays cap early —
   below the fold nothing waits on a queue. */
@keyframes rise-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:none; } }
.boot-stagger > * { animation:rise-in .38s var(--ease-out) backwards; }
.boot-stagger > *:nth-child(1) { animation-delay:.03s; }
.boot-stagger > *:nth-child(2) { animation-delay:.07s; }
.boot-stagger > *:nth-child(3) { animation-delay:.11s; }
.boot-stagger > *:nth-child(4) { animation-delay:.15s; }
.boot-stagger > *:nth-child(5) { animation-delay:.19s; }
.boot-stagger > *:nth-child(n+6) { animation-delay:.22s; }
/* Hero settle: a barely-there breath at the end of the net-worth count-up. */
@keyframes nw-settle { 0% { transform:scale(1); } 35% { transform:scale(1.015); } 100% { transform:scale(1); } }
.metric-value.nw-settle { animation:nw-settle .45s var(--ease-out); transform-origin:left center; }

/* Sheet reveal: the Wrapped / Rundown content rises in staggered when the sheet opens,
   so the shareable recap arrives as an EVENT, not a static poster. Reuses the rise-in
   entrance vocabulary; JS adds .sheet-in on open and removes it so it re-triggers. */
.sheet-in > * { animation:rise-in .4s var(--ease-out) backwards; }
.sheet-in > *:nth-child(1) { animation-delay:.02s; }
.sheet-in > *:nth-child(2) { animation-delay:.06s; }
.sheet-in > *:nth-child(3) { animation-delay:.10s; }
.sheet-in > *:nth-child(4) { animation-delay:.14s; }
.sheet-in > *:nth-child(5) { animation-delay:.18s; }
.sheet-in > *:nth-child(n+6) { animation-delay:.21s; }
/* Allocation bars sweep out from the left as the Rundown reveals (GPU transform, not width). */
@keyframes bar-sweep { from { transform:scaleX(0); } to { transform:scaleX(1); } }
.sheet-in .rd-alloc-bar > span { animation:bar-sweep .55s var(--ease-out) .18s backwards; transform-origin:left center; }

/* ---- Bottom tab bar ---- */
.tabbar {
  position:fixed; left:0; right:0; bottom:0; z-index:60;
  display:flex; align-items:stretch;
  background:var(--bg);                               /* fallback */
  background:color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter:saturate(1.4) blur(18px); backdrop-filter:saturate(1.4) blur(18px);
  /* Pin to its own compositor layer. iOS standalone PWAs intermittently let a
     position:fixed element (especially one with a backdrop-filter) detach during
     momentum scroll and float mid-screen; promoting it keeps it stuck to the
     viewport. translate3d(0,0,0) (not translateZ) avoids re-introducing a 2D
     transform that the wide-screen pill rule below would otherwise clobber. */
  transform:translate3d(0,0,0); will-change:transform; -webkit-backface-visibility:hidden;
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
.tabbar-btn {
  flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  background:none; border:none; cursor:pointer;
  padding:9px 2px 7px; min-height:52px;
  color:var(--muted-2); transition:color .15s var(--ease), transform .15s var(--ease), opacity .15s var(--ease);
  -webkit-tap-highlight-color:transparent;
}
.tabbar-btn .tabbar-icon { display:inline-flex; }
.tabbar-btn .tabbar-icon svg { width:25px; height:25px; display:block; }
.tabbar-btn .icon-filled { display:none; }
.tabbar-btn.active { color:var(--accent); }
.tabbar-btn.active .icon-outline { display:none; }
.tabbar-btn.active .icon-filled { display:inline-flex; }
/* Becoming active replays a tiny icon pop — the tab itself confirms the landing.
   (Applying .active re-triggers the animation each switch; the initial boot pop is
   invisible behind the splash/first paint.) */
@keyframes tab-icon-pop { 0% { transform:scale(.8); } 60% { transform:scale(1.1); } 100% { transform:scale(1); } }
.tabbar-btn.active .tabbar-icon { animation:tab-icon-pop .26s var(--ease-out); }
.tabbar-label { font-size:var(--fs-nano); font-weight:600; letter-spacing:0; white-space:nowrap; }
.tabbar-btn:active { opacity:.6; transform:scale(.9); }

/* ---- Floating Sync pill — single Sync action, thumb-reachable from any tab ---- */
/* Sync lives in the top bar now (anchored at the top — it doesn't follow scroll
   the way the AI FAB does). Most styling comes from .btn-ghost.icon-only; this
   just handles the hidden/disabled/spinning states. */
.sync-top[hidden] { display:none; }
.sync-top:disabled { opacity:.4; cursor:default; }
.sync-top.spinning svg { animation:fab-spin .9s linear infinite; }
@keyframes fab-spin { to { transform:rotate(360deg); } }
/* Post-sync beat: the arrows give way to a green check for ~1.4s (JS toggles .done),
   so a successful sync ends with a visible landing, not just a toast. */
.sync-top.done svg { display:none; }
.sync-top.done::after { content:"✓"; color:var(--positive); font-size:var(--fs-lg); font-weight:800; line-height:1; animation:pop-in .3s var(--ease-spring); }
@keyframes pop-in { from { transform:scale(.4); opacity:0; } to { transform:scale(1); opacity:1; } }

/* Wide screens: center the tab bar into a floating pill-bar. */
@media (min-width:560px) {
  .tabbar { left:50%; right:auto; transform:translateX(-50%); bottom:16px;
    max-width:480px; width:calc(100% - 40px);
    border:1px solid var(--line); border-radius:26px; padding:0;
    box-shadow:0 10px 30px rgba(var(--shadow-color),.18); }
  /* Column layout (icon over label) packs 5 tabs into the pill without the
     icon+label rows overflowing the way a horizontal layout would. */
  .tabbar-btn { flex-direction:column; gap:3px; padding:10px 6px; min-height:54px; }
  .tabbar-btn .tabbar-icon svg { width:21px; height:21px; }
  .tabbar-label { font-size:var(--fs-micro); }
}

/* ---- Settings → Account rows ---- */
.settings-list { display:flex; flex-direction:column; gap:8px; }
.settings-row {
  display:flex; align-items:center; gap:14px; width:100%; text-align:left;
  background:var(--surface-2); border:1px solid transparent; border-radius:var(--radius-sm);
  padding:14px 16px; cursor:pointer; color:var(--text); font:inherit; min-height:60px;
  transition:.15s var(--ease);
}
.settings-row:hover { background:var(--hover); border-color:var(--line); }
.settings-row:active { opacity:.7; }
.settings-row-icon { flex:none; display:grid; place-items:center; width:36px; height:36px;
  border-radius:10px; background:var(--accent-soft); color:var(--accent); }
.settings-row-icon svg { width:20px; height:20px; }
.settings-row-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.settings-row-title { font-size:var(--fs-sub); font-weight:600; letter-spacing:-.01em; }
.settings-row-sub { font-size:var(--fs-small); color:var(--muted); }
.settings-row-chev { color:var(--muted-2); font-size:var(--fs-title); line-height:1; flex:none; }

/* Shared danger styling (delete-account row + button) */
.danger, .recovery-title.danger { color:var(--red); }
.btn-ghost.danger:hover { border-color:var(--red); }

/* Collapsible disclosure header — "Plan ahead" (Overview) + "Admin tools"
   (Settings). Reuses the settings-row look but as a full-width toggle. */
#planningGroup, #adminGroup { margin-bottom:34px; }
.planning-toggle {
  display:flex; align-items:center; gap:14px; width:100%; text-align:left; margin-bottom:0;
  background:var(--surface-2); border:1px solid transparent; border-radius:var(--radius-sm);
  padding:14px 16px; cursor:pointer; color:var(--text); font:inherit; min-height:60px;
  transition:.15s var(--ease);
}
.planning-toggle:hover { background:var(--hover); border-color:var(--line); }
.planning-toggle:active { opacity:.7; }
.planning-toggle-icon { flex:none; display:grid; place-items:center; width:36px; height:36px;
  border-radius:10px; background:var(--accent-soft); color:var(--accent); }
.planning-toggle-icon svg { width:20px; height:20px; }
.planning-toggle-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.planning-toggle-main strong { font-size:var(--fs-sub); font-weight:600; letter-spacing:-.01em; }
.planning-toggle-main span { font-size:var(--fs-small); color:var(--muted); }
.planning-toggle-chev { color:var(--muted-2); font-size:var(--fs-caption); line-height:1; flex:none;
  transition:transform .18s var(--ease); }
.planning-toggle.open .planning-toggle-chev { transform:rotate(180deg); }
/* The disclosure body adds a little breathing room above its first card. */
#planningBody, #adminBody { margin-top:14px; }

/* Account & security modal — grouped settings sections. */
.settings-group { margin-top:18px; }
.settings-group:first-of-type { margin-top:12px; }
.settings-group-title { font-size:var(--fs-small); font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:var(--muted-2); margin:0 0 8px; }
.settings-group-action { display:flex; justify-content:flex-end; margin-top:4px; }

/* Login screen respects the safe areas too. */
.login-screen { padding:max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }

/* ===== Biometric lock screen (full-screen, opaque, above everything) ===== */
.lock-screen { position:fixed; inset:0; z-index:1000; display:none; align-items:center; justify-content:center;
  background:var(--bg); padding:max(24px, env(safe-area-inset-top)) 24px max(24px, env(safe-area-inset-bottom)); }
.lock-screen.open { display:flex; }
.lock-card { text-align:center; max-width:320px; width:100%; display:flex; flex-direction:column; gap:12px; align-items:center; }
.lock-card .logo { width:64px; height:64px; border-radius:16px; }
.lock-card .logo img { width:64px; height:64px; border-radius:16px; }
.lock-card h1 { margin:4px 0 0; font-size:var(--fs-display); }
.lock-sub { color:var(--muted); font-size:var(--fs-label); margin:0 0 6px; }
.lock-card .btn-primary { width:100%; justify-content:center; }
.lock-card .btn-ghost { font-size:var(--fs-caption); }

/* ===== Pull-to-refresh indicator ===== */
.ptr { position:absolute; top:0; left:0; right:0; display:flex; justify-content:center; pointer-events:none;
  opacity:0; transform:translateY(-8px); transition:opacity .15s ease; z-index:5; }
.ptr-spinner { width:26px; height:26px; margin-top:8px; border-radius:50%;
  border:2.5px solid var(--line-strong); border-top-color:var(--accent); }
.ptr.spinning { opacity:1 !important; transform:translateY(0) !important; }
.ptr.spinning .ptr-spinner { animation:ptr-spin .7s linear infinite; }
@keyframes ptr-spin { to { transform:rotate(360deg); } }

/* ===== "What changed" digest card (top of Overview) ===== */
.digest { position:relative; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm);
  padding:12px 40px 12px 14px; margin:0 0 14px; display:flex; flex-direction:column; gap:8px;
  touch-action:pan-y; transition:transform .2s var(--ease), opacity .2s var(--ease); }
.digest[hidden] { display:none; }
.digest.digest-gone { transform:scale(.96); opacity:0; }
@media (prefers-reduced-motion:reduce) { .digest { transition:none !important; } }
.digest-x { position:absolute; top:8px; right:8px; width:28px; height:28px; min-height:0; padding:0;
  display:flex; align-items:center; justify-content:center; border:none; border-radius:999px;
  background:transparent; color:var(--muted); cursor:pointer; transition:.15s var(--ease); }
.digest-x:hover { background:var(--hover); color:var(--text); }
.digest-x svg { width:15px; height:15px; }
.digest-since { font-size:var(--fs-small); font-weight:500; color:var(--muted); letter-spacing:.01em; }
.digest-delta { font-size:var(--fs-sub); line-height:1.2; }
.digest-delta:empty { display:none; }
.digest-badges { display:flex; flex-wrap:wrap; gap:6px; }
.digest-badges:empty { display:none; }
.digest-badge { font-size:var(--fs-small); font-weight:500; padding:3px 9px; border-radius:999px;
  background:var(--surface-2); color:var(--text); white-space:nowrap; }
.digest-badge.ath, .digest-badge.streak { background:var(--accent-soft); color:var(--accent); }

/* one-time confetti burst on a new all-time high */
.confetti-bit { position:fixed; top:16%; width:8px; height:12px; border-radius:2px; z-index:60;
  pointer-events:none; opacity:0; animation:confetti 1.2s ease-out forwards; }
@keyframes confetti { 0% { opacity:1; transform:translate(0,0) rotate(0); }
  100% { opacity:0; transform:translate(var(--dx), 62vh) rotate(var(--dr)); } }

/* ===== Retirement outlook (Monte Carlo projection) ===== */
.proj-inputs { display:grid; grid-template-columns:repeat(2,1fr); gap:10px 12px; margin:0 0 14px; }
.proj-inputs label { display:flex; flex-direction:column; gap:5px; font-size:var(--fs-small); font-weight:500; color:var(--muted); }
.proj-inputs input { font-size:var(--fs-sub); padding:10px 12px; }
.proj-stats { display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin:0 0 12px; }
.proj-stat { background:var(--surface-2); border-radius:var(--radius-sm); padding:10px 13px; }
.proj-stat-label { font-size:var(--fs-small); color:var(--muted); margin-bottom:3px; }
.proj-stat-value { font-size:var(--fs-title); font-weight:650; letter-spacing:-.02em; }
.proj-note { font-size:var(--fs-micro); line-height:1.45; color:var(--muted-2); margin-top:10px; }
.scenario-actions { margin:-2px 0 10px; }
.scenario-list { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; margin-bottom:12px; }
.scenario-card { position:relative; background:var(--surface-2); border-radius:var(--radius-sm); padding:10px 34px 10px 12px; }
.scenario-name { font-size:var(--fs-small); font-weight:650; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.scenario-result { font-size:var(--fs-small); color:var(--muted); margin-top:3px; }
.scenario-del { position:absolute; right:7px; top:7px; border:0; background:transparent; color:var(--muted); cursor:pointer; font-size:var(--fs-body); }

/* Retirement outlook → "Adjust accounts" (exclude illiquid assets like a home) */
.proj-adjust { margin:0 0 14px; }
.proj-adjust-btn { display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  background:var(--surface-2); border:1px solid transparent; border-radius:var(--radius-sm);
  padding:10px 12px; cursor:pointer; color:var(--text); font-size:var(--fs-caption); font-weight:500; }
.proj-adjust-btn:hover { border-color:var(--line-strong); }
.proj-adjust-btn > svg { width:17px; height:17px; flex:none; color:var(--accent); }
.proj-adjust-btn > span:nth-child(2) { flex:1; }
.proj-excl-count { font-size:var(--fs-small); color:var(--accent); font-weight:600; }
.proj-adjust-chev { color:var(--muted-2); transition:transform .18s var(--ease); }
.proj-adjust-btn.open .proj-adjust-chev { transform:rotate(180deg); }
.proj-accounts { display:flex; flex-direction:column; gap:6px; margin:8px 0 0; }
.proj-accounts[hidden] { display:none; }
.proj-acct { display:flex; align-items:center; gap:10px; padding:9px 11px; border-radius:11px;
  background:var(--surface-2); font-size:var(--fs-caption); cursor:pointer; }
.proj-acct input { width:18px; height:18px; flex:none; accent-color:var(--accent); cursor:pointer; }
.proj-acct-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.proj-acct-amt { font-weight:600; color:var(--muted); }
.proj-acct.off { opacity:.5; }
.proj-acct.off .proj-acct-name { text-decoration:line-through; }
.proj-adjust-note { font-size:var(--fs-micro); line-height:1.45; color:var(--muted-2); margin-top:8px; }

/* ===== Month-in-review nudge (Overview) ===== */
.wrapped-nudge { display:flex; align-items:center; gap:12px; width:100%; text-align:left;
  margin:0 0 14px; padding:12px 14px; border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--surface); color:var(--text); cursor:pointer; transition:.15s var(--ease); }
.wrapped-nudge[hidden] { display:none; }
.wrapped-nudge:hover { background:var(--hover); }
.wrapped-nudge-spark { flex:none; width:34px; height:34px; border-radius:10px; display:flex; align-items:center; justify-content:center;
  background:var(--accent-soft); color:var(--accent); }
.wrapped-nudge-spark svg { width:18px; height:18px; }
.wrapped-nudge-main { display:flex; flex-direction:column; gap:1px; flex:1; min-width:0; }
.wrapped-nudge-main strong { font-size:var(--fs-label); font-weight:650; }
.wrapped-nudge-main span { font-size:var(--fs-small); color:var(--muted); }
.wrapped-nudge-chev { color:var(--muted-2); font-size:var(--fs-h2); flex:none; }

/* ===== "Net Worth Wrapped" month-in-review modal ===== */
.wrapped-head { text-align:center; margin:6px 0 18px; }
.wrapped-month { font-size:clamp(1.375rem, max(7vw, calc(1.375rem * var(--dynamic-type-scale, 1))), 1.75rem); font-weight:700; letter-spacing:-.02em; }
.wrapped-sub { font-size:var(--fs-caption); color:var(--muted); text-transform:uppercase; letter-spacing:.14em; margin-top:2px; }
.wrapped-hero { text-align:center; margin:0 0 18px; }
.wrapped-hero-label { font-size:var(--fs-small); color:var(--muted); margin-bottom:4px; }
.wrapped-hero-val { font-size:clamp(1.75rem, max(9vw, calc(1.75rem * var(--dynamic-type-scale, 1))), 2.375rem); font-weight:700; letter-spacing:-.03em; line-height:1.05; }
.wrapped-hero-val.up { color:var(--positive); } .wrapped-hero-val.down { color:var(--red); }
.wrapped-hero-range { font-size:var(--fs-caption); color:var(--muted); margin-top:6px; }
.wrapped-progress { font-size:var(--fs-small); color:var(--muted-2); margin-top:8px; letter-spacing:.02em; }
.wrapped-tiles { display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin:0 0 16px; }
.wrapped-tile { background:var(--surface-2); border-radius:var(--radius-sm); padding:11px 13px; }
.wrapped-tile-label { font-size:var(--fs-micro); color:var(--muted); margin-bottom:3px; }
.wrapped-tile-val { font-size:var(--fs-lg); font-weight:650; letter-spacing:-.01em; }
.wrapped-tile-val.up { color:var(--positive); } .wrapped-tile-val.down { color:var(--red); }
.wrapped-performer { background:var(--surface-2); border-radius:var(--radius-sm); padding:11px 13px; margin:0 0 16px; }
.wrapped-performer-label { font-size:var(--fs-micro); color:var(--muted); margin-bottom:8px; }
.wrapped-performer-row { display:flex; align-items:center; gap:11px; }
.wrapped-performer-row .logo-wrap { width:34px; height:34px; flex:none; }
.wrapped-performer-id { flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; }
.wrapped-performer-tick { font-size:var(--fs-sub); font-weight:700; letter-spacing:-.01em; }
.wrapped-performer-name { font-size:var(--fs-small); color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.wrapped-performer-pct { font-size:var(--fs-lg); font-weight:700; color:var(--accent); flex:none; }

.wrapped-badges { display:flex; flex-wrap:wrap; gap:6px; justify-content:center; margin:0 0 16px; }
.wrapped-badge { font-size:var(--fs-small); font-weight:500; padding:5px 12px; border-radius:999px; background:var(--accent-soft); color:var(--accent); }
/* overflow:visible so the summit dot's halo at the last point isn't clipped by the SVG's
   default overflow:hidden (same as svg#lineChart). */
.wrapped-spark { width:100%; height:96px; margin:0 0 14px; overflow:visible; }
.wrapped-foot { text-align:center; font-size:var(--fs-micro); color:var(--muted-2); letter-spacing:.04em; }
.wrapped-empty { text-align:center; color:var(--muted); font-size:var(--fs-label); padding:18px 0; line-height:1.5; }

/* ===== Net worth percentile by age (Fed SCF 2022) ===== */
.pct-head { display:flex; align-items:center; gap:14px; margin:0 0 16px; }
.pct-age { display:flex; flex-direction:column; gap:5px; font-size:var(--fs-small); font-weight:500; color:var(--muted); flex:none; width:84px; }
.pct-age input { font-size:var(--fs-sub); padding:10px 12px; text-align:center; }
.pct-rank { flex:1; min-width:0; }
.pct-rank-big { font-size:clamp(1.625rem, max(8vw, calc(1.625rem * var(--dynamic-type-scale, 1))), 2.0625rem); font-weight:700; letter-spacing:-.02em; line-height:1.05; color:var(--accent); }
.pct-rank-big .pct-rank-word { font-size:var(--fs-sub); font-weight:600; color:var(--text); letter-spacing:0; }
.pct-rank-sub { font-size:var(--fs-small); color:var(--muted); margin-top:4px; line-height:1.35; }
.pct-scale { position:relative; height:10px; border-radius:999px; margin:0 0 6px;
  background:linear-gradient(90deg, var(--surface-2), var(--accent-soft)); overflow:visible; }
.pct-scale-fill { height:100%; border-radius:999px; background:var(--accent); opacity:.55; transition:width .35s var(--ease); }
.pct-scale-marker { position:absolute; top:50%; width:16px; height:16px; border-radius:999px;
  background:var(--accent); border:3px solid var(--surface); transform:translate(-50%,-50%);
  box-shadow:0 1px 4px rgba(var(--shadow-color),.35); transition:left .35s var(--ease); }
.pct-ticks { display:flex; justify-content:space-between; font-size:var(--fs-nano); color:var(--muted-2); }

/* ===== AI copilot chat ===== */
.copilot-modal { display:flex; flex-direction:column; max-height:82vh; }
.copilot-thread { flex:1; min-height:220px; max-height:48vh; overflow-y:auto; display:flex; flex-direction:column;
  gap:10px; padding:4px 2px 10px; }
.copilot-empty { color:var(--muted); font-size:var(--fs-label); line-height:1.5; text-align:center; margin:auto; padding:18px 8px; }
.copilot-msg { max-width:85%; padding:10px 13px; border-radius:16px; font-size:var(--fs-label); line-height:1.45; word-wrap:break-word; overflow-wrap:anywhere; }
/* User text + plain error/fallback bubbles preserve their own line breaks; the
   assistant bubble is rendered markdown (structure comes from <p>/<ul>/<h*>). */
.copilot-msg.user { align-self:flex-end; background:var(--accent); color:#00210f; border-bottom-right-radius:5px; white-space:pre-wrap; }
[data-theme="light"] .copilot-msg.user { color:#fff; }
.copilot-msg.assistant { align-self:flex-start; background:var(--surface-2); color:var(--text); border-bottom-left-radius:5px; }
.copilot-msg.assistant.error { background:var(--red-soft); color:var(--red); white-space:pre-wrap; }
/* Rendered-markdown elements inside an assistant answer. */
.copilot-msg.assistant > :first-child { margin-top:0; }
.copilot-msg.assistant > :last-child { margin-bottom:0; }
.copilot-msg.assistant p { margin:7px 0; }
.copilot-msg.assistant h4, .copilot-msg.assistant h5, .copilot-msg.assistant h6 { font-size:var(--fs-label); font-weight:650; margin:10px 0 3px; }
.copilot-msg.assistant ul, .copilot-msg.assistant ol { margin:5px 0; padding-left:20px; }
.copilot-msg.assistant li { margin:2px 0; }
.copilot-msg.assistant code { background:var(--surface); padding:1px 5px; border-radius:5px; font-size:var(--fs-caption); }
.copilot-msg.assistant pre { background:var(--surface); padding:9px 11px; border-radius:9px; overflow:auto; margin:7px 0; }
.copilot-msg.assistant pre code { background:none; padding:0; }
.copilot-msg.assistant a { color:var(--accent); text-decoration:underline; }
/* Blinking caret shown while the answer is still streaming in. */
.copilot-caret { display:inline-block; width:2px; height:1em; margin-left:2px; background:var(--accent); vertical-align:text-bottom; animation:copilot-caret-blink 1s step-end infinite; }
@keyframes copilot-caret-blink { 0%,100% { opacity:1; } 50% { opacity:0; } }
@media (prefers-reduced-motion:reduce) { .copilot-caret { animation:none; } }
.copilot-typing { display:inline-block; letter-spacing:2px; animation:reval-pulse 1.1s ease-in-out infinite; }
/* Trailing spacer that lets the latest question scroll to the top of the thread
   while its answer streams in below (height is managed inline by app.js). */
.copilot-spacer { flex:none; }
.copilot-input { display:flex; gap:8px; align-items:center; margin-top:6px; }
.copilot-input input { flex:1; }
.copilot-input .btn-primary.icon-only { width:44px; height:44px; flex:none; padding:0; display:flex; align-items:center; justify-content:center; }
.copilot-input .btn-primary svg { width:20px; height:20px; }

/* ===== asset performance: per-entity % badges + mini history charts ===== */
.chg { display:inline-block; font-size:var(--fs-micro); font-weight:600; letter-spacing:.01em; white-space:nowrap; margin-top:2px; }
.chg.good { color:var(--positive); }
.chg.bad { color:var(--red); }
/* dimmed timeframe suffix on the day-change badge ("today") */
.chg-per { color:var(--muted); font-weight:500; }
/* holdings rows are tappable: tapping opens the stock-research overlay */
.h-item { display:block; }
.h-row.tappable { cursor:pointer; }
/* account-detail block inside the edit modal: crypto coverage / DeFi note. */
.acct-hist { margin:2px 0 14px; padding:12px; background:var(--surface-2); border-radius:var(--radius-sm); }
.acct-cov { margin-top:10px; }
.acct-defi { margin-top:10px; display:flex; flex-direction:column; gap:5px; }
.acct-defi-txt { font-size:var(--fs-small); color:var(--muted); line-height:1.45; }

/* ===================== AI features ===================== */
/* Flagship copilot entry. Docked just ABOVE the bottom tab bar (~53px tall), in the
   right-hand thumb arc. Icon-only round button so it never occludes a value beneath it;
   the "Ask Ascend" label rides aria-label + a one-time coachmark. */
.ai-fab {
  position:fixed; right:16px; z-index:62;
  bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  display:inline-flex; align-items:center; justify-content:center;
  width:52px; height:52px; padding:0;
  background:var(--accent); color:#00210f; border:none; cursor:pointer;
  border-radius:999px;
  box-shadow:0 6px 18px rgba(var(--shadow-color),.28);
  /* Keep on its own compositor layer so it doesn't detach/float during iOS
     momentum scroll (same fix as .tabbar). */
  transform:translate3d(0,0,0); will-change:transform; -webkit-backface-visibility:hidden;
  /* Only transform/opacity/filter animate — a stable FAB that hides-while-scrolling and
     returns to the SAME spot (no content-reactive reposition), so `bottom` is never
     animated. Bottom clearance on .wrap keeps the last row above the resting FAB. */
  transition:filter .18s var(--ease), opacity .18s var(--ease), transform .18s var(--ease);
}
[data-theme="light"] .ai-fab { color:#fff; box-shadow:0 6px 16px rgba(47,125,94,.32); }
/* Icon-only so the floating control never covers a value (e.g. the bottom-right
   "Savings rate" cash-flow tile). The label lives in aria-label + the one-time
   coachmark below, so discoverability survives without occluding content. */
.ai-fab-icon svg { width:22px; height:22px; flex:none; display:block; }
.ai-fab:hover { filter:brightness(1.06); }
.ai-fab:active { transform:scale(.92); }
.ai-fab[hidden] { display:none; }
/* Scroll-to-hide: while the list is moving, tuck the icon away so it never sits over a
   right-aligned value (e.g. a holding's balance); it returns a beat after scrolling stops.
   pointer-events:none prevents a stray tap mid-scroll. */
.ai-fab.scrolling { opacity:0; transform:translate3d(0,8px,0) scale(.9); pointer-events:none; }
@media (prefers-reduced-motion:reduce) { .ai-fab.scrolling { transform:none; } }

/* One-time intro that points at the icon FAB so a first-time user knows the AI is
   here — shown once (localStorage), auto-dismisses on tap/scroll/timeout. Sits
   ABOVE the FAB, so it never covers dashboard content. */
.ai-fab-coach {
  position:fixed; right:16px; z-index:63;
  bottom:calc(var(--tabbar-h) + 52px + 12px + env(safe-area-inset-bottom));
  max-width:230px; padding:11px 13px;
  background:var(--surface); color:var(--text);
  border:1px solid var(--line); border-radius:14px;
  box-shadow:0 8px 24px rgba(var(--shadow-color),.22);
  font-size:var(--fs-caption); line-height:1.35;
  animation:ai-coach-in .26s var(--ease) both;
}
.ai-fab-coach strong { font-weight:650; }
.ai-fab-coach::after {
  content:""; position:absolute; right:24px; bottom:-7px;
  width:13px; height:13px; background:var(--surface);
  border-right:1px solid var(--line); border-bottom:1px solid var(--line);
  transform:rotate(45deg);
}
.ai-fab-coach[hidden] { display:none; }
@keyframes ai-coach-in { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion:reduce) { .ai-fab-coach { animation:none; } }
@media (min-width:560px) { .ai-fab-coach { right:24px; bottom:calc(78px + 52px + 12px + env(safe-area-inset-bottom)); } }
.ai-fab { animation:ai-fab-pulse 2.4s var(--ease) 1; }
@keyframes ai-fab-pulse { 0%,100% { box-shadow:0 6px 18px rgba(var(--shadow-color),.28); } 40% { box-shadow:0 6px 18px rgba(var(--shadow-color),.28), 0 0 0 7px var(--accent-soft); } }
@media (prefers-reduced-motion:reduce) { .ai-fab { animation:none; } }
@media (min-width:560px) { .ai-fab { right:24px; bottom:calc(78px + env(safe-area-inset-bottom)); } }
/* Desktop: the content column (.wrap) caps at 880px (border-box) and centres, so on a wide
   window a viewport-anchored FAB floats out in the empty right margin, detached from the
   content. Pin it (and its coachmark) to the column's right edge instead. `right` is used
   on purpose — .ai-fab.scrolling/:active already own the `transform` channel, so anchoring
   via translate would fight the scroll-hide animation. Breakpoint = the 880px column cap
   (must match .wrap's max-width): the column stops growing at exactly 880px, so the calc is
   valid and continuous from there up — no crowded seam against the margin. env() left-inset
   is 0 on desktop, so (100vw-880)/2 is the true side margin and +16px insets the FAB. */
@media (min-width:880px) {
  .ai-fab { right:calc((100vw - 880px) / 2 + 16px); }
  .ai-fab-coach { right:calc((100vw - 880px) / 2 + 16px); }
}

/* Members search box */
.members-search { display:flex; align-items:center; gap:8px; background:var(--surface-2);
  border:1px solid var(--line); border-radius:999px; padding:0 14px; margin-bottom:12px; }
.members-search svg { width:17px; height:17px; flex:none; color:var(--muted); }
.members-search input { flex:1; min-width:0; background:none; border:none; padding:11px 0; font:inherit; color:var(--text); }
.members-search input:focus { outline:none; }
/* The input suppresses its own outline, so surface keyboard focus on the wrapper border. */
.members-search:focus-within { border-color:var(--accent); }

/* Proactive insight card (Overview). */
.insight-card { position:relative; display:flex; gap:11px; align-items:flex-start;
  background:linear-gradient(135deg, var(--accent-soft), var(--surface)); border:1px solid var(--line);
  border-radius:var(--radius-sm); padding:13px 38px 13px 13px; margin:0 0 14px; }
.insight-card[hidden] { display:none; }
.insight-spark { flex:none; width:30px; height:30px; border-radius:9px; display:flex; align-items:center; justify-content:center;
  background:var(--accent); color:#00210f; }
[data-theme="light"] .insight-spark { color:#fff; }
.insight-spark svg { width:18px; height:18px; }
.insight-main strong { display:block; font-size:var(--fs-label); margin-bottom:2px; }
.insight-main p { margin:0; font-size:var(--fs-caption); color:var(--muted); line-height:1.45; }
.insight-close { position:absolute; top:8px; right:8px; width:28px; height:28px; min-height:0; padding:0;
  display:flex; align-items:center; justify-content:center; border:none; border-radius:999px;
  background:transparent; color:var(--muted); cursor:pointer; transition:.15s var(--ease); }
.insight-close:hover { background:var(--hover); color:var(--text); }
.insight-close svg { width:15px; height:15px; }

/* ---------- Weekly Rundown: entry card + full report ---------- */
.rundown-card { position:relative; display:flex; align-items:center; gap:13px; width:100%; text-align:left; cursor:pointer;
  background:linear-gradient(135deg, var(--accent-soft), var(--surface)); border:1px solid var(--line);
  border-radius:var(--radius-sm); padding:14px; margin:0 0 14px; transition:.15s var(--ease); }
.rundown-card[hidden] { display:none; }
.rundown-card:hover { border-color:var(--line-strong); }
.rundown-card.unseen { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent-soft); }
.rundown-card-open { display:flex; align-items:center; gap:13px; min-width:0; flex:1; padding:0;
  border:0; background:transparent; color:inherit; text-align:left; cursor:pointer; }
.rundown-card-dismiss { width:30px; height:30px; min-height:0; padding:0; flex:none; display:flex;
  align-items:center; justify-content:center; border:0; border-radius:999px; background:transparent;
  color:var(--muted); cursor:pointer; }
.rundown-card-dismiss:hover { background:var(--hover); color:var(--text); }
.rundown-card-dismiss svg { width:15px; height:15px; }
.rundown-card-icon { flex:none; width:38px; height:38px; border-radius:11px; display:flex; align-items:center; justify-content:center;
  background:var(--accent); color:#00210f; }
[data-theme="light"] .rundown-card-icon { color:#fff; }
.rundown-card-icon svg { width:20px; height:20px; }
.rundown-card-main { display:flex; flex-direction:column; gap:2px; flex:1; min-width:0; }
.rundown-card-main strong { font-size:var(--fs-label); font-weight:650; }
/* Two-line clamp (not a single-line mid-word cut like "…is ou…") on the card subtitle,
   which carries a real sentence — the Signal theme / Rundown lead (design review). */
.rundown-card-main span { font-size:var(--fs-small); color:var(--muted); overflow:hidden;
  display:-webkit-box; -webkit-line-clamp:2; line-clamp:2; -webkit-box-orient:vertical; }
.rundown-card-chev { color:var(--muted-2); font-size:var(--fs-title); flex:none; }

/* ---------- Pro teasers: locked previews shown to free users ---------- */
.pro-pill { flex:none; display:inline-flex; align-items:center; gap:4px; font-size:var(--fs-micro); font-weight:650;
  letter-spacing:.02em; color:#00210f; background:var(--accent); border-radius:999px; padding:3px 9px 3px 7px; }
[data-theme="light"] .pro-pill { color:#fff; }
.pro-pill svg { width:13px; height:13px; }
.pro-teaser { position:relative; border-radius:var(--radius-sm); overflow:hidden; }
/* Honest label so the blurred example never reads as the user's withheld data. */
.pro-teaser-label { position:absolute; top:8px; right:10px; z-index:2; font-size:var(--fs-micro); font-weight:600;
  color:var(--muted-2); background:var(--surface); border:1px solid var(--line); border-radius:999px; padding:2px 9px; }
.pro-teaser-blur { filter:blur(5px); -webkit-filter:blur(5px); pointer-events:none; user-select:none; opacity:.85; }
.pro-lock { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; gap:7px; padding:18px; background:rgba(21,25,22,.60);
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px); }
[data-theme="light"] .pro-lock { background:rgba(243,238,227,.62); }
.pro-lock-icon { flex:none; width:42px; height:42px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:var(--accent); color:#00210f; margin-bottom:2px; }
[data-theme="light"] .pro-lock-icon { color:#fff; }
.pro-lock-icon svg { width:22px; height:22px; }
.pro-lock strong { font-size:var(--fs-sub); font-weight:650; }
.pro-lock > span { font-size:var(--fs-small); color:var(--muted); max-width:34ch; line-height:1.45; }
.pro-lock .btn-primary { margin-top:6px; }

/* Free "discovery shelf": tappable chips for stocks that already have a cached
   brief, so a free user can read + share a real teaser without guessing a ticker. */
.research-shelf { margin-bottom:14px; }
/* Collapsed "Browse research" disclosure: a toggle row that opens a searchable list,
   so the chip list scales instead of spilling every cached ticker open at once. */
.research-shelf-toggle { display:flex; align-items:center; gap:8px; width:100%; padding:8px 0; border:none;
  background:none; cursor:pointer; color:var(--muted); text-align:left; }
.research-shelf-toggle:hover { color:var(--text); }
.research-shelf-head { font-size:var(--fs-micro); font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:inherit; margin:0; }
.research-shelf-count { font-size:var(--fs-nano); font-weight:700; line-height:1; padding:3px 7px; border-radius:999px;
  background:var(--surface-2); color:var(--muted); }
.research-shelf-chev { margin-left:auto; display:inline-flex; color:var(--muted); transition:transform .18s var(--ease); }
.research-shelf-chev svg { width:16px; height:16px; }
.research-shelf-toggle[aria-expanded="true"] .research-shelf-chev { transform:rotate(90deg); }
.research-shelf-panel { padding-top:4px; }
.research-shelf-search { display:flex; align-items:center; gap:8px; background:var(--surface-2); border:1px solid var(--line);
  border-radius:999px; padding:0 14px; margin-bottom:10px; }
.research-shelf-search svg { width:16px; height:16px; flex:none; color:var(--muted); }
.research-shelf-search input { flex:1; min-width:0; background:none; border:none; padding:10px 0; font:inherit; font-size:var(--fs-label);
  color:var(--text); text-transform:uppercase; }
.research-shelf-search input::placeholder { text-transform:none; }
.research-shelf-search input:focus { outline:none; }
.research-shelf-search:focus-within { border-color:var(--accent); }
.research-shelf-empty { color:var(--muted); font-size:var(--fs-caption); padding:6px 2px; }
/* One horizontal, swipeable row instead of a wrapping grid — stays a clean single
   line however many briefs exist (a wrapping grid turned into a tall wall at scale).
   Scrollbar hidden; snap + edge padding so the last chip doesn't clip. When the
   search filter hides chips, [hidden] collapses them out of the row. */
.research-shelf-row { display:flex; flex-wrap:nowrap; gap:8px; overflow-x:auto; padding:1px 1px 3px;
  scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; scrollbar-width:none; overscroll-behavior-x:contain; }
.research-shelf-row::-webkit-scrollbar { display:none; }
.research-chip { display:inline-flex; align-items:center; gap:7px; padding:6px 12px 6px 7px; border:1px solid var(--line-strong);
  border-radius:999px; background:var(--surface); color:var(--text); font-size:var(--fs-caption); font-weight:600; line-height:1;
  flex:none; scroll-snap-align:start; }
.research-chip[hidden] { display:none; }
.research-chip:hover { border-color:var(--accent); background:var(--hover); }
.research-chip-logo { width:20px; height:20px; border-radius:5px; overflow:hidden; flex:none; display:inline-flex; align-items:center; justify-content:center; }
/* contain (not cover) + a hair of padding so the whole brand mark fits inside the
   tiny 20px chip icon instead of being zoomed/cropped edge-to-edge ("smushed"). */
.research-chip-logo img { width:100%; height:100%; object-fit:contain; padding:2px; box-sizing:border-box; }
/* Pro lock inside a free user's teaser report (in the stock overlay). */
.research-teaser-lock { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:14px; padding:12px 14px; border-radius:13px; background:var(--accent-soft); }
.research-teaser-lock .rt-lock-svg { width:18px; height:18px; flex:none; color:var(--accent); }
.research-teaser-lock > span { flex:1 1 160px; font-size:var(--fs-caption); color:var(--text); line-height:1.4; }
.research-teaser-lock .btn-primary { margin-left:auto; }
.research-empty { color:var(--muted); font-size:var(--fs-label); line-height:1.5; padding:10px 0; }

@media (min-width:560px){ .modal-lg { max-width:580px; } }
.rundown-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.rundown-head h3 { margin:0; }
.rundown-head .sub { margin:3px 0 0; }
/* Past-rundowns archive picker (PROD-05) — a subtle secondary control under the meta line. */
.rundown-week-select { margin:8px 0 0; font-size:var(--fs-small); color:var(--muted); background:var(--surface-2);
  border:1px solid var(--line); border-radius:8px; padding:5px 8px; max-width:240px; cursor:pointer; }
.overlay-x { width:30px; height:30px; min-height:0; padding:0; flex:none; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:999px; background:transparent; color:var(--muted); cursor:pointer; }
.overlay-x:hover { background:var(--hover); color:var(--text); }
.overlay-x svg { width:16px; height:16px; }
/* Header actions for the stock-research ("holding report") overlay: a prominent
   Share pill sits next to the close X so the share-to-grow loop is impossible to miss. */
.research-head-actions { display:flex; align-items:center; gap:8px; flex:none; }
.btn-share {
  display:inline-flex; align-items:center; gap:6px; flex:none; line-height:1;
  background:var(--accent); color:#00210f; border:none; font-weight:600; font-size:var(--fs-caption);
  padding:7px 13px; border-radius:999px;
}
[data-theme="light"] .btn-share { color:#fff; }
.btn-share:hover { filter:brightness(1.08); }
.btn-share svg { width:15px; height:15px; }

.rundown-body { margin-top:6px; }
.rd-summary { font-size:var(--fs-label); line-height:1.5; margin:6px 0 16px; }
.rd-stats { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:6px; }
/* The tile floor rides --dynamic-type-scale so the 3-up row reflows to 2-up / 1-up as
   Dynamic Type grows (values would otherwise outgrow a fixed 90px tile and clip). At the
   default scale of 1 this is exactly 90px, so the normal layout is unchanged. */
.rd-stat { flex:1; min-width:calc(5.625rem * var(--dynamic-type-scale, 1)); background:var(--surface-2); border-radius:var(--radius-sm); padding:11px 13px; }
.rd-stat-label { font-size:var(--fs-micro); color:var(--muted); margin-bottom:3px; }
.rd-stat-value { font-size:var(--fs-body); font-weight:650; }
.rd-stat-value.green { color:var(--accent); }
.rd-stat-value.red { color:var(--red); }
.rd-sec { margin-top:20px; }
.rd-sec h4 { font-size:var(--fs-small); text-transform:uppercase; letter-spacing:.05em; color:var(--muted); margin:0 0 9px; }
.rd-chips { display:flex; flex-wrap:wrap; gap:7px; }
.rd-chip { font-size:var(--fs-caption); font-weight:600; padding:5px 10px; border-radius:999px; background:var(--surface-2); }
.rd-chip.up { color:var(--accent); background:var(--accent-soft); }
.rd-chip.down { color:var(--red); background:var(--red-soft); }
.rd-alloc { display:flex; flex-direction:column; gap:8px; }
.rd-alloc-row { display:flex; align-items:center; gap:10px; }
.rd-alloc-label { font-size:var(--fs-caption); width:96px; flex:none; }
.rd-alloc-bar { flex:1; height:7px; background:var(--surface-2); border-radius:999px; overflow:hidden; }
.rd-alloc-bar span { display:block; height:100%; background:var(--accent); border-radius:999px; }
.rd-alloc-pct { font-size:var(--fs-small); color:var(--muted); width:42px; text-align:right; flex:none; }
.rd-note { font-size:var(--fs-caption); color:var(--muted); line-height:1.45; margin:11px 0 0; }
.rd-holds { display:flex; flex-direction:column; gap:8px; }
.rd-hold { display:flex; flex-direction:column; gap:4px; width:100%; text-align:left; cursor:pointer;
  background:var(--surface-2); border:1px solid transparent; border-radius:var(--radius-sm); padding:11px 13px; }
.rd-hold:hover { border-color:var(--line); }
.rd-hold-top { display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.rd-hold-tkr { font-weight:650; font-size:var(--fs-label); }
.rd-hold-pct { font-size:var(--fs-small); color:var(--muted); }
.rd-hold-line { font-size:var(--fs-caption); line-height:1.4; }
.rd-hold-line.bull { color:var(--accent); }
.rd-hold-line.bear { color:var(--red); }
.rd-hold-more { font-size:var(--fs-small); color:var(--muted-2); margin-top:2px; }
.rd-hold-detail { margin-top:8px; padding-top:9px; border-top:1px solid var(--line); font-size:var(--fs-caption); line-height:1.45; }
.rd-hold-detail[hidden] { display:none; }
.rd-hold-loading { color:var(--muted); font-size:var(--fs-caption); }
.rd-hd-summary { margin:0 0 8px; }
.rd-hd-personal { margin:0 0 8px; color:var(--accent); font-weight:600; }
.rd-hd-col { margin-top:8px; }
.rd-hd-col h5 { margin:0 0 4px; font-size:var(--fs-small); text-transform:uppercase; letter-spacing:.04em; color:var(--muted); }
.rd-hd-col.bull h5 { color:var(--accent); }
.rd-hd-col.bear h5 { color:var(--red); }
.rd-hd-col ul, .rd-list { margin:0; padding-left:18px; }
.rd-list { display:flex; flex-direction:column; gap:5px; font-size:var(--fs-caption); line-height:1.45; }
.rd-list li { margin:0; }
.rd-disclaimer { font-size:var(--fs-micro); color:var(--muted-2); margin:20px 0 0; }

/* Earnings: compact one-line rows (capped to the next few in JS) so the section
   stays short instead of mirroring the whole Holdings list. */
.earn-list { display:flex; flex-direction:column; }
.earn-row { display:flex; align-items:center; gap:10px; padding:8px 4px; border-top:1px solid var(--line); transition:.15s var(--ease); }
/* .earn-row sets display:flex, an author rule that would otherwise override the
   UA [hidden] rule — so hide collapsed "extra" rows explicitly. */
.earn-row[hidden] { display:none; }
.earn-row:first-child { border-top:none; }
.earn-row.tappable { cursor:pointer; }
.earn-row.tappable:hover { background:var(--hover); }
.earn-row .logo-wrap { width:24px; height:24px; border-radius:7px; }
.earn-row .logo-fallback { font-size:var(--fs-nano); }
.earn-ticker { flex:1; min-width:0; font-size:var(--fs-label); font-weight:600; letter-spacing:-.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.earn-when { flex:none; font-size:var(--fs-small); color:var(--muted); white-space:nowrap; }
/* "+N more" is a real <button> (toggles the hidden rows); strip the UA chrome. */
.earn-more { display:block; width:100%; text-align:left; padding:8px 4px; font-size:var(--fs-small); color:var(--muted-2);
  background:none; border:none; border-top:1px solid var(--line); cursor:pointer; transition:color .15s var(--ease); }
.earn-more:hover { color:var(--text); }
/* Earnings row → stock research affordance + the research overlay header. */
.earn-chev { color:var(--muted-2); font-size:var(--fs-h2); flex:none; margin-left:4px; align-self:center; }

/* ---- consolidated "Across your holdings" digest ---- */
/* Weekly AI "what matters" note — sits above the factual blocks. */
.digest-note { background:var(--accent-soft); border-radius:12px; padding:13px 15px; margin-bottom:18px; }
.digest-note-head { font-size:var(--fs-label); font-weight:700; letter-spacing:-.01em; margin-bottom:4px; }
.digest-note-summary { margin:0; font-size:var(--fs-caption); line-height:1.5; color:var(--text); }
.digest-note-bullets { margin:8px 0 0; padding-left:18px; }
.digest-note-bullets li { font-size:var(--fs-caption); line-height:1.45; margin:2px 0; }
.digest-note-foot { margin-top:9px; font-size:var(--fs-nano); color:var(--muted-2); }
.digest-block + .digest-block { margin-top:18px; }
.digest-h { margin:0 0 6px; font-size:var(--fs-small); font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--muted-2); }
/* Upcoming block: the "Upcoming" label on the left, the earnings-date count on the right
   (moved here from the section header so the count sits with the dates it counts). */
.digest-h-row { display:flex; align-items:baseline; justify-content:space-between; gap:10px; margin:0 0 6px; }
.digest-h-row .digest-h { margin:0; }
.digest-h-count { font-size:var(--fs-small); font-weight:600; color:var(--muted); white-space:nowrap; }
.digest-kind { font-size:var(--fs-micro); font-weight:600; color:var(--muted); letter-spacing:0; }
.digest-cats { list-style:none; margin:0; padding:0; }
.digest-cats li { padding:7px 4px; font-size:var(--fs-caption); line-height:1.45; color:var(--text); }
/* Separator BETWEEN visible rows only — so filtering (which hides rows) never
   leaves a stray top border above the first shown row or a gap where one is hidden. */
.digest-cats li:not([hidden]) ~ li:not([hidden]) { border-top:1px solid var(--line); }
/* Per-holding filter chips above the catalyst list. One scrollable row (the list can
   now cover every holding), so a long book scrolls sideways instead of stacking. */
.digest-cat-filter { display:flex; flex-wrap:nowrap; gap:6px; margin:0 0 10px; overflow-x:auto;
  -webkit-overflow-scrolling:touch; scrollbar-width:none; padding-bottom:2px; }
.digest-cat-filter::-webkit-scrollbar { display:none; }
.digest-cat-chip { flex:0 0 auto; font:inherit; font-size:var(--fs-micro); font-weight:700; letter-spacing:.3px; padding:4px 10px;
  border:none; border-radius:999px; cursor:pointer; background:var(--surface-2); color:var(--muted); transition:.15s var(--ease); }
.digest-cat-chip:hover { color:var(--text); }
.digest-cat-chip.active { background:var(--accent-soft); color:var(--accent); }
.digest-cats-empty { color:var(--muted); font-size:var(--fs-caption); padding:8px 4px; }
.digest-cat-tk { display:inline-block; font-weight:700; font-size:var(--fs-micro); color:var(--accent); background:var(--accent-soft);
  padding:1px 7px; border-radius:999px; margin-right:6px; letter-spacing:.3px; vertical-align:1px; }
.digest-news-tk { font-weight:700; color:var(--accent); }
.stock-research-title { display:flex; align-items:center; gap:11px; min-width:0; }
.stock-research-title h3 { margin:0; }
.stock-research-title .sub { margin:2px 0 0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.stock-research-logo { flex:none; display:flex; align-items:center; }
/* Breathing room between the overlay header and the research body so the title
   block and the disclaimer banner don't crowd / overlap. */
#stockResearchBody { margin-top:16px; }

/* ---- research overlay: Finnhub key stats / earnings track / news ---- */
#stockResearchFacts:not(:empty) { margin-top:16px; }
.kstat-meta { font-size:var(--fs-small); color:var(--muted); margin-bottom:9px; }
.kstat-grid { display:grid; grid-template-columns:repeat(2, 1fr); gap:8px; }
@media (min-width:420px){ .kstat-grid { grid-template-columns:repeat(3, 1fr); } }
.kstat { background:var(--surface-2); border-radius:var(--radius-sm); padding:9px 11px; display:flex; flex-direction:column; gap:2px; min-width:0; }
.kstat-label { font-size:var(--fs-micro); color:var(--muted); white-space:nowrap; }
.kstat-val { font-size:var(--fs-label); font-weight:650; letter-spacing:-.01em; }
.kstat-val .kstat-chg { font-size:var(--fs-micro); font-weight:600; }
.kstat-chg.up { color:var(--positive); } .kstat-chg.down { color:var(--red); }
.kstat-label { display:inline-flex; align-items:center; gap:3px; min-width:0; }
/* Truncate the label TEXT on the inner span so overflow:hidden never clips the info
   button's 44px tap expander (which lives in the flex row, outside this span). */
.kstat-label-txt { min-width:0; overflow:hidden; text-overflow:ellipsis; }
.kstat-info { position:relative; display:inline-flex; align-items:center; justify-content:center; width:15px; height:15px; padding:0; border:1px solid var(--line-strong); border-radius:50%; background:transparent; color:var(--muted); font-size:var(--fs-nano); font-weight:700; line-height:1; cursor:pointer; flex:none; }
/* Expand the tap target to ~44px without changing the visible 15px dot (a11y). */
.kstat-info::before { content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:44px; height:44px; }
.kstat-info:hover { color:var(--text); border-color:var(--muted); }
.kstat-info.active { background:var(--accent); color:#00210f; border-color:var(--accent); }
[data-theme="light"] .kstat-info.active { color:#fff; }
.kstat-def { margin-top:9px; background:var(--accent-soft); color:var(--accent); font-size:var(--fs-small); line-height:1.45; padding:9px 11px; border-radius:var(--radius-sm); }
.kstat-def-label { font-weight:650; }
.research-extra { margin-top:18px; }
.research-extra > h4 { font-size:var(--fs-small); text-transform:uppercase; letter-spacing:.05em; color:var(--muted); margin:0 0 9px; }
.etrack-headline { font-size:var(--fs-label); font-weight:600; margin-bottom:8px; }
.etrack-sub { font-weight:400; color:var(--muted); font-size:var(--fs-small); }
.etrack-chips { display:flex; flex-wrap:wrap; gap:7px; }
.etrack-chip { font-size:var(--fs-small); font-weight:600; padding:4px 9px; border-radius:999px; background:var(--surface-2); white-space:nowrap; }
.etrack-chip.up { color:var(--accent); background:var(--accent-soft); }
.etrack-chip.down { color:var(--red); background:var(--red-soft); }
.news-list { display:flex; flex-direction:column; }
.news-item { display:flex; flex-direction:column; gap:2px; padding:9px 2px; border-top:1px solid var(--line); text-decoration:none; color:var(--text); }
.news-item:first-child { border-top:none; }
.news-item:hover .news-headline { color:var(--accent); }
.news-headline { font-size:var(--fs-caption); line-height:1.35; font-weight:500; }
.news-meta { font-size:var(--fs-micro); color:var(--muted); }

/* Admin AI kill-switch toggles. */
.ai-flag-list { display:flex; flex-direction:column; gap:2px; }
.ai-flag-row { display:flex; align-items:center; gap:12px; padding:11px 4px; cursor:pointer; position:relative; }
.ai-flag-row + .ai-flag-row { border-top:1px solid var(--line); }
.ai-flag-row.master .ai-flag-title { font-weight:600; }
/* Admin "Generate signal now" affordance under the flag list. */
.ai-run-row { display:flex; align-items:center; gap:12px; margin-top:12px; padding-top:12px; border-top:1px solid var(--line); flex-wrap:wrap; }
.ai-run-row .hint { flex:1; min-width:160px; font-size:var(--fs-small); color:var(--muted); }
.ai-flag-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; }
.ai-flag-title { font-size:var(--fs-label); }
.ai-flag-sub { font-size:var(--fs-small); color:var(--muted); }
.ai-flag-toggle { position:absolute; opacity:0; width:0; height:0; }
.ai-flag-switch { flex:none; width:42px; height:25px; border-radius:999px; background:var(--surface-2);
  border:1px solid var(--line-strong); position:relative; transition:background .18s var(--ease); }
.ai-flag-switch::after { content:""; position:absolute; top:2px; left:2px; width:19px; height:19px; border-radius:50%;
  background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.3); transition:transform .18s var(--ease); }
.ai-flag-toggle:checked ~ .ai-flag-switch { background:var(--accent); border-color:transparent; }
.ai-flag-toggle:checked ~ .ai-flag-switch::after { transform:translateX(17px); }
.ai-flag-toggle:focus-visible ~ .ai-flag-switch { outline:2px solid var(--accent); outline-offset:2px; }
@media (prefers-reduced-motion:reduce) { .ai-flag-switch, .ai-flag-switch::after { transition:none !important; } }

/* Stock research. */
.research-bar { display:flex; gap:8px; margin-bottom:10px; }
.research-bar input { flex:1; min-width:0; text-transform:uppercase; }
.research-result:empty { display:none; }
.research-loading, .research-err { font-size:var(--fs-caption); color:var(--muted); padding:8px 2px; }
.research-brief { position:relative; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); padding:14px; }
/* Dismiss ✕ for the inline research report — same small round control as the Home cards. */
.research-dismiss { position:absolute; top:10px; right:10px; width:30px; height:30px; min-height:0; padding:0; flex:none;
  display:flex; align-items:center; justify-content:center; border:0; border-radius:999px; background:transparent;
  color:var(--muted); cursor:pointer; }
.research-dismiss:hover { background:var(--hover); color:var(--text); }
.research-dismiss svg { width:15px; height:15px; }
.research-ticker { font-size:var(--fs-lg); font-weight:700; margin-bottom:6px; }
/* Reserve room for the dismiss ✕ ONLY when the button is actually present — the blurred
   teaser sample reuses .research-brief > .research-ticker but has NO dismiss button, so
   key off the button itself (it precedes the ticker) rather than the wrapper. */
.research-dismiss ~ .research-ticker { padding-right:26px; }
.research-summary { margin:0 0 10px; font-size:var(--fs-caption); line-height:1.5; }
.research-personal { margin:0 0 10px; font-size:var(--fs-caption); font-weight:500; color:var(--accent); }
.research-col { margin-bottom:10px; }
.research-col h4 { margin:0 0 4px; font-size:var(--fs-caption); }
.research-col.bull h4 { color:var(--accent); }
.research-col.bear h4 { color:var(--red); }
.research-col ul { margin:0; padding-left:18px; }
.research-col li { font-size:var(--fs-caption); line-height:1.5; margin-bottom:3px; }
.research-disclaimer { font-size:var(--fs-micro); color:var(--muted-2); margin-top:8px; font-style:italic; }
/* Prominent, consistent "not advice" banner shown at the TOP of every AI surface. */
.ai-disclaimer { font-size:var(--fs-small); line-height:1.4; color:var(--muted); background:var(--surface-2);
  border:1px solid var(--line); border-left:3px solid var(--accent); border-radius:var(--radius-sm);
  padding:8px 11px; margin:0 0 12px; }
.ai-disclaimer.sm { font-size:var(--fs-micro); padding:6px 9px; margin:0 0 10px; }
.research-sources { font-size:var(--fs-micro); color:var(--muted); margin-top:6px; }
.research-sources a { color:var(--accent); }

/* Copilot suggestion chips + memory box + wrapped narration. */
.copilot-suggest { display:flex; flex-wrap:wrap; gap:7px; margin-top:12px; }
.copilot-chip { font:inherit; font-size:var(--fs-small); padding:7px 12px; border-radius:999px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--line); color:var(--text); min-height:36px; }
.copilot-chip:hover { border-color:var(--accent); color:var(--accent); }
.memory-box { background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-sm);
  padding:13px; font-size:var(--fs-caption); line-height:1.55; white-space:pre-wrap; min-height:80px; max-height:300px; overflow:auto; }
.wrapped-narration { background:var(--accent-soft); border-radius:var(--radius-sm); padding:12px 14px;
  font-size:var(--fs-caption); line-height:1.55; margin:4px 0 0; }
.wrapped-narration[hidden] { display:none; }
.wrapped-narrate-btn[hidden] { display:none; }
.wrapped-narrate-btn { width:100%; justify-content:center; margin-top:8px; }

/* Upgrade-to-Pro sheet */
.pro-perks { list-style:none; margin:2px 0 14px; padding:0; display:grid; gap:9px; }
.pro-perks li { display:flex; gap:9px; align-items:flex-start; font-size:var(--fs-caption); line-height:1.4; color:var(--text); }
.pro-perks li svg { flex:none; width:18px; height:18px; color:var(--accent); margin-top:1px; }
.plan-options { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:0 0 6px; }
.plan-option { text-align:left; border:1.5px solid var(--line); background:var(--surface); border-radius:var(--radius-sm);
  padding:12px; cursor:pointer; display:flex; flex-direction:column; gap:2px; transition:border-color .12s var(--ease), box-shadow .12s var(--ease); }
.plan-option.selected { border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent); }
.plan-option:active { transform:scale(.98); }
.plan-option:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.plan-option .plan-name { font-weight:600; font-size:var(--fs-caption); color:var(--muted); }
.plan-option .plan-price { font-size:var(--fs-h2); font-weight:700; color:var(--text); }
.plan-option .plan-per { font-size:var(--fs-small); font-weight:600; color:var(--muted); }
.plan-option .plan-note { font-size:var(--fs-small); color:var(--accent); min-height:15px; }
.settings-row.is-pro .settings-row-title { color:var(--accent); }

/* Returns vs the market (Pro) */
.returns-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:4px; }
.returns-figure { display:flex; flex-direction:column; gap:2px; }
.returns-twr { font-size:clamp(1.625rem, max(8vw, calc(1.625rem * var(--dynamic-type-scale, 1))), 2.0625rem); font-weight:700; line-height:1.05; color:var(--text); letter-spacing:-0.01em; }
.returns-sub { font-size:var(--fs-small); color:var(--muted); }
.returns-verdict { font-size:var(--fs-label); color:var(--text); margin:2px 0 10px; }
.returns-chart { width:100%; height:170px; display:block; color:var(--muted); overflow:visible; }
.returns-legend { display:flex; gap:16px; margin-top:6px; font-size:var(--fs-small); color:var(--muted); }
.returns-legend .lg-you::before { content:""; display:inline-block; width:14px; height:0; border-top:2.5px solid var(--accent); vertical-align:middle; margin-right:6px; }
.returns-legend .lg-bench::before { content:""; display:inline-block; width:14px; height:0; border-top:1.6px dashed var(--muted); vertical-align:middle; margin-right:6px; }
/* Chart-less fallback: two plain comparison chips (no line swatches) when there's no
   index series to draw, so the legend can't dangle over empty space. */
.returns-compare { display:flex; gap:10px; margin-top:10px; }
.returns-compare .rc-stat { flex:1; display:flex; flex-direction:column; gap:2px; padding:9px 12px; background:var(--surface-2); border-radius:var(--radius-sm); }
.returns-compare .rc-k { font-size:var(--fs-micro); color:var(--muted); }
.returns-compare .rc-v { font-size:var(--fs-body); font-weight:650; color:var(--text); font-variant-numeric:tabular-nums; }
.returns-note { font-size:var(--fs-micro); color:var(--muted-2); margin-top:8px; line-height:1.45; }

/* ---- zero-state action card (Overview, no accounts yet) ---- */
.empty-hero { text-align:center; background:linear-gradient(135deg, var(--accent-soft), var(--surface));
  border:1px solid var(--line); border-radius:var(--radius); padding:24px 20px; margin:4px 0 18px; }
.empty-hero[hidden] { display:none; }
.empty-hero h2 { margin:0 0 6px; font-size:var(--fs-lg); }
.empty-hero p { margin:0 auto 16px; max-width:34ch; color:var(--muted); font-size:var(--fs-label); line-height:1.45; }
.empty-hero-actions { display:flex; gap:10px; justify-content:center; flex-wrap:wrap; }
.empty-hero-foot { margin:14px auto 0; font-size:var(--fs-small); color:var(--muted); opacity:.85; }
/* "What you'll unlock" preview under the connect CTA — aspirational, NOT a paywall
   (no lock pills): shows the value that lands once the user activates (path to 9). */
.unlock-strip { text-align:left; max-width:340px; margin:20px auto 4px; padding-top:16px; border-top:1px solid var(--line); }
.unlock-strip-head { margin:0 0 10px; font-size:var(--fs-small); font-weight:600; letter-spacing:.02em; text-transform:uppercase; color:var(--muted-2); }
.unlock-item { display:flex; align-items:flex-start; gap:10px; margin:0 0 9px; font-size:var(--fs-caption); line-height:1.4; color:var(--muted); }
.unlock-item strong { color:var(--text); font-weight:600; }
.unlock-ic { flex:none; width:26px; height:26px; border-radius:8px; display:grid; place-items:center;
  background:var(--accent-soft); color:var(--accent); }
.unlock-ic svg { width:15px; height:15px; }
/* Day-1 connect-first: with no accounts, drop the $0 stats, the empty Trend/Allocation
   modules, and the Pro teasers so the connect card is the only focus (path to 9). */
#panel-overview.is-empty .stats,
#panel-overview.is-empty #overviewTrendSection,
#panel-overview.is-empty #overviewAllocSection,
#panel-overview.is-empty #rundownTeaser,
#panel-overview.is-empty #rundownCard,
#panel-overview.is-empty #signalCard { display:none; }

/* ---- "why pay" positioning line in the upgrade sheet ---- */
.upgrade-why { margin:2px 0 14px; padding:10px 12px; border-radius:var(--radius-sm);
  background:var(--hover); border:1px solid var(--line); color:var(--muted); font-size:var(--fs-small); line-height:1.45; }

/* ---- crypto coverage honesty (major-tokens chip + DeFi disclosure) ---- */
.cov-chip { display:inline-block; font-size:var(--fs-nano); font-weight:700; letter-spacing:.02em; text-transform:uppercase;
  color:var(--muted); background:var(--hover); border:1px solid var(--line); border-radius:6px; padding:1px 6px; margin-left:6px; vertical-align:middle; }
/* "DeFi not counted" reads as upside ("your true total may be higher"), so it takes the
   brand accent rather than the neutral major-tokens grey — a soft nudge, not an error. */
.cov-chip.defi { color:var(--accent); background:var(--accent-soft); border-color:transparent; }
.crypto-coverage-note { margin-top:10px; }
/* Inline "why?" affordance on the one-line DeFi footnote — taps a short toast with the
   full explanation instead of a 40-word wall inside the wallet list (design review). */
.coverage-info { display:inline-flex; align-items:center; justify-content:center; margin-left:5px;
  min-width:24px; min-height:24px; padding:0; background:none; border:none; cursor:pointer;
  color:var(--accent); font-size:var(--fs-small); line-height:1; vertical-align:-1px; }
.coverage-info:active { opacity:.6; }

/* ---- admin: DeFi demand backlog (read-only build-order signal) ---- */
.defi-backlog { display:flex; flex-direction:column; gap:8px; }
.defi-bk-item { display:flex; flex-direction:column; }
.defi-bk-row { display:flex; align-items:center; gap:12px; width:100%; padding:10px 12px; background:var(--surface-2);
  border:1px solid var(--line); border-radius:var(--radius-sm); text-align:left; font:inherit; color:inherit; cursor:pointer; }
.defi-bk-row:hover { background:var(--hover); }
.defi-bk-main { flex:1; min-width:0; }
.defi-bk-title { font-weight:650; font-size:var(--fs-label); }
.defi-bk-pattern { font-size:var(--fs-nano); font-weight:700; text-transform:uppercase; letter-spacing:.02em;
  color:var(--muted); background:var(--hover); border-radius:6px; padding:1px 6px; margin-left:7px; vertical-align:middle; }
.defi-bk-ex { font-size:var(--fs-small); color:var(--muted); margin-top:3px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.defi-bk-stats { display:flex; flex-direction:column; align-items:flex-end; gap:1px; flex:none; font-size:var(--fs-small); color:var(--muted); }
.defi-bk-stat b { color:var(--text); font-size:var(--fs-label); }
.defi-bk-chev { flex:none; color:var(--muted); font-size:var(--fs-lg); line-height:1; transition:transform .15s ease; }
.defi-bk-row[aria-expanded="true"] .defi-bk-chev { transform:rotate(90deg); }
.defi-bk-detail { display:flex; flex-direction:column; gap:1px; padding:4px 6px 2px; }
/* `display:flex` above outranks the UA `[hidden]{display:none}` (same specificity,
   later wins), so toggling the detail's `hidden` failed to collapse it. Restore it. */
.defi-bk-detail[hidden] { display:none; }
.defi-bk-note { font-size:var(--fs-small); color:var(--muted); padding:8px 6px; }
.defi-pos { display:flex; align-items:center; gap:10px; padding:7px 8px; border-radius:8px; text-decoration:none; color:inherit; }
.defi-pos:hover { background:var(--surface-2); }
.defi-pos-sym { font-weight:600; font-size:var(--fs-caption); flex:none; max-width:40%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.defi-pos-addr { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:var(--fs-small); color:var(--muted); }
.defi-pos-meta { margin-left:auto; font-size:var(--fs-small); color:var(--muted); white-space:nowrap; }
.defi-pos-ext { flex:none; color:var(--muted); font-size:var(--fs-micro); }

/* ---- cash-flow tile (money in vs out) ---- */
.cashflow-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:4px; }
.cf-cell { background:var(--hover); border:1px solid var(--line); border-radius:var(--radius-sm); padding:11px 13px; }
.cf-label { font-size:var(--fs-small); color:var(--muted); margin-bottom:3px; }
.cf-value { font-size:var(--fs-h2); font-weight:700; color:var(--text); }
.cf-value.green { color:var(--accent); }
.cf-value.red { color:var(--red); }
@media (min-width:560px) { .cashflow-grid { grid-template-columns:repeat(4,1fr); } }

/* ---- copilot cap → inline upgrade card (instead of a dead red error) ---- */
.copilot-msg.upsell { background:linear-gradient(135deg, var(--accent-soft), var(--surface));
  border:1px solid var(--line-strong); display:flex; flex-direction:column; align-items:flex-start; gap:10px; }
.copilot-upsell-text { margin:0; font-size:var(--fs-label); line-height:1.45; color:var(--text); }
.copilot-upsell-btn { align-self:stretch; }
/* A Pro-tool upsell button appended under a normal assistant answer bubble. */
.copilot-msg.assistant .copilot-upsell-btn { display:block; margin-top:10px; }
/* "Try again" under a failed answer bubble — re-asks the same question. */
.copilot-retry { display:block; margin-top:10px; min-height:36px; padding:7px 14px; border-radius:999px;
  background:transparent; color:var(--accent); border:1px solid var(--line-strong); font-weight:600;
  font-size:var(--fs-caption); cursor:pointer; }
.copilot-retry:hover { border-color:var(--accent); }
.copilot-retry:active { transform:scale(.97); }
/* Free-tier message meter under the copilot input. */
.copilot-quota { margin:8px 2px 0; text-align:center; font-size:var(--fs-small); }

/* ---- X Signal Report (Pro) ---- */
.signal-theme { font-size:var(--fs-label); font-weight:600; color:var(--text); margin:2px 0 12px; }
/* read-time intelligence layer (Tier A+B): lead headline, portfolio rollup, and the
   per-post direction / size / NEW / corroboration / claim / catalyst affordances. */
.signal-lead { font-size:var(--fs-sub); font-weight:600; line-height:1.45; color:var(--text); margin:2px 0 10px; }
/* Tier C — the synthesized cross-account narrative ("Today's story"), above the lead. */
.signal-synth { border:1px solid var(--line); border-radius:14px; background:var(--surface-2); padding:12px 14px; margin:2px 0 14px; }
.signal-synth-head { font-size:var(--fs-micro); font-weight:700; text-transform:uppercase; letter-spacing:.5px; color:var(--accent); margin-bottom:6px; }
.signal-synth-headline { font-size:var(--fs-sub); font-weight:600; line-height:1.4; color:var(--text); margin:0 0 8px; }
.signal-synth-story { padding:8px 0; border-top:1px solid var(--line); }
.signal-synth-story:last-child { padding-bottom:0; }
.signal-synth-title { font-size:var(--fs-caption); font-weight:600; color:var(--text); display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.signal-synth-summary { font-size:var(--fs-caption); line-height:1.5; color:var(--muted); margin:3px 0 0; }
.signal-synth-accts { font-size:var(--fs-micro); color:var(--muted); margin-top:4px; }
.signal-synth-stance { font-size:var(--fs-nano); font-weight:700; text-transform:uppercase; letter-spacing:.3px; padding:1px 7px; border-radius:999px; background:var(--hover); color:var(--muted); }
.signal-synth-stance.bullish { background:var(--accent-soft); color:var(--positive); }
.signal-synth-stance.bearish { background:var(--red-soft); color:var(--red); }
.signal-synth-stance.mixed { background:var(--hover); color:var(--muted); }
.signal-synth-pending { font-size:var(--fs-caption); color:var(--muted); font-style:italic; margin:2px 0 12px; }
.signal-rollup { display:flex; flex-wrap:wrap; gap:6px; margin:0 0 14px; }
.signal-rollup:empty { display:none; }
.signal-roll-chip { font-size:var(--fs-micro); font-weight:600; padding:2px 9px; border-radius:999px; background:var(--hover); color:var(--muted); }
.signal-roll-chip.bear { background:var(--red-soft); color:var(--red); }
.signal-roll-chip.bull { background:var(--accent-soft); color:var(--accent); }
.signal-badges { display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin:1px 0 5px; }
.signal-dir { font-size:var(--fs-micro); font-weight:700; }
.signal-dir.bear { color:var(--red); }
.signal-dir.bull { color:var(--positive); }
.signal-chip { font-size:var(--fs-micro); font-weight:600; padding:1px 7px; border-radius:999px; background:var(--hover); color:var(--muted); }
.signal-chip.new { background:var(--accent-soft); color:var(--accent); letter-spacing:.3px; }
.signal-claim { font-size:var(--fs-micro); font-weight:700; padding:1px 7px; border-radius:999px; background:var(--accent-soft); color:var(--accent); }
.signal-corr { font-size:var(--fs-small); font-weight:600; color:var(--muted); margin:1px 0 4px; }
.signal-cat { font-size:var(--fs-small); font-weight:600; color:var(--text); margin:1px 0 4px; }
.signal-also { font-size:var(--fs-micro); color:var(--muted); margin-top:5px; }
.signal-item.echo { opacity:.82; }
.signal-item.echo .signal-syn { font-size:var(--fs-caption); }
.signal-item { padding:12px 0; border-top:1px solid var(--line); }
.signal-item:first-of-type { border-top:none; }
/* Impact rail — the shared "this is about YOUR book" accent across the AI reading
   surfaces (Signal posts + Podcast calls). A held item gets a left-edge rail colored by
   stance and a small indent, so the eye learns ONE language: held = anchored, its hue the
   direction. General items stay flush. */
.impact-rail { box-shadow:inset 3px 0 0 var(--rail, var(--muted-2)); padding-left:13px; }
.impact-rail.bull { --rail:var(--positive); }
.impact-rail.bear { --rail:var(--red); }
.impact-rail.neutral { --rail:var(--muted-2); }
/* Keep the 13px indent even where the host sets a `padding` shorthand LATER in source
   (.podcast-call does, at equal specificity) — otherwise held podcast calls would sit
   flush against the rail while held Signal posts get the indent. Compound = same value,
   order-independent, so both surfaces render one identical held language. */
.signal-item.impact-rail, .podcast-call.impact-rail { padding-left:13px; }
.signal-item-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:4px; }
.signal-item-head .signal-av { width:30px; height:30px; }
.signal-item-head .signal-av b { font-size:var(--fs-small); }
.signal-handle { font-weight:600; font-size:var(--fs-caption); color:var(--text); }
.signal-tk { font-size:var(--fs-micro); font-weight:600; padding:1px 7px; border-radius:999px; background:var(--hover); color:var(--muted); }
.signal-tk.held { background:var(--accent); color:#00210f; }
[data-theme="light"] .signal-tk.held { color:#fff; }
.signal-syn { font-size:var(--fs-label); line-height:1.5; color:var(--text); }
.signal-why { font-size:var(--fs-caption); line-height:1.45; color:var(--muted); margin-top:3px; }
.signal-item-foot { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:7px; }
.signal-foot-left { display:flex; align-items:center; gap:10px; min-width:0; flex:1 1 auto; }
.signal-time { font-size:var(--fs-small); color:var(--muted); white-space:nowrap; flex:none; }
.signal-eng { font-size:var(--fs-small); color:var(--muted); letter-spacing:.2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Engagement as two compact chips (likes + reposts) instead of an ellipsized grey run. */
.signal-engchips { display:inline-flex; gap:6px; flex:none; }
.signal-engchip { font-size:var(--fs-micro); color:var(--muted); background:var(--surface-2); border-radius:999px; padding:2px 8px; white-space:nowrap; font-variant-numeric:tabular-nums; }
.signal-src { font-size:var(--fs-small); font-weight:600; color:var(--accent); text-decoration:none; white-space:nowrap; }
.signal-src:hover { text-decoration:underline; }
.signal-item-actions { display:flex; align-items:center; gap:14px; flex:none; }
.signal-share { appearance:none; -webkit-appearance:none; background:none; border:none; padding:0; cursor:pointer;
  font:inherit; font-size:var(--fs-small); font-weight:600; color:var(--muted); white-space:nowrap; }
.signal-share:hover { color:var(--text); text-decoration:underline; }
/* Free teaser: counts + theme above the Pro lock (reuses .research-teaser-lock). */
.signal-teaser-lead { font-size:var(--fs-label); line-height:1.5; color:var(--text); margin:2px 0 14px; }
/* Overlay-header actions: a "Manage" shortcut to the (bottom-of-sheet) account editor,
   beside the close X. A quiet outlined pill — a nav shortcut, not a primary CTA. */
.signal-head-actions { display:flex; align-items:center; gap:8px; flex:none; }
.signal-manage-btn { display:inline-flex; align-items:center; gap:6px; flex:none; line-height:1;
  background:var(--surface-2); color:var(--text); border:1px solid var(--line); font-weight:600;
  font-size:var(--fs-caption); padding:6px 12px; border-radius:999px; cursor:pointer; min-height:0; }
.signal-manage-btn:hover { border-color:var(--muted); }
.signal-manage-btn:active { transform:scale(.97); }
.signal-manage-btn svg { width:15px; height:15px; }
/* On very narrow screens collapse Manage to its icon so the close X is never pushed
   off-screen. With the Refresh button removed the header only holds Manage + X, so the
   label fits comfortably at 375/390 — only collapse below 340. */
@media (max-width:340px) { .signal-manage-btn span { display:none; } .signal-manage-btn { padding:6px; } }
/* Brief history nav in the overlay header (‹ Today ›). */
.signal-day-nav { display:flex; align-items:center; gap:10px; margin-top:6px; }
.signal-day-arrow { appearance:none; -webkit-appearance:none; width:24px; height:24px; padding:0; line-height:1;
  border:1px solid var(--line); border-radius:7px; background:var(--surface-2); color:var(--text);
  font-size:var(--fs-sub); cursor:pointer; }
.signal-day-arrow:hover:not(:disabled) { border-color:var(--muted); }
.signal-day-arrow:disabled { opacity:.4; cursor:default; }
.signal-day-label { font-size:var(--fs-small); font-weight:600; color:var(--muted); min-width:54px; text-align:center; }
/* manage-accounts panel */
.signal-manage { margin-top:16px; padding-top:14px; border-top:1px solid var(--line); }
.signal-manage-head { font-size:var(--fs-small); font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); margin-bottom:8px; }
.signal-list { display:flex; flex-direction:column; gap:6px; }
.signal-handle-row { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:7px 10px; border-radius:10px; background:var(--surface); font-size:var(--fs-caption); }
/* `display:flex` above is an author rule, so it outranks the UA `[hidden]{display:none}`
   (author origin wins regardless of specificity) — without this the collapsed "+N more"
   extra rows render anyway, so the list starts fully expanded and the toggle does nothing.
   Mirrors the same fix on .earn-row / .defi-bk-detail. */
.signal-handle-row[hidden] { display:none; }
.signal-handle-id { display:flex; align-items:center; gap:10px; min-width:0; }
.signal-handle-id .signal-av { width:30px; height:30px; }
.signal-handle-at { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.signal-remove { border:none; background:transparent; color:var(--muted); font-size:var(--fs-small); font-weight:600; cursor:pointer; padding:2px 4px; }
.signal-remove:hover { color:var(--red); }
/* Touch a11y for the small signal controls: press feedback everywhere (iOS has no
   :hover) + a 44pt hit-area on the icon day-arrows. Inline text buttons get feedback
   only (a 44pt ::before would overlap their neighbours). */
.signal-share:active, .signal-remove:active { opacity:.6; }
.signal-day-arrow:active:not(:disabled) { transform:scale(.9); }
.signal-day-arrow { position:relative; }
.signal-day-arrow::before { content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:44px; height:44px; }
.signal-add { display:flex; gap:8px; margin-top:10px; }
.signal-add input { flex:1; min-width:0; padding:9px 12px; border-radius:10px; border:1px solid var(--line); background:var(--bg); color:var(--text); font-size:var(--fs-label); }
.signal-add input:focus { outline:none; border-color:var(--accent); }
.signal-add-hint { font-size:var(--fs-small); color:var(--muted); margin:8px 2px 0; min-height:1em; }
.signal-sec-head { font-size:var(--fs-small); font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--accent); margin:14px 0 2px; }
.signal-sec-head.muted { color:var(--muted); }
.signal-sec-head:first-of-type { margin-top:4px; }
/* ---- add-account typeahead ---- */
.signal-suggest { margin-top:8px; border:1px solid var(--line); border-radius:12px; overflow-y:auto; -webkit-overflow-scrolling:touch; touch-action:pan-y; max-height:46vh; background:var(--bg); }
.signal-sg-head { font-size:var(--fs-micro); font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); padding:9px 12px 4px; }
.signal-sg-row { display:flex; align-items:center; gap:10px; width:100%; text-align:left; padding:9px 11px; border:none; border-top:1px solid var(--line); background:transparent; cursor:pointer; color:var(--text); }
.signal-sg-row:first-child { border-top:none; }
.signal-sg-head + .signal-sg-row { border-top:none; }
.signal-sg-row:hover:not(:disabled) { background:var(--hover); }
.signal-sg-row:disabled { cursor:default; opacity:.55; }
.signal-av { position:relative; flex:none; width:34px; height:34px; border-radius:50%; overflow:hidden; background:var(--hover); display:grid; place-items:center; }
.signal-av b { font-size:var(--fs-label); font-weight:700; color:var(--muted); }
/* No background on the img: the parent .signal-av supplies the grey ring, so a 0×0
   broken img can't paint an opaque disc over the monogram before it's removed. */
.signal-av img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.signal-av.search { font-size:var(--fs-lg); color:var(--muted); }
.signal-sg-main { display:flex; flex-direction:column; min-width:0; flex:1; }
.signal-sg-name { font-size:var(--fs-label); font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.signal-sg-sub { font-size:var(--fs-small); color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.signal-sg-plus { flex:none; font-size:var(--fs-h2); line-height:1; font-weight:600; color:var(--accent); }
.signal-sg-following { flex:none; font-size:var(--fs-micro); color:var(--muted); }
.signal-sg-empty { padding:12px; font-size:var(--fs-caption); color:var(--muted); }
.signal-sg-search .signal-sg-name { color:var(--accent); }
/* ---- Explore: browse the full catalog by category ---- */
.signal-explore-chips { display:flex; flex-wrap:nowrap; gap:6px; overflow-x:auto; padding:9px 10px;
  border-bottom:1px solid var(--line); -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.signal-explore-chips::-webkit-scrollbar { display:none; }
.signal-explore-chip { flex:0 0 auto; font:inherit; font-size:var(--fs-micro); font-weight:700; letter-spacing:.3px; padding:5px 11px;
  border:none; border-radius:999px; cursor:pointer; background:var(--surface-2); color:var(--muted); transition:.15s var(--ease); }
.signal-explore-chip:hover { color:var(--text); }
.signal-explore-chip.active { background:var(--accent-soft); color:var(--accent); }
.signal-explore-list { max-height:320px; overflow-y:auto; -webkit-overflow-scrolling:touch; }
.signal-explore-list .signal-sg-head:first-child { border-top:none; }

/* ===================== Podcast "Tape" ===================== */
#podcastsBody { display:flex; flex-direction:column; gap:10px; }
.podcast-eps { display:flex; flex-direction:column; gap:10px; }
/* per-show filter chips */
.podcast-filter { display:flex; flex-wrap:nowrap; gap:6px; overflow-x:auto; padding-bottom:2px;
  -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.podcast-filter::-webkit-scrollbar { display:none; }
.podcast-chip { flex:0 0 auto; font:inherit; font-size:var(--fs-micro); font-weight:700; letter-spacing:.3px; padding:5px 11px;
  border:none; border-radius:999px; cursor:pointer; background:var(--surface-2); color:var(--muted); transition:.15s var(--ease); }
.podcast-chip:hover { color:var(--text); }
.podcast-chip.active { background:var(--accent-soft); color:var(--accent); }
.podcast-showmore { align-self:flex-start; font:inherit; font-size:var(--fs-caption); font-weight:600; cursor:pointer;
  background:none; border:none; color:var(--accent); padding:4px 2px; }
.podcast-showmore:hover { text-decoration:underline; }
.podcast-empty { text-align:center; padding:14px 8px 6px; color:var(--muted); font-size:var(--fs-label); line-height:1.5; }
.podcast-empty .btn-primary { margin-top:12px; }
.podcast-card { display:flex; gap:12px; align-items:flex-start; width:100%; text-align:left; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-sm); padding:10px; font:inherit;
  transition:border-color .15s var(--ease), transform .04s var(--ease); }
.podcast-card:hover { border-color:var(--accent); }
.podcast-card:active { transform:scale(.995); }
.podcast-thumb { width:96px; height:54px; flex:0 0 auto; border-radius:10px; overflow:hidden;
  background:var(--surface-2); display:block; }
.podcast-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
.podcast-card-main { min-width:0; flex:1; }
.podcast-show { font-size:var(--fs-micro); font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--muted);
  display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.podcast-title { font-size:var(--fs-label); font-weight:600; color:var(--text); line-height:1.35; margin:3px 0 0;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
/* The episode-overlay heading is a raw YouTube title (uncontrolled, routinely
   80-120+ chars); clamp it to 2 lines so it can't push the player down the sheet. */
#podcastEpTitle { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.podcast-lead { font-size:var(--fs-caption); color:var(--accent); font-weight:600; margin-top:4px; line-height:1.4; }
.podcast-held { color:var(--accent); font-weight:700; }
.podcast-date { color:var(--muted-2); font-weight:600; text-transform:none; letter-spacing:0; }
.podcast-date::before { content:"·"; margin-right:6px; color:var(--muted-2); }
.podcast-status { color:var(--muted-2); font-weight:600; text-transform:none; letter-spacing:0; }
.podcast-callcount { color:var(--muted-2); font-weight:600; text-transform:none; letter-spacing:0; }

/* episode overlay */
.podcast-player { width:100%; aspect-ratio:16/9; background:#000; border-radius:12px; overflow:hidden; margin-bottom:12px; }
/* Note/link mode (sample preview or a non-embeddable "Watch on YouTube" linkout): no
   16:9 black frame — size to the compact content so a call card stays above the fold. */
.podcast-player.is-note { aspect-ratio:auto; background:transparent; }
.podcast-player iframe, .podcast-player #ytPlayerMount { width:100%; height:100%; border:0; }
/* A link-out (embedding disabled, or the sample-preview) is a note, not a video — render
   it as a self-contained, theme-aware button that reads on the modal surface in BOTH
   themes (never white-on-cream), independent of the dropped 16:9 black frame. */
.podcast-linkout { display:flex; align-items:center; justify-content:center; gap:8px; min-height:56px; padding:0 16px;
  border-radius:12px; background:var(--surface-2); border:1px solid var(--line); color:var(--accent);
  text-decoration:none; font-weight:700; font-size:var(--fs-sub); }
.podcast-linkout:hover { border-color:var(--accent); }
.podcast-foryou { font-size:var(--fs-sub); font-weight:600; line-height:1.45; color:var(--text); margin:0 0 12px; }
/* Demo-tour placeholder where the real YouTube player mounts (no video in the offline tour). */
/* Compact placeholder (not a full 16:9 box) so the first call card is above the fold on
   open — a real embeddable episode still uses the full-size .podcast-player video. */
.podcast-demo-player { display:grid; place-items:center; text-align:center; min-height:84px; border-radius:12px;
  background:var(--surface-2); border:1px dashed var(--line); color:var(--muted); font-size:var(--fs-caption); padding:14px 16px; }
.podcast-call { border-top:1px solid var(--line); padding:12px 0; }
.podcast-call:first-of-type { border-top:none; }
.podcast-call-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:var(--fs-label); }
.podcast-name { font-weight:700; color:var(--text); }
.podcast-heldchip { font-size:var(--fs-nano); font-weight:800; letter-spacing:.4px; padding:3px 8px; border-radius:999px;
  background:var(--accent-soft); color:var(--accent); }
.podcast-tickerchip { font-size:var(--fs-micro); font-weight:700; padding:2px 7px; border-radius:999px; background:var(--surface-2); color:var(--muted); }
.podcast-nametag { font-size:var(--fs-micro); font-weight:600; color:var(--muted-2); font-style:italic; }
.podcast-stance { font-size:var(--fs-nano); font-weight:800; text-transform:uppercase; letter-spacing:.4px; padding:2px 7px; border-radius:999px; }
.podcast-stance.bullish { background:var(--accent-soft); color:var(--accent); }
.podcast-stance.bearish { background:var(--red-soft); color:var(--red); }
/* Conviction is THE editorial payload of the Tape — make it the loudest chip on the
   line: a filled amber badge (direction-neutral; the stance chip carries direction).
   color:var(--bg) contrasts on --warn in both themes without a new token. */
.podcast-conv { font-size:var(--fs-nano); font-weight:800; text-transform:uppercase; letter-spacing:.4px; padding:3px 8px; border-radius:999px; background:var(--warn); color:var(--bg); }
.podcast-quote { margin:8px 0 0; padding:0 0 0 12px; border-left:3px solid var(--accent-soft);
  font-size:var(--fs-label); line-height:1.5; color:var(--text); font-style:italic; }
.podcast-quote.blurred { filter:blur(4px); user-select:none; border-left-color:var(--line); font-style:normal; }
.podcast-call-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:8px; }
.podcast-speaker { font-size:var(--fs-small); color:var(--muted); font-weight:600; }
.podcast-jump { font:inherit; font-size:var(--fs-small); font-weight:700; cursor:pointer; padding:4px 10px; border-radius:999px;
  border:1px solid var(--line); background:var(--surface-2); color:var(--accent); }
.podcast-jump:hover { border-color:var(--accent); }
.podcast-more { margin-top:6px; }
.podcast-more > summary { cursor:pointer; font-size:var(--fs-caption); font-weight:600; color:var(--muted); padding:8px 0; list-style:none; }
.podcast-more > summary::-webkit-details-marker { display:none; }
.podcast-lock { text-align:center; padding:14px 8px; color:var(--muted); font-size:var(--fs-caption); line-height:1.5;
  border-top:1px solid var(--line); margin-top:4px; }
.podcast-lock .btn-primary { margin-top:10px; }

/* manage/catalog sheet */
.podcast-catalog-list { max-height:60vh; overflow-y:auto; -webkit-overflow-scrolling:touch; }
.podcast-cat-row { display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:11px 2px; border-top:1px solid var(--line); }
.podcast-cat-row:first-child { border-top:none; }
.podcast-cat-name { font-size:var(--fs-label); font-weight:600; color:var(--text); }
.podcast-cat-cat { font-size:var(--fs-micro); font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--muted-2); margin-top:2px; }
.podcast-follow.on { color:var(--accent); border-color:var(--accent); }
