/* Genie — Gemini-style design system. Dark by default, light via [data-theme].
   No external assets: the Google Sans stack is progressive (falls back to
   system-ui where the font isn't installed; nothing is fetched). */

:root {
  /* ---- dark palette (default) — Gemini's warm near-black ---- */
  --bg: #131314;
  --bg-elev: #1e1f20;
  --bg-elev-1: #232425;
  --bg-elev-2: #2a2b2d;
  --bg-elev-3: #333537;
  --border: #3a3b3d;
  --border-strong: #5f6063;
  --text: #e3e3e3;
  --text-dim: #c4c7c5;
  --text-faint: #9aa0a6;

  /* Mono accent — white on dark (black on light, see the light palette).
     --on-accent flips the text drawn on accent fills so it stays legible.
     The composed --grad/--grad-h below also flatten to this accent. */
  --accent: #ffffff;
  --accent-2: #ffffff;
  --on-accent: #131314;
  /* Tonal accent wash for selected/active surfaces (e.g. active pill tabs).
     Derived from --accent so it follows the theme (a faint white tint on dark,
     a faint black tint on light) — never a hardcoded hue. */
  --accent-dim: color-mix(in srgb, var(--accent) 14%, transparent);
  /* Selected pill fill for the 聪明钱/Quant tab rows. It LIVES here, not only on
     .sm-root, because the surfaces that use it escape that scope: the Quant pane
     mounts in .sm-mount and the positions sheet mounts on <body> (a .sm-root
     descendant can't hold a position:fixed scrim — see .sm-sheet-scrim). An
     undefined custom property makes `background` invalid at computed-value time,
     so the ACTIVE tab fell back to transparent while every inactive tab kept its
     --bg-elev-1 pill — the selection read exactly backwards, and the owner tapped
     "Recent activity" believing they were leaving it (2026-07-31). */
  --sm-chip-on: color-mix(in srgb, var(--text) 16%, transparent);
  --grad-a: #4285f4;
  --grad-b: #9b72cb;
  --grad-c: #d96570;
  /* Functional accents (gradient text, progress bars, buttons) flatten to the mono
     accent — white on dark, black on light, theme-aware via var(--accent). The raw
     --grad-a/b/c stops above stay multi-color for the hero card + the two-color
     earn/team charts (which need distinguishable segments). */
  --grad: var(--accent);
  --grad-h: var(--accent);

  /* Text selection — a soft Gemini-blue wash. --grad-a is the same #4285f4 in
     both themes while --bg flips, so this single value stays a medium blue on
     dark and a light periwinkle on light; pairing it with --text keeps the
     selected copy legible. (Before, no ::selection rule existed and the OS
     default rendered the dim hint text dark-on-dark in the light theme.) */
  --selection-bg: color-mix(in srgb, var(--grad-a) 38%, var(--bg));
  --selection-fg: var(--text);

  --user-bubble: #2a3441;
  --user-bubble-border: #38465c;
  --ok: #6dd58c;
  --warn: #f3c969;
  --err: #f28b82;

  /* Smart-money palette — matched to the 预测市场 (predict) screen so the two
     sibling surfaces share one identity: bright green + coral-red. */
  --sm-up: #00D907;
  --sm-down: #FF5A3C;
  /* Ink on an --sm-up fill (CTAs, NEW chip, home badge): near-black on the
     bright dark-theme green, white on the deeper light-theme green. */
  --sm-on-up: #04140b;

  /* Destructive actions (delete / revert / failed). */
  --danger: #f28b82;
  --danger-text: #ffb4ab;
  --danger-bg: #3a1a1d;
  --danger-border: #6e2b2f;

  /* Banner tints. */
  --banner-info-bg: #1b2740;
  --banner-info-fg: #cdddff;
  --banner-info-bd: #2c4170;
  --banner-warn-bg: #3a3015;
  --banner-warn-fg: #f2dca0;
  --banner-warn-bd: #5e4d1f;
  --banner-error-bg: #3a1a1d;
  --banner-error-fg: #ffc9c9;
  --banner-error-bd: #6e2b2f;

  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 8px 28px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 18px 50px rgba(0, 0, 0, 0.6);

  /* Rounded-geometry scale. --radius-hero/-lg cover the larger Gemini-generous
     surfaces (heroes, modals, big cards) that previously hardcoded 20–28px. */
  --radius-hero: 28px;
  --radius-lg: 20px;
  --radius: 16px;
  --radius-md: 14px;
  --radius-sm: 12px;
  /* Small squircle for icon tiles / badge squares (was a sprawl of 8–11px). */
  --radius-tile: 10px;
  --gap: 16px;
  /* Material 3 "emphasized" easing. */
  --ease: cubic-bezier(0.2, 0, 0, 1);

  /* Type — Gemini's families. Text for body/UI, Flex for display/brand.
     Loaded from Google Fonts in index.html; falls back to the system font. */
  --font-ui: "Google Sans Text", "Google Sans Flex", "Google Sans", Roboto, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-display: "Google Sans Flex", "Google Sans Text", "Google Sans", Roboto, system-ui, sans-serif;
  --font-mono: "Google Sans Code", var(--font-mono);
}

/* ---- light palette — Gemini's soft blue-grey on white ---- */
:root[data-theme="light"] {
  /* Pure-white base so the installed PWA's status-bar strip (black-translucent shows
     the app bg through it) reads white in light theme, matching the app. The elevation
     ramp below was at/under the old grey --bg, so on white it now GAINS definition:
     flat panels (--bg-elev-1) read as a soft grey, inputs (--bg-elev-2/3) as deeper
     grey, and white cards/dialogs (--bg-elev) separate via their shadows/borders. */
  --bg: #ffffff;
  --bg-elev: #ffffff;
  --bg-elev-1: #f1f4f9;
  --bg-elev-2: #e9eef6;
  --bg-elev-3: #dde3ea;
  --border: #dadce0;
  --border-strong: #c4c7c5;
  --text: #1f1f1f;
  --text-dim: #444746;
  --text-faint: #80868b;

  --accent: #000000;
  --accent-2: #000000;
  --on-accent: #ffffff;

  --user-bubble: #d3e3fd;
  --user-bubble-border: #a8c7fa;
  --ok: #1a7f37;
  --warn: #b06000;
  --err: #d93025;

  /* Light theme keeps DARKER greens/reds: #00D907 as text on white fails
     contrast, and smart-money uses the accent for large PnL numbers (unlike
     predict, which uses white text). Coral-shifted red echoes #FF5A3C. */
  --sm-up: #008f04;
  --sm-down: #d9432a;
  --sm-on-up: #ffffff;

  --danger: #d93025;
  --danger-text: #b3261e;
  --danger-bg: #fce8e6;
  --danger-border: #f3b4ad;

  --banner-info-bg: #e8f0fe;
  --banner-info-fg: #1967d2;
  --banner-info-bd: #aecbfa;
  --banner-warn-bg: #fef7e0;
  --banner-warn-fg: #b06000;
  --banner-warn-bd: #fde293;
  --banner-error-bg: #fce8e6;
  --banner-error-fg: #c5221f;
  --banner-error-bd: #f3b4ad;

  --shadow-sm: 0 1px 2px rgba(60, 64, 67, 0.16);
  --shadow-md: 0 4px 18px rgba(60, 64, 67, 0.18);
  --shadow-lg: 0 12px 42px rgba(60, 64, 67, 0.24);
}

* { box-sizing: border-box; }

/* Themed text selection (must follow :root so the tokens resolve). ::selection
   and ::-moz-selection are kept as separate rules — an unknown pseudo in a
   shared selector list drops the whole rule in some engines. */
::selection { background: var(--selection-bg); color: var(--selection-fg); }
::-moz-selection { background: var(--selection-bg); color: var(--selection-fg); }

/* Material Symbols Rounded (Gemini-style menu icons) — SELF-HOSTED (was a Google
   Fonts <link>). The CDN font failed to load on flaky mobile connections and,
   because it uses font-display:block, glyphs fell back to raw ligature text
   ("chevron_right", "more_vert", …) that overflowed every layout. Serving the
   subset from our own origin (same connection as the app itself) removes that
   dependency. The @font-face + the base class below replicate what Google's
   stylesheet used to provide; styles.css then sets our thin optical axes. */
@font-face {
  font-family: "Material Symbols Rounded";
  font-style: normal;
  font-weight: 100 700;
  font-display: block;
  /* ?v=N is LOAD-BEARING: the server serves this file immutable/1y, so a
     changed font MUST change its URL or installed clients keep the old
     glyph set forever (the bell rendered as raw "NOTIFICATIONS" on prod).
     Bump the version every time the woff2 is regenerated. */
  src: url("/material-symbols.woff2?v=2") format("woff2");
}
.material-symbols-rounded {
  font-family: "Material Symbols Rounded";
  font-weight: normal;
  font-style: normal;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: "liga";
  font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  font-size: 20px;
  line-height: 1;
  font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
  vertical-align: middle;
  user-select: none;
}
.nav-drawer-icon.material-symbols-rounded { font-size: 22px; }

/* App-wide currency convention: an icon + the number (Power = bolt ⚡, Credits = toll ◉),
   never the spelled-out word. The icon inherits the surrounding color so it reads on any
   surface; it sits slightly larger than the digits and optically aligned. */
.cur { display: inline-flex; align-items: center; gap: 3px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cur .cur-icon { font-size: 1.1em; line-height: 1; }
.cur .cur-val { line-height: 1; }
/* Match the sizes of the bespoke number styles these chips replaced. */
.store-bal-cur .cur { font-size: 17px; font-weight: 700; color: var(--text); }
.store-card-amount .cur { font-size: 26px; font-weight: 700; color: var(--text); }
.store-card-cost .cur { font-size: 15px; font-weight: 700; }
.agentset-stat-cur .cur { font-family: var(--font-display); font-size: 30px; font-weight: 700; color: var(--text); }

html, body {
  margin: 0;
  height: 100%;
}
/* Dark ground behind the body so any sub-pixel height gap never flashes the page's
   default white — a belt for the iOS standalone viewport quirk handled below. */
html { background: var(--bg); }

body {
  background: var(--bg);
  color: var(--text);
  /* Gemini's reading scale: 16px body / 1.5 line-height. */
  font: 16px/1.5 var(--font-ui);
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}

/* Installed to the Home Screen (PWA): with no forcing apple status-bar style, iOS reserves
   the status bar and the web view sits BELOW it (inset). -webkit-fill-available resolves to
   that inset web-view height, filling it with no bottom gap; 100vh is the fallback. Scoped
   to standalone so the browser keeps 100dvh (correct there against Safari's dynamic toolbars). */
@media (display-mode: standalone) {
  body { height: 100vh; height: -webkit-fill-available; }
}

/* ---------------- system notification (Gemini dropdown) ----------------
   A compact, rounded alert that slides down from the top — NOT a full-width
   bar. Centered, capped width, with a kind icon + a dismiss button. */
.banner {
  position: fixed; top: 12px; left: 50%; z-index: 50;
  display: none; align-items: flex-start; gap: 10px;
  width: max-content; max-width: min(460px, calc(100vw - 28px));
  padding: 11px 10px 11px 14px;
  font-size: 13.5px; font-weight: 500; line-height: 1.42; text-align: left;
  border-radius: var(--radius); border: 1px solid transparent;
  box-shadow: var(--shadow-md);
  transform: translate(-50%, -12px); opacity: 0;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}
.banner.show { display: flex; }
.banner.is-in { transform: translate(-50%, 0); opacity: 1; }
.banner-icon { flex: none; font-size: 20px; margin-top: -1px; opacity: 0.95; }
.banner-msg { flex: 1 1 auto; min-width: 0; padding-top: 1px; }
.banner-close {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; margin: -2px 0 0 2px; padding: 0;
  background: none; border: none; cursor: pointer; border-radius: 999px;
  color: inherit; opacity: 0.6; font-size: 18px; transition: opacity 0.15s var(--ease), background 0.15s var(--ease);
}
.banner-close:hover { opacity: 1; background: color-mix(in srgb, currentColor 16%, transparent); }
.banner-action {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  margin: -1px 0 0; padding: 4px 11px;
  background: color-mix(in srgb, currentColor 14%, transparent);
  border: none; border-radius: 999px; cursor: pointer;
  color: inherit; font: inherit; font-weight: 600; font-size: 12.5px; white-space: nowrap;
  transition: background 0.15s var(--ease);
}
.banner-action:hover { background: color-mix(in srgb, currentColor 24%, transparent); }
.banner-action-icon { font-size: 15px; margin-left: -2px; }
.banner-info { background: var(--banner-info-bg); color: var(--banner-info-fg); border-color: var(--banner-info-bd); }
.banner-warn { background: var(--banner-warn-bg); color: var(--banner-warn-fg); border-color: var(--banner-warn-bd); }
.banner-error { background: var(--banner-error-bg); color: var(--banner-error-fg); border-color: var(--banner-error-bd); }
@media (prefers-reduced-motion: reduce) { .banner { transition: opacity 120ms linear; transform: translate(-50%, 0); } }

/* ---------------- floating menu button (no top bar) ---------------- */

/* The hamburger floats over the content, top-left (Gemini sidebar pattern).
   Sits below the session overlay (z 80) so a full-screen mobile session covers
   it; on the home view and desktop split-view it's visible over empty space. */
.nav-fab {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 40;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  background: transparent;
  color: var(--text-dim);
  border: none;
  border-radius: 50%;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.nav-fab:hover { background: var(--bg-elev-2); color: var(--text); }
.nav-fab .material-symbols-rounded { font-size: 24px; }

/* Home loop strip — explicit "you can act now" rows under the earnings widget: a free-pull CTA
   with the exact count, and a card-progress bar that flips to a bold Ready-to-upgrade CTA. */
/* Home hub — a fixed top-right corner cluster on the tasks screen (mirrors the top-left
   hamburger): the star-up progress/CTA + a free-gacha count. Sits in the viewport corner,
   never in the composer's flow. Collapses when there's nothing to act on. */
.home-hub-slot:empty { display: none; }
.home-hub {
  position: fixed;
  top: 12px;
  right: 12px;
  z-index: 40;
  /* Match the hamburger's 48px box at the same top so the pills' centers line up with it
     (they're only 40px tall) — stays aligned across breakpoints since both share `top`. */
  height: 48px;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Hidden while a task is open (home chrome collapses to the task list) — matches the
   earn/mascot slots. Every other screen hides it via the .tasks-view display cascade. */
.layout.session-active .home-hub { display: none; }

.hub-pill {
  display: inline-flex; align-items: center; gap: 7px;
  height: 40px; padding: 0 13px 0 11px;
  border: 1px solid var(--border); border-radius: 999px;
  background: color-mix(in srgb, var(--bg-elev-1) 86%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  color: var(--text); cursor: pointer; font: inherit;
  transition: transform 0.12s var(--ease), filter 0.12s var(--ease), background 0.12s var(--ease);
}
.hub-pill:hover { background: var(--bg-elev-2); }
.hub-pill:active { transform: translateY(1px); }
.hub-pill .material-symbols-rounded { font-size: 22px; }
.hub-count { font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: 0.2px; }

/* Free-gacha gift pill — brand gradient so it reads as a reward waiting to be opened. */
.hub-gift {
  border-color: transparent; color: #fff;
  background: linear-gradient(135deg, #0036ff, #c600ff);
  box-shadow: 0 8px 20px -12px rgba(120, 40, 255, 0.9);
  animation: hub-gift-pulse 2.4s var(--ease) infinite;
}
.hub-gift:hover { filter: brightness(1.06); background: linear-gradient(135deg, #0036ff, #c600ff); }
@keyframes hub-gift-pulse {
  0%, 100% { box-shadow: 0 8px 20px -12px rgba(120, 40, 255, 0.9); }
  50%      { box-shadow: 0 8px 26px -10px rgba(160, 60, 255, 1); }
}

/* Star-up in-progress — a compact progress bar toward the next star + the exact fraction, so
   "2/5 towards ★N" reads at a glance. */
.hub-up:not(.is-ready) .hub-up-star { color: #a78bfa; }
.hub-bar {
  flex: none; width: 46px; height: 6px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--text) 15%, transparent);
}
.hub-bar-fill {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #0036ff, #c600ff); transition: width 0.5s var(--ease);
}

/* Star-up ready — a glowing brand pill that periodically WIGGLES to invite the upgrade tap
   (it's a live CTA: tapping stars the agent up). Rest for most of the cycle, then a quick
   left-right shake with a tiny pop — attention without a constant distraction. */
.hub-up.is-ready {
  border-color: transparent; color: #fff;
  background: linear-gradient(135deg, #6d28d9, #c600ff);
  box-shadow: 0 10px 26px -10px rgba(170, 70, 255, 1);
  transform-origin: center;
  animation: hub-up-wiggle 2.4s var(--ease) infinite;
}
.hub-up.is-ready:hover { filter: brightness(1.07); background: linear-gradient(135deg, #6d28d9, #c600ff); }
.hub-up.is-ready .material-symbols-rounded { color: #fff; }
.hub-up-star { font-size: 13px; font-weight: 800; letter-spacing: 0.2px; }
@keyframes hub-up-wiggle {
  0%, 58%, 100% { transform: rotate(0) scale(1); }
  64% { transform: rotate(-9deg) scale(1.06); }
  70% { transform: rotate(7deg)  scale(1.06); }
  76% { transform: rotate(-6deg) scale(1.04); }
  82% { transform: rotate(4deg)  scale(1.03); }
  88% { transform: rotate(-2deg) scale(1.02); }
  94% { transform: rotate(1deg)  scale(1.01); }
}
@media (prefers-reduced-motion: reduce) {
  .hub-gift, .hub-up.is-ready { animation: none; }
  .hub-bar-fill { transition: none; }
}

/* Mobile: align the hub's top edge with the hamburger (both drop under the safe-area inset). */
@media (max-width: 720px) { .home-hub { top: calc(env(safe-area-inset-top) + 4px); } }

/* Agent chooser dropdown — now the head of the nav drawer (full width). */
.agent-switch { position: relative; }
.agent-switch-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  background: transparent;
  color: var(--text);
  border: none;
  border-radius: 999px;
  padding: 10px 14px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.agent-switch-btn:hover { background: var(--bg-elev-2); }
/* Push the caret to the far right so the pill reads as a full-width selector. */
.agent-switch-btn .agent-switch-caret { margin-left: auto; }
.agent-switch-btn .agent-switch-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agent-switch-btn .agent-switch-mark {
  font-size: 18px;
  font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.agent-switch-btn .agent-switch-caret { color: var(--text-faint); font-size: 20px; }

/* Shared dropdown menu surface (hamburger menu + agent list). */
.nav-menu {
  position: absolute;
  top: calc(100% + 8px);
  min-width: 220px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 6px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* Agent menu drops down under the full-width switcher at the drawer head. A long agent list
   must scroll INSIDE the menu instead of running off the bottom of the viewport — cap to the
   viewport height and scroll; keep the "Your agents" head pinned so it stays visible. */
.nav-menu.from-agent {
  left: 0; right: 0;
  max-height: calc(100vh - 84px);
  max-height: calc(100dvh - 84px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav-menu.from-agent .nav-menu-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-elev);
}
.nav-menu.from-right { right: 0; }
/* Account menu sits at the drawer foot, so it opens upward. */
.nav-menu.from-user { left: 0; right: 0; bottom: calc(100% + 8px); top: auto; }

/* Agent card in the chooser dropdown: name + power/credits/expiry, with a settings
   button. Tall enough to host the stats comfortably. Main area switches agents. */
.agent-card { display: flex; align-items: stretch; gap: 2px; }
.agent-card-main {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
  text-align: left;
  background: transparent;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding: 11px 8px 11px 12px;
  color: var(--text);
  transition: background 0.15s var(--ease);
}
.agent-card-main:hover { background: var(--bg-elev-2); }
.agent-card.is-current .agent-card-main { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.agent-card-radio { flex: none; font-size: 18px; color: var(--text-dim); }
.agent-card.is-current .agent-card-radio { color: var(--accent); }
.agent-card-info { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; }
.agent-card-name-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.agent-card-name {
  /* No flex-grow: the star badge should hug the name, not float to the far edge. */
  flex: 0 1 auto; min-width: 0;
  font-size: 14px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Admin-suspended mark — same "bad" red used for a suspended USER elsewhere in admin. */
.agent-card-suspended {
  flex: none; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px 7px; border-radius: 999px;
  color: var(--err); background: color-mix(in srgb, var(--err) 16%, transparent);
}
/* Stats stack on two lines (credits, then days left) — a single row truncates or
   wraps unpredictably as numbers and translations grow. */
.agent-card-stats { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; }
.agent-card-stat {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.agent-card-stat .material-symbols-rounded { font-size: 15px; color: var(--accent); }
.agent-card-divider { color: var(--text-faint); font-size: 12px; opacity: 0.6; }
.agent-card-sub { font-size: 12px; color: var(--text-faint); white-space: nowrap; }
.agent-card-sub.is-warn { color: var(--warn); }
.agent-card-sub.is-danger { color: var(--danger-text); }
.agent-card-gear {
  flex: none; width: 40px; height: 40px; align-self: center;
  display: grid; place-items: center;
  background: transparent; border: none; cursor: pointer;
  color: var(--text-dim); border-radius: 999px;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.agent-card-gear:hover { background: var(--bg-elev-2); color: var(--text); }
.agent-card-gear .material-symbols-rounded { font-size: 19px; }

/* Star tier pill — a tier-colored "★ N" badge shared across every agent surface
   (picker, dropdown, settings, Earn card) so a high-star agent reads as different at
   a glance. Tint escalates with tier; the buy-agent card uses its own frosted badge.
   See src/web/star.ts. */
.star-pill {
  flex: none; display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.star-pill-ic { font-size: 13px; font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 20; }
.star-pill--md { font-size: 13px; padding: 4px 11px; }
.star-pill--md .star-pill-ic { font-size: 15px; }
.star-pill--low { color: var(--text-dim); background: var(--bg-elev-3); }
.star-pill--mid { color: var(--warn); background: color-mix(in srgb, var(--warn) 18%, transparent); }
.star-pill--high { color: #fff; background: linear-gradient(135deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); }

.user-avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  font-size: 14px;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--grad);
}

/* Account profile row at the bottom of the drawer. */
.nav-drawer-foot {
  margin-top: auto;
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  padding-top: 8px;
}
.nav-foot-gear { flex: 0 0 auto; }
.user-profile-btn {
  display: flex;
  align-items: center;
  gap: 11px;
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: none;
  border-radius: 999px;
  padding: 8px 10px;
  cursor: pointer;
  color: var(--text);
  text-align: left;
  transition: background 0.15s var(--ease);
}
.user-profile-btn:hover { background: var(--bg-elev-2); }
.user-profile-meta { display: flex; flex-direction: column; min-width: 0; }
.user-profile-name { font-size: 14px; font-weight: 600; }
.user-profile-email {
  font-size: 12px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.user-profile-email:empty { display: none; }

.nav-menu[hidden] { display: none; }
.nav-menu-head {
  padding: 8px 10px 6px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-faint);
}
.nav-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--text);
  font-size: 13.5px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.nav-menu-item:hover { background: var(--bg-elev-2); }
.nav-menu-item .nav-menu-icon { width: 20px; text-align: center; color: var(--text-dim); }
.nav-menu-item.is-current { color: var(--accent); }
.nav-menu-item .nav-menu-sub { margin-left: auto; font-size: 11px; color: var(--text-faint); }
.nav-menu-sep { height: 1px; background: var(--border); margin: 10px 2px; }

/* Hamburger menu — left slide-in drawer overlaying the chat. */
.nav-drawer-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0);
  transition: background 0.25s ease;
}
.nav-drawer-overlay.open { background: rgba(0, 0, 0, 0.45); }
.nav-drawer-overlay[hidden] { display: none; }
.nav-drawer {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 300px;
  max-width: 82vw;
  background: var(--bg-elev);
  border: none;
  box-shadow: var(--shadow-lg);
  /* Rounded right edge so the panel reads as a floating sheet, not a full-bleed
     slab (its left edge is off-screen, so only the right corners show). Generous
     radius to match the reference. */
  border-radius: 0 var(--radius-hero) var(--radius-hero) 0;
  padding: 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transform: translateX(-100%);
  transition: transform 0.25s var(--ease);
}
.nav-drawer-overlay.open .nav-drawer { transform: translateX(0); }
/* Head holds the agent switcher; leave room for the floating hamburger above. */
.nav-drawer-head {
  padding: 2px 0 10px;
  margin-bottom: 6px;
}
.nav-drawer-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--text);
  font-size: 15px;
  padding: 14px 12px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.nav-drawer-item:hover { background: var(--bg-elev-2); }
/* Current screen: the same rounded pill, kept lit so the drawer always shows
   where you are. */
.nav-drawer-item.is-current { background: var(--bg-elev-2); }
.nav-drawer-item.is-current .nav-drawer-icon { color: var(--text); }
.nav-drawer-icon { width: 22px; text-align: center; color: var(--text-dim); }

/* ---------------- Tasks home (Gemini-style) + session slide-in ---------------- */
/* A centered column positioned in the upper-middle: gradient greeting, pill
   composer, then the Recent task list. The whole view scrolls if long. */
.tasks-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  background: transparent;
  /* The view itself doesn't scroll — the greeting/composer/recent header is
     pinned and only the task list scrolls (Gemini's home layout). */
  overflow: hidden;
}
.home-center {
  width: 100%;
  max-width: 760px;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0 24px;
}
/* Hero band: occupies the top ~half of the desktop home. The greeting sits in the
   top-middle as the focal point (padding-top) with the composer cluster below it;
   the Recent row and task list follow as siblings underneath. */
.home-header {
  flex: 0 0 auto;
  min-height: 50vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  /* Tight — the mascot is the focal point and sits close to the top. */
  padding-top: 5vh;
  position: relative;
  z-index: 2;
}
.home-greeting,
.home-composer-wrap,
.recent-bar { flex: 0 0 auto; }

/* The greeting as a speech bubble the mascot appears to be saying — a small
   centered tonal pill sitting just under the mascot, with a triangular tail
   (::after) poking up to touch it. Replaces the old full-width gradient
   hero heading now that the mascot itself (not the text) is the focal point. */
.home-greeting {
  position: relative;
  width: fit-content;
  max-width: min(88%, 380px);
  margin: 0 auto 40px;
  text-align: center;
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.4;
  font-weight: 600;
  letter-spacing: -0.1px;
  color: var(--text);
  background: var(--bg-elev-1);
  padding: 13px 20px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.home-greeting::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -6px;
  width: 12px;
  height: 12px;
  background: var(--bg-elev-1);
  transform: translateX(-50%) rotate(45deg);
}

/* "New task" button — only shown in the split view, where it replaces the hero
   composer (the full composer lives on the full-screen home instead). */
.home-new-task {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: fit-content;
  margin: 4px auto 18px;
  background: var(--bg-elev-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  padding: 15px 32px 15px 26px;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.home-new-task:hover { background: var(--bg-elev-3); }
.home-new-task .material-symbols-rounded { font-size: 22px; color: var(--text-dim); }

/* ── Home earnings widget (home-earn.ui.ts) ─────────────────────────────────
   A light card with two parts: a MUST-HAVE primary figure (total earned — always
   there) over a DYNAMIC good-to-know note (agent expiring, funds ready, today's
   gain, daily rate…). The number stays put while the note keeps it current, without
   the overload of a full dashboard. Taps through to the Earn screen. */
/* Mascot hero: a large, purely-decorative live mascot centered above the
   greeting (Gemini's centered-orb pattern) — no card chrome, no text.
   ~1/6 of the viewport height, clamped so it stays sane on very short/tall
   screens. */
/* NEGATIVE gap: the canvas renders the ball at ~48% of its own box (the rest
   is jump-arc headroom), leaving ~26% (≈9.6vh at the current 37vh canvas) of
   empty space below the ball. Pull the greeting up through part of that dead
   space — -5.1vh leaves ≈4.5vh of visual air between ball and bubble (1.5x
   the previous gap, per feedback). The canvas overlap is transparent and
   harmless. */
.home-mascot-slot { flex: 0 0 auto; margin: 0 0 clamp(-90px, -5.1vh, -16px); display: flex; justify-content: center; transition: opacity 0.35s ease; }
/* While the splash celebration plays, keep the hero slot empty (one mascot on
   screen at a time); it eases in as the overlay fades — the mascot settles home. */
html.is-app-ready:not(.is-splash-gone):not(.is-splash-skip) .home-mascot-slot { opacity: 0; }
.home-mascot-canvas {
  display: block;
  /* Sized up twice by feedback (1.5x, then 1.5x again) — the mascot IS the
     hero of the tasks screen, and the Recent list is capped at 3 cards to
     make the room. The 2D port frames the ball at ~48% of the canvas
     (jump headroom), so the visible ball is about half these values. */
  width: clamp(158px, 37vh, 470px);
  height: clamp(158px, 37vh, 470px);
  cursor: pointer;
  transition: filter 0.15s var(--ease);
}
.home-mascot-canvas:hover { filter: brightness(1.08); }
/* Static pre-WebGL placeholder (index.html): the canvas footprint, with the flat
   mascot scaled to ~48% to match where the live ball renders inside its frame
   (jump headroom). scale() keeps the layout box, so the header band doesn't jump
   when the live mascot mounts. */
.home-mascot-ph {
  display: block;
  width: clamp(158px, 37vh, 470px);
  height: clamp(158px, 37vh, 470px);
  object-fit: contain;
  transform: scale(0.48);
  animation: mascot-ph-breathe 2s ease-in-out infinite;
}
@keyframes mascot-ph-breathe {
  50% { transform: scale(0.5); opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
  .home-mascot-ph { animation: none; }
}

/* Narrower than the composer column and outlined rather than filled, so the
   stats widget reads as a distinct info card, not another input. */
.home-earn-slot { flex: 0 0 auto; width: 100%; max-width: 560px; margin: 0 auto 44px; }
.home-earn-slot:empty { display: none; } /* collapse when there's nothing to show (or toggled off) */
.home-earn {
  display: flex; flex-direction: column; width: 100%;
  padding: 14px 17px;
  border-radius: var(--radius-lg);
  background: transparent;
  border: 1px solid var(--border);
  cursor: pointer; text-align: left;
  transition: transform 0.13s var(--ease), background 0.15s var(--ease);
}
.home-earn:hover { background: var(--bg-elev-2); }
.home-earn:active { transform: scale(0.992); }
.home-earn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home-earn-chev { flex: none; font-size: 22px; color: var(--text-faint); }

/* Primary — the must-have earnings figure (always shown). */
.home-earn-primary { display: flex; align-items: center; gap: 12px; }
.home-earn-primary-main { flex: 1 1 auto; min-width: 0; }
.home-earn-label { font-size: 11.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-faint); }
.home-earn-value {
  font-family: var(--font-display); font-size: 27px; font-weight: 700; line-height: 1.1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
/* The running total with today's gain riding alongside it, baseline-aligned so
   the small "Today +$…" tag sits neatly against the big figure. */
.home-earn-value-row { display: flex; align-items: baseline; gap: 10px; margin-top: 2px; flex-wrap: wrap; }
.home-earn-today {
  flex: 0 0 auto;
  font-size: 13px; font-weight: 650; line-height: 1;
  color: var(--ok); font-variant-numeric: tabular-nums;
  padding: 3px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--ok) 15%, transparent);
  white-space: nowrap;
}

/* Note — the dynamic good-to-know line under a divider. */
.home-earn-note {
  display: flex; align-items: center; gap: 11px;
  margin-top: 13px; padding-top: 13px;
  border-top: 1px solid color-mix(in srgb, var(--text-faint) 16%, transparent);
}
.home-earn-note-ic {
  display: grid; place-items: center; flex: none;
  width: 34px; height: 34px; border-radius: var(--radius-tile);
  background: color-mix(in srgb, var(--accent) 16%, var(--bg-elev-2)); color: var(--accent);
}
.home-earn-note-ic .material-symbols-rounded { font-size: 19px; }
.home-earn-note-body { flex: 1 1 auto; min-width: 0; }
.home-earn-note-title {
  font-size: 13.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-earn-note-sub {
  font-size: 12px; color: var(--text-faint); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-earn-note-action {
  flex: none; border: none; cursor: pointer; border-radius: 999px;
  padding: 7px 14px; font: inherit; font-size: 12.5px; font-weight: 600;
  transition: filter 0.14s var(--ease), background 0.14s var(--ease);
}
.home-earn-note-action.is-primary { background: var(--grad); color: var(--on-accent); }
.home-earn-note-action.is-primary:hover { filter: brightness(1.08); }
.home-earn-note-action.is-warn { background: color-mix(in srgb, var(--warn) 22%, var(--bg-elev-2)); color: var(--warn); }
.home-earn-note-action.is-warn:hover { background: color-mix(in srgb, var(--warn) 32%, var(--bg-elev-2)); }

/* Note tones — the icon chip color. */
.home-earn-note.is-warn .home-earn-note-ic { background: color-mix(in srgb, var(--warn) 18%, var(--bg-elev-2)); color: var(--warn); }
.home-earn-note.is-positive .home-earn-note-ic { background: color-mix(in srgb, var(--ok) 18%, var(--bg-elev-2)); color: var(--ok); }
.home-earn-note.is-info .home-earn-note-ic { background: color-mix(in srgb, var(--accent) 16%, var(--bg-elev-2)); color: var(--accent); }
.home-earn-note.is-neutral .home-earn-note-ic { background: var(--bg-elev-2); color: var(--text-dim); }

/* New-user brand card — a single inviting row, no primary figure. */
.home-earn--brand { flex-direction: row; align-items: center; gap: 13px; }
.home-earn--brand .home-earn-note-ic { width: 40px; height: 40px; background: color-mix(in srgb, var(--grad-b) 22%, var(--bg-elev-2)); color: var(--text); }
.home-earn--brand .home-earn-note-ic .material-symbols-rounded { font-size: 22px; }
.home-earn--brand .home-earn-note-title { font-size: 15px; }

/* Skeleton: a value bar over a note row, reserving the card height on a cold load. */
.home-earn--skeleton { cursor: default; pointer-events: none; }
.home-earn-skel { background: var(--bg-elev-2); border-radius: 8px; animation: home-earn-pulse 1.3s ease-in-out infinite; }
.home-earn-skel-value { width: 42%; height: 26px; }
.home-earn-skel-note {
  display: flex; align-items: center; gap: 11px;
  margin-top: 13px; padding-top: 13px;
  border-top: 1px solid color-mix(in srgb, var(--text-faint) 16%, transparent);
}
.home-earn-skel-ic { width: 34px; height: 34px; border-radius: var(--radius-tile); flex: none; }
.home-earn-skel-line { height: 13px; flex: 1 1 auto; max-width: 58%; }
@keyframes home-earn-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) { .home-earn-skel { animation: none; } }

/* ── Home self-improvement notice (home-improve.ui.ts) ──────────────────────
   Part of the stats widget: renders as a divider row INSIDE the earnings card
   (only when the daemon has unseen passes — the slot collapses otherwise).
   Brand-new-user fallback: as a sibling below the "Start earning" row card,
   where the default standalone-card look below applies. */
.home-earn-improve-slot:empty { display: none; }
.home-earn-slot > .home-earn-improve-slot { margin-top: 10px; }
.home-earn .home-earn-improve-slot { margin-top: 0; }
.home-earn .home-improve {
  background: transparent; border-radius: 0; padding: 13px 0 0; margin-top: 13px;
  border-top: 1px solid color-mix(in srgb, var(--text-faint) 16%, transparent);
}
.home-earn .home-improve:hover { background: transparent; }
.home-earn .home-improve:active { transform: none; }
.home-earn .home-improve-ic { width: 34px; height: 34px; }
.home-earn .home-improve-ic .material-symbols-rounded { font-size: 19px; }
.home-improve {
  display: flex; align-items: center; gap: 13px; width: 100%;
  padding: 15px 17px;
  border-radius: var(--radius-lg);
  background: var(--bg-elev-1);
  cursor: pointer; text-align: left;
  transition: transform 0.13s var(--ease), background 0.15s var(--ease);
}
.home-improve:hover { background: var(--bg-elev-2); }
.home-improve:active { transform: scale(0.992); }
.home-improve:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home-improve-ic {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: var(--radius-tile);
  background: color-mix(in srgb, var(--grad-b) 22%, var(--bg-elev-2)); color: var(--text);
}
.home-improve-ic .material-symbols-rounded { font-size: 22px; }
/* Needs-review tint when there are staged (ask-first) changes. */
.home-improve.is-review .home-improve-ic { background: color-mix(in srgb, var(--warn) 18%, var(--bg-elev-2)); color: var(--warn); }
.home-improve-body { flex: 1 1 auto; min-width: 0; }
.home-improve-title {
  font-size: 14px; font-weight: 650; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-improve-sub {
  font-size: 12px; color: var(--text-faint); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.home-improve-action {
  flex: none; border: none; cursor: pointer; border-radius: 999px;
  font-size: 12px; font-weight: 600; padding: 6px 13px;
  background: color-mix(in srgb, var(--warn) 22%, var(--bg-elev-2)); color: var(--warn);
}
.home-improve-action:hover { background: color-mix(in srgb, var(--warn) 32%, var(--bg-elev-2)); }
.home-improve-chev { flex: none; font-size: 22px; color: var(--text-faint); }

/* Pill composer — in-flow between the stats widget and the Recent list (per
   feedback; it used to be pinned to the viewport bottom). position:relative
   is load-bearing: the slash palette anchors to this wrap and opens upward. */
.home-composer-wrap {
  position: relative;
  width: 100%;
  z-index: 30;
  margin: 0 0 26px;
}
.home-composer {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--bg-elev-1);
  border: none;
  border-radius: var(--radius-hero);
  /* Spacious, Gemini-style: the input floats in generous padding rather than
     hugging a slim pill. */
  padding: 19px 24px;
  box-shadow: var(--shadow-md);
  transition: box-shadow 0.15s var(--ease);
}
.home-composer:focus-within { box-shadow: var(--shadow-md); }
.home-composer-plus {
  flex: 0 0 auto;
  font-size: 22px;
  line-height: 1;
  color: var(--text-dim);
}
.home-input {
  flex: 1 1 auto;
  resize: none;
  max-height: 200px;
  background: transparent;
  color: var(--text);
  border: none;
  outline: none;
  padding: 4px 0;
  font: inherit;
  font-size: 16px;
  line-height: 1.5;
}
.home-input::placeholder { color: var(--text-faint); }
/* The home palette opens UPWARD — the composer is pinned to the viewport
   bottom now, so a downward drop would run off-screen. Higher-specificity
   selector so it beats the base `.cmd-palette` bottom/left/right (defined
   later in this file). */
.home-composer-wrap .home-palette {
  top: auto;
  bottom: calc(100% + 8px);
  left: 0;
  right: 0;
}

/* Assist chips — a horizontally-scrollable row of shortcut pills above BOTH
   composers (home + session; Gemini's mobile pattern). Tapping one fills the
   composer; 聪明钱 opens its board. Always visible (owner call 2026-07-13). */
.home-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  margin: 0 0 10px;
  padding: 2px;
  scroll-snap-type: x proximity;
}
.home-chips::-webkit-scrollbar { display: none; }
.home-chips:empty,
.home-chips.is-hidden { display: none; }
.home-chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 40px;
  padding: 9px 15px;
  border: none;
  border-radius: 999px;
  background: var(--bg-elev-1);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: background 0.14s var(--ease), transform 0.1s var(--ease);
}
.home-chip:hover { background: var(--bg-elev-2); }
.home-chip:active { transform: scale(0.97); }
.home-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home-chip .material-symbols-rounded { font-size: 18px; color: var(--text-dim); }
.home-chip .home-chip-emoji { font-size: 15px; line-height: 1; }

/* Recent header: sort dropdown on the left, keyword search on the right. Now a
   sibling of the task list (sits just below the ~50vh hero band on desktop; pins
   beneath the nav on mobile — see the phone media block). */
.recent-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 4px 6px;
}
.recent-sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: none;
  padding: 4px 6px 4px 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.recent-sort:hover { color: var(--text); }
.recent-caret { font-size: 20px; color: var(--text-faint); }
/* Collapses to a single search icon; clicking it expands the pill (see main.ts). */
.recent-search-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  width: 260px;
  max-width: 55%;
  /* Constant height in both states so expanding/collapsing the search never
     changes the header height (which would make the composer jump). */
  height: 40px;
  margin-left: auto;
  padding: 0 14px;
  overflow: hidden;
  background: var(--bg-elev-1);
  border: none;
  border-radius: 999px;
  transition: width 0.22s var(--ease), padding 0.22s var(--ease),
    background 0.15s var(--ease);
}
/* No stroke — matches the rest of the borderless UI; just a tonal fill on focus. */
.recent-search-wrap:focus-within { background: var(--bg-elev-2); }
.recent-search-ic {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  padding: 0;
  color: var(--text-faint);
  cursor: pointer;
}
.recent-search-ic .material-symbols-rounded { font-size: 19px; }
.recent-search {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
}
.recent-search::placeholder { color: var(--text-faint); }

/* Collapsed: just a round icon button; the input is clipped to zero width. */
.recent-search-wrap.is-collapsed {
  width: 40px;
  padding: 0;
  gap: 0;
  background: transparent;
  border-color: transparent;
}
.recent-search-wrap.is-collapsed .recent-search-ic {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: var(--text-dim);
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.recent-search-wrap.is-collapsed .recent-search-ic .material-symbols-rounded { font-size: 22px; }
.recent-search-wrap.is-collapsed .recent-search-ic:hover { background: var(--bg-elev-2); color: var(--text); }
.recent-search-wrap.is-collapsed .recent-search {
  width: 0;
  flex: 0 0 0;
  opacity: 0;
  pointer-events: none;
}
/* Relative time tag on a task row (revealed with the kebab on hover/focus). */
.task-time {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--text-faint);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.task-row:focus-within .task-time { opacity: 1; }
@media (hover: hover) {
  .task-row:hover .task-time { opacity: 1; }
}
.tasks-list {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* The composer sits in-flow above the list now — just a small tail gap. */
  padding-bottom: 28px;
  gap: 12px;
}
.tasks-empty {
  color: var(--text-faint);
  padding: 20px 4px;
  font-size: 13.5px;
}
/* Inline "Retry" link in the task-fetch error state (see renderTasksError). */
.tasks-retry {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  text-decoration: underline;
}
.tasks-retry:hover { opacity: 0.7; }
/* Loading indicator shown while the task list is first fetched. */
.tasks-loading {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--text-faint);
  padding: 18px 4px;
  font-size: 13.5px;
}
.spinner {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
/* Skeleton placeholders for the Recent task list while it loads. The list lives in
   the agent's sandbox, so a cold fetch can take many seconds; shimmer rows shaped
   like real task rows read as "loading" instead of a blank screen. */
.tasks-skeleton { display: flex; flex-direction: column; }
.task-skel-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 20px 14px 20px 18px;
}
.task-skel-main {
  display: flex;
  flex-direction: column;
  gap: 9px;
  flex: 1 1 auto;
  min-width: 0;
}
.skel {
  background: var(--bg-elev-2);
  border-radius: 7px;
  animation: skel-pulse 1.3s ease-in-out infinite;
  animation-delay: var(--skel-d, 0s);
}
.skel-dot { width: 22px; height: 22px; flex: 0 0 auto; border-radius: 50%; }
.skel-line { height: 11px; border-radius: 6px; }
.skel-line--title { max-width: 100%; }
.skel-line--sub { width: 32%; height: 9px; }
@keyframes skel-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; opacity: 0.5; }
}
/* Taller, calmer task cards with a relaxed rhythm (Gemini). Background stays
   transparent — elevation comes from hover only, not a resting fill. */
.task-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: var(--radius-hero);
  padding: 20px 14px 20px 18px;
  cursor: pointer;
  width: 100%;
  transition: background 0.12s var(--ease);
}
@media (hover: hover) {
  .task-row:hover { background: var(--bg-elev-1); }
}
.task-row-main {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  flex: 1 1 auto;
}
@media (hover: hover) {
  .task-row:hover .task-title { color: var(--accent); }
}
/* Kebab on a task row: hidden until the row is hovered/focused or its menu is open. */
.task-kebab {
  flex: 0 0 auto;
  opacity: 0;
  width: 30px;
  height: 30px;
  font-size: 18px;
  line-height: 1;
  border-radius: 50%;
  transition: opacity 0.12s ease, background 0.12s ease;
}
.task-row:focus-within .task-kebab,
.task-kebab[aria-expanded="true"] { opacity: 1; }
@media (hover: hover) {
  .task-row:hover .task-kebab { opacity: 1; }
}
/* Touch devices have no hover to reveal these, and a sticky :hover would force a
   double-tap (first tap highlights the row, second opens it). Show the time + kebab
   always so a single tap opens the task. */
@media (hover: none) {
  .task-time { opacity: 1; }
  .task-kebab { opacity: 1; }
}
.task-pin-mark { color: var(--text-faint); margin-right: 4px; font-size: 16px; }
.task-title {
  font-size: 15px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.task-subtitle {
  font-size: 13px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
/* Live-state chip on a task row (working / needs your reply / interrupted). Only
   rendered while the state is known (agent warm) — idle tasks carry no chip. */
.task-state {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 500;
  border-radius: 999px;
  padding: 2px 10px;
  background: var(--bg-elev-2);
  color: var(--text-dim);
  white-space: nowrap;
}
.task-state::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.task-state-working { color: var(--accent); }
.task-state-working::before { animation: task-state-pulse 1.4s ease-in-out infinite; }
.task-state-needs_input { color: var(--warn, #e0a63d); }
.task-state-stopped { color: var(--text-faint); }
@keyframes task-state-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

.session-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(0, 0, 0, 0);
  transition: background 0.25s ease;
}
.session-overlay.open { background: rgba(0, 0, 0, 0.4); }
.session-overlay[hidden] { display: none; }
.session-panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 760px;
  max-width: 100%;
  border: none;
  border-left: 1px solid transparent;
  border-radius: 0;
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform 0.25s var(--ease);
}
.session-overlay.open .session-panel { transform: translateX(0); }
.session-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid transparent;
  /* Sit above the transcript so its fade mask paints over the scrolling text. */
  position: relative;
  z-index: 2;
  background: var(--bg-elev);
}
/* Gemini-style fade: text scrolling up under the title bar dissolves into the bg.
   Kept SHORT (owner 2026-07-29: "reduce the drop shadow from the nav bar") — at
   40px it read as a heavy drop shadow on the client surfaces, which don't scroll
   text under the bar at all; 12px is enough to soften the edge for the ones that
   do. */
.session-bar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 12px;
  background: linear-gradient(to bottom, var(--bg-elev), transparent);
  pointer-events: none;
}
.session-title {
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}
/* Status pill (Gemini "Complete" / "Working") between the title and the kebab. */
.session-status {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-dim);
  background: var(--bg-elev-2);
  border-radius: 999px;
  padding: 4px 12px;
  white-space: nowrap;
}
.session-status[hidden] { display: none; }
.session-status.working { color: var(--accent); }
.session-menu-btn {
  flex: 0 0 auto;
  font-size: 20px;
  line-height: 1;
}
/* Back chevron: only on mobile/narrow, where the session is a full-screen overlay
   (no task list beside it). On desktop the split-view already shows the list.
   Selector is `.session-bar .session-back` (specificity 0,2,0) so it beats the
   later `.icon-btn { display: grid }` rule the button also matches. */
.session-back { flex: 0 0 auto; }
.session-back svg { display: block; }
.session-bar .session-back { display: none; }
@media (max-width: 899px) {
  .session-bar .session-back { display: inline-flex; align-items: center; justify-content: center; }
}
/* …unless back means "the screen you came from" rather than "the task list":
   a deep-linked sub-screen (Quant → asset detail) hides its own inner back row
   because the header carries one, so on desktop the chevron IS the only exit
   (owner 2026-07-29). main.ts sets .has-back with the override. */
.session-bar.has-back .session-back { display: inline-flex; align-items: center; justify-content: center; }

/* Kebab dropdown menu (task rename / pin / delete / close) ----------------- */
.kebab-menu {
  position: fixed;
  z-index: 200;
  min-width: 184px;
  padding: 6px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.kebab-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 12px;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.kebab-item:hover { background: var(--bg-elev-1); }
.kebab-item.danger { color: var(--danger); }
.kebab-ic {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  color: var(--text-faint);
}
.kebab-ic .material-symbols-rounded { font-size: 18px; }
.kebab-item.danger .kebab-ic { color: var(--danger); }
.kebab-sep {
  height: 1px;
  margin: 6px 4px;
  background: var(--border);
}

/* Desktop split-view: opening a task slides the list into a left column and the
   session becomes the right pane (instead of a full-screen modal overlay). */
/* Gemini-style edge handle: a slim pill anchored just inside the session
   pane's left edge at mid-height, so it stays in the same spot relative to
   the pane whether the task list is expanded or folded. Only meaningful in
   the desktop split view — hidden everywhere else (mobile session is already
   full-screen). */
.session-edge-toggle { display: none; }
@media (min-width: 900px) {
  .tasks-view {
    transition:
      flex-basis 0.3s var(--ease),
      max-width 0.3s var(--ease),
      opacity 0.25s var(--ease);
  }
  .layout.session-active .session-edge-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    left: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 52px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-elev-2);
    color: var(--text-dim);
    cursor: pointer;
    z-index: 5;
    opacity: 0.75;
    transition: opacity 0.15s var(--ease), background 0.15s var(--ease);
  }
  .layout.session-active .session-edge-toggle:hover {
    opacity: 1;
    background: var(--bg-elev-3);
    color: var(--text);
  }
  .layout.session-active .session-edge-toggle .material-symbols-rounded {
    font-size: 16px;
  }
  /* Full-width mode (Gemini-style expand): collapse the left task column so the
     session pane takes the whole viewport. Toggled by the edge handle. */
  .layout.session-active.session-wide .tasks-view {
    flex-basis: 0%;
    max-width: 0;
    min-width: 0;
    opacity: 0;
    pointer-events: none;
    border-right: none;
  }
  /* With the left column gone the session pane owns the whole viewport, so the
     floating hamburger has nothing left to float over: it sat right against the
     back arrow and the title, and the bar had to be padded 64px to dodge it
     (owner 2026-07-29: "should not be here visibly blocking"). Hide it and give
     the padding back — this is the same rule a full-screen mobile session already
     follows, where the overlay covers the FAB outright. The drawer stays one tap
     away: the edge handle restores the split, and the FAB with it. */
  body:has(.layout.session-active.session-wide) .nav-fab {
    display: none;
  }
  .layout.session-active {
    justify-content: flex-start;
  }
  .layout.session-active .tasks-view {
    flex: 0 0 40%;
    max-width: 560px;
    align-items: stretch;
    border-right: 1px solid transparent;
    /* The column itself doesn't scroll — only the task list inside it does. */
    overflow: hidden;
  }
  /* Split the left column into a fixed header (New task + Recent/search) and a
     scrollable task list below — like Gemini's sidebar. */
  .layout.session-active .home-center {
    max-width: none;
    width: 100%;
    height: 100%;
    min-height: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
  }
  /* No 1/3 band in the narrow split column — the header is just the compact
     New-task button + Recent/search, anchored at the top. */
  .layout.session-active .home-header { min-height: 0; justify-content: flex-start; padding-top: 0; }
  .layout.session-active .home-new-task { display: flex; flex: 0 0 auto; margin: 28px auto 18px; }
  .layout.session-active .recent-bar { flex: 0 0 auto; margin: 0 0 4px; padding: 0 28px; }
  .layout.session-active .tasks-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 6px 28px 24px;
  }
  /* The big hero greeting is a home-screen flourish; hide it in the narrow
     split-view column. The hero composer collapses to the "New task" button. */
  .layout.session-active .home-greeting { display: none; }
  .layout.session-active .home-composer-wrap { display: none; }
  /* The earnings widget is a home-screen flourish too — hide it in the narrow column. */
  .layout.session-active .home-earn-slot { display: none; }
  .layout.session-active .home-mascot-slot { display: none; }
  /* Session becomes an in-flow right pane (not a fixed full-screen modal). */
  .layout.session-active .session-overlay {
    position: relative;
    inset: auto;
    flex: 1 1 0;
    min-width: 0;
    background: transparent;
    z-index: 1;
  }
  .layout.session-active .session-panel {
    position: relative;
    width: 100%;
    height: 100%;
    max-width: none;
    border-left: 1px solid transparent;
    box-shadow: none;
  }
}
.model-indicator {
  font-size: 12.5px;
  color: var(--text-dim);
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  white-space: nowrap;
  max-width: 50vw;
  overflow: hidden;
  text-overflow: ellipsis;
}
.model-indicator.no-model { color: var(--warn); border-color: var(--banner-warn-bd); }

.icon-btn {
  background: transparent;
  color: var(--text-dim);
  border: none;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  font-size: 17px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.icon-btn:hover { background: var(--bg-elev-2); color: var(--text); }

/* ---------------- layout ---------------- */

.layout {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  min-height: 0;
  overflow: hidden;
}

.chat-col {
  display: flex;
  flex-direction: column;
  min-height: 0;
  width: 100%;
  max-width: 860px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* ---------------- transcript ---------------- */

.transcript {
  flex: 1 1 auto;
  overflow-y: auto;
  /* Bottom padding halved with the fade (owner 2026-08-03: "reduce the
     shadow area by 50%") — still clears the 16px gradient at rest. */
  padding: 24px 24px 22px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  scroll-behavior: smooth;
}
/* A client surface (Quant, the board, Predict) leads with its OWN tab bar, so the
   24px of chat-message breathing room under the title bar only pushed it away
   from the nav (owner 2026-07-29). A conversation still gets the full inset. */
.transcript:has(> .sm-mount) { padding-top: 8px; }

/* ---- conversational message rows (Gemini layout) ----
   User: a right-aligned tonal bubble, no avatar, no label.
   Assistant: full-width text with just the gradient "spark" to its left. */

.msg {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}

.msg-avatar {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 20px;
  font-weight: 650;
  line-height: 1;
  user-select: none;
}
/* Assistant pfp: a filled circular avatar with the Genie spark. Solid color so it
   ALWAYS paints — the old transparent `background-clip:text` spark could render blank
   if the gradient/clip didn't paint, leaving the reply with no avatar at all. */
.msg-assistant .msg-avatar {
  background: var(--bg-elev-2);
  color: var(--accent);
  font-size: 25px;
  font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24;
}
/* The live mascot avatar (main.ts's shared, reparented canvas) — the one
   assistant row it's currently attached to swaps its icon avatar for this.
   The canvas renders the ball at ~48% of its own box (jump headroom), so at
   100% the pfp read tiny; per feedback it's 1.5x — done by overflowing the
   canvas out of the SAME 42px layout slot (negative margins) so message
   indentation stays identical across rows. The visible ball lands at ~30px,
   still inside the slot; only jump/celebrate arcs spill past it, over
   transparent background. */
.msg-avatar.msg-avatar-mascot { background: transparent; overflow: visible; }
.msg-avatar-mascot .msg-avatar-mascot-canvas { width: 150%; height: 150%; margin: -25%; }
.msg-avatar-mascot-canvas { display: block; width: 100%; height: 100%; }

/* No sender labels — the bubble (user) and spark (assistant) carry the role. */
.msg-sender { display: none; }

/* User turn → right-aligned tonal bubble. */
.msg-user { justify-content: flex-end; }
.msg-user .msg-avatar { display: none; }
.msg-user .msg-content {
  flex: 0 1 auto;
  align-items: flex-end;
  max-width: 82%;
}
.msg-user .msg-body {
  background: var(--user-bubble);
  color: var(--text);
  padding: 11px 17px;
  border-radius: var(--radius-lg);
  width: fit-content;
  max-width: 100%;
}

/* Assistant turn → airy, full-width reading column. */
.msg-assistant .msg-content { line-height: 1.7; }

.msg-content {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.msg-body {
  font-size: 16px;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  position: relative;
}
.msg-body.busy {
  /* Reserve space so the absolutely-positioned working dot never overlaps the
     last line of streamed text. */
  padding-bottom: 14px;
}
.msg-body.busy::after {
  /* The body holds BLOCK-level children (.bubble-thinking, .bubble-text,
     .bubble-activity), so an inline-block dot would drop onto its own line.
     Anchor it to the body (which is position: relative) so it reads as a
     steady "working" indicator at the bottom-left in every phase — including
     agent_start when .bubble-text is still empty. */
  content: "";
  position: absolute;
  left: 0;
  bottom: 2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 0.25; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1.1); }
}

/* "Genie is working" indicator: a pulsing dot + a cycling playful line, shown in
   the gap before the first output so model latency reads as activity, not a hang. */
.working-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 0;
  color: var(--text-dim);
  font-size: 14px;
  font-style: normal;
}
.working-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse 1s ease-in-out infinite;
}
.working-label { animation: workingFade 2.6s ease-in-out infinite; }
@keyframes workingFade {
  0% { opacity: 0.45; }
  12% { opacity: 0.95; }
  88% { opacity: 0.95; }
  100% { opacity: 0.45; }
}
/* Suppress the bare busy dot while the richer indicator is up (avoid two dots). */
.msg-body.busy:has(.working-indicator)::after { display: none; }

.bubble-text { white-space: pre-wrap; }
.bubble-text:empty { display: none; }

/* ---------------- markdown (assistant body) ---------------- */

/* Rendered markdown owns its own block spacing, so the pre-wrap whitespace used
   for plain streamed text is turned off here. */
.markdown { white-space: normal; }
.markdown:empty { display: none; }

/* Collapse outer margins so a bubble doesn't gain leading/trailing whitespace. */
.markdown > :first-child { margin-top: 0; }
.markdown > :last-child { margin-bottom: 0; }

.markdown p { margin: 0 0 8px; }

.markdown h1,
.markdown h2,
.markdown h3 {
  margin: 14px 0 6px;
  line-height: 1.3;
  font-weight: 650;
}
.markdown h1 { font-size: 19px; }
.markdown h2 { font-size: 17px; }
.markdown h3 { font-size: 15px; color: var(--text-dim); }

.markdown ul,
.markdown ol {
  margin: 6px 0 8px;
  padding-left: 22px;
}
.markdown li { margin: 2px 0; }
.markdown li::marker { color: var(--text-faint); }

.markdown blockquote {
  margin: 8px 0;
  padding: 2px 0 2px 11px;
  border-left: 3px solid var(--border);
  color: var(--text-dim);
}

/* GFM tables (md.ts): themed like the view tables, scrolling in their own
   wrapper so a wide table never stretches the chat bubble. */
.markdown .md-table-wrap {
  margin: 8px 0;
  overflow-x: auto;
}
.markdown table {
  border-collapse: collapse;
  font-size: 13px;
  min-width: 60%;
}
.markdown th,
.markdown td {
  padding: 5px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
.markdown th {
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
}
.markdown tr:last-child td { border-bottom: none; }
.markdown th.md-right,
.markdown td.md-right { text-align: right; }
.markdown th.md-center,
.markdown td.md-center { text-align: center; }

.markdown strong { font-weight: 650; }
.markdown em { font-style: italic; }

.markdown code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1px 5px;
}

.markdown pre {
  margin: 8px 0;
  padding: 11px 13px;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow-x: auto;
}
.markdown pre code {
  display: block;
  background: none;
  border: none;
  padding: 0;
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre;
  color: var(--text);
}

.markdown a {
  color: var(--accent);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.markdown a:hover { text-decoration: underline; }

/* Collapsible "process" block — folded one-line summary the user can unfold to
   see the streamed reasoning + tool activity (Gemini pattern). Folded by default. */
.process { margin: 0 0 12px; }
.process-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: none;
  padding: 3px 0;
  cursor: pointer;
  color: var(--text-dim);
  font: inherit;
  font-size: 13.5px;
  font-weight: 500;
  transition: color 0.15s var(--ease);
}
.process-toggle:hover { color: var(--text); }
.process-caret {
  font-size: 18px;
  color: var(--text-faint);
  transition: transform 0.2s var(--ease);
}
.process.open .process-caret { transform: rotate(90deg); }
.process-detail {
  display: none;
  margin-top: 10px;
}
.process.open .process-detail { display: block; }

/* Process timeline (Gemini-style): reasoning + tool steps as nodes on a rail. */
.process-timeline {
  position: relative;
  padding: 2px 0;
}
/* The vertical rail connecting node icons. */
.process-timeline::before {
  content: "";
  position: absolute;
  left: 11px;
  top: 12px;
  bottom: 12px;
  width: 1.5px;
  background: var(--border);
}
.tl-step {
  display: grid;
  grid-template-columns: 24px 1fr;
  column-gap: 12px;
  align-items: start;
}
.tl-icon {
  position: relative;
  z-index: 1;
  grid-column: 1;
  display: grid;
  place-items: center;
  width: 24px;
  height: 22px;
  color: var(--text-faint);
  background: var(--bg-elev); /* mask the rail so the icon reads as a node */
  font-size: 18px;
  font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 20;
}
.tl-icon.ok { color: var(--ok); }
.tl-icon.failed { color: var(--err); }
.tl-body {
  grid-column: 2;
  min-width: 0;
  padding-bottom: 12px;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--text-dim);
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
.tl-line { margin: 0; }
/* Tool nodes: name reads a touch stronger than reasoning lines. */
.tl-step.tl-tool .tl-line {
  color: var(--text);
  font-weight: 500;
}
/* Compact markdown inside reasoning nodes — proper numbered lists, code, breaks. */
.tl-body.markdown p { margin: 0 0 5px; }
.tl-body.markdown ol,
.tl-body.markdown ul { margin: 3px 0 5px; padding-left: 22px; }
.tl-body.markdown li { margin: 1px 0; }
.tl-body.markdown li::marker { color: var(--text-faint); }
.tl-body.markdown h1,
.tl-body.markdown h2,
.tl-body.markdown h3 { font-size: 13.5px; margin: 5px 0 2px; color: var(--text-dim); }

/* Streamed reasoning — Gemini style: small, muted, NOT italic. */
.bubble-thinking {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-dim);
  white-space: pre-wrap;
  border-left: 2px solid var(--border);
  padding-left: 12px;
  margin: 0;
}

.bubble-activity {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}
.tool-chip {
  font-size: 11.5px;
  padding: 3px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev-1);
  color: var(--text-dim);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.tool-chip::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-faint);
}
.tool-chip.pending::before { background: var(--accent); animation: pulse 1s ease-in-out infinite; }
.tool-chip.ok::before { background: var(--ok); }
.tool-chip.failed { color: var(--danger-text); border-color: var(--danger-border); }
.tool-chip.failed::before { background: var(--err); }

/* ---------------- composer ---------------- */

/* Positioning context so the slash palette can float ABOVE the composer. */
.composer-wrap {
  flex: 0 0 auto;
  position: relative;
}
/* Quant is full-bleed (owner 2026-08-03): the composer — and the HUD row
   under it (Chill · credits · Top up) and the context hint, which read as one
   section — slides off while that screen is up; a downward swipe brings it
   back (quant.ts owns the class). IN FLOW both ways: a position:fixed
   variant shoved the whole column up past the top edge on the phone, so the
   hide is a max-height collapse + slide, which cannot move anything above
   it. The 40vh ceiling only exists to give the transition endpoints. */
.composer-wrap,
.usage-hud,
.context-hint {
  transition: transform 0.26s ease, opacity 0.18s ease, max-height 0.26s ease;
  max-height: 40vh;
}
body.qn-composer-off .composer-wrap,
body.qn-composer-off .usage-hud,
body.qn-composer-off .context-hint {
  max-height: 0;
  overflow: hidden;
  transform: translateY(18px);
  opacity: 0;
  pointer-events: none;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
}
/* Gemini-style fade: a gradient mask above the composer so transcript text
   dissolves into the panel background instead of being hard-cut at the edge. */
.composer-wrap::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  /* 16px, was 32 (owner 2026-08-03: "reduce the shadow area by 50%"). */
  height: 16px;
  background: linear-gradient(to top, var(--bg-elev), transparent);
  pointer-events: none;
}

/* Gemini-style centered "island" composer: a rounded pill, max-width, centered in
   the panel, floating above the usage HUD (no full-width top border). Roomier
   padding than a typical chat input — Gemini's composer reads spacious, not
   cramped around the text. */
.composer {
  flex: 0 0 auto;
  display: flex;
  gap: 12px;
  align-items: flex-end;
  width: 100%;
  max-width: 768px;
  margin: 0 auto;
  padding: 18px 16px 18px 26px;
  border: none;
  border-radius: var(--radius-hero);
  background: var(--bg-elev-1);
  box-shadow: var(--shadow-md);
  transition: box-shadow 0.15s var(--ease);
}
/* No stroke, no focus highlight — elevation comes from the shadow alone. */
.composer:focus-within { box-shadow: var(--shadow-md); }
.composer-plus {
  flex: 0 0 auto;
  font-size: 23px;
  line-height: 1;
  color: var(--text-dim);
  align-self: center;
}
.composer-wrap { padding: 9px 24px 22px; }

/* Credit readout — shares the usage-hud row with the context/cost labels. */
.credit-balance {
  flex: 0 0 auto;
  display: inline-flex; /* centers the coin glyph + number on the row's vertical axis */
  align-items: center;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  white-space: nowrap;
}
.credit-balance.low { color: var(--warn); }
.credit-balance[hidden] { display: none; } /* display:inline-flex above beats the UA [hidden] rule */

/* Weekly allowance bar (docs/WEEKLY_ALLOWANCE.md) — replaces the raw balance while
   the subscription allowance is active: progress through the week, not a draining
   number. Full detail (credits used/granted, wallet) lives in the tooltip. */
.allowance-hud {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-left: auto; /* takes over the balance's right-alignment while it's hidden */
  font-size: 11.5px;
  line-height: 1; /* shrink text boxes to glyph height so flex centering is optical, not box math */
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  white-space: nowrap;
}
.allowance-hud[hidden] { display: none; }
.allowance-track {
  width: 64px;
  height: 6px;
  border-radius: 999px;
  background: var(--bg-elev-3);
  overflow: hidden;
}
.allowance-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.3s var(--ease);
}
.allowance-hud.warn .allowance-fill { background: var(--warn); }
.allowance-pct { color: var(--text); font-weight: 500; }
.allowance-hud.warn .allowance-pct { color: var(--warn); }
.allowance-reset { color: var(--text-dim); opacity: 0.75; }

/* Session savings pill — a summary line at the bottom of the newest reply
   ("−12% credits saved"). Plain words, no glyph; percentage, not a raw credit
   count; the number flips up odometer-style when the session rate changes. */
.savings-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px; /* leading spaces inside the label spans collapse — gap owns spacing */
  margin-top: 10px;
  padding: 3px 11px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ok) 14%, var(--bg-elev-2));
  color: var(--ok);
  font-size: 11.5px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  user-select: none;
}
.savings-pill-pre:empty,
.savings-pill-post:empty { display: none; }
.savings-pill { line-height: 1.25; } /* one shared line box → labels + digits share an optical center */
.savings-pill-num {
  position: relative;
  display: inline-block;
  overflow: hidden;
  height: 1.25em;
}
/* block, not inline-block: baseline alignment inside the clipped window pushed
   the digits ~2px below the labels' center. A block child fills from the top. */
.savings-pill-digit { display: block; line-height: 1.25; }
.savings-pill-digit.flip-in { animation: savings-flip-in 0.35s var(--ease); }
.savings-pill-digit.flip-out {
  position: absolute;
  left: 0;
  top: 0;
  animation: savings-flip-out 0.35s var(--ease) forwards;
}
@keyframes savings-flip-in {
  from { transform: translateY(115%); }
  to { transform: translateY(0); }
}
@keyframes savings-flip-out {
  from { transform: translateY(0); opacity: 1; }
  to { transform: translateY(-115%); opacity: 0; }
}

.credit-topup {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  background: var(--bg-elev-2);
  border: none;
  cursor: pointer;
  padding: 4px 13px;
  border-radius: 999px;
  transition: background 0.15s var(--ease);
}
.credit-topup:hover { background: var(--bg-elev-3); }
.credit-topup:focus { outline: none; }
.credit-topup:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Receipts sheet — recent credit_ledger movements, opened by tapping the HUD
   balance or the weekly bar. Transparency on demand, no ambient taxi meter. */
.receipts-backdrop {
  position: fixed;
  inset: 0;
  z-index: 12000;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  /* Side + bottom insets so the sheet FLOATS with margins (Gemini style), never
     edge-to-edge; also clears the phone's home indicator. */
  padding: 0 12px calc(12px + env(safe-area-inset-bottom, 0px));
}
.receipts-sheet {
  width: min(560px, 100%);
  max-height: 62vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev-1, var(--bg));
  /* Fully rounded — a floating card, not a docked panel. */
  border-radius: 28px;
  padding: 20px 20px 12px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.45);
}
.receipts-title {
  font-size: 14px;
  font-weight: 650;
  margin-bottom: 10px;
}
.receipts-list { overflow-y: auto; font-size: 13px; }
.receipt-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 2px;
  border-bottom: 1px solid var(--bg-elev-2);
}
.receipt-row:last-child { border-bottom: none; }
.receipt-ic { font-size: 18px; color: var(--text-dim); }
.receipt-text { flex: 1 1 auto; min-width: 0; }
.receipt-label {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.receipt-time { font-size: 11px; color: var(--text-dim); }
.receipt-amt {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.receipt-amt.pos { color: var(--ok); }

/* The HUD money elements now open the receipts sheet. */
.credit-balance, .allowance-hud { cursor: pointer; }

/* Long-conversation hint — a quiet line above the HUD, not a scary banner. */
.context-hint {
  padding: 4px 24px 0;
  font-size: 11.5px;
  color: var(--warn);
  opacity: 0.9;
  white-space: normal;
}
.context-hint[hidden] { display: none; }

/* Budget-guard select (Settings → Agent) — matches the switch rows' scale. */
.agentset-select {
  flex: 0 0 auto;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--bg-elev-2);
  border: 1px solid var(--bg-elev-3);
  border-radius: 8px;
  padding: 5px 8px;
}

/* Composer-HUD Effort toggle — a compact pill that flips Chill ⇄ YOLO. Chill reads
   muted (the safe default); YOLO lights up to signal the agent is in autonomous mode. */
.effort-hud {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-faint);
  background: var(--bg-elev-2);
  border: none;
  cursor: pointer;
  padding: 3px 11px;
  border-radius: 999px;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.effort-hud:hover { background: var(--bg-elev-3); }
.effort-hud:focus { outline: none; }
.effort-hud:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.effort-hud.is-yolo { color: var(--on-accent); background: var(--accent); }

/* ---------------- slash command palette ---------------- */

.cmd-palette {
  position: absolute;
  left: 24px;
  right: 24px;
  max-width: 768px;
  margin: 0 auto;
  bottom: calc(100% + 2px);
  z-index: 50;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  -webkit-overflow-scrolling: touch;
}
.cmd-palette[hidden] { display: none; }

/* "↓ New messages" — appears when the transcript's auto-follow is suppressed
   because the user scrolled up mid-stream. Tapping snaps back to the bottom and
   it hides once the user is there. Mirrors .cmd-palette's anchor: absolute,
   centered, floating just above the composer. */
.scroll-resume {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 10px);
  z-index: 40;
  margin: 0 auto;
  width: fit-content;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px 7px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  font: 500 13px/1 system-ui, -apple-system, sans-serif;
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.scroll-resume .material-symbols-rounded { font-size: 18px; }
.scroll-resume.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
  .scroll-resume { transition: opacity 0.18s ease; transform: none; }
}

.cmd-group {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-faint);
  padding: 6px 8px 4px;
}

.cmd-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid transparent;
}
.cmd-item.selected {
  background: var(--bg-elev-2);
  border-color: var(--accent);
}
.cmd-name {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
}
.cmd-desc {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmd-empty {
  padding: 9px 10px;
  font-size: 12.5px;
  color: var(--text-faint);
  font-style: italic;
}
.composer-input {
  flex: 1 1 auto;
  resize: none;
  max-height: 200px;
  background: transparent;
  color: var(--text);
  border: none;
  border-radius: 0;
  padding: 11px 0;
  align-self: center;
  font: inherit;
  font-size: 16.5px;
  line-height: 1.55;
  outline: none;
}
.composer-input::placeholder { color: var(--text-faint); }

/* Icon-only round button. Gemini-style: a monochrome light/dark circle (not the
   brand gradient); the stop state just swaps the arrow glyph for a square. */
.composer-send {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background: var(--text);
  color: var(--bg);
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  transition: filter 0.15s, transform 0.1s var(--ease);
}
.composer-send .material-symbols-rounded {
  font-size: 22px;
  font-variation-settings: "FILL" 0, "wght" 500, "GRAD" 0, "opsz" 24;
}
.composer-send:hover { filter: brightness(0.9); }
.composer-send:active { transform: scale(0.94); }
.composer-send:focus { outline: none; }
.composer-send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Empty + idle: drop the button out of layout (used by the home composer). */
.composer-send.is-hidden { display: none; }
/* …except on the home composer, where we keep the button's box RESERVED (just
   invisible) so the pill's height and the input's width don't jump the instant
   the user starts typing and the send arrow appears. */
.home-composer .composer-send.is-hidden { display: grid; visibility: hidden; }

/* Idle + empty (Gemini): a plain mic, not the filled circle. */
.composer-send.is-mic {
  background: transparent;
  color: var(--text-dim);
}
.composer-send.is-mic:hover { background: var(--bg-elev-2); color: var(--text); filter: none; }
.composer-send.is-listening { color: var(--accent); animation: pulse 1.1s ease-in-out infinite; }

/* Processing: same monochrome button, square glyph (set in JS). */
.composer-send.is-stop {
  background: var(--text);
  color: var(--bg);
}
.composer-send.is-stop:hover { filter: brightness(0.9); }
.composer-send.is-stop .material-symbols-rounded {
  font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24;
}

/* ---------------- usage HUD (statusline under composer) ---------------- */

/* Clean status line, no hard divider — blends into the panel like the rest of
   the UI. Deliberately minimal: effort toggle · credits left · Top up (token/
   context/cost telemetry lives in the admin panel, not user chrome). The Top-up
   pill rides the far edge so the strip still spans the composer. */
.usage-hud {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 2px 24px 12px;
  background: transparent;
  font-family: var(--font-ui);
}
/* Chill anchors the left edge; the credits chip + Top-up pill ride together on the
   right (credits immediately left of the button it feeds). */
.usage-hud .credit-balance { margin-left: auto; }

/* ---------------- surfaces (inline in transcript) ---------------- */

.surface-card {
  flex-shrink: 0; /* prevent transcript flex from collapsing the surface */
  align-self: stretch;
  /* Inset to align with the assistant content column (avatar width + row gap),
     so generated surfaces read as part of the assistant's turn. */
  margin-left: 56px;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.surface-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 14px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}
.surface-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Header icon buttons (pin, collapse) — dim until hover, accent when active. */
.surface-btn {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text-dim);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.6;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.surface-btn:hover { opacity: 1; background: var(--bg-elev-1); }
.surface-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.surface-btn.active { opacity: 1; color: var(--accent); }
/* Refresh in flight: spin until the next render frame lands for this surface. */
.surface-refresh.is-refreshing {
  opacity: 1;
  color: var(--accent);
  animation: surface-refresh-spin 1s linear infinite;
}
@keyframes surface-refresh-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .surface-refresh.is-refreshing { animation: none; }
}
.surface-pin.active { font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24; }

/* Pinned: the card sticks to the top of the transcript so an interactive surface (a game,
   a live dashboard) stays visible while chat streams below. CSS-only — the card keeps its
   DOM slot, so an iframe surface never reloads. The FRAME (not the whole card) is height-
   capped, so a tall surface scrolls INTERNALLY and chat keeps a readable strip below. */
.surface-card.pinned {
  position: sticky;
  top: 6px;
  z-index: 6;
  box-shadow: var(--shadow-lg);
}
.surface-card.pinned .surface-frame { max-height: min(62dvh, 900px); }
/* Phones: the desktop pinned cap clips an interactive surface's bottom controls
   (a game's action bar ends up below the fold, reachable only by scrolling inside
   the frame). Give pinned iframes most of the viewport — chat still peeks below,
   and fullscreen is one tap away. */
@media (max-width: 720px) {
  .surface-card.pinned .surface-frame { max-height: min(78dvh, 900px); }
}

/* Fullscreen: the card takes over the viewport IN PLACE — CSS-only, like pinning,
   because re-parenting an iframe reloads its srcdoc and resets the game. Declared
   after .pinned so position:fixed wins when both classes are set. The frame drops
   every height cap (including the JS px height from content reports) and flexes to
   fill under the header. */
.surface-card.fullscreen {
  position: fixed;
  inset: 0;
  z-index: 90;
  margin: 0;
  border: 0;
  border-radius: 0;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}
.surface-card.fullscreen .surface-frame {
  flex: 1 1 auto;
  height: auto !important;
  max-height: none !important;
}
/* Desktop: the nav hamburger is a fixed FAB in the ROOT stacking context, so it
   paints over the fullscreen card's top-left corner — inset the header so the
   title doesn't run under it. (A full-width SESSION solves the same collision by
   hiding the FAB instead; a card can't, because the FAB is still the only nav
   while a card is open over the split view.) Mobile needs none: the session
   overlay (z 80) covers the FAB there. */
@media (min-width: 721px) {
  .surface-card.fullscreen .surface-header { padding-left: 64px; }
}

/* Collapse: hide the body via display:none — an iframe keeps its state, so pausing a
   game/dashboard never resets it. */
.surface-card.collapsed .surface-frame,
.surface-card.collapsed .surface-view { display: none; }
.surface-frame {
  display: block;
  width: 100%;
  height: 340px;
  border: 0;
  background: transparent;
}

/* render_ui state: "loading" — a subtle in-progress cue on the card header,
   whether the surface has real (partial) content yet or is a bare skeleton. */
.surface-card.surface-loading .surface-header {
  animation: surface-header-pulse 1.3s ease-in-out infinite;
}
@keyframes surface-header-pulse {
  0%, 100% { opacity: 0.65; }
  50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .surface-card.surface-loading .surface-header { animation: none; }
}

/* ---------------- structured view surfaces (chart/table) ---------------- */
/* Rendered in-page by views.ts from a data spec — no iframe. Series palette +
   semantic colors are defined here so the canvas renderer can read them. */
.surface-view {
  /* Vibrant trading-desk palette (matches the approved report mockup): gold-first
     series (the classic EMA look), crypto-desk green/red for candles. */
  --view-c1: #e8c447;
  --view-c2: #6fa8ff;
  --view-c3: #c77dff;
  --view-c4: #5ad8cc;
  --view-c5: #ff9f6e;
  --view-up: #16c784;
  --view-down: #ea3943;
  padding: 12px 14px 14px;
}
.view-stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.view-stack-horizontal {
  flex-direction: row;
  flex-wrap: wrap;
}
.view-stack-horizontal > .view-stack-item {
  flex: 1 1 0;
  min-width: 140px;
}
.view-grid {
  display: grid;
  gap: 12px;
}
.view-stack-item,
.view-grid-item {
  min-width: 0;
}
/* Bare loading placeholder (no view/html yet) — an instant "something is
   coming" shape instead of a blank card for the whole wait. */
.view-skeleton-line,
.view-skeleton-block {
  background: var(--bg-elev-2);
  border-radius: 6px;
  animation: view-skeleton-pulse 1.3s ease-in-out infinite;
}
.view-skeleton-line {
  height: 13px;
  width: 38%;
  margin-bottom: 10px;
}
.view-skeleton-line.long {
  width: 62%;
}
.view-skeleton-block {
  height: 120px;
  width: 100%;
}
@keyframes view-skeleton-pulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 0.9; }
}
@media (prefers-reduced-motion: reduce) {
  .view-skeleton-line,
  .view-skeleton-block { animation: none; }
}
.view-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 8px;
}
.view-caption {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 8px;
}
.view-chart-wrap {
  position: relative;
  width: 100%;
}
.view-canvas {
  display: block;
  width: 100%;
}
/* Interactive chart: crosshair on hover, grabbing while panning. touch-action
   pan-y lets the page scroll vertically while horizontal drag / pinch are ours. */
.view-chart-wrap .view-canvas {
  cursor: crosshair;
  touch-action: pan-y;
}
.view-chart-wrap .view-canvas.is-panning {
  cursor: grabbing;
}
.view-chart-tip {
  position: absolute;
  z-index: 3;
  pointer-events: none;
  background: var(--bg-elev-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 9px;
  font-size: 11.5px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
}
.view-chart-tip-h {
  font-weight: 600;
  margin-bottom: 2px;
  color: var(--text-dim);
}
.view-chart-tip-r {
  color: var(--text);
}
/* Dataset switcher (e.g. timeframes) above an interactive chart. */
.view-chart-tabs {
  display: flex;
  gap: 4px;
  margin: 2px 0 6px;
}
.view-chart-tab {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 3px 10px;
  font: 500 11.5px/1.4 inherit;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  cursor: pointer;
}
.view-chart-tab:hover {
  color: var(--text);
}
.view-chart-tab:focus-visible {
  outline: 2px solid var(--accent, #8ab4f8);
  outline-offset: 1px;
}
.view-chart-tab.is-active {
  background: var(--bg-elev-2);
  border-color: var(--border);
  color: var(--text);
}
/* Series legend: one toggleable chip per line (click = show/hide, scale re-fits). */
.view-chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 4px;
  margin: 0 0 4px;
}
.view-chart-key {
  appearance: none;
  background: none;
  border: 0;
  border-radius: 6px;
  padding: 2px 8px 2px 6px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: 500 11px/1.4 inherit;
  font-family: inherit;
  color: var(--text-dim);
  cursor: pointer;
}
.view-chart-key:hover {
  color: var(--text);
  background: var(--bg-elev-1);
}
.view-chart-key:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.view-chart-key-dot {
  width: 8px;
  height: 8px;
  border-radius: 3px;
  flex: 0 0 auto;
}
.view-chart-key.is-off {
  opacity: 0.45;
}
.view-chart-key.is-off .view-chart-key-name {
  text-decoration: line-through;
}
.view-table-wrap {
  width: 100%;
  overflow-x: auto;
}
.view-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.view-table th,
.view-table td {
  padding: 6px 10px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.view-table th {
  font-weight: 600;
  color: var(--text-dim);
  font-size: 12px;
}
.view-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.view-table tbody tr:last-child td {
  border-bottom: 0;
}

/* card */
.view-card-headline {
  font-size: 30px;
  font-weight: 650;
  line-height: 1.1;
  color: var(--text);
  letter-spacing: -0.01em;
}
.view-arrow {
  font-size: 0.6em;
  vertical-align: middle;
}
.view-card-subhead {
  font-size: 13px;
  color: var(--text-dim);
  margin-top: 2px;
}
.view-card-rows {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.view-card-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}
.view-card-label {
  color: var(--text-dim);
}
.view-card-value {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* kpi grid */
.view-kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
}
.view-kpi {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
}
.view-kpi-label {
  font-size: 11.5px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.view-kpi-value {
  font-size: 20px;
  font-weight: 600;
  color: var(--text);
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
.view-kpi-delta {
  font-size: 12px;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* list */
.view-list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: viewlist;
}
.view-list[class] {
  /* keep zero default padding for both ol/ul */
}
ol.view-list {
  counter-reset: viewlist;
}
.view-list-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13.5px;
}
.view-list-item:last-child {
  border-bottom: 0;
}
ol.view-list .view-list-main::before {
  counter-increment: viewlist;
  content: counter(viewlist) ". ";
  color: var(--text-dim);
}
.view-list-main {
  min-width: 0;
}
.view-list-text {
  color: var(--text);
}
.view-list-secondary {
  color: var(--text-dim);
  margin-left: 8px;
  font-size: 12.5px;
}
.view-list-value {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* form */
.view-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.view-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.view-field-label {
  font-size: 12px;
  color: var(--text-dim);
}
.view-input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.view-input:focus {
  outline: none;
  border-color: var(--accent);
}
textarea.view-input {
  min-height: 64px;
  resize: vertical;
}
.view-checkbox {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  align-self: flex-start;
}
.view-form-status {
  font-size: 12px;
  color: var(--text-dim);
  min-height: 14px;
}

/* buttons */
.view-buttons-text {
  font-size: 13.5px;
  color: var(--text);
  margin-bottom: 8px;
}
.view-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.view-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  cursor: pointer;
  transition: filter 0.12s, opacity 0.12s;
}
.view-btn:hover:not(:disabled) {
  filter: brightness(1.12);
}
.view-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #06121f;
}
.view-btn.danger {
  background: #f28b82;
  border-color: #f28b82;
  color: #2a0a08;
}
.view-btn.selected {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.view-btn:disabled {
  opacity: 0.55;
  cursor: default;
}
.view-form .view-btn {
  align-self: flex-start;
}

/* ---------------- explorer (interactive filter/search/sort/drill) ---------------- */
.view-explorer {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.view-exp-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
}
.view-exp-search {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 13px;
  padding: 7px 11px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
}
.view-exp-search:focus {
  outline: none;
  border-color: var(--border-strong);
}
.view-exp-count {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  white-space: nowrap;
}
.view-exp-facets {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}
.view-exp-facet-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.view-exp-facet-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.view-exp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.view-exp-chip {
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text-dim);
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s, color 0.14s;
}
.view-exp-chip:hover {
  border-color: var(--border-strong);
  color: var(--text);
}
.view-exp-chip.active {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--text);
}
.view-exp-summary {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 10px);
  background: var(--bg-elev-1);
}
.view-exp-summary-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}
.view-exp-summary-body {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
.view-exp-donut {
  flex: 0 0 auto;
}
.view-exp-donut.interactive {
  cursor: pointer;
}
.view-exp-summary-body .view-exp-bars {
  flex: 1 1 220px;
  min-width: 200px;
}
.view-exp-bars {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.view-exp-bar-row {
  display: grid;
  grid-template-columns: minmax(64px, 24%) 1fr auto;
  align-items: center;
  gap: 10px;
  transition: background 0.12s, opacity 0.2s;
}
.view-exp-bar-row.clickable {
  cursor: pointer;
  border-radius: 6px;
  padding: 2px 4px;
  margin: 0 -4px;
}
.view-exp-bar-row.clickable:hover {
  background: var(--bg-elev-2);
}
.view-exp-bar-row.active {
  background: var(--accent-dim);
}
.view-exp-bar-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: 6px;
  vertical-align: middle;
}
.view-exp-bar-name {
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.view-exp-bar-track {
  height: 8px;
  border-radius: 999px;
  background: var(--bg-elev-3);
  overflow: hidden;
}
.view-exp-bar-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--view-c1, #6ea8fe);
  transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.view-exp-bar-val {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  min-width: 40px;
  text-align: right;
}
.view-exp-map-wrap {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 10px);
  overflow: hidden;
}
.view-exp-map {
  display: block;
  width: 100%;
  cursor: crosshair;
}
/* Standalone `map` view wrapper — hosts the canvas + the Google Maps chip. */
.view-map-wrap {
  position: relative;
}
.view-map-wrap .view-canvas {
  cursor: pointer;
}
/* Google Maps handoff chip, overlaid top-right of every map card. A plain link
   (no SDK/key/third-party script); href resolves to the filtered pins on tap. */
.view-map-glink {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-elev);
  opacity: 0.92;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}
.view-map-glink:hover {
  opacity: 1;
  background: var(--bg-elev-1);
}
.view-map-glink .material-symbols-rounded {
  font-size: 13px;
}
.view-exp-table-wrap {
  overflow-x: auto;
}
/* Row-windowing sentinel: extends the rendered window on tap (and auto-fires
   via IntersectionObserver as it scrolls into view). */
.view-exp-more-row td { padding: 0; }
.view-exp-more {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: 12.5px;
  cursor: pointer;
}
.view-exp-more:hover { color: var(--text); }
.view-exp-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.view-exp-table th {
  position: sticky;
  top: 0;
  text-align: left;
  font-weight: 600;
  color: var(--text-dim);
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elev);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.view-exp-table th:hover {
  color: var(--text);
}
.view-exp-table th.num {
  text-align: right;
}
.view-exp-sort-ind {
  color: var(--accent);
  font-size: 11px;
}
.view-exp-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
.view-exp-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.view-exp-row {
  cursor: pointer;
  transition: background 0.12s;
}
.view-exp-row:hover {
  background: var(--bg-elev-1);
}
.view-exp-row.expanded {
  background: var(--accent-dim);
}
.view-exp-row.hl {
  background: var(--accent-dim);
}
.view-exp-row.expanded td {
  border-bottom-color: transparent;
}
.view-exp-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text-dim);
}
.view-exp-tag.clickable {
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.view-exp-tag.clickable:hover {
  border-color: var(--border-strong);
  color: var(--text);
}
.view-exp-tag.on {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--text);
}
.view-exp-detail-row td {
  padding: 0 10px 12px;
  background: var(--accent-dim);
}
.view-exp-detail {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 6px 18px;
  padding: 4px 4px 2px;
}
.view-exp-detail-pair {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* Actions footer inside the drill-in: drill chip + Google Maps link on one
   full-width row, aligned and vertically centered. */
.view-exp-detail-actions {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 2px;
}
.view-exp-detail-actions .view-exp-maplink { margin-top: 0; }
.view-exp-detail-actions .view-act { margin-top: 0; }
.view-exp-detail-key {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.view-exp-maplink {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--link, #8ab4f8);
  text-decoration: none;
}
.view-exp-maplink:hover {
  text-decoration: underline;
}
.view-exp-maplink .material-symbols-rounded {
  font-size: 14px;
}

.view-exp-detail-val {
  font-size: 13px;
  color: var(--text);
}

/* Tappable follow-up chip (docs/TAPPABLE_ACTIONS.md): one tap = one agent turn. */
.view-act {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: center;
  margin-top: 4px;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--link, #8ab4f8);
  cursor: pointer;
  white-space: nowrap;
}
.view-act:hover { background: var(--bg-elev-1); }
.view-act:disabled { opacity: 0.5; cursor: default; }
.view-act .material-symbols-rounded { font-size: 13px; }
.view-list-item .view-act { margin-left: 8px; }
.view-kpi .view-act { margin-top: 6px; }

/* ---------------- gallery (filterable image grid + lightbox) ---------------- */
.view-gallery {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.view-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}
.view-gallery-tile {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-elev-1);
  cursor: pointer;
  transition: border-color 0.14s, transform 0.14s;
}
.view-gallery-tile:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
}
.view-gallery-thumb {
  aspect-ratio: 1 / 1;
  background: var(--bg-elev-2);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.view-gallery-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.view-gallery-tile.broken .view-gallery-thumb::after {
  content: "🖼";
  font-size: 26px;
  opacity: 0.4;
}
.view-gallery-tile.broken .view-gallery-img {
  display: none;
}
.view-gallery-cap {
  padding: 8px 10px;
}
.view-gallery-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.view-gallery-sub {
  font-size: 11px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.view-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.view-lightbox-box {
  position: relative;
  max-width: 92vw;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
}
.view-lightbox-img {
  max-width: 92vw;
  max-height: 78vh;
  object-fit: contain;
  border-radius: 8px;
  background: var(--bg-elev);
}
.view-lightbox-meta {
  text-align: center;
  color: var(--text);
}
.view-lightbox-title {
  font-size: 15px;
  font-weight: 600;
}
.view-lightbox-sub {
  font-size: 13px;
  color: var(--text-dim);
  margin-top: 2px;
}
.view-lightbox-link {
  font-size: 13px;
  color: var(--accent);
  text-decoration: none;
}
.view-lightbox-close {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.view-lightbox-close:hover {
  background: var(--bg-elev-2);
}

/* ---------------- web-search sources (Path 2) ---------------- */
.sources-panel {
  /* Align under the assistant content column, matching surface cards. */
  margin: 10px 0 2px;
  padding: 10px 14px;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.sources-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 6px;
}
.sources-list {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.sources-list li {
  font-size: 13px;
  line-height: 1.45;
}
.sources-list a.src-link {
  color: var(--accent);
  text-decoration: none;
}
.sources-list a.src-link:hover {
  text-decoration: underline;
}
.sources-list .src-host {
  margin-left: 6px;
  color: var(--text-faint);
  font-size: 11.5px;
}

/* ---------------- settings modal ---------------- */

.settings-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  display: grid;
  place-items: center;
  padding: 20px;
  /* Above the auth/agent gate (.auth-overlay z 1000) so the buy-agent modal,
     reachable from the agent picker, layers on top of it. */
  z-index: 1100;
}
.settings-panel {
  width: min(560px, 100%);
  max-height: 90dvh;
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.settings-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  background: var(--bg-elev);
  /* Above ALL scrolling content — the gacha card's animated transform and the
     buy-modal value card (z-index 3, rides over the hero deck) would otherwise
     paint over this sticky bar. */
  z-index: 4;
}
.settings-titlebar h2 { margin: 0; font-size: 17px; }
.settings-close {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  background: transparent;
  border: none;
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}
.settings-close:hover { color: var(--text); background: var(--bg-elev-2); }

/* ---------------- in-app confirm / prompt dialog (confirm.ui.ts) ----------------
   Tonal Gemini replacement for window.confirm()/prompt(); centered, --ease motion. */
.confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 1400;
}
.confirm-dialog {
  width: min(400px, 100%);
  background: var(--bg-elev);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px;
  animation: confirm-in 0.2s var(--ease);
}
@keyframes confirm-in {
  from { opacity: 0; transform: scale(0.96) translateY(8px); }
  to { opacity: 1; transform: none; }
}
.confirm-title { font-family: var(--font-display); font-size: 19px; font-weight: 600; color: var(--text); line-height: 1.3; }
.confirm-msg { margin-top: 8px; font-size: 14px; line-height: 1.5; color: var(--text-dim); }
.confirm-input {
  width: 100%; margin-top: 16px;
  background: var(--bg-elev-2); border: none; border-radius: var(--radius-sm);
  padding: 12px 14px; font: inherit; font-size: 15px; color: var(--text);
  outline: 2px solid transparent; transition: outline-color 0.15s var(--ease);
}
.confirm-input::placeholder { color: var(--text-faint); }
.confirm-input:focus { outline-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.confirm-btn {
  border: none; border-radius: 999px; padding: 11px 20px; min-height: 44px;
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: background 0.15s var(--ease), filter 0.15s var(--ease);
}
.confirm-btn-cancel { background: var(--bg-elev-2); color: var(--text); }
.confirm-btn-cancel:hover { background: var(--bg-elev-3); }
.confirm-btn-ok { background: var(--accent); color: var(--on-accent); }
.confirm-btn-ok:hover { filter: brightness(1.08); }
.confirm-btn-danger { background: var(--danger); color: var(--on-accent); }
.confirm-btn-danger:hover { filter: brightness(1.08); }

.settings-section {
  padding: 18px 20px;
  border-bottom: 1px solid var(--border);
}
.settings-section:last-child { border-bottom: none; }
/* Admin panels: each section is its own Gemini card (elevated surface, rounded, gapped)
   instead of a divider-separated block, so sections read as distinct — matches the
   settings-view treatment but without its narrow width cap (admin holds wide tables/charts). */
.admin-body .settings-section {
  background: var(--bg-elev-1);
  border: none;
  border-radius: var(--radius-lg);
  padding: 28px 32px;
  margin: 0 0 20px;
}
.admin-body .settings-section:last-child { margin-bottom: 0; }
@media (max-width: 640px) {
  .admin-body .settings-section { padding: 22px 20px; }
}
.settings-section h3 { margin: 0 0 12px; font-size: 14px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }

.settings-radios { display: flex; gap: 20px; margin-bottom: 14px; font-size: 14px; }

/* Billing modal (M6) — reuses .settings-overlay/.settings-panel/.settings-section. */
.billing-body { max-height: 70vh; overflow-y: auto; }
/* Buy-agent: let the panel itself own the scroll (90dvh) so the popup runs as tall
   as it can and content rarely needs scrolling. No nested scroll region. */
.buy-agent-body { max-height: none; overflow: visible; }
.billing-note { padding: 24px 20px; color: var(--text-faint); font-size: 14px; }
/* One-line heads-up under the buy CTA (on-chain): payment comes from your wallet. */
.buy-hint { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; text-align: center; margin-top: 2px; }
/* Trial terms at the reinvest checkout — amber, icon + text, matching the yield card's trial
   banner so the "unlock at first fee" deal reads the same at the point of decision. */
.buy-hint--trial {
  display: flex; align-items: center; gap: 8px; text-align: left; color: var(--text);
  margin-top: 8px; padding: 9px 11px; border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, var(--bg-elev-1));
}
.buy-hint--trial .material-symbols-rounded { flex: none; font-size: 18px; color: var(--warn); }

/* Buy-agent titlebar: groups the "New agent" heading with the reinvest "Locked liquidity"
   badge so the funding source reads as a header-level fact, not fine print. */
.buy-title-group { display: flex; align-items: center; gap: 10px; min-width: 0; }
.buy-locked-badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap;
  background: color-mix(in srgb, var(--grad-b) 18%, var(--bg-elev-1)); color: var(--text);
}
.buy-locked-badge .material-symbols-rounded { font-size: 14px; color: var(--grad-b); }

/* Funding modal (self-custodial deposit) — same Gemini design language as the Wallet
   Receive modal: wallet-modal panel (24px radius, display-font title), wallet QR +
   address + white pill, with a guided amount card / network / withdrawal checklist.
   The 90dvh panel owns the scroll (wallet-modal-body has no inner clip). */
/* wallet-modal sets overflow:visible (its modals are short); the funding modal is
   taller, so restore the panel's own 90dvh scroll (3-class beats .wallet-modal). */
.settings-panel.wallet-modal.fund-panel { overflow-y: auto; }
.fund-body { display: flex; flex-direction: column; gap: 14px; }
.fund-body .wallet-qr,
.fund-body .wallet-receive-addr,
.fund-body .wallet-modal-intro,
.fund-body .auth-btn { margin: 0; }
.fund-card { background: var(--bg-elev-1); border-radius: var(--radius); padding: 16px 18px; }
.fund-card-label {
  font-size: 12px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-dim);
}
.fund-amount { text-align: center; }
.fund-amount-big {
  font-family: var(--font-display); font-size: 30px; font-weight: 700; line-height: 1.1;
  color: var(--text); font-variant-numeric: tabular-nums; margin-top: 7px;
}
.fund-amount-sub { font-size: 13px; color: var(--text-faint); margin-top: 7px; }
.fund-guide { display: flex; flex-direction: column; gap: 12px; }
.fund-guide-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.fund-net-value { font-size: 14px; font-weight: 600; color: var(--text); }
.fund-steps {
  margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 7px;
  font-size: 13.5px; line-height: 1.5; color: var(--text-dim);
}
.fund-warn { font-size: 12.5px; color: var(--warn); line-height: 1.5; text-align: center; }
.fund-reassure { font-size: 12.5px; color: var(--text-faint); line-height: 1.55; text-align: center; }
.fund-status { font-size: 13px; color: var(--text-faint); text-align: center; }
.billing-balance { font-size: 14px; color: var(--text); margin-bottom: 12px; }
.billing-balance.billing-warn { color: var(--warn); }
.billing-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.billing-buy {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 12px;
  background: var(--bg-elev-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: background 0.12s var(--ease);
}
.billing-buy:hover:not(:disabled) { background: var(--bg-elev-3); }
.billing-buy:disabled { opacity: 0.5; cursor: default; }
.billing-grid-1 { grid-template-columns: 1fr; }
.settings-radios label { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }

/* ------------------------------------------------------------------ */
/* Buy-agent screen (Gemini design)                                    */
/* ------------------------------------------------------------------ */
.buy-agent { display: flex; flex-direction: column; gap: 15px; }
.buy-agent .settings-label { margin-top: 0; }

/* Buy-agent modal (owner redesign v4): a single-column DROP STAGE at every width —
   hero deck, money block, configure, CTA, fine print — centered on one 430px axis. */
.settings-panel.buy-agent-panel { width: min(620px, 100%); }
.buy-agent { position: relative; }
/* Theatrical vignette behind the stage (static — motion lives in the mascot/rays). */
.buy-agent::before {
  content: "";
  position: absolute;
  inset: -10px;
  pointer-events: none;
  background: radial-gradient(56% 32% at 50% 20%, rgba(80, 40, 255, 0.12), transparent 70%);
}
.buy-agent-grid { position: relative; display: flex; flex-direction: column; align-items: center; gap: 20px; }
.buy-agent-card-col { display: flex; flex-direction: column; gap: 18px; width: 100%; align-items: center; }
.buy-agent-form-col { display: flex; flex-direction: column; gap: 16px; width: min(430px, 100%); min-width: 0; }

/* Pre-sheet loading veil: while the catalog/quote load, the overlay holds only a
   shimmering mystery card (aurora drift + a soft pulse around the Genie mark) — the
   New-agent sheet then appears fully formed instead of opening half-empty. */
.buy-loading-stage { display: grid; place-items: center; pointer-events: none; }
.buy-card-skel {
  width: min(58vw, 216px); aspect-ratio: 5 / 7; border-radius: var(--radius-lg);
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(125deg, var(--grad-a), var(--grad-b) 42%, var(--grad-c) 72%, var(--grad-a) 100%);
  background-size: 240% 240%;
  box-shadow: 0 24px 48px -16px rgba(0, 0, 0, 0.6);
  animation: buy-card-aurora 4.5s ease-in-out infinite, buy-skel-pulse 1.6s ease-in-out infinite;
}
.buy-card-skel .material-symbols-rounded { font-size: 46px; filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.4)); animation: buy-skel-spark 1.6s ease-in-out infinite; }
@keyframes buy-skel-pulse { 0%, 100% { transform: scale(0.98); } 50% { transform: scale(1); } }
@keyframes buy-skel-spark { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.18); opacity: 1; } }
/* The aurora drift these cards animate with — referenced by the gacha mystery back
   too, which previously pointed at keyframes that were never defined. */
@keyframes buy-card-aurora {
  0%, 100% { background-position: 0% 30%; }
  50% { background-position: 100% 70%; }
}
@media (prefers-reduced-motion: reduce) { .buy-card-skel, .buy-card-skel .material-symbols-rounded { animation: none; } }

/* Hero card — the Genie mascot (Three.js/WebGL orb) on a calm dark back (a
   portrait collectible card). The card lives on a "stage" so entrance
   (scale/fade) and the floating idle animation don't fight over `transform`
   (they're on different elements). */
/* No perspective/preserve-3d here: a 3D context forces the card + ghost fan into
   separate compositor layers, and WebKit updates those a frame late inside an
   overflow scroller — the deck visibly trailed the rest of the sheet during
   momentum scroll on iOS. Nothing uses 3D transforms (hover is filter-only). */
.buy-card-stage {
  width: 100%;
  max-width: 264px;
  margin: 0 auto;
  animation: buy-card-in 0.6s var(--ease) both;
}
.buy-card {
  position: relative;
  width: 100%;
  aspect-ratio: 5 / 7;
  max-height: 370px;
  margin: 0 auto;
  padding: 14px 14px 16px;
  border-radius: var(--radius-lg);
  border: 1.5px solid rgba(255, 255, 255, 0.14);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* Calm dark backdrop — the mascot's own rim glow is the color story here, so
     the card behind it stays quiet instead of competing for attention. */
  background: radial-gradient(120% 90% at 50% 8%, #1d1e26, var(--bg-elev-1) 60%, #0c0c10 100%);
  animation:
    buy-card-float 6s ease-in-out infinite,
    buy-card-glow 4.5s ease-in-out infinite;
  transition: filter 0.25s var(--ease);
}
/* Static top gloss for depth. */
.buy-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 75% at 22% 0%, rgba(255, 255, 255, 0.12), transparent 55%);
  pointer-events: none;
}
/* Prismatic ray burst behind the mascot (the star-upgrade sheet's backdrop recipe:
   near-black card base + burst at half opacity). The WebGL2 canvas fills the host;
   the card's overflow:hidden clips it to the rounded corners. */
.buy-card-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Golden/purple ray field (owner call). A coherent two-hue burst (set in
     billing.ui.ts) reads far calmer than the old rainbow, so it can sit at a
     visible opacity while the mascot's rim glow stays the focal point. */
  opacity: 0.42;
}
.buy-card-mascot {
  position: absolute;
  top: 8%;
  left: 0;
  width: 100%;
  height: 68%;
  display: block;
  cursor: pointer;
}
/* Head row: NAME (left) + star-tier pill (right), riding above the mascot canvas. */
.buy-card-head {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  color: #fff;
}
/* Foot: the price alone, centred, dressed as a swing tag. The drop-shadow lives HERE
   (not on the tag) because clip-path on the tag would clip its own box-shadow. */
.buy-card-foot {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: center;
  color: #fff;
  filter: drop-shadow(0 5px 12px rgba(0, 0, 0, 0.45));
}
.buy-card-name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Price tag: a light ticket with a pointed left end and a punched string-hole, so the
   number reads as THE PRICE — echoing the light CTA button — not as another data pill. */
.buy-card-price {
  position: relative;
  flex: 0 0 auto;
  font-size: 13.5px;
  font-weight: 700;
  white-space: nowrap;
  color: #14151a;
  background: linear-gradient(180deg, #ffffff, #e8eaf1);
  padding: 6px 14px 6px 24px;
  border-radius: 3px 8px 8px 3px;
  clip-path: polygon(14px 0, 100% 0, 100% 100%, 14px 100%, 0 50%);
}
.buy-card-price::before {
  content: "";
  position: absolute;
  left: 11px;
  top: 50%;
  width: 5px;
  height: 5px;
  transform: translateY(-50%);
  border-radius: 50%;
  background: rgba(20, 21, 26, 0.55); /* the punched hole */
}
/* Star-tier badge in the head row — a frosted dark pill so the star reads on the
   bright card (the generic tier-colored .star-pill would clash with it). */
.buy-card-star {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 3px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.28); color: #fff;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  backdrop-filter: blur(2px);
}
.buy-card-star-ic { font-size: 15px; font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 20; }
/* Hover: brighten + saturate (not transform — that's owned by the float animation). */
.buy-card-stage:hover .buy-card { filter: brightness(1.08) saturate(1.12); }
/* Light theme (owner call: the card must look BRIGHT, not a dark slab). The burst
   canvas is opaque (webgl2 alpha:false — black bg + rays), so invert+hue-rotate
   turns it into a white burst with the same ray hues; the card ground and head
   ink flip to match. */
:root[data-theme="light"] .buy-card {
  background: radial-gradient(120% 90% at 50% 8%, #ffffff, #eef0f8 60%, #e2e6f3 100%);
  border-color: rgba(25, 28, 48, 0.12);
}
:root[data-theme="light"] .buy-card-burst { filter: invert(1) hue-rotate(180deg); opacity: 0.45; }
:root[data-theme="light"] .buy-card-head { color: #14151a; }
:root[data-theme="light"] .buy-card-name { text-shadow: 0 1px 8px rgba(255, 255, 255, 0.5); }
:root[data-theme="light"] .buy-card-reroll { background: rgba(20, 21, 26, 0.08); }

@keyframes buy-card-in {
  from { opacity: 0; transform: translateY(16px) scale(0.93); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes buy-card-float {
  0%, 100% { transform: translateY(0) rotate(-0.4deg); }
  50% { transform: translateY(-8px) rotate(0.4deg); }
}
@keyframes buy-card-glow {
  0%, 100% { box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5), 0 0 0 rgba(120, 90, 220, 0); }
  50% { box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55), 0 0 34px rgba(120, 90, 220, 0.45); }
}
@media (prefers-reduced-motion: reduce) {
  .buy-card-stage,
  .buy-card { animation: none; }
}

/* Tier / supply progress */
.buy-tier { display: flex; flex-direction: column; gap: 8px; }
.buy-tier-head { display: flex; align-items: baseline; justify-content: space-between; font-size: 13px; }
.buy-tier-label { color: var(--text-dim); }
.buy-tier-count { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.buy-progress { height: 8px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; }
.buy-progress-fill {
  height: 100%;
  min-width: 6px;
  border-radius: 999px;
  background: var(--grad);
  transition: width 0.3s var(--ease);
}
.buy-tier-sub { font-size: 12px; color: var(--text-faint); }

/* Earn pitch (billing.ui.ts yieldPreview). Gold hero number (count-up) + always-visible,
   color-coded value lines. Calm, Gemini-organized: a soft static gold wash, no animation,
   no horizontal scroll — the value reads at a glance. Cash-math detail sits in the
   "How payouts work" disclosure. */
.buy-earn--hype {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, #ffd166 26%, var(--border));
  border-radius: var(--radius-lg, 18px);
  background:
    radial-gradient(130% 120% at 0% 0%, color-mix(in srgb, #ffd166 10%, transparent), transparent 55%),
    var(--bg-elev-1);
}
.buy-earn-ic { font-size: 20px; flex: 0 0 auto; }
.buy-earn-kicker {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600;
  color: color-mix(in srgb, #ffd166 60%, var(--text));
}
.buy-earn-kicker .buy-earn-ic { font-size: 18px; color: #ffd166; }
.buy-earn-badge {
  margin-left: auto;
  font-size: 11px; font-weight: 700;
  padding: 2px 8px; border-radius: 999px;
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 18%, transparent);
}
.buy-earn-bignum { display: flex; align-items: baseline; gap: 10px; }
.buy-earn-bignum-val {
  font-size: 38px; font-weight: 800; line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.buy-earn-bignum-unit { display: flex; flex-direction: column; line-height: 1.25; }
.buy-earn-bignum-unit-day { font-size: 14px; font-weight: 600; color: var(--text-dim); }
.buy-earn-bignum-unit-mo { font-size: 12px; color: var(--text-faint); }
/* Always-visible color-coded value lines (no scroll, nothing hidden). */
.buy-earn-values { display: flex; flex-direction: column; gap: 10px; }
.buy-earn-v { display: flex; align-items: flex-start; gap: 11px; }
.buy-earn-v-ic { font-size: 22px; flex: 0 0 auto; margin-top: 1px; }
.buy-earn-v-main { display: flex; flex-direction: column; gap: 1px; }
.buy-earn-v-title { font-size: 14px; font-weight: 700; color: var(--text); }
.buy-earn-v-sub { font-size: 12px; line-height: 1.35; color: var(--text-faint); }
.buy-earn-rows { display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.buy-earn-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.buy-earn-row .buy-earn-ic { font-size: 18px; color: var(--text-faint); }
.buy-earn-row-label { color: var(--text-dim); flex: 1 1 auto; }
.buy-earn-row-val { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* Fields */
.buy-field { display: flex; flex-direction: column; gap: 7px; }

/* Coupon: input + Apply button, with a validity message below. */
.buy-coupon-row { display: flex; gap: 8px; align-items: stretch; }
.buy-coupon-row .settings-input { flex: 1 1 auto; }
.buy-coupon-apply {
  flex: 0 0 auto;
  padding: 0 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elev-2);
  color: var(--text);
  font: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease), opacity 0.15s var(--ease);
}
.buy-coupon-apply:hover:not(:disabled) { background: var(--bg-elev-3); }
.buy-coupon-apply:disabled { opacity: 0.55; cursor: default; }
.buy-coupon-msg { font-size: 12.5px; line-height: 1.4; text-align: center; margin-top: 6px; }
.buy-coupon-msg.is-ok { color: var(--ok); }
.buy-coupon-msg.is-err { color: var(--warn); }
/* Original price struck through next to the discounted one on the hero card. */
.buy-card-price-was { text-decoration: line-through; opacity: 0.6; font-weight: 500; margin-right: 7px; }

/* Quantity: quick chips + numeric stepper */
.buy-qty { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.buy-qty-chips { display: flex; gap: 7px; }
.buy-chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev-1);
  color: var(--text-dim);
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.buy-chip:hover { border-color: var(--accent); }
.buy-chip.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.buy-chip.is-danger { color: var(--err); }
.buy-chip.is-danger:hover { border-color: var(--err); background: color-mix(in srgb, var(--err) 14%, transparent); }
.buy-stepper {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elev-1);
}
.buy-step-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s;
}
.buy-step-btn:hover:not(:disabled) { background: var(--bg-elev-2); }
.buy-step-btn:disabled { opacity: 0.35; cursor: default; }
.buy-qty-input {
  width: 44px;
  text-align: center;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  -moz-appearance: textfield;
}
.buy-qty-input::-webkit-outer-spin-button,
.buy-qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Implicit consent (owner call): a quiet caption under the CTA; it shakes on press
   as the "you just agreed to this" beat. */
/* Consent line inside the gradient CTA — quiet white, normal weight against the
   800-weight price row it inherits from the button. */
.buy-consent { display: block; text-align: center; font-size: 11.5px; font-weight: 600; color: rgba(255, 255, 255, 0.78); }
.buy-cta .buy-terms-link { cursor: pointer; }
.buy-consent.is-shake { animation: buy-consent-shake 0.4s ease; }
@keyframes buy-consent-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(3px); }
}
.buy-terms-link { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.buy-terms-link:hover { color: var(--text); }

/* CTA — always armed (implicit consent), brand gradient use #3, shimmer sweep.
   Live price leads; the struck MSRP total + −N% chip trail on the right. */
.buy-cta-group { display: flex; flex-direction: column; gap: 8px; }
.buy-cta {
  position: relative;
  overflow: hidden;
  width: 100%;
  padding: 13px 22px 10px;
  border: none;
  border-radius: 999px;
  background: linear-gradient(100deg, #0036ff, #7b2bff 55%, #c600ff);
  color: #fff;
  font-size: 16.5px;
  font-weight: 800;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  box-shadow: 0 14px 40px rgba(90, 30, 230, 0.4);
}
.buy-cta-main { display: flex; align-items: center; justify-content: center; gap: 10px; }
.buy-cta:hover:not(:disabled) {
  /* .billing-buy:hover swaps in a flat elev-3 at higher specificity — re-pin the gradient. */
  background: linear-gradient(100deg, #0036ff, #7b2bff 55%, #c600ff);
  filter: brightness(1.07);
}
.buy-cta:disabled { opacity: 0.7; }
.buy-cta::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 70px;
  left: -90px;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.35), transparent);
  animation: buy-cta-sweep 2.8s ease-in-out infinite;
}
@keyframes buy-cta-sweep {
  0%, 55% { left: -90px; }
  85%, 100% { left: 115%; }
}
.buy-cta-was { text-decoration: line-through; opacity: 0.55; font-weight: 600; font-size: 0.78em; }
.buy-cta-off { background: rgba(255, 255, 255, 0.18); border-radius: 999px; padding: 3px 10px; font-size: 0.68em; font-weight: 800; }
@media (prefers-reduced-motion: reduce) { .buy-cta::after { animation: none; display: none; } }

.settings-fields { display: flex; flex-direction: column; gap: 7px; }
.settings-label { font-size: 12.5px; color: var(--text-dim); margin-top: 6px; }

.settings-input, .settings-textarea {
  width: 100%;
  background: var(--bg-elev-1);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font: inherit;
  font-size: 14px;
  outline: none;
}
.settings-input:focus, .settings-textarea:focus { border-color: var(--accent); }
.settings-textarea { resize: vertical; font-family: var(--font-mono); font-size: 12.5px; }

/* Warn-only cash-safety notice under the settings Save button (the save still succeeds). */
.settings-cash-warning {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--banner-warn-bg);
  color: var(--banner-warn-fg);
  border: 1px solid var(--banner-warn-bd);
  font-size: 12.5px;
  line-height: 1.5;
  display: grid;
  gap: 4px;
}
.settings-cash-warning strong { font-weight: 700; }

/* ── Admin → Danger zone: live-money master switches (yield & payments) ─────── */
/* Red-accented nav item so the destructive panel reads as set-apart at a glance. */
.admin-nav-item.is-danger .admin-nav-icon { color: var(--danger-text); }
.admin-nav-item.is-danger.is-active .admin-nav-label,
.admin-nav-item.is-danger.is-active .admin-nav-icon { color: var(--danger-text); }
.admin-danger-banner {
  display: flex; gap: 10px; align-items: flex-start;
  margin-bottom: 20px; padding: 16px 22px;
  border-radius: var(--radius-md);
  background: var(--danger-bg); color: var(--danger-text);
  border: 1px solid var(--danger-border);
  font-size: 13px; line-height: 1.5;
}
.admin-danger-banner .material-symbols-rounded { font-size: 20px; flex: none; }
.admin-danger-banner strong { font-weight: 700; }
.danger-toggle {
  border: 1px solid var(--danger-border);
  background: color-mix(in srgb, var(--danger-bg) 55%, var(--bg-elev-1));
  border-radius: var(--radius-lg);
  padding: 24px 28px; display: grid; gap: 12px;
}
.danger-toggle + .danger-toggle { margin-top: 18px; }
@media (max-width: 640px) {
  .danger-toggle { padding: 18px 20px; }
}
.danger-toggle-title { font-size: 15px; font-weight: 700; color: var(--text); }
.danger-toggle-desc { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; margin-top: 3px; }
.danger-seg { display: flex; gap: 8px; flex-wrap: wrap; }
.danger-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 18px; border-radius: var(--radius-md); cursor: pointer;
  background: var(--bg-elev-1); border: none; color: var(--text-dim);
  font: inherit; font-size: 13.5px; font-weight: 600;
  box-shadow: inset 0 0 0 1.5px transparent;
  transition: color 0.15s var(--ease), background 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.danger-opt:hover { color: var(--text); background: var(--bg-elev-2); }
.danger-opt:disabled { opacity: 0.55; cursor: default; }
/* Selected state: neutral for Off, amber for partial (admins only), red for fully Live. */
.danger-opt.is-active {
  color: var(--text); background: var(--bg-elev-2);
  box-shadow: inset 0 0 0 1.5px var(--border);
}
.danger-opt.is-active.is-partial {
  color: var(--banner-warn-fg); background: var(--banner-warn-bg);
  box-shadow: inset 0 0 0 1.5px var(--banner-warn-bd);
}
.danger-opt.is-active.is-live {
  color: #fff; background: var(--danger);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--danger) 60%, transparent);
}
.danger-status { font-size: 12.5px; color: var(--text-dim); min-height: 16px; }
.danger-status.is-ok { color: var(--ok); }
.danger-status.is-warn { color: var(--banner-warn-fg); }
.danger-status.is-err { color: var(--danger-text); }

.settings-actions { display: flex; gap: 10px; margin-top: 16px; }
.settings-inline-row { display: flex; gap: 8px; }
.settings-inline-row .settings-input { flex: 1 1 auto; }

.settings-btn {
  background: var(--bg-elev-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s var(--ease);
}
.settings-btn:hover { background: var(--bg-elev-3); }
.settings-btn.primary {
  background: var(--grad);
  color: var(--on-accent);
  border: none;
  font-weight: 650;
}
.settings-btn.primary:hover { filter: brightness(1.08); }

.settings-status { font-size: 13px; margin-top: 10px; min-height: 18px; color: var(--text-dim); }
.settings-status.ok { color: var(--ok); }
.settings-status.err { color: var(--err); }

/* Language picker (International settings) — selectable cards, one per language. */
.settings-lang-options { display: flex; flex-direction: column; gap: 8px; max-width: 420px; }
.settings-lang-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 13px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elev-1);
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.settings-lang-option:hover { background: var(--bg-elev-2); }
.settings-lang-option.is-active { border-color: var(--accent); background: var(--bg-elev-2); }
.settings-lang-name { flex: 1 1 auto; font-weight: 500; }
.settings-lang-check { font-size: 20px; color: var(--accent, #6b9fff); opacity: 0; }
.settings-lang-option.is-active .settings-lang-check { opacity: 1; }

.skill-list { list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.skill-empty { color: var(--text-faint); font-size: 13px; }
.skill-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 11px;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.skill-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.skill-name { font-size: 13.5px; font-weight: 600; }
.skill-desc { font-size: 12px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skill-del {
  flex: 0 0 auto;
  background: transparent;
  color: var(--danger-text);
  border: 1px solid var(--danger-border);
  border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 38px; padding: 6px 14px;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.skill-del:hover { background: var(--danger-bg); }
/* Neutral counterpart to .skill-del for non-destructive row actions (e.g. Edit). */
.skill-edit {
  flex: 0 0 auto;
  background: var(--bg-elev-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 38px; padding: 6px 14px;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.skill-edit:hover { background: var(--bg-elev-3); }

/* ---------------- self-improvement section ---------------- */

.settings-hint {
  margin: 0 0 14px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-dim);
}

/* Composer-shortcut editor (admin → Home screen): one row per pill — auto emoji,
   name input, prompt textarea, remove. */
.chip-editor-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 12px;
}
.chip-editor-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.chip-editor-emoji {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  font-size: 20px;
  line-height: 1;
  background: var(--bg-elev-1);
  border-radius: 12px;
}
.chip-editor-fields {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chip-editor-prompt {
  min-height: 40px;
  resize: vertical;
}
.chip-editor-remove {
  flex: 0 0 auto;
  width: 32px;
  height: 40px;
  border: none;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  font-size: 15px;
  border-radius: 10px;
}
.chip-editor-remove:hover {
  background: var(--bg-elev-1);
  color: var(--text);
}
.chip-editor-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.chip-editor-add {
  background: var(--bg-elev-1);
  color: var(--text);
}

/* Master + per-pass toggles share this row layout: checkbox + label/hint stack. */
.settings-toggle {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 0;
  cursor: pointer;
}
.settings-toggle input[type="checkbox"] {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.settings-toggle input[type="checkbox"]:disabled { cursor: default; }
.settings-toggle-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.settings-toggle-label { font-size: 14px; color: var(--text); }
.settings-toggle-hint { font-size: 12px; color: var(--text-faint); }

/* Per-pass toggles are indented under the master switch and dim when it's off. */
.settings-subtoggles {
  display: flex;
  flex-direction: column;
  margin-left: 26px;
  padding-left: 12px;
  border-left: 1px solid var(--border);
}
.settings-subtoggles.disabled {
  opacity: 0.45;
  pointer-events: none;
}

.improve-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.improve-empty { color: var(--text-faint); font-size: 13px; font-style: italic; }

.improve-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 11px;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.improve-item.reverted { opacity: 0.55; }
.improve-item.reverted .improve-summary { text-decoration: line-through; }

.improve-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.improve-summary {
  font-size: 13.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.improve-sub {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.improve-time {
  font-size: 11.5px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.improve-tags { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.improve-phase {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  background: var(--bg-elev-2);
}
/* Phase tags are mono tonal chips — distinguished by their text label, not color
   (reflect/dreaming/curator inherit the base; manual reads quieter). */
.improve-phase.phase-manual { color: var(--text-faint); }
.improve-kinds {
  font-size: 11.5px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.improve-revert {
  flex: 0 0 auto;
  background: transparent;
  color: var(--danger-text);
  border: 1px solid var(--danger-border);
  border-radius: 999px;
  padding: 5px 13px;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  transition: background 0.15s;
}
.improve-revert:hover { background: var(--danger-bg); }
.improve-revert:disabled { opacity: 0.5; cursor: default; }

/* Pending-approval card (Material/Gemini): content stacked, then one aligned action
   footer — a neutral outline Reject (dismissive, left) and a filled primary Approve
   (affirmative, right), both equal-height pill icon+label buttons. Replaces the old
   mismatched plain-text Approve + red outline Reject. */
.approval-card {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 11px;
  padding: 13px 14px;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.approval-actions { display: flex; justify-content: flex-end; gap: 8px; }
.approval-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  height: 34px; padding: 0 15px; border-radius: 999px;
  border: 1px solid transparent;
  font: inherit; font-size: 13px; font-weight: 600; line-height: 1; cursor: pointer;
  transition: background 0.15s var(--ease), filter 0.15s var(--ease), color 0.15s var(--ease), transform 0.1s var(--ease);
}
.approval-btn .material-symbols-rounded { font-size: 17px; }
.approval-btn:disabled { opacity: 0.5; cursor: default; }
.approval-btn--reject { background: transparent; color: var(--text-dim); border-color: var(--border); }
.approval-btn--reject:not(:disabled):hover { background: var(--bg-elev-2); color: var(--text); }
.approval-btn--approve { background: var(--accent); color: var(--on-accent); }
.approval-btn--approve:not(:disabled):hover { filter: brightness(1.06); }
.approval-btn:not(:disabled):active { transform: scale(0.97); }

.improve-reverted-tag {
  flex: 0 0 auto;
  font-size: 11px;
  font-style: italic;
  color: var(--text-faint);
}

/* ---------------- shared toast (toast.ui.ts) ---------------- */
/* Above sheets/modals/confirm dialogs so an action taken inside an overlay (e.g. the
   admin Details sheet) still surfaces its confirmation. Mirrors .sched-toast. */
.genie-toast {
  /* Centered via inset-0 + auto margins, NOT left:50%: a fixed box at left:50%
     shrink-to-fits into the space right of center (HALF the viewport), so long
     messages wrapped into a skinny multi-line blob on phones. fit-content sizes
     to the text; max-width caps it; the 16px insets are the phone edge gutter. */
  position: fixed; left: 16px; right: 16px; bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  margin: 0 auto; width: fit-content; max-width: 560px;
  transform: translateY(6px);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 12px 20px; border-radius: 999px;
  background: var(--bg-elev-3); color: var(--text);
  font-size: 13.5px; line-height: 1.35;
  opacity: 0; pointer-events: none; z-index: 20000;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.32);
  transition: opacity var(--ease) 160ms, transform var(--ease) 160ms;
}
.genie-toast.is-shown { opacity: 1; transform: translateY(0); }
.genie-toast.is-error { background: var(--danger-bg); color: var(--danger); }
.genie-toast-icon { font-size: 18px; color: var(--ok); }
.genie-toast.is-error .genie-toast-icon { color: var(--danger); }

/* ---------------- toast ---------------- */

/* (legacy .toast removed — main.ts now uses the shared .genie-toast snackbar) */

/* ---------------- slide-up options modal (ui_request) ---------------- */

.modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(0, 0, 0, 0);
  z-index: 300;
  transition: background 0.25s ease;
}
.modal-overlay.show {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
}

.modal-sheet {
  width: 100%;
  max-width: 860px;
  max-height: 80dvh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px calc(20px + env(safe-area-inset-bottom, 0px));
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
  box-shadow: var(--shadow-lg);
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.modal-overlay.show .modal-sheet { transform: translateY(0); }

.modal-question {
  font-size: 15px;
  color: var(--text);
}
.modal-question p:first-child { margin-top: 0; }

/* The facts under the question. The sheet covers the chat, so this is what the
   user decides on — quieter than the question, scrolls if long, never taller
   than a third of the screen so the options stay reachable. */
.modal-context {
  margin-top: 6px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-dim);
  max-height: 32vh;
  overflow-y: auto;
}
.modal-context p:first-child { margin-top: 0; }
.modal-context p:last-child { margin-bottom: 0; }

.modal-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.modal-option {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  text-align: left;
  background: var(--bg-elev-1);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font: inherit;
  font-size: 14.5px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.modal-option:hover,
.modal-option.selected {
  background: var(--bg-elev-2);
  border-color: var(--accent);
}
.modal-option:focus-visible { outline: none; border-color: var(--accent); }
.modal-option-num {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.modal-option.selected .modal-option-num {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.modal-option-num:empty { display: none; }
.modal-option-text { flex: 1 1 auto; min-width: 0; }

.modal-input {
  width: 100%;
  resize: none;
  max-height: 160px;
  background: var(--bg-elev-1);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  font: inherit;
  font-size: 14.5px;
  line-height: 1.45;
  outline: none;
}
.modal-input:focus { border-color: var(--accent); }
.modal-input::placeholder { color: var(--text-faint); }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.modal-btn {
  background: var(--bg-elev-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.modal-btn:hover { background: var(--bg-elev-3); }
.modal-btn.primary {
  background: var(--grad);
  color: var(--on-accent);
  border: none;
  font-weight: 650;
}
.modal-btn.primary:hover { filter: brightness(1.08); }

@media (prefers-reduced-motion: reduce) {
  .modal-sheet { transition: none; }
  .modal-overlay { transition: none; }
}

/* ---------------- responsive ---------------- */

@media (max-width: 860px) {
  .model-indicator { max-width: 38vw; }
}

@media (max-width: 520px) {
  /* --- Phone home: the WHOLE column scrolls as one (mascot → bubble → earnings
     scroll away as you browse), and only the Recent row pins — right beneath the
     floating hamburger — so task browsing stays anchored (Gemini's mobile home).

     Shared gutter so the earnings card and the task list are the same width and
     centered, and their content aligns on a single left edge (gutter + 16px). --- */

  /* The single scroll container is .home-center now (not just the list), so the
     hero scrolls with everything else. .tasks-view is just the clipping frame;
     the fixed composer + top scrim escape it via position:fixed. The gutter var
     is scoped here so every descendant (card, Recent bar, list) shares it. */
  .tasks-view { overflow: hidden; }
  .home-center {
    --home-gutter: 16px;
    padding: 0;
    overflow-y: auto;
    /* Never scroll sideways — the assist-chip row scrolls within its own box, so
       the page itself must not pick up a stray horizontal scroll. */
    overflow-x: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  /* Opaque top strip: as the hero and task rows scroll up they dissolve behind
     the floating hamburger, above the pinned Recent bar. Sits under the hamburger
     (z 40) but over the scrolling content. */
  .home-center::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: calc(env(safe-area-inset-top) + 52px);
    background: var(--bg);
    z-index: 20;
    pointer-events: none;
  }

  /* The hero is now just the top of the scroll — no min-height, no pin. Mascot
     rides high; its transparent canvas headroom tucks under the top scrim. */
  .home-header { min-height: 0; padding-top: calc(env(safe-area-inset-top) + 4.5vh); z-index: 2; }

  /* Hamburger floats top-left over the scrim. */
  .nav-fab { top: calc(env(safe-area-inset-top) + 4px); }

  /* Even vertical rhythm below the ball: bubble → earnings → Recent step 24 / 24,
     and the ball-to-bubble gap is tightened to ~16 by pulling the bubble up
     through more of the canvas' below-ball headroom. No slack band, so a third
     task row clears the fold. */
  .home-mascot-canvas { width: clamp(150px, 37vh, 330px); height: clamp(150px, 37vh, 330px); }
  .home-mascot-ph { width: clamp(150px, 37vh, 330px); height: clamp(150px, 37vh, 330px); }
  .home-mascot-slot { margin: 0 0 clamp(-78px, -6.5vh, -26px); }
  .home-greeting {
    margin: 0 auto 24px;
    padding: 11px 18px;
    font-size: 15.5px;
    line-height: 1.35;
  }

  /* Earnings card and task list share the gutter → same width, centered. */
  .home-earn-slot { width: auto; margin: 0 var(--home-gutter) 24px; }
  .home-earn { padding: 14px 16px; }

  /* Recent row: pinned beneath the top scrim as the hero scrolls away. Solid fill
     with a short fade below so task rows dissolve as they pass under it. Its label
     lines up with the task titles (gutter + 16); its search icon with the kebabs. */
  .recent-bar {
    position: sticky;
    top: calc(env(safe-area-inset-top) + 52px);
    z-index: 15;
    margin: 0;
    padding: 6px var(--home-gutter) 8px calc(var(--home-gutter) + 16px);
    background: var(--bg);
  }
  .recent-bar::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: 14px;
    background: linear-gradient(to bottom, var(--bg), transparent);
    pointer-events: none;
  }
  /* ≥44px hit area for the sort dropdown. */
  .recent-sort { min-height: 44px; }

  /* The list flows in the shared scroll (no inner scroll of its own) and clears
     the docked composer at the bottom (composer + assist chips + safe area). */
  .tasks-list {
    flex: 0 0 auto;
    overflow: visible;
    padding: 0 var(--home-gutter);
    padding-bottom: calc(150px + env(safe-area-inset-bottom));
    gap: 4px;
  }
  /* Title aligns with the earnings label (gutter + 16); kebab hugs the right. */
  .task-row { padding: 16px 6px 16px 16px; }
  .task-title { font-size: 16px; }
  .task-subtitle { font-size: 13.5px; }
  /* Always-visible on touch (see the hover:none rule); ≥44px for the kebab. */
  .task-time { font-size: 12.5px; }
  .task-kebab { width: 44px; height: 44px; font-size: 20px; }

  /* Docked composer, thumb-zone: anchored to the viewport bottom with a scrim so
     list rows dissolve beneath it. The assist-chip row rides just above the pill. */
  .home-composer-wrap {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: 0;
    width: auto;
    margin: 0;
    padding: 10px 0 calc(10px + env(safe-area-inset-bottom));
    background: linear-gradient(to top, var(--bg) 72%, transparent);
  }
  .home-composer { padding: 18px 18px 18px 22px; border-radius: var(--radius-hero); gap: 14px; }
  .home-composer-plus { font-size: 26px; }
  .home-input { font-size: 16.5px; line-height: 1.55; }

  /* Search + drawer/menu rows: ≥44–48px hit areas. */
  .recent-search-wrap { height: 44px; }
  .recent-search-wrap.is-collapsed,
  .recent-search-wrap.is-collapsed .recent-search-ic { width: 44px; height: 44px; }
  .nav-drawer-item { min-height: 52px; }
  .nav-menu-item { min-height: 44px; }

  .brand-name { display: none; }
  /* Tighter side margins on a phone so the chat + composer use the full width
     (matches the home composer's 12px inset). */
  .transcript { gap: 14px; padding-left: 12px; padding-right: 12px; }
  .composer-wrap { padding-left: 12px; padding-right: 12px; }
  .msg { gap: 9px; }
  .msg-avatar { width: 36px; height: 36px; font-size: 17.5px; }
  .surface-card { margin-left: 45px; }
  .cmd-palette { left: 8px; right: 8px; max-height: 50dvh; }
  .cmd-desc { font-size: 12px; }
}

/* ------------------------------------------------------------------ */
/* Auth overlay (login / invite gate)                                  */
/* ------------------------------------------------------------------ */

.auth-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: radial-gradient(1200px 600px at 50% -10%, var(--bg-elev) 0%, var(--bg) 60%);
}

.auth-card {
  position: relative;
  width: 100%;
  max-width: 380px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 26px 24px;
  box-shadow: var(--shadow-lg);
}
.auth-close {
  position: absolute;
  top: 12px;
  right: 12px;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.auth-close:hover { background: var(--bg-elev-2); color: var(--text); }
.auth-close .material-symbols-rounded { font-size: 20px; }

.auth-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
}
.auth-mark {
  font-size: 30px;
  font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24;
  /* The first impression — the one sanctioned use of the signature blue→purple→pink
     gradient on the brand mark (mirrors the profile avatar hero). */
  background: linear-gradient(135deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.auth-brand-name { font-family: var(--font-display); font-size: 17px; font-weight: 600; letter-spacing: 0.2px; }

.auth-title { font-family: var(--font-display); font-size: 24px; font-weight: 600; line-height: 1.2; margin-bottom: 6px; }
.auth-sub { color: var(--text-dim); font-size: 13.5px; line-height: 1.45; margin-bottom: 18px; }

/* Loading / wallet-finalize state: indeterminate spinner + label (the wallet
   setup can take several seconds — never a static line that reads as hung). */
.auth-loading {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 14px 0 6px; color: var(--text-dim); font-size: 13.5px; text-align: center;
}
.auth-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--bg-elev-3); border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .auth-spinner { animation-duration: 2.4s; } }

.auth-body { display: flex; flex-direction: column; gap: 10px; }

.auth-input {
  width: 100%;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 15px;
  min-height: 44px;
  padding: 13px;
  outline: none;
}
.auth-input:focus { border-color: var(--accent); }

.auth-btn {
  width: 100%;
  min-height: 44px;
  background: var(--grad);
  color: var(--on-accent);
  border: none;
  border-radius: 999px;
  font-size: 14.5px;
  font-weight: 600;
  padding: 13px;
  cursor: pointer;
  transition: filter 0.15s ease, opacity 0.15s ease;
}
.auth-btn:hover { filter: brightness(1.08); }
.auth-btn:disabled { opacity: 0.55; cursor: default; }
/* Secondary / ghost button (e.g. "Skip for now" in the delegation step). */
.auth-btn-ghost {
  background: transparent;
  color: var(--text-dim);
  border: 1px solid var(--border);
}
.auth-btn-ghost:hover { filter: none; opacity: 1; color: var(--text); border-color: var(--border-strong); }

/* Send success summary (wallet-send island) — our own post-send breakdown, replacing
   Privy's gas-only "Total". */
.send-summary {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 4px 0 14px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev-1);
}
.send-summary-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 14px; }
.send-summary-label { color: var(--text-dim); }
.send-summary-value { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
/* The Total row sits below a divider and reads a touch larger — it's the headline number. */
.send-summary-total { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 15px; }
.send-summary-total .send-summary-label { color: var(--text); font-weight: 600; }
.send-summary-link {
  display: inline-block;
  margin-bottom: 16px;
  color: var(--accent);
  font-size: 13.5px;
  text-decoration: none;
}
.send-summary-link:hover { text-decoration: underline; }

/* Info modal (profile / wallet / team) — reuses .auth-overlay / .auth-card. */
.info-card { min-width: 320px; }
.info-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13.5px;
}
.info-row-label { color: var(--text-dim); flex: 0 0 auto; }
.info-row-value {
  color: var(--text);
  text-align: right;
  word-break: break-all;
  font-family: var(--font-mono);
  font-size: 12.5px;
}
.info-card .auth-sub { margin-top: 14px; margin-bottom: 16px; }

.auth-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 12.5px;
  padding: 4px;
  cursor: pointer;
  align-self: flex-start;
}
.auth-link-icon { font-size: 16px; }
.auth-link:hover { color: var(--text); }
.auth-link:disabled { opacity: 0.5; cursor: default; }
.auth-link:disabled:hover { color: var(--text-dim); }

.auth-err {
  margin-top: 14px;
  color: var(--err);
  font-size: 12.5px;
  line-height: 1.4;
}

/* Agent picker (reuses .auth-overlay / .auth-card) */
.agent-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}
.agent-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 13px 15px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.agent-row:hover {
  border-color: var(--accent);
  background: var(--bg-elev-2);
}
.agent-name { font-size: 14.5px; font-weight: 600; }
.agent-meta { font-size: 12px; color: var(--text-dim); display: inline-flex; align-items: center; gap: 8px; }

/* ── Schedules screen (standalone view, not a modal) ────────────────────────
   Lives in #schedules-view inside .layout; shown when .layout.schedules-active
   replaces the home (tasks) view. Reuses .settings-btn/.settings-input/.settings-*. */
.schedules-view {
  display: none;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.layout.schedules-active .tasks-view,
.layout.brain-active .tasks-view,
.layout.skills-active .tasks-view,
.layout.earn-active .tasks-view,
.layout.earnings-active .tasks-view,
.layout.store-active .tasks-view,
.layout.wallet-active .tasks-view,
.layout.profile-active .tasks-view,
.layout.team-active .tasks-view,
.layout.settings-active .tasks-view,
.layout.admin-active .tasks-view,
.layout.partner-active .tasks-view { display: none; }
.layout.schedules-active #schedules-view { display: block; }
.layout.brain-active #brain-view { display: block; }
.layout.skills-active #skills-view { display: block; }
.layout.earn-active #earn-view { display: block; }
/* Earn screen: on desktop it scrolls wholesale like the other screens (full
   headline + subtitle at the top). On mobile (≤720) it switches to a pinned
   header + inner scroll (see the earnings media block below). */
.layout.earnings-active #earnings-view { display: block; }
.layout.store-active #store-view { display: block; }
.layout.wallet-active #wallet-view { display: block; }
.layout.profile-active #profile-view { display: block; }
.layout.team-active #team-view { display: block; }
.layout.settings-active #settings-view { display: block; }
.layout.admin-active #admin-view { display: block; }
.layout.partner-active #partner-view { display: block; }

/* Partner screen (Agent Sales System). Reuses .sched-* / .earn-tile primitives. */
.partner-tier {
  margin: 8px 0 4px;
  padding: 18px 20px;
  border: none;
  border-radius: var(--radius);
  background: var(--bg-elev-1);
}
.partner-tier-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.partner-tier-name { font-size: 1.15rem; font-weight: 650; color: var(--text); }
.partner-tier-rates { font-size: 0.85rem; color: var(--text-dim); }
.partner-tier-next { margin-top: 8px; font-size: 0.85rem; color: var(--text-faint); }
.partner-list { display: flex; flex-direction: column; gap: 2px; }
.partner-section-label { margin: 16px 0 6px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); }
.partner-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; border-radius: var(--radius-sm);
  transition: background 0.12s var(--ease);
}
.partner-row:hover { background: var(--bg-elev-1); }
.partner-row-main { flex: 1 1 auto; font-weight: 500; color: var(--text); }
.partner-row-sub { color: var(--text-dim); font-size: 0.85rem; }
.partner-row-date { color: var(--text-faint); font-size: 0.8rem; min-width: 90px; text-align: right; }
.partner-amount { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 110px; text-align: right; color: var(--text); }
.partner-amount.is-pos { color: var(--ok); }
.partner-amount.is-neg { color: var(--text-dim); }

/* Admin screen: categorized sidebar + content panels. */
.admin-layout {
  display: flex;
  gap: 28px;
  max-width: 1100px;
  margin: 0 auto;
  padding: 56px 24px 96px;
  align-items: flex-start;
}
.admin-nav {
  position: sticky;
  top: 56px;
  flex: 0 0 220px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.admin-nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: var(--radius-tile);
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.92rem;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.admin-nav-item { border-radius: 999px; }
.admin-nav-item:hover { background: var(--bg-elev-1); color: var(--text); }
.admin-nav-item.is-active {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  font-weight: 600;
}
.admin-nav-item.is-active .admin-nav-icon { color: var(--accent); }
.admin-nav-icon { font-size: 20px; }
.admin-nav-label { flex: 1 1 auto; }
/* Unread-improvements badge on a nav tab (Self-improvement). Accent pill with a
   count; sits at the row's trailing edge on desktop, floats on the icon on mobile. */
.admin-nav-badge {
  flex: none; min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--accent); color: var(--on-accent);
  font-size: 11px; font-weight: 700; line-height: 1;
}
/* Transparent wrapper on desktop (items stay direct flex children of the vertical
   sidebar); becomes the horizontal scroller only in the mobile bottom-bar rule. */
.admin-nav-scroll { display: contents; }
.admin-content { flex: 1 1 auto; min-width: 0; }
/* Settings/Admin panels reuse .billing-body for spacing but are NOT a modal —
   drop the modal's `max-height: 70vh; overflow-y: auto` so the panel grows with its
   content and the page (#…-view) scrolls naturally, instead of clipping the list
   with dead space below. */
.admin-body { max-height: none; overflow: visible; }
.admin-panel-head { margin-bottom: 18px; }
.admin-panel-head .sched-headline { margin: 0 0 4px; }
.admin-panel-head .sched-subtitle { margin: 0; }

@media (max-width: 720px) {
  /* The sidebar becomes a bottom-anchored tab bar (icon + name per tab). It fills
     the width when there are few items (Settings, 3) and scrolls horizontally when
     there are many (Admin, 12). Content gets bottom padding so it isn't hidden
     behind the fixed bar. */
  /* Column layout: stretch content to full width (the base align-items:flex-start
     would otherwise shrink each panel to its content width). */
  .admin-layout { flex-direction: column; padding: 0 16px 88px; align-items: stretch; }
  .admin-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    z-index: 50;
    flex-basis: auto;
    width: 100%;
    padding: 6px 4px;
    padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px));
    /* Read as a distinct elevated bar (the old --surface-0 fallback was nearly the
       page bg, so the bar vanished and content looked hard-"cut off" against it). */
    background: var(--bg-elev);
    border-top: 1px solid var(--border);
    /* The inner .admin-nav-scroll does the horizontal scrolling; the nav itself must
       NOT clip, so its ::before fade can paint above the bar. */
    overflow: visible;
  }
  /* Fade above the bar so scrolling content dissolves into it instead of slicing flat
     at the top edge. Absolute + bottom:100% pins it directly above the fixed bar — no
     magic numbers — and the gradient ends on the bar's own color for a seamless join. */
  .admin-nav::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 28px;
    background: linear-gradient(to top, var(--bg-elev), transparent);
    pointer-events: none;
  }
  /* Right-edge fade hinting more tabs to scroll to — only when the bar actually
     overflows (the JS toggles .is-scrollable via a ResizeObserver), so the 3-tab
     Settings bar doesn't get a misleading fade. Fixes "couldn't find Pricing/Grants"
     (admin tabs 7-12 were scrolled off with no affordance). */
  .admin-nav.is-scrollable::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 24px;
    background: linear-gradient(to right, transparent, var(--bg-elev));
    pointer-events: none;
    z-index: 1;
  }
  .admin-nav-scroll {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 0;
    width: 100%;
    overflow-x: auto;
    scrollbar-width: none; /* hide the scrollbar; the bar still scrolls by touch */
  }
  .admin-nav-scroll::-webkit-scrollbar { display: none; }
  /* Gemini / Material-3 nav bar: the active indicator is a pill behind the ICON
     (not the whole tab — that read as a wide, short slab with dead space), with the
     label below. Tabs are evenly distributed at a comfortable height. */
  /* flex: 1 0 auto — grow to fill the bar when there are few tabs (Settings, 3), but
     never shrink below a comfortable min-width when there are many (Admin, 12), so the
     bar scrolls horizontally instead of cramming every tab to ~38px. */
  .admin-nav-item {
    flex: 1 0 auto;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 6px;
    width: auto;
    min-width: 64px;
    padding: 8px 6px 7px;
    border-radius: 0;
    background: transparent;
    text-align: center;
    font-size: 0.72rem;
    line-height: 1.15;
  }
  .admin-nav-item:hover { background: transparent; }
  .admin-nav-item.is-active { background: transparent; color: var(--text); font-weight: 400; }
  .admin-nav-icon {
    display: grid;
    place-items: center;
    width: 48px;
    height: 30px;
    border-radius: 999px;
    font-size: 21px;
    color: var(--text-dim);
    transition: background 0.15s var(--ease), color 0.15s var(--ease);
  }
  .admin-nav-item.is-active .admin-nav-icon {
    background: color-mix(in srgb, var(--accent) 22%, transparent);
    color: var(--accent);
  }
  .admin-nav-label { display: block; flex: 0 0 auto; white-space: nowrap; color: var(--text-faint); }
  .admin-nav-item.is-active .admin-nav-label { color: var(--text); font-weight: 600; }
}

/* Mobile fit for the rest of the account & settings screens (the nav above is
   handled separately). Without these, label/value rows, the admin data tables, the
   partner dashboard, and the billing grid overflow or crush at ~390px. */
@media (max-width: 720px) {
  /* Label+value rows (Profile, Team, Admin Sales, Connected agents): the label was
     flex:0 0 auto and the row never wrapped, so a long label or trailing action
     buttons pushed past the edge. Let it wrap and let the label shrink. */
  .info-row { flex-wrap: wrap; }
  .info-row-label { flex: 1 1 auto; min-width: 0; }
  .admin-row-actions, .profile-wallet-actions { margin-left: auto; }

  /* Admin data tables: give the table a real min-width so the .dt-wrap's
     overflow-x:auto actually scrolls horizontally instead of crushing 6 columns
     into ~358px. Tighten cell padding to claw back a little room. */
  .dt-table { min-width: 620px; }
  .dt-table th, .dt-table td { padding: 10px 12px; }

  /* Billing modal: stack the 2-up credit/pass purchase buttons to a single column
     so "50 USDT → 100,000"-style labels don't wrap raggedly at ~150px. */
  .billing-grid { grid-template-columns: 1fr; }

  /* Partner / Sales (and Earn) dashboard: 4 stat tiles wrap to 2×2 instead of being
     squeezed to ~76px each; ledger rows drop their fixed date/amount min-widths so
     the main column keeps room (and can wrap). */
  .earn-dash { flex-wrap: wrap; }
  .earn-tile { flex: 1 1 calc(50% - 6px); }
  .partner-row { flex-wrap: wrap; }
  .partner-row-main { min-width: 0; }
  .partner-row-date,
  .partner-amount { min-width: 0; }
}

/* Mobile nav-bar headers: across every screen the headline becomes a left-aligned
   title in a ~56px top bar sitting just to the right of the floating hamburger
   (.nav-fab, fixed top:12, 48px, z-40), vertically centered with it; the subtitle
   is hidden and content scrolls UNDER the pinned bar. The bar stays under the
   hamburger on the z-axis (z-30) so the menu stays tappable, and paints the page
   bg so scrolling content disappears cleanly. Covers .sched-intro (Profile / Team /
   Partner / Schedules / Skills / Store / XP) and .admin-panel-head (Admin / Settings).
   Earnings (.earnings-topbar) and Tasks (.home-greeting) already do this themselves. */
@media (max-width: 720px) {
  /* Center the hamburger in the 56px bar so it lines up with the title. */
  .nav-fab { top: calc(env(safe-area-inset-top) + 4px); }
  /* Kill the desktop top padding on mobile (.layout > … specificity beats the base
     `.sched-center { padding: 72px … }` defined later in the file). */
  .layout .sched-center { padding-top: 0; }

  .admin-panel-head,
  .sched-intro {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    min-height: calc(56px + env(safe-area-inset-top));
    padding: env(safe-area-inset-top) 16px 0 68px; /* left pad clears the hamburger */
    background: var(--bg);
  }
  /* Break each header out of its container's side padding so the bar spans full
     width and the title lands ~60px from the viewport edge (tight to the
     hamburger), matching the Earnings/Tasks bars. */
  .sched-intro { margin: 0 -24px 8px; }       /* .sched-center has 24px side padding */
  .admin-panel-head { margin: 0 -16px 8px; }  /* .admin-layout has 16px side padding */
  .admin-panel-head .sched-headline,
  .sched-intro .sched-headline {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: -0.2px;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .admin-panel-head .sched-subtitle,
  .sched-intro .sched-subtitle { display: none; }
}

/* Breathing room above an action button that follows a field group, so the
   Save/Grant/Create button never sits flush against the input above it. */
.admin-body .admin-save-row { margin-top: 16px; }

/* Overview stat tiles. */
.admin-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 10px;
  margin-bottom: 24px;
}
.admin-stat {
  padding: 18px 20px;
  border: none;
  border-radius: var(--radius);
  background: var(--bg-elev-1);
  transition: background 0.12s var(--ease);
}
.admin-stat:hover { background: var(--bg-elev-2); }
.admin-stat-value { font-size: 1.7rem; font-weight: 650; letter-spacing: -0.5px; color: var(--text); font-variant-numeric: tabular-nums; }
.admin-stat-label { margin-top: 4px; font-size: 0.8rem; color: var(--text-faint); }
/* Money-safety anomaly badge: a non-zero count is a warning the owner should notice at a glance. */
.admin-stat-alert { background: color-mix(in srgb, var(--danger, #e5484d) 14%, var(--bg-elev-1)); box-shadow: inset 0 0 0 1px var(--danger, #e5484d); }
.admin-stat-alert .admin-stat-value { color: var(--danger, #e5484d); }
.admin-inline-action { margin-bottom: 14px; max-width: 280px; }

/* CRM sales dashboard: section sub-heads, tier bars, daily-GMV sparkline. */
#admin-view .admin-subhead {
  margin: 22px 0 8px;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.crm-tier-row { display: flex; align-items: center; gap: 12px; padding: 9px 0; }
.crm-tier-name { flex: 0 0 96px; font-size: 13.5px; color: var(--text-dim); }
.crm-tier-count { flex: 0 0 auto; min-width: 36px; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text); }
.crm-bar { flex: 1 1 auto; height: 8px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; }
.crm-bar-fill { height: 100%; border-radius: 999px; background: var(--grad); min-width: 2px; transition: width 0.3s var(--ease); }
/* Daily-GMV bar chart + time-frame selector. */
.crm-chart-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.crm-range { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.crm-range .buy-chip { min-width: 0; min-height: 36px; padding: 6px 12px; font-size: 12.5px; }
.crm-range-custom { margin-top: 8px; }
.crm-range-custom.is-collapsed { display: none; }
.crm-date {
  background: var(--bg-elev-1);
  color: var(--text);
  border: none;
  border-radius: 999px;
  padding: 6px 12px;
  font: inherit;
  font-size: 12.5px;
  outline: none;
  color-scheme: light dark; /* native picker adapts to the active theme */
}
.crm-date:focus { background: var(--bg-elev-2); }
.crm-date-sep { color: var(--text-faint); }
/* Inline label + control, e.g. the drill-down sheet's Agent picker — lighter-weight
   than a full admin-subhead eyebrow since it sits directly beside its control. */
.crm-field-row { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.crm-field-label { flex: 0 0 auto; font-size: 0.8rem; color: var(--text-faint); }
.crm-field-row .dt-filter { flex: 1 1 auto; }
.crm-chart-wrap {
  position: relative;
  margin-top: 8px;
  padding: 14px 14px 10px;
  border-radius: var(--radius);
  background: var(--bg-elev-1);
}
.crm-chart {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 150px;
}
/* Y-axis gridlines (max down to 0), appended as the last child of .crm-chart so they sit
   ABOVE the bars — each line's value label carries its own background chip, so a tall bar
   passing behind never blots out the scale. Replaces a single top-right max figure with the
   full intermediate scale. `pointer-events: none` keeps it out of the tooltip's hit-testing
   (see chart.ui.ts's attachChartTooltip, which reads the column beneath via elementFromPoint). */
.crm-chart-grid {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
}
.crm-chart-gridline { position: relative; border-top: 1px dashed color-mix(in srgb, var(--border-strong) 70%, transparent); }
.crm-chart-gridline-label {
  position: absolute;
  right: 0;
  top: 0;
  transform: translateY(-50%);
  background: var(--bg-elev-1);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.crm-chart-col {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  cursor: pointer;
}
.crm-chart-bar {
  width: 100%;
  border-radius: 3px 3px 0 0;
  background: var(--grad);
  min-height: 2px;
  opacity: 0.88;
  transition: opacity 0.12s var(--ease);
}
.crm-chart-col:hover .crm-chart-bar { opacity: 1; }
/* Stacked cost chart (admin-models.ui.ts): segments fill a column bottom-up. */
.crm-chart-stack { display: flex; flex-direction: column-reverse; width: 100%; height: 100%; }
.crm-chart-seg { width: 100%; min-height: 0; opacity: 0.9; }
.crm-chart-col:hover .crm-chart-seg { opacity: 1; }

/* --- Admin model routing chain + catalog picker + cost legend (admin-models.ui.ts) --- */
.mdl-chain { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }
.mdl-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-elev-2); }
.mdl-rank { flex: 0 0 auto; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; background: var(--bg-elev-2); color: var(--text-dim); font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.mdl-main { flex: 1 1 auto; min-width: 0; }
.mdl-name { font-weight: 600; color: var(--text); overflow-wrap: anywhere; line-height: 1.3; }
.mdl-meta { font-size: 12px; color: var(--text-dim); display: flex; gap: 10px; flex-wrap: wrap; margin-top: 2px; }
.mdl-actions { flex: 0 0 auto; display: inline-flex; gap: 2px; }
.mdl-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: none; background: transparent; color: var(--text-dim); border-radius: 8px; cursor: pointer; }
.mdl-iconbtn:hover:not(:disabled) { background: var(--bg-elev-3); color: var(--text); }
.mdl-iconbtn:disabled { opacity: 0.3; cursor: default; }
.mdl-iconbtn.is-danger:hover { color: var(--err); }
.mdl-iconbtn .material-symbols-rounded { font-size: 20px; }
.mdl-tag { flex: 0 0 auto; font-size: 11px; color: var(--text-faint); }
.mdl-picker { border: 1px solid var(--border); border-radius: 12px; margin-top: 8px; overflow: hidden; background: var(--bg-elev-2); }
.mdl-pick-search { width: 100%; box-sizing: border-box; border: none; border-bottom: 1px solid var(--border); background: transparent; color: var(--text); padding: 10px 12px; font: inherit; font-size: 14px; }
.mdl-pick-search:focus { outline: none; }
.mdl-pick-list { max-height: 300px; overflow: auto; }
.mdl-pick-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
.mdl-pick-row:last-child { border-bottom: none; }
.mdl-pick-row:hover { background: var(--bg-elev-2); }
.mdl-score { flex: 0 0 auto; min-width: 34px; text-align: center; padding: 2px 6px; border-radius: 6px; font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok); }
.mdl-score.is-none { background: var(--bg-elev-2); color: var(--text-faint); }
.mdl-legend { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 10px; font-size: 12px; }
.mdl-legend-item { display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); }
.mdl-swatch { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }
.mdl-recon { display: flex; gap: 18px; flex-wrap: wrap; margin: 8px 0 2px; font-size: 13px; color: var(--text-dim); }
.mdl-recon strong { color: var(--text); font-variant-numeric: tabular-nums; }
.crm-chart-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
/* Hover (mouse) / tap (touch) value readout — see chart.ui.ts's attachChartTooltip.
   Positioned relative to .crm-chart-wrap (its offset parent), just above the pointed-at
   column; hidden until a pointer lands on a column. */
.crm-chart-tooltip {
  position: absolute;
  z-index: 2;
  transform: translateX(-50%);
  white-space: pre-line;
  text-align: center;
  background: var(--bg-elev-2);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: 11.5px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--shadow-md);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.1s var(--ease);
}
.crm-chart-tooltip.is-visible { opacity: 1; visibility: visible; }

/* Sales / admin grouped lists as Gemini tonal cards (GMV overview, partners,
   payouts): a single rounded surface with hairline row dividers, no outer stroke. */
#admin-view .admin-card,
#admin-view .admin-list {
  background: var(--bg-elev-1);
  border-radius: var(--radius);
  padding: 2px 16px;
  margin-top: 10px;
}
#admin-view .admin-card .info-row,
#admin-view .admin-list .info-row { padding: 13px 0; }
#admin-view .admin-card .info-row:last-child,
#admin-view .admin-list .info-row:last-child { border-bottom: none; }
/* A muted empty-state line ("No payouts yet.") shouldn't look like a card. */
#admin-view .admin-list > .admin-note:only-child { padding: 14px 0; }

/* Partner/payout row action buttons → compact tonal pills (not full-width). */
#admin-view .admin-row-actions .billing-buy {
  width: auto;
  padding: 7px 16px;
  background: var(--bg-elev-2);
  border: none;
  font-size: 0.82rem;
}
#admin-view .admin-row-actions .billing-buy:hover:not(:disabled) { background: var(--bg-elev-3); }

/* Mascot test panel: centered WebGL preview + expression-trigger chips. */
.admin-mascot-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 32px 16px 24px;
  background: var(--bg-elev-1);
  border-radius: var(--radius);
  margin-top: 10px;
}
.admin-mascot-stage { width: 220px; height: 220px; }
.admin-mascot-canvas { display: block; width: 100%; height: 100%; cursor: pointer; }
.admin-mascot-chips { justify-content: center; }

/* Primary CTAs in admin (Save, Enroll, Grant, Create, Run rebate) → spark gradient. */
#admin-view .billing-grid-1 > .billing-buy {
  background: var(--grad);
  color: var(--on-accent);
  border: none;
  font-weight: 600;
  padding: 13px 16px;
}
#admin-view .billing-grid-1 > .billing-buy:hover:not(:disabled) { background: var(--grad); filter: brightness(1.06); }
#admin-view .billing-grid-1 > .billing-buy:disabled { opacity: 0.5; }

/* Admin data table (search / sort / filter / paginate) — Gemini: borderless tonal
   fills, rounded pills, elevation via fill not stroke. */
.dt-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.dt-search { display: flex; align-items: center; gap: 8px; flex: 1 1 240px; min-width: 200px;
  padding: 9px 14px; border: none; border-radius: 999px;
  background: var(--bg-elev-1); }
.dt-search .material-symbols-rounded { font-size: 19px; color: var(--text-faint); }
.dt-search-input { flex: 1 1 auto; border: none; background: transparent; color: var(--text); font: inherit; outline: none; }
.dt-filter {
  padding: 9px 14px; border: none; border-radius: 999px;
  background: var(--bg-elev-1); color: var(--text); font: inherit; font-size: 0.9rem; cursor: pointer;
  transition: background 0.12s var(--ease);
}
.dt-filter:hover { background: var(--bg-elev-2); }
.dt-wrap { overflow-x: auto; border: none; border-radius: var(--radius); background: var(--bg-elev-1); }
.dt-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.dt-table th, .dt-table td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.dt-table tbody tr:last-child td { border-bottom: none; }
.dt-table thead th { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); font-weight: 600; white-space: nowrap; }
.dt-table tbody tr { transition: background 0.1s var(--ease); }
.dt-table tbody tr:hover { background: var(--bg-elev-2); }
.dt-table td.is-num, .dt-table th.is-num { text-align: right; font-variant-numeric: tabular-nums; }
/* Trial-monitor: flag likely sybil farms (many trials, zero conversions). */
.admin-trial-tbl tbody tr.is-suspect td:first-child { color: var(--danger-text); font-weight: 600; }
.admin-trial-tbl tbody tr.is-suspect { background: var(--danger-bg); }
/* Cap a column's width (e.g. a long email) so it doesn't crowd out the more
   important numeric columns; full value is still available via title on hover. */
.dt-col-narrow { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dt-th-btn { display: inline-flex; align-items: center; gap: 4px; background: none; border: none; color: inherit; font: inherit;
  text-transform: inherit; letter-spacing: inherit; cursor: pointer; padding: 0; }
.dt-th-btn:hover { color: var(--text); }
.dt-table th.is-sorted { color: var(--text); }
.dt-sort-ind { font-size: 0.8rem; color: var(--accent); }
.dt-msg { text-align: center; padding: 28px 14px; color: var(--text-faint); }
.dt-actions-col { width: 1%; }
.dt-actions { text-align: right; white-space: nowrap; }
.dt-row-actions { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.dt-action {
  display: inline-flex; align-items: center; justify-content: center; min-height: 36px;
  padding: 6px 14px; border: none; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text); font: inherit; font-size: 0.82rem; cursor: pointer;
  transition: background 0.12s var(--ease);
}
.dt-action:hover:not(:disabled) { background: var(--bg-elev-3); }
.dt-action:disabled { opacity: 0.5; cursor: default; }
.dt-action-danger { color: var(--err); }
.dt-action-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--err) 16%, transparent); }
.dt-link { color: var(--accent); text-decoration: none; }
.dt-link:hover { text-decoration: underline; }
.dt-mono { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 0.82rem; color: var(--text-dim); }
/* Copyable code token (e.g. a user's invite code): tonal pill, fill-based, accent-tint confirm. */
.dt-code {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.8rem; letter-spacing: 0.02em;
  padding: 3px 10px; border: none; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text-dim); cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.dt-code:hover { background: var(--bg-elev-3); color: var(--text); }
.dt-code.is-copied { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.dt-badges { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.dt-badge { padding: 3px 10px; border-radius: 999px; font-size: 0.76rem; font-weight: 500; }
.dt-badge-good { background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok); }
.dt-badge-bad { background: color-mix(in srgb, var(--err) 18%, transparent); color: var(--err); }
.dt-badge-info { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
.dt-badge-muted { background: var(--bg-elev-3); color: var(--text-dim); }
.dt-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; }
.dt-summary { font-size: 0.85rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.dt-page-btns { display: flex; gap: 6px; }
.dt-page-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  border: none; border-radius: 50%; background: var(--bg-elev-1);
  color: var(--text); cursor: pointer; transition: background 0.12s var(--ease);
}
.dt-page-btn:hover:not(:disabled) { background: var(--bg-elev-2); }
.dt-page-btn:disabled { opacity: 0.35; cursor: default; }
.dt-page-btn .material-symbols-rounded { font-size: 18px; }

/* Coupon create form — labeled fields, each with an inline "?" help toggle that expands a
   detailed explanation card. Gemini-style: calm tonal surfaces, generous spacing, soft
   rounded geometry, restrained accents. */
.coupon-form { gap: 16px; }
.coupon-field { display: flex; flex-direction: column; gap: 6px; }
.coupon-field-head { display: flex; align-items: center; gap: 6px; min-height: 18px; }
.coupon-field-label { margin-top: 0; font-weight: 500; letter-spacing: 0.1px; }
.coupon-info {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 1px; flex: 0 0 auto;
  border: none; border-radius: 50%; background: transparent;
  color: var(--text-faint); cursor: pointer; line-height: 1;
  font-size: 16px; /* Material symbol glyph size (overrides the 20px base) */
  transition: color 0.15s ease, background 0.15s ease;
}
.coupon-info:hover { color: var(--text); background: var(--bg-elev-2); }
.coupon-info:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.coupon-info.is-active { color: var(--accent); background: var(--accent-dim); }
.coupon-help {
  display: none;
  margin-top: 2px;
  font-size: 12px; line-height: 1.55; color: var(--text-dim);
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.coupon-help.is-open { display: block; animation: coupon-help-in 0.16s ease; }
@keyframes coupon-help-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }

/* Coupons: multi-select table + bulk action bar */
.coupon-table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 0.85rem; }
.coupon-table th {
  text-align: left; padding: 7px 8px; white-space: nowrap;
  color: var(--text-faint); font-weight: 600; font-size: 0.74rem; letter-spacing: 0.02em;
  border-bottom: 1px solid var(--border);
}
.coupon-table td { padding: 9px 8px; border-bottom: 1px solid var(--border); color: var(--text); vertical-align: middle; }
.coupon-table tbody tr:last-child td { border-bottom: none; }
.coupon-table tbody tr:hover td { background: var(--bg-elev-2); }
.coupon-th-check { width: 1%; white-space: nowrap; }
.coupon-th-check input { cursor: pointer; vertical-align: middle; }
.coupon-code { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-weight: 600; letter-spacing: 0.04em; }
.coupon-row-actions { width: 1%; text-align: right; }
.coupon-copy {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; cursor: pointer; padding: 3px; border-radius: 6px;
  color: var(--text-dim); font-size: 18px; line-height: 1;
}
.coupon-copy:hover { color: var(--text); background: var(--bg-elev-2); }
.coupon-bulk-bar { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.coupon-bulk-count { margin-right: auto; color: var(--text-dim); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.billing-buy.is-compact { width: auto; padding: 6px 14px; font-size: 0.85rem; }
.billing-buy.is-danger { background: var(--danger); color: var(--on-accent); }
/* Re-assert the red on hover: the base .billing-buy:hover rule is more specific and
   would otherwise repaint it neutral gray exactly as the user goes to click. */
.billing-buy.is-danger:hover:not(:disabled) { background: var(--danger); filter: brightness(1.08); }

/* Admin sales section helpers. */
.admin-list { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.admin-row-actions { display: flex; gap: 6px; margin-left: 12px; }
.admin-note { color: var(--text-faint); font-size: 0.85rem; padding: 8px 2px; }
.admin-warn {
  padding: 12px 14px; margin-bottom: 10px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--warn) 16%, transparent); border: none;
  color: var(--text); font-size: 0.85rem;
}

.sched-center {
  max-width: 760px;
  margin: 0 auto;
  padding: 72px 24px 96px;
}

/* Intro */
.sched-headline {
  font-family: "Google Sans Flex", "Google Sans Text", Roboto, system-ui, sans-serif;
  font-size: 32px;
  font-weight: 400;
  letter-spacing: -0.2px;
  margin: 0 0 10px;
}
.sched-subtitle {
  font-size: 14.5px;
  color: var(--text-dim);
  line-height: 1.55;
  margin: 0;
  max-width: 620px;
}

/* Create actions */
.sched-cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin: 26px 0 8px; }
.sched-cta {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: 999px;
  border: none; background: var(--bg-elev-2);
  color: var(--text); font: inherit; font-size: 14px; font-weight: 500; cursor: pointer;
  transition: background var(--ease) 150ms, filter var(--ease) 150ms;
}
.sched-cta:hover { background: var(--bg-elev-3); }
.sched-cta .material-symbols-rounded { font-size: 20px; }
.sched-cta-primary { background: var(--grad); color: var(--on-accent); font-weight: 600; }
.sched-cta-primary:hover { filter: brightness(1.08); background: var(--grad); }

/* Empty state */
.sched-empty { padding: 48px 8px; text-align: center; color: var(--text-dim); }
.sched-empty p { margin: 4px 0; }
.sched-empty-sub { font-size: 13px; color: var(--text-faint); }

/* Grouped list of cards */
.sched-group-label {
  font-size: 13px; font-weight: 600; color: var(--text);
  margin: 30px 0 12px;
}
.sched-cards { display: flex; flex-direction: column; gap: 12px; }
.sched-card {
  position: relative;
  background: var(--bg-elev-1);
  border-radius: var(--radius);
  padding: 18px 20px;
  cursor: pointer;
  transition: background var(--ease) 150ms;
}
.sched-card:hover { background: var(--bg-elev-2); }
.sched-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Keep the title/meta clear of the absolutely-positioned kebab. */
.sched-card-info { padding-right: 40px; }
.sched-card-title { font-size: 15px; font-weight: 500; color: var(--text); }
.sched-card-when { font-size: 13.5px; color: var(--text-dim); margin-top: 4px; }
.sched-card-last { font-size: 13px; color: var(--text-faint); margin-top: 2px; }
.sched-card-last.is-err { color: var(--danger); }

/* Overflow ("⋯") button + popover menu — Run / Edit / Pause / Delete (Gemini style) */
.sched-kebab {
  position: absolute; top: 14px; right: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 999px;
  background: transparent; border: none; color: var(--text-dim); cursor: pointer;
  transition: background var(--ease) 120ms, color var(--ease) 120ms;
}
.sched-kebab:hover { background: var(--bg-elev-3); color: var(--text); }
.sched-kebab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sched-kebab .material-symbols-rounded { font-size: 20px; }

.sched-menu {
  position: fixed; z-index: 1200; min-width: 188px;
  background: var(--bg-elev-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 6px; box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
}
.sched-menu-item {
  display: flex; align-items: center; gap: 12px;
  background: transparent; border: none; color: var(--text);
  font-size: 14px; text-align: left; padding: 9px 12px; border-radius: var(--radius-tile); cursor: pointer;
  transition: background var(--ease) 120ms;
}
.sched-menu-item:hover { background: var(--bg-elev-3); }
.sched-menu-item .material-symbols-rounded { font-size: 20px; color: var(--text-dim); }
.sched-menu-item.is-danger { color: var(--danger); }
.sched-menu-item.is-danger .material-symbols-rounded { color: var(--danger); }
.sched-menu-sep { height: 1px; background: var(--border); margin: 6px 6px; }

/* Create / edit form */
.sched-form { display: flex; flex-direction: column; gap: 12px; margin-top: 18px; }
.sched-form-head { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
.sched-field { display: flex; flex-direction: column; gap: 5px; }
.sched-field[hidden] { display: none; }
.sched-objective { min-height: 78px; font-family: inherit; font-size: 14px; }
/* Tonal select — drop native OS chrome, draw a Gemini chevron instead. */
.sched-select {
  appearance: none; -webkit-appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%239aa0a6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 18px;
}
.sched-select::-ms-expand { display: none; }

/* Toast for run-now / errors — fixed to the viewport since there's no modal panel.
   z-index above EVERYTHING, including the confetti canvas (10000) and welcome-overlay
   (9999) — verified live that a gacha-win toast's ~3.2s window overlaps the confetti
   burst's ~5s window for most realistic dismiss timings, and confetti (being decorative,
   full-screen, z-index 10000) was rendering ON TOP of the toast for that whole overlap.
   bottom offset adds env(safe-area-inset-bottom) — every OTHER bottom-pinned element in
   this app (composer, home header, nav) already accounts for it; this one didn't, so on a
   real phone with the browser's bottom toolbar/home-indicator showing, the toast could be
   rendering fine (confirmed via headless testing at every viewport) while sitting fully
   behind that OS chrome — invisible despite being "there." */
.sched-toast {
  position: fixed; left: 50%; bottom: calc(28px + env(safe-area-inset-bottom, 0px)); transform: translate(-50%, 6px);
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 16px; border-radius: 999px;
  background: var(--bg-elev-3); color: var(--text);
  font-size: 13px; opacity: 0; pointer-events: none; z-index: 20000;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.32);
  transition: opacity var(--ease) 160ms, transform var(--ease) 160ms;
}
.sched-toast.is-shown { opacity: 1; transform: translate(-50%, 0); }
.sched-toast.is-error { background: var(--danger-bg); color: var(--danger); }
/* Optional leading icon (see flash()'s iconName param) — same size/role as the top banner's
   info/warn/error glyph. */
.sched-toast .material-symbols-rounded { font-size: 16px; flex: none; color: var(--text-dim); }
.sched-toast.is-error .material-symbols-rounded { color: var(--danger); }

/* ── Skills screen (standalone view; Marketplace | Installed) ────────────────
   Reuses .sched-center/.sched-intro/.sched-headline/.sched-subtitle/.sched-empty/
   .sched-toast and the .sched-menu popover. Gemini-style: pill controls, tonal
   surfaces, an expandable search mirroring the recent-tasks search. */

/* Controls row: sort chip on the left, expandable search pinned right. */
.skills-controls { display: flex; align-items: center; gap: 10px; min-height: 40px; margin-bottom: 16px; }
.skills-sort-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-elev-1); border: none; cursor: pointer;
  color: var(--text); font-size: 13.5px; font-weight: 500;
  height: 40px; padding: 0 8px 0 16px; border-radius: 999px;
  transition: background var(--ease) 120ms;
}
.skills-sort-chip:hover { background: var(--bg-elev-2); }
.skills-chip-label { color: var(--text-faint); font-weight: 400; }
.skills-chip-arrow { color: var(--text-dim); font-size: 13px; }
.skills-sort-chip .material-symbols-rounded { font-size: 20px; color: var(--text-dim); margin-left: -2px; }

.skills-sort-menu { min-width: 210px; }
.skills-menu-check { width: 20px; display: inline-flex; justify-content: center; flex: none; }
.skills-menu-check .material-symbols-rounded { font-size: 18px; color: var(--accent); }
.skills-sort-menu .sched-menu-item { gap: 8px; }

/* Expandable search — same behavior as the recent-tasks search. */
.skills-search-wrap {
  display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
  width: 260px; max-width: 60%; height: 40px; margin-left: auto; padding: 0 14px;
  overflow: hidden; background: var(--bg-elev-1); border: none; border-radius: 999px;
  transition: width 0.22s var(--ease), padding 0.22s var(--ease), background 0.15s var(--ease);
}
.skills-search-wrap:focus-within { background: var(--bg-elev-2); }
.skills-search-ic {
  flex: 0 0 auto; display: grid; place-items: center;
  background: transparent; border: none; padding: 0; color: var(--text-faint); cursor: pointer;
}
.skills-search-ic .material-symbols-rounded { font-size: 19px; }
.skills-search {
  flex: 1 1 auto; min-width: 0; background: transparent; border: none; outline: none;
  color: var(--text); font: inherit; font-size: 13.5px;
}
.skills-search::placeholder { color: var(--text-faint); }
.skills-search-wrap.is-collapsed { width: 40px; padding: 0; gap: 0; background: transparent; }
.skills-search-wrap.is-collapsed .skills-search-ic {
  width: 40px; height: 40px; border-radius: 50%; color: var(--text-dim);
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.skills-search-wrap.is-collapsed .skills-search-ic .material-symbols-rounded { font-size: 22px; }
.skills-search-wrap.is-collapsed .skills-search-ic:hover { background: var(--bg-elev-2); color: var(--text); }
.skills-search-wrap.is-collapsed .skills-search { width: 0; flex: 0 0 0; opacity: 0; pointer-events: none; }

/* Cards */
.skills-cards { display: flex; flex-direction: column; gap: 10px; }
.skills-card {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--bg-elev-1); border-radius: var(--radius-lg); padding: 16px 20px; cursor: pointer;
  transition: background var(--ease) 150ms;
}
.skills-card:hover { background: var(--bg-elev-2); }
.skills-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.skills-card-info { min-width: 0; flex: 1; }
.skills-card-title { font-size: 15px; font-weight: 600; color: var(--text); }
/* "Update available" chip beside an installed skill's name — accent-tinted so a
   stale skill reads at a glance without shouting. */
.skills-badge-update {
  display: inline-block; margin-left: 8px; padding: 2px 8px; vertical-align: 1px;
  border-radius: 999px; font-size: 11px; font-weight: 600;
  color: var(--accent); background: var(--accent-dim);
}
.skills-card-desc {
  font-size: 13px; color: var(--text-dim); margin-top: 4px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.skills-stats { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 9px; font-size: 12px; color: var(--text-faint); }
.skills-stats > span { position: relative; }
.skills-stats > span + span::before {
  content: "·"; position: absolute; left: -8px; color: var(--text-faint);
}
.skills-rate { display: inline-flex; align-items: center; gap: 3px; }
.skills-rate .material-symbols-rounded { font-size: 14px; }

.skills-action {
  flex: none; align-self: center; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; min-height: 44px;
  background: var(--accent); color: var(--on-accent); border: none;
  border-radius: 999px; padding: 9px 20px; font-size: 13px; font-weight: 600; cursor: pointer;
  transition: filter var(--ease) 120ms, background var(--ease) 120ms, color var(--ease) 120ms;
}
.skills-action:hover { filter: brightness(1.08); }
.skills-action:disabled { cursor: default; }
.skills-action.is-installed {
  background: transparent; color: var(--text-faint);
  box-shadow: inset 0 0 0 1px var(--border); filter: none;
}
.skills-action.is-danger {
  background: transparent; color: var(--text-dim); box-shadow: inset 0 0 0 1px var(--border); filter: none;
}
.skills-action.is-danger:hover { color: var(--danger); box-shadow: inset 0 0 0 1px var(--danger); background: var(--danger-bg); }

/* Skill detail modal — a centered popup. Reuses the .settings-overlay backdrop,
   but centers with flexbox (grid place-items can mis-place a single auto-sized
   child). A column layout caps the height and lets the description scroll while
   the header, KPIs, and action stay pinned. */
.skills-modal-overlay { display: flex; align-items: center; justify-content: center; }
.skills-sheet {
  display: flex;
  flex-direction: column;
  width: min(460px, 100%);
  max-height: 86dvh;
  background: var(--bg-elev);
  border-radius: var(--radius-lg);
  padding: 24px;
  box-shadow: var(--shadow-lg);
  transform: scale(0.96);
  opacity: 0;
  transition: transform 0.18s var(--ease), opacity 0.18s var(--ease);
}
.skills-sheet.is-open { transform: scale(1); opacity: 1; }
.skills-sheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.skills-sheet-title { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.25; }
.skills-sheet-sub { font-size: 13px; color: var(--text-dim); margin-top: 6px; flex: none; }
.skills-kpis { display: flex; gap: 10px; margin: 20px 0; flex: none; }
.skills-kpis[hidden] { display: none; }
.skills-kpi { flex: 1; background: var(--bg-elev-1); border-radius: var(--radius); padding: 14px 10px; text-align: center; }
.skills-kpi-value { font-size: 18px; font-weight: 600; color: var(--text); }
.skills-kpi-label { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.5px; margin-top: 5px; }
/* The description takes the remaining height and scrolls when long. */
.skills-sheet-desc {
  flex: 1 1 auto; min-height: 64px; overflow-y: auto;
  font-size: 14px; color: var(--text); line-height: 1.6; margin: 4px 0 20px;
  padding-right: 4px;
}
.skills-sheet-actions { display: flex; flex: none; }
.skills-sheet-action { width: 100%; text-align: center; padding: 12px; font-size: 14px; }

/* ── Earn screen (standalone view) ──────────────────────────────────────────
   Reuses .sched-center/.sched-intro/.sched-headline/.sched-subtitle/.sched-region/
   .sched-empty/.sched-toast and the .tabs-primary underline tabs. */
.earn-jackpot {
  margin: 18px 0 14px;
  padding: 22px 24px;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 26%, var(--bg-elev-1)), var(--bg-elev-1));
  border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.earn-jackpot-head { display: flex; align-items: center; justify-content: space-between; }
.earn-jackpot-label { font-size: 12.5px; font-weight: 600; letter-spacing: 0.6px; text-transform: uppercase; color: var(--text-dim); }
.earn-jackpot-edit { background: transparent; border: none; color: var(--text-dim); cursor: pointer; padding: 4px; border-radius: 999px; }
.earn-jackpot-edit:hover { color: var(--text); background: var(--bg-elev-2); }
.earn-jackpot-edit .material-symbols-rounded { font-size: 18px; }
.earn-jackpot-value { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; }
.earn-jackpot-xp { font-size: 40px; font-weight: 700; line-height: 1; color: var(--text); }
.earn-jackpot-unit { font-size: 16px; font-weight: 600; color: var(--accent); }
.earn-jackpot-editor { display: flex; flex-direction: column; gap: 6px; }

.earn-dash { display: flex; gap: 12px; margin-bottom: 6px; }
.earn-tile { flex: 1; background: var(--bg-elev-1); border-radius: var(--radius); padding: 16px; text-align: center; }
.earn-tile-value { font-size: 22px; font-weight: 700; color: var(--text); }
.earn-tile-label { font-size: 12px; color: var(--text-faint); margin-top: 4px; }

.earn-bounties { display: flex; flex-direction: column; gap: 12px; }
.earn-new { align-self: flex-start; }
.earn-bounty-list { display: flex; flex-direction: column; gap: 10px; }
.earn-bounty {
  display: flex; align-items: center; gap: 14px;
  background: var(--bg-elev-1); border-radius: var(--radius); padding: 14px 16px;
}
.earn-bounty-icon {
  flex: none; width: 46px; height: 46px; border-radius: var(--radius-sm); overflow: hidden;
  display: grid; place-items: center; background: var(--bg-elev-2); color: var(--accent);
}
.earn-bounty-icon img { width: 100%; height: 100%; object-fit: cover; }
.earn-bounty-icon .material-symbols-rounded { font-size: 26px; }
.earn-bounty-meta { min-width: 0; flex: 1; }
.earn-bounty-titlerow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.earn-bounty-name { font-size: 15px; font-weight: 600; color: var(--text); }
.earn-bounty-reward {
  font-size: 11.5px; font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent); padding: 2px 8px; border-radius: 999px;
}
.earn-bounty-desc {
  font-size: 13px; color: var(--text-dim); margin-top: 3px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.earn-bounty-sub { font-size: 12px; color: var(--text-faint); margin-top: 4px; }
.earn-bounty-actions { flex: none; display: flex; align-items: center; gap: 6px; }
.earn-icon-btn {
  width: 40px; height: 40px; border-radius: 999px; flex: none; cursor: pointer;
  background: transparent; border: none; color: var(--text-dim);
  display: inline-grid; place-items: center; transition: background var(--ease) 120ms, color var(--ease) 120ms;
}
.earn-icon-btn:hover { background: var(--bg-elev-2); color: var(--text); }
.earn-icon-btn .material-symbols-rounded { font-size: 19px; }
.earn-icon-btn.is-danger:hover { color: var(--danger); background: var(--danger-bg); }
.skills-action.is-ghost { background: transparent; color: var(--text-faint); box-shadow: inset 0 0 0 1px var(--border); filter: none; }

.earn-ranks { display: flex; flex-direction: column; gap: 22px; }
.earn-rank-title { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin-bottom: 8px; }
.earn-rank-list { display: flex; flex-direction: column; gap: 4px; }
.earn-rank-row {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 14px; border-radius: var(--radius-sm); background: var(--bg-elev-1);
}
.earn-rank-row.is-me { background: color-mix(in srgb, var(--accent) 14%, var(--bg-elev-1)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent); }
.earn-rank-pos { font-size: 13px; font-weight: 700; color: var(--text-dim); width: 40px; flex: none; }
.earn-rank-name { flex: 1; min-width: 0; font-size: 14px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.earn-rank-xp { font-size: 13px; font-weight: 600; color: var(--text-dim); flex: none; }

/* Congrats notif — slides up from the bottom when a reward lands. */
.earn-congrats {
  position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 140%);
  z-index: 1300; display: flex; align-items: center; gap: 14px;
  background: var(--bg-elev-2); border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-lg); padding: 14px 18px; box-shadow: var(--shadow-lg);
  max-width: min(440px, calc(100vw - 32px)); transition: transform 0.34s var(--ease);
}
.earn-congrats.is-shown { transform: translate(-50%, 0); }
.earn-congrats-emoji { font-size: 28px; line-height: 1; color: var(--accent); }
.earn-congrats-title { font-size: 15px; font-weight: 700; color: var(--text); }
.earn-congrats-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.earn-congrats-view {
  margin-left: 6px; flex: none; background: var(--accent); color: var(--on-accent); border: none;
  border-radius: 999px; padding: 8px 14px; font-size: 13px; font-weight: 600; cursor: pointer;
}

/* Earn: season end text + Ranks sub-tabs (Agents | Team) */
.earn-jackpot-ends { font-size: 12px; color: var(--text-dim); }
.earn-subtabs { display: flex; gap: 8px; margin-bottom: 16px; }
.earn-subtab {
  display: inline-flex; align-items: center; justify-content: center; min-height: 40px;
  background: var(--bg-elev-1); border: none; cursor: pointer;
  color: var(--text-dim); font-size: 13px; font-weight: 500;
  padding: 8px 16px; border-radius: 999px;
  transition: color var(--ease) 120ms, background var(--ease) 120ms;
}
.earn-subtab:hover { color: var(--text); }
.earn-subtab.is-active { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--bg-elev-1)); }

/* ── Two clear levels of navigation ────────────────────────────────────────
   Primary: the Activity | Ranks section tabs render as bold underline tabs (not
   pills) so they're unmistakably the top level. Secondary: the in-section filters
   (All/Yield/… and the Ranks sub-tabs) stay as small, quiet pills. */
/* Material-3 / Gemini primary tabs: text labels on a full-width rail (the divider),
   with a rounded active indicator that sits on the rail. */
.tabs-primary { gap: 6px; margin: 16px 0 16px; border-bottom: 1px solid var(--border); }
.tabs-primary .earn-subtab {
  position: relative; align-items: flex-end; min-height: 40px;
  background: none; border-radius: 0;
  padding: 11px 14px 9px; font-size: 14.5px; font-weight: 600; letter-spacing: 0.2px;
  color: var(--text-dim);
}
.tabs-primary .earn-subtab:first-child { padding-left: 2px; }
.tabs-primary .earn-subtab:hover { background: none; color: var(--text); }
.tabs-primary .earn-subtab.is-active { background: none; color: var(--accent); }
/* Active indicator: a rounded bar that spans the label (inset matches the side
   padding) and sits on the divider rail just below it. */
.tabs-primary .earn-subtab.is-active::after {
  content: ""; position: absolute; left: 14px; right: 14px; bottom: 0; height: 3px;
  border-radius: 3px 3px 0 0; background: var(--accent);
}
.tabs-primary .earn-subtab:first-child.is-active::after { left: 2px; }

/* ── User-level Earn screen (earnings.ui.ts) ───────────────────────────────
   Reward-pool overview + sub-dashboard tiles + Leaderboards/Details tabs.
   Reuses .earn-dash/.earn-tile, .earn-subtab, .earn-rank-* primitives.
   Desktop: a normal centered column that scrolls wholesale (headline + subtitle
   like the other screens). Mobile: a pinned header + inner scroll (media block). */
.earnings-screen { display: block; }
/* Desktop header: big headline + subtitle, matching .sched-intro. */
.earnings-topbar {
  width: 100%; max-width: 760px; margin: 0 auto;
  padding: 72px 24px 4px;
}
.earnings-topbar-title {
  margin: 0 0 10px;
  font-family: "Google Sans Flex", "Google Sans Text", Roboto, system-ui, sans-serif;
  font-size: 32px; font-weight: 400; letter-spacing: -0.2px; line-height: 1.1;
}
.earnings-subtitle {
  margin: 0; max-width: 620px;
  font-size: 14.5px; color: var(--text-dim); line-height: 1.55;
}
/* One scroll column: giveaway + hero + actions all scroll together (nothing pinned). */
.earnings-scroll {
  width: 100%; max-width: 760px; margin: 0 auto;
  padding: 0 24px 96px;
}
.earnings-jackpot-slot { margin: 12px 0 16px; }
.earnings-hero-slot { margin-bottom: 6px; }

/* Mobile: compact pinned top bar (title beside the hamburger); everything below scrolls
   together in one region, the tabs sticking just under the top bar. */
@media (max-width: 720px) {
  .layout.earnings-active #earnings-view { display: flex; flex-direction: column; overflow: hidden; }
  .earnings-screen { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
  .earnings-topbar {
    flex: 0 0 auto;
    display: flex; align-items: center;
    max-width: none; margin: 0;
    min-height: calc(56px + env(safe-area-inset-top));
    padding: env(safe-area-inset-top) 16px 0 68px; /* left pad clears the wider hamburger */
    background: var(--bg);
  }
  .earnings-topbar-title { margin: 0; font-size: 18px; font-weight: 600; line-height: 1; }
  .earnings-subtitle { display: none; }
  .earnings-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; max-width: none; padding: 0 16px 96px; }
}

.earnings-overview {
  background: var(--bg-elev-1);
  border-radius: var(--radius-lg);
  padding: 20px 22px;
  margin-bottom: 12px;
}
.earnings-overview-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.earnings-overview-label {
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--text-dim);
}
.earnings-overview-expiry {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--text-dim);
}
.earnings-overview-expiry .material-symbols-rounded { font-size: 16px; }
.earnings-pool {
  font-family: var(--font-display);
  font-size: 42px; font-weight: 700; line-height: 1.05; margin-top: 10px;
  background: var(--grad-h); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.earnings-progress { margin-top: 18px; }
.earnings-progress-track {
  height: 10px; border-radius: 999px; background: var(--bg-elev-3); overflow: hidden;
}
.earnings-progress-bar {
  height: 100%; width: 0; border-radius: 999px; background: var(--grad-h);
  transition: width 0.7s var(--ease);
}
.earnings-progress-meta {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 9px; font-size: 12.5px; color: var(--text-dim);
}
.earnings-progress-meta strong { color: var(--text); font-weight: 600; }

/* Keep all 4 stat tiles on one line (override the shared 2×2 wrap rule); the
   value font scales down on narrow phones so "$3,847.55" still fits at 4-up. */
.earn-dash.earnings-dash { flex-wrap: nowrap; gap: 8px; }
.earnings-dash .earn-tile { flex: 1 1 0; min-width: 0; padding: 15px 8px; }
.earnings-dash .earn-tile-value {
  font-size: clamp(14px, 4.2vw, 22px);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.earnings-dash .earn-tile-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* "How yield works" primer — a foldable card directly under the yield dashboard. */
.yield-how {
  margin: 10px 0 4px; border-radius: var(--radius-lg);
  border: 1px solid var(--border); background: var(--surface-2, rgba(255, 255, 255, 0.03));
}
.yield-how-summary {
  display: block; cursor: pointer; list-style: none;
  padding: 12px 14px; font-size: 13.5px; font-weight: 700; color: var(--text);
}
.yield-how-summary::-webkit-details-marker { display: none; }
.yield-how-bar { display: flex; align-items: center; gap: 8px; }
.yield-how-title { flex: 1 1 auto; min-width: 0; text-align: left; }
.yield-how-summary .material-symbols-rounded { font-size: 18px; color: var(--accent); }
/* Chevron rotates when open. */
.yield-how-summary .material-symbols-rounded:last-child { color: var(--text-dim); transition: transform 0.18s var(--ease); }
.yield-how[open] .yield-how-summary .material-symbols-rounded:last-child { transform: rotate(180deg); }
.yield-how-list { list-style: none; margin: 0; padding: 0 14px 12px; display: flex; flex-direction: column; gap: 10px; }
.yield-how-row { display: flex; align-items: flex-start; gap: 10px; font-size: 12.5px; line-height: 1.45; color: var(--text-dim); }
.yield-how-row .material-symbols-rounded { font-size: 17px; color: var(--accent); flex: none; margin-top: 1px; }

/* Leaderboard amount (reuses .earn-rank-row layout, prominent value column). */
.earnings-rank-amount {
  flex: none; font-size: 14px; font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Detail list rows (yield / referral). */
.earnings-list { display: flex; flex-direction: column; gap: 4px; }
.earnings-row {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border-radius: var(--radius-sm); background: var(--bg-elev-1);
}
.earnings-row-main { flex: 1 1 auto; min-width: 0; }
.earnings-row-title {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 14px; font-weight: 500; color: var(--text);
}
.earnings-row-sub { font-size: 12px; color: var(--text-faint); margin-top: 3px; }
.earnings-type {
  font-size: 11px; font-weight: 600; color: var(--text-dim);
  background: var(--bg-elev-2); padding: 2px 8px; border-radius: 999px;
}
.earnings-row-amount {
  font-size: 14px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; text-align: right;
}
.earnings-row-amount.is-pos { color: var(--ok); }
.earnings-row-date { font-size: 11.5px; color: var(--text-faint); text-align: right; margin-top: 3px; }

/* ── Store screen (store.ui.ts) ────────────────────────────────────────────
   Active-agent balance strip + two bundle grids (Power, Credits). Reuses the
   .sched-center scaffold and .skills-action button. */
/* Store header: the title and the wallet HUD share ONE row (so the HUD is always
   level with "Store"), with the subtitle on the line below. On mobile the row fills
   the sticky bar and centers with the floating hamburger. */
.store-intro-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex: 1 1 auto; min-width: 0; }
.store-intro-row .sched-headline { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Wallet HUD: Power + Credits grouped in one bordered pill. */
.store-wallet {
  flex: none; display: inline-flex; align-items: center; gap: 10px;
  padding: 6px 12px; border-radius: 999px;
  background: var(--bg-elev-1); border: 1px solid var(--border);
}
.store-wallet .cur { font-size: 14px; font-weight: 600; color: var(--text); }
.store-wallet-sep { width: 1px; height: 14px; background: var(--border-strong); }

.store-balance {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  background: var(--bg-elev-1); border-radius: var(--radius); padding: 14px 18px; margin: 4px 0 22px;
}
.store-balance-agent { font-size: 14px; font-weight: 600; color: var(--text); }
.store-balance-pills { display: flex; gap: 22px; }
.store-bal { display: flex; align-items: center; gap: 9px; }
.store-bal .material-symbols-rounded { font-size: 22px; color: var(--accent); }
.store-bal-value { font-size: 17px; font-weight: 700; color: var(--text); line-height: 1.1; font-variant-numeric: tabular-nums; }
.store-bal-label { font-size: 11.5px; color: var(--text-faint); }

.store-section { margin-top: 26px; }
.store-section-title {
  font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-dim); margin: 0 0 4px;
}
.store-section-hint { font-size: 12.5px; color: var(--text-faint); margin: 0 0 14px; line-height: 1.5; }

.store-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.store-card {
  position: relative;
  display: flex; flex-direction: column; gap: 8px;
  background: var(--bg-elev-1); border-radius: var(--radius); padding: 16px;
}
/* Best-value card: accent ring + a faint accent wash so the promotion reads at a glance. */
.store-card.is-best {
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-elev-1));
}
.store-badge {
  position: absolute; top: 12px; right: 12px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.3px; color: var(--on-accent);
  background: var(--accent); border-radius: 999px; padding: 2px 9px; white-space: nowrap;
}
.store-card-name { font-size: 13px; font-weight: 600; color: var(--text-dim); }
/* Reserve room under the absolute "Best value" badge so a long name can't slide beneath it. */
.store-card.is-best .store-card-name { max-width: calc(100% - 88px); }
/* Per-bundle discount / bonus chip — its own line (never crowds the price+Buy row), nowrap. */
.store-card-save {
  align-self: flex-start;
  font-size: 11px; font-weight: 700; white-space: nowrap; line-height: 1.3;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  border-radius: 999px; padding: 2px 9px;
}
.store-card-save.is-best { color: var(--on-accent); background: var(--accent); }
.store-card-amount { display: flex; align-items: baseline; gap: 6px; }
.store-card-amount .material-symbols-rounded { font-size: 22px; color: var(--accent); align-self: center; }
.store-card-amount > span:nth-of-type(1) { font-size: 26px; font-weight: 700; color: var(--text); line-height: 1; font-variant-numeric: tabular-nums; }
.store-card-unit { font-size: 13px; font-weight: 600; color: var(--text-faint); }
.store-card-sub { font-size: 12px; color: var(--text-faint); }
/* Price ABOVE a full-width button (two lines) — in the narrow 2-column grid a
   side-by-side row squeezes/misaligns the button (esp. with CJK labels). */
.store-card-foot { display: flex; flex-direction: column; align-items: stretch; gap: 10px; margin-top: auto; padding-top: 6px; }
.store-card-price { font-size: 16px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
/* Price shown as the locked-earnings chip: shrink to its content, don't stretch to the
   card-foot's full width (the foot is a stretch column). */
.store-card-foot .earn-lock-chip { align-self: flex-start; }
.store-card-cost { display: inline-flex; align-items: center; gap: 3px; }
.store-card-cost .material-symbols-rounded { font-size: 17px; color: var(--accent); }
.store-buy { width: 100%; align-self: stretch; padding: 11px 18px; min-height: 44px; }

/* ── Top-up modal (topup.ui.ts) — one job: allocate Credits pool → agent.
   Gemini language: a centered balance hero, quick-amount chips, one primary
   action. Reuses .settings-overlay/.settings-panel + .buy-chip + .auth-btn. ── */
.settings-panel.topup-panel { width: min(440px, 100%); }
.topup-body { padding: 8px 22px 24px; }

/* Hero: the pool balance, in the app's balance-card treatment (mirrors .wallet-hero). */
.topup-hero {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 28px 20px; margin: 4px 0 22px;
  border-radius: var(--radius-hero);
  background:
    radial-gradient(125% 80% at 50% -10%, color-mix(in srgb, var(--grad-b) 16%, transparent), transparent 62%),
    var(--bg-elev-1);
  text-align: center;
}
.topup-hero-label {
  font-family: var(--font-display);
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.1px;
  color: var(--text-faint);
}
.topup-hero-num { display: inline-flex; align-items: center; gap: 8px; }
.topup-hero-num .cur-icon { font-size: 32px; color: var(--accent); }
.topup-hero-num .cur-val {
  font-family: var(--font-display);
  font-size: 46px; font-weight: 500; line-height: 1.02; letter-spacing: -1.4px;
  font-variant-numeric: tabular-nums; color: var(--accent);
}

.topup-label { font-size: 13px; font-weight: 600; color: var(--text-dim); margin: 0 0 10px; }
.topup-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.topup-input {
  width: 100%; box-sizing: border-box; padding: 12px 14px; margin-bottom: 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--bg-elev-1); color: var(--text);
  font-size: 16px; font-variant-numeric: tabular-nums;
}
.topup-input:focus { outline: none; border-color: var(--border-strong); }
.topup-primary { width: 100%; }
.topup-foot { text-align: center; font-size: 12.5px; color: var(--text-faint); margin: 14px 0 0; }
.topup-empty { text-align: center; font-size: 14px; color: var(--text-dim); margin: 2px 0 16px; }

.topup-note { padding: 14px 2px 0; font-size: 12.5px; color: var(--text-dim); line-height: 1.5; text-align: center; }
.topup-note.is-error { color: var(--err); }

/* ── Wallet screen (portfolio · receive · send) — Gemini-style ─────────────── */
/* Fill the .sched-center column so the hero card / actions / asset rows share the
   exact left+right edges of the headline + subtitle (no indented, off-balance card). */
.wallet-body { width: 100%; margin: 12px 0 0; }

/* Hero: the headline number rendered in Gemini's signature blue→purple→pink
   gradient (Google Sans Flex display face), on a softly gradient-lit card. */
.wallet-hero {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 36px 20px 30px; margin: 6px 0 22px;
  border-radius: var(--radius-hero);
  background:
    radial-gradient(125% 80% at 50% -10%, color-mix(in srgb, var(--grad-b) 16%, transparent), transparent 62%),
    var(--bg-elev-1);
  text-align: center;
}
.wallet-hero-label {
  font-family: var(--font-display);
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 1.1px;
  color: var(--text-faint);
}
.wallet-hero-total {
  font-family: var(--font-display);
  font-size: 56px; font-weight: 500; line-height: 1.02; letter-spacing: -1.6px;
  font-variant-numeric: tabular-nums;
  /* Mono accent: white on dark, black on light (matches the app's greeting). */
  color: var(--accent);
}
.wallet-address {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 4px; min-height: 40px;
  background: var(--bg-elev-2); border: none; border-radius: 999px; padding: 8px 16px;
  color: var(--text-dim); font: inherit; font-size: 13px; cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.wallet-address:hover { color: var(--text); background: var(--bg-elev-3); }
.wallet-address .material-symbols-rounded { font-size: 16px; }
.wallet-degraded { font-size: 12px; color: var(--warn); margin-top: 6px; }
/* Network badge — quiet/neutral on mainnet, a loud warning on any test network. */
.wallet-net {
  display: inline-flex; align-items: center; gap: 5px; align-self: center; margin-top: 10px;
  padding: 3px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 600; letter-spacing: 0.2px;
  background: var(--bg-elev-2); color: var(--text-faint);
}
.wallet-net.is-test {
  background: color-mix(in srgb, var(--warn) 18%, var(--bg-elev-2)); color: var(--warn);
}
.wallet-net .material-symbols-rounded { font-size: 15px; }
/* Receive-screen test-network warning — sits above the address/QR. */
.wallet-receive-warn {
  display: flex; align-items: flex-start; gap: 8px; margin: 0 0 12px;
  padding: 11px 13px; border-radius: var(--radius-md); line-height: 1.5; font-size: 13px;
  background: color-mix(in srgb, var(--warn) 14%, var(--bg-elev-1)); color: var(--text);
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
}
.wallet-receive-warn .material-symbols-rounded { font-size: 19px; color: var(--warn); flex: none; }

/* Manual refresh — a subtle icon button in the hero's top-right corner. */
.wallet-refresh {
  position: absolute; top: 14px; right: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%; border: none; cursor: pointer;
  background: transparent; color: var(--text-faint);
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.wallet-refresh:hover { background: var(--bg-elev-2); color: var(--text); }
.wallet-refresh:disabled { cursor: default; }
.wallet-refresh .material-symbols-rounded { font-size: 20px; }
/* Earnings shortcut pinned to the balance card (top-left, mirrors refresh top-right). */
.wallet-hero-earn {
  position: absolute; top: 14px; left: 14px;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px 6px 9px; border-radius: 999px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 35%, transparent);
  color: var(--text-dim);
  font: inherit; font-size: 12px; font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.wallet-hero-earn:hover { background: var(--bg-elev-2); color: var(--text); }
.wallet-hero-earn .material-symbols-rounded { font-size: 16px; }
.wallet-refresh.is-spinning .material-symbols-rounded { animation: wallet-spin 0.8s linear infinite; }
@keyframes wallet-spin { to { transform: rotate(360deg); } }

/* Receive / Send — Gemini tonal pills. */
.wallet-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 28px; }
.wallet-action {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  padding: 15px 16px; border-radius: 999px; border: none; cursor: pointer;
  background: var(--bg-elev-1); color: var(--text); font: inherit; font-size: 15px; font-weight: 600;
  transition: background 0.15s var(--ease), transform 0.15s var(--ease);
}
.wallet-action:hover { background: var(--bg-elev-2); }
.wallet-action:active { transform: scale(0.985); }
.wallet-action .material-symbols-rounded { font-size: 20px; color: var(--text-dim); }

/* Asset list — Gemini list rows with a tonal hover. */
.wallet-tokens { display: flex; flex-direction: column; margin: 0 -8px; }
.wallet-token {
  display: flex; align-items: center; gap: 13px; padding: 12px 12px; border-radius: var(--radius);
  transition: background 0.15s var(--ease);
}
.wallet-token:hover { background: var(--bg-elev-1); }
.wallet-token-logo { width: 38px; height: 38px; border-radius: 50%; flex: none; object-fit: cover; background: var(--bg-elev-2); }
.wallet-token-logo-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 16px; font-weight: 600; color: var(--text-dim);
}
.wallet-token-main { flex: 1 1 auto; min-width: 0; }
.wallet-token-symbol { font-size: 15px; font-weight: 600; color: var(--text); }
.wallet-token-name { font-size: 12.5px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wallet-token-right { text-align: right; flex: none; }
.wallet-token-usd { font-size: 15px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.wallet-token-bal { font-size: 12.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.wallet-token-empty { font-size: 13.5px; color: var(--text-faint); padding: 16px 12px; }
.wallet-empty { color: var(--text-faint); }
.wallet-spam-toggle {
  background: none; border: none; color: var(--text-dim); font: inherit; font-size: 13px;
  cursor: pointer; padding: 12px; margin: 4px -8px 0; text-align: left; border-radius: var(--radius-sm);
}
.wallet-spam-toggle:hover { color: var(--text); background: var(--bg-elev-1); }

/* Activity (in/out history) — rows are explorer links; incoming amounts are green,
   settled-withdrawal arrivals get an accent highlight. */
.wallet-tabs { margin-top: 22px; }
.wallet-tab-content { min-height: 80px; }
.wallet-activity { display: flex; flex-direction: column; margin: 0 -8px; }
.wallet-act-row {
  display: flex; align-items: center; gap: 13px; padding: 11px 12px; border-radius: var(--radius);
  text-decoration: none; color: inherit; transition: background 0.15s var(--ease);
}
.wallet-act-row:hover { background: var(--bg-elev-1); }
.wallet-act-icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 36px; height: 36px; border-radius: 50%; overflow: hidden;
  background: var(--bg-elev-2); color: var(--text-dim); font-size: 19px;
}
.wallet-act-main { flex: 1 1 auto; min-width: 0; }
.wallet-act-title {
  display: flex; align-items: center; gap: 8px;
  font-size: 14.5px; font-weight: 600; color: var(--text);
}
.wallet-act-badge {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent);
  padding: 2px 7px; border-radius: 999px;
}
.wallet-act-badge.is-pending { color: var(--warn); background: color-mix(in srgb, var(--warn) 16%, transparent); }
.wallet-act-badge.is-failed { color: var(--danger); background: color-mix(in srgb, var(--danger) 16%, transparent); }
/* Activity subcategory chips — All | Withdrawals. */
.wallet-act-filters { display: flex; gap: 6px; margin: 2px 0 10px; }
.wallet-act-filter {
  border: 1px solid var(--border); background: transparent; color: var(--text-dim);
  font: inherit; font-size: 12px; font-weight: 600;
  padding: 4px 12px; border-radius: 999px; cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.wallet-act-filter:hover { color: var(--text); background: var(--bg-elev-1); }
.wallet-act-filter.is-active {
  color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.wallet-act-sub { font-size: 12.5px; color: var(--text-faint); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wallet-act-right { text-align: right; flex: none; }
.wallet-act-amount { font-size: 14.5px; font-weight: 700; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.wallet-act-amount.is-pos { color: var(--ok); }
.wallet-act-date { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
/* Settled earnings withdrawal landing — the highlighted "withdraw received" row. */
.wallet-act-row.is-withdrawal { background: color-mix(in srgb, var(--ok) 9%, transparent); }
.wallet-act-row.is-withdrawal:hover { background: color-mix(in srgb, var(--ok) 14%, transparent); }
.wallet-act-row.is-withdrawal .wallet-act-icon { background: color-mix(in srgb, var(--ok) 18%, var(--bg-elev-1)); color: var(--ok); }

/* Receive modal: QR + address. */
.wallet-qr { display: flex; align-items: center; justify-content: center; margin: 18px 0; }
.wallet-qr-img { width: 232px; height: 232px; image-rendering: pixelated; border-radius: var(--radius-lg); background: #fff; padding: 14px; box-shadow: var(--shadow-md); }
.wallet-qr-loading { font-size: 13px; color: var(--text-faint); padding: 46px 0; }
.wallet-receive-addr { background: var(--bg-elev-1); border-radius: var(--radius-md); padding: 13px 16px; margin-bottom: 12px; word-break: break-all; text-align: center; }
.wallet-receive-addr code { font-size: 13px; color: var(--text-dim); font-family: var(--font-mono); letter-spacing: 0.2px; }

/* Send modal: labeled fields (inputs reuse .auth-input / button reuses .auth-btn). */
.wallet-field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.wallet-field-label { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.wallet-field-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wallet-send-hint { font-size: 12px; color: var(--warn); margin: -6px 0 14px; min-height: 16px; line-height: 1.5; }

/* Custom asset dropdown — a dark, themed replacement for the native <select>, whose
   option popup renders in the OS (light) theme and can't be styled. */
.wallet-asset-wrap { position: relative; }
.wallet-asset-trigger {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg-elev-1); color: var(--text); font: inherit; font-size: 15px; cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.wallet-asset-trigger:hover { background: var(--bg-elev-2); }
.wallet-asset-label { flex: 1 1 auto; text-align: left; }
.wallet-asset-chev { font-size: 22px; color: var(--text-faint); }
.wallet-asset-logo { width: 24px; height: 24px; border-radius: 50%; flex: none; object-fit: cover; background: var(--bg-elev-2); font-size: 11px; }
.wallet-asset-menu {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 6;
  padding: 6px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg-elev-2); box-shadow: var(--shadow-lg);
  max-height: 244px; overflow-y: auto;
}
.wallet-asset-option {
  width: 100%; display: flex; align-items: center; gap: 11px;
  padding: 9px 10px; border-radius: var(--radius-sm); border: none; background: none; color: var(--text);
  font: inherit; cursor: pointer; text-align: left; transition: background 0.12s var(--ease);
}
.wallet-asset-option:hover { background: var(--bg-elev-3); }
.wallet-asset-opt-main { flex: 1 1 auto; min-width: 0; }
.wallet-asset-opt-sym { font-size: 14px; font-weight: 600; }
.wallet-asset-opt-bal { font-size: 12px; color: var(--text-faint); }
.wallet-asset-check { font-size: 19px; color: var(--accent); opacity: 0; flex: none; }
.wallet-asset-option.is-selected .wallet-asset-check { opacity: 1; }

/* Quick-amount chips. */
.wallet-amount-chips { display: flex; gap: 6px; }
.wallet-chip {
  display: inline-flex; align-items: center; justify-content: center; min-height: 40px;
  padding: 8px 16px; border-radius: 999px; border: none; cursor: pointer;
  background: var(--bg-elev-2); color: var(--text-dim); font: inherit; font-size: 12px; font-weight: 600;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.wallet-chip:hover { background: var(--bg-elev-3); color: var(--text); }

/* Wallet modals (Receive / Send) — shared Gemini chrome so both read identically:
   softer corners than the default settings panel, a borderless title, and even body
   padding so the QR / fields / address / buttons all align to the same gutters. */
/* overflow:visible so the asset dropdown menu can float past the body/panel edges
   (wallet modals are short, so they never need the panel's own scroll). */
.settings-panel.wallet-modal { border-radius: var(--radius-lg); overflow: visible; }
.settings-panel.wallet-modal .settings-titlebar { border-bottom: none; padding: 18px 22px 6px; }
.settings-panel.wallet-modal .settings-titlebar h2 { font-family: var(--font-display); font-weight: 500; }
.wallet-modal-body { padding: 4px 22px 22px; max-height: none; overflow: visible; }
.wallet-modal-intro { font-size: 14px; color: var(--text-dim); line-height: 1.55; margin: 2px 0 8px; }
/* The primary modal button (Copy address / Confirm send) is the same white pill in both. */
.wallet-modal-body .auth-btn { margin-top: 4px; }

/* ── Settings → Appearance: light/dark segmented toggle ────────────────────── */
.theme-toggle-row { display: flex; gap: 10px; margin-top: 8px; }
.theme-opt {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 20px; border-radius: var(--radius-md); cursor: pointer;
  background: var(--bg-elev-1); border: none; color: var(--text-dim);
  font: inherit; font-size: 14px; font-weight: 600;
  box-shadow: inset 0 0 0 1.5px transparent;
  transition: color 0.15s var(--ease), background 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.theme-opt .material-symbols-rounded { font-size: 20px; }
.theme-opt:hover { color: var(--text); background: var(--bg-elev-2); }
.theme-opt.is-active {
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elev-1));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* Settings → Agent: inline input + action button rows. */
.settings-row { display: flex; gap: 8px; align-items: center; margin-top: 6px; }
.settings-row .settings-input { flex: 1 1 auto; min-width: 0; }
.settings-row .skills-action { flex: none; }

/* ── Settings → Agent panel (Gemini-style cards) ───────────────────────────── */
.agentset { display: flex; flex-direction: column; gap: 14px; max-width: 560px; }
.agentset-card { background: var(--bg-elev-1); border-radius: var(--radius-lg); padding: 20px 22px; }
.agentset-label {
  font-size: 12px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 14px;
}

/* advanced web search toggle */
.agentset-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.agentset-toggle-text { min-width: 0; }
.agentset-toggle-row .agentset-label { margin-bottom: 4px; }
.agentset-hint { font-size: 12.5px; line-height: 1.4; color: var(--text-faint); text-transform: none; letter-spacing: 0; font-weight: 400; }
.agentset-switch {
  appearance: none; -webkit-appearance: none; flex: 0 0 auto; position: relative;
  width: 44px; height: 26px; border-radius: 999px; cursor: pointer;
  background: var(--bg-elev-2); border: 1px solid var(--border); transition: background 0.15s var(--ease);
}
.agentset-switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
  border-radius: 50%; background: var(--text-dim); transition: transform 0.15s var(--ease), background 0.15s var(--ease);
}
.agentset-switch:checked { background: var(--accent); border-color: var(--accent); }
.agentset-switch:checked::after { transform: translateX(18px); background: var(--bg); }
.agentset-switch:disabled { opacity: 0.6; cursor: default; }

.agentset-name-row { display: flex; gap: 10px; align-items: center; }
.agentset-name,
.agentset-input {
  flex: 1 1 auto; min-width: 0;
  background: var(--bg-elev-2); border: none; border-radius: var(--radius-sm);
  padding: 12px 14px; color: var(--text); font: inherit; outline: none;
  transition: box-shadow 0.15s var(--ease);
}
.agentset-name { font-size: 16px; font-weight: 600; }
.agentset-input { font-size: 14px; }
.agentset-name::placeholder,
.agentset-input::placeholder { color: var(--text-faint); }
.agentset-name:focus,
.agentset-input:focus { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent); }

/* identity card: label + the agent's star tier, side by side */
.agentset-label-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.agentset-label--inline { margin-bottom: 0; }

/* Yield section: star + price/yield/unlock tiles, a one-line takeaway, and an
   expandable "How yield works" explainer. Values come from GET /api/agents. */
.agentset-ystats { display: flex; flex-wrap: wrap; gap: 8px; }
.agentset-ystat {
  flex: 1 1 0; min-width: 110px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: var(--bg-elev-2); border-radius: var(--radius-sm); padding: 12px 8px;
}
.agentset-ystat-val { font-size: 16px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agentset-ystat-label { font-size: 11px; font-weight: 600; color: var(--text-dim); }
.agentset-ystat-sub { font-size: 10px; color: var(--text-faint); }
.agentset-yhint { font-size: 12px; color: var(--text-dim); margin-top: 12px; line-height: 1.45; }
.agentset-ydisc { margin-top: 12px; }
.agentset-ydisc-sum {
  font-size: 13px; font-weight: 600; color: var(--text-dim); cursor: pointer;
  list-style: none; display: inline-flex; align-items: center; gap: 6px; padding: 8px 0;
}
.agentset-ydisc-sum::-webkit-details-marker { display: none; }
.agentset-ydisc-chev { font-size: 18px; transition: transform 0.15s var(--ease); }
.agentset-ydisc[open] .agentset-ydisc-chev { transform: rotate(90deg); }
.agentset-ydisc-list { margin: 8px 0 8px; padding-left: 20px; display: flex; flex-direction: column; gap: 12px; }
.agentset-ydisc-list li { font-size: 13px; color: var(--text-dim); line-height: 1.65; }
.agentset-ydisc-list li::marker { color: var(--text-faint); }

.agentset-save,
.agentset-btn {
  flex: none; cursor: pointer; border: none; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text);
  font: inherit; font-size: 14px; font-weight: 600; padding: 11px 22px;
  transition: background 0.15s var(--ease), filter 0.15s var(--ease);
}
.agentset-save:hover,
.agentset-btn:hover { background: var(--bg-elev-3); }
.agentset-btn-primary { background: var(--accent); color: var(--on-accent); }
.agentset-btn-primary:hover { background: var(--accent); filter: brightness(1.06); }
.agentset-btn-sm { font-size: 12.5px; padding: 7px 16px; }
/* Star-up CTA: wears the brand gradient (the feature's color on the Earn screen). */
.agentset-btn-starup {
  color: #fff;
  background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c));
  box-shadow: 0 6px 18px -10px color-mix(in srgb, var(--grad-b) 90%, transparent);
}
.agentset-btn-starup:hover {
  background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c));
  filter: brightness(1.08);
}

/* Gemini-style settings list rows (Yield economics, Credits, Subscription). */
.agentset-rows { margin-top: 4px; }
.agentset-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 13px 0; }
.agentset-row + .agentset-row,
.agentset-rowgroup + .agentset-rowgroup { border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.agentset-row-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.agentset-row-label { font-size: 14px; font-weight: 500; color: var(--text); }
.agentset-row-sub { font-size: 12px; color: var(--text-faint); display: flex; align-items: center; gap: 3px; flex-wrap: wrap; }
.agentset-row-side { flex: none; display: flex; align-items: center; gap: 10px; }
.agentset-row-val { font-size: 14.5px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.agentset-row-val.is-warn { color: var(--warn); }
.agentset-row-val.is-danger { color: var(--danger-text); }
/* Collapsed-by-default controls under a row (top-up field, month picker). [hidden]
   must beat the blocks' own display values. */
.agentset-subctl { display: flex; flex-direction: column; gap: 10px; padding: 2px 0 12px; }
.agentset-rowgroup > .agentset-field { margin: 2px 0 12px; }
.agentset-subctl[hidden], .agentset-field[hidden] { display: none; }
/* Upgrade block: x/y counter pill + card-set progress bar (earn-screen language). */
.agentset-upgrade { border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.agentset-upgrade .uset-actions { display: flex; justify-content: flex-end; margin-top: 16px; }
.agentset-upgrade .agentset-hint { margin-top: 12px; }
.agentset-prog {
  flex: none; font-size: 12.5px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text); background: var(--bg-elev-2); border-radius: 999px; padding: 5px 12px;
}
.agentset-progbar { height: 6px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; margin: 1px 0 4px; }
.agentset-progbar-fill {
  height: 100%; border-radius: 999px; min-width: 6px;
  background: linear-gradient(90deg, var(--grad-a), var(--grad-b));
  transition: width 0.3s var(--ease);
}

.agentset-id-row { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.agentset-id {
  font-family: var(--font-mono); font-size: 12px; color: var(--text-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agentset-copy {
  flex: none; display: grid; place-items: center; cursor: pointer;
  background: transparent; border: none; color: var(--text-faint);
  padding: 4px; border-radius: var(--radius-tile); transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.agentset-copy:hover { background: var(--bg-elev-2); color: var(--text); }
.agentset-copy .material-symbols-rounded { font-size: 16px; }

.agentset-stat { display: flex; align-items: baseline; gap: 8px; }
.agentset-stat-value {
  font-family: var(--font-display); font-size: 32px; font-weight: 700; line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.agentset-stat-unit { font-size: 14px; font-weight: 600; color: var(--text-faint); }
.agentset-meta { font-size: 12.5px; color: var(--text-faint); margin-top: 8px; }
.agentset-field { display: flex; gap: 10px; align-items: center; margin-top: 14px; }

.agentset-status { font-family: var(--font-display); font-size: 24px; font-weight: 600; color: var(--text); }
.agentset-status-expired { color: var(--danger-text); }
.agentset-status-none { color: var(--text-faint); }
.agentset-months { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0 16px; }
.agentset-chip {
  cursor: pointer; border: none; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text-dim);
  font: inherit; font-size: 13px; font-weight: 600; padding: 8px 16px;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.agentset-chip:hover { color: var(--text); }
.agentset-chip.is-active { color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--bg-elev-2)); }
.agentset-buy-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.agentset-cost { font-size: 13px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* User settings panels in the same Gemini card style as the Agent panel. Scoped to
   the settings screen so admin/profile forms (which also use .settings-section) are
   untouched. Cards: International, Spending, Appearance, Self-improvement. */
#settings-view .settings-section {
  background: var(--bg-elev-1);
  border: none;
  border-radius: var(--radius-lg);
  padding: 20px 22px;
  margin: 0 0 14px;
  max-width: 560px;
}
#settings-view .settings-section h3,
#settings-view .settings-label {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* Tonal (borderless) inputs — the Gemini field style. Shared by the Settings
   screen and the Schedules create/edit form (both use .settings-input/-textarea)
   so neither falls back to the legacy 1px-border + white-on-focus outline. */
#settings-view .settings-input,
#settings-view .settings-textarea,
#schedules-view .settings-input,
#schedules-view .settings-textarea {
  background: var(--bg-elev-2);
  border: none;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  transition: box-shadow 0.15s var(--ease);
}
#settings-view .settings-input:focus,
#settings-view .settings-textarea:focus,
#schedules-view .settings-input:focus,
#schedules-view .settings-textarea:focus {
  border: none;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent);
}
/* Language rows + theme toggle sit a touch below the card label. */
#settings-view .settings-lang-options,
#settings-view .theme-toggle-row { margin-top: 4px; }
#settings-view .settings-hint { margin-top: 10px; }

/* User settings cards (International / Spending / Appearance), Gemini style — same
   card language as the Agent panel, self-contained (no inherited .settings-* quirks). */
.uset { display: flex; flex-direction: column; gap: 14px; max-width: 560px; }
.uset-card { background: var(--bg-elev-1); border-radius: var(--radius-lg); padding: 20px 22px; }
/* Memory / Instructions / Persona editor: each textarea sits in its own uset-card section */
.uset-card > .settings-textarea { margin-top: 10px; background: var(--bg-elev-2); }
/* Memory graph view (docs/SECOND_BRAIN.md): 2D force-directed canvas of the topic wiki. */
.memory-graph-canvas {
  display: block; width: 100%; height: 320px; margin-top: 10px;
  background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 10px;
  touch-action: none; /* let pointer-drag work without the page panning */
}
.memory-graph-empty { margin-top: 10px; }
.memory-graph-legend {
  display: flex; flex-wrap: wrap; gap: 16px; margin-top: 10px;
  font-size: 11px; color: var(--text-faint);
}
.mg-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.mg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); display: inline-block; }
.mg-line { width: 16px; height: 0; border-top: 2px solid var(--text-faint); display: inline-block; }
.mg-inferred { border-top-style: dashed; border-top-color: var(--accent); }
/* Brain screen — layout comes from the shared .sched-center/.sched-intro/.sched-headline/
   .sched-subtitle convention (see renderBrain); only the canvas + empty message are specific. */
.memory-graph-canvas.brain-canvas { height: min(62vh, 600px); margin-top: 16px; }
/* Positioned box the canvas + floating legend + empty-state share (see renderBrain).
   On mobile it goes full-bleed and the legend/detail overlay it. */
.brain-graph { position: relative; }
.brain-empty { margin: 40px 0; text-align: center; }
/* The sheet backdrop is mobile-only; inert on desktop. */
.brain-backdrop { display: none; }
/* Detail panel: the "open note" for a clicked topic node. Class-driven (not the
   `hidden` attr) so the mobile sheet can transition; on desktop it's an inline card
   shown only once a node is opened. */
.brain-detail {
  display: none; margin-top: 14px; padding: 14px 16px;
  background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 12px;
}
.brain-detail.open { display: block; }
.brain-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.brain-detail-title { font-size: 16px; font-weight: 600; letter-spacing: 0.1px; }
.brain-detail-close {
  background: none; border: none; color: var(--text-dim); font-size: 13px; line-height: 1;
  cursor: pointer; padding: 4px 6px; border-radius: 6px;
}
.brain-detail-close:hover { color: var(--text); background: color-mix(in srgb, var(--text) 10%, transparent); }
/* Each topic note renders as its own block (a lead line / a sentence-per-line body /
   an arrow-timeline stepped list), separated by a hairline, so a long note reads as
   structure instead of a wall of text. */
.brain-detail-list { margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.brain-note { display: flex; flex-direction: column; gap: 7px; }
.brain-note + .brain-note { border-top: 1px solid var(--border); padding-top: 14px; }
.brain-note strong { color: var(--text); font-weight: 600; }
.brain-line { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text-dim); }
.brain-steps { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.brain-step {
  position: relative; margin: 0; padding-left: 18px;
  font-size: 13px; line-height: 1.55; color: var(--text-dim);
}
.brain-step::before {
  content: ""; position: absolute; left: 2px; top: 6px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
}
/* Connector line down the timeline. */
.brain-step:not(:last-child)::after {
  content: ""; position: absolute; left: 4.5px; top: 13px; bottom: -10px; width: 1px; background: var(--border);
}
.brain-detail-muted { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text-faint); }

/* Brain lenses + activity Stream (docs/BRAIN_UX.md P1+P3). The lens row reuses the
   Material-3/Gemini primary-tab primitives (.earn-subtabs.tabs-primary — labels on a
   divider rail, rounded indicator). The Brain screen's SIGNATURE is the brand
   (mascot-ring) gradient: it fills the active-lens indicator, rings "just learned"
   map nodes (canvas, memoryGraph.ts), and borders an expanded Stream receipt.
   Everything else stays the app's monochrome system. */
:root {
  --brand-a: #0036ff;
  --brand-b: #c600ff;
}
.brain-lenses.tabs-primary { margin-top: 4px; }
.brain-lenses .earn-subtab.is-active { color: var(--text); }
.brain-lenses .earn-subtab.is-active::after {
  background: linear-gradient(90deg, var(--brand-a), var(--brand-b));
}
/* Map status chip — floats over the graph like the legend. */
.brain-mapstat {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 2;
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  font-size: 11px; color: var(--text-dim); pointer-events: none;
  background: color-mix(in srgb, var(--bg-elev-2) 85%, transparent);
  border: 1px solid var(--border); border-radius: 999px; padding: 4px 13px;
}
.brain-mapstat-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: linear-gradient(135deg, var(--brand-a), var(--brand-b));
}
.brain-stream { display: flex; flex-direction: column; gap: 8px; max-width: 640px; margin-top: 10px; }
/* Author `display:flex` defeats the hidden attribute (UA [hidden] rule loses) — the
   lens switcher and the expand/collapse bodies all rely on it. */
.brain-lens-view[hidden],
.brain-ev-body[hidden],
.brain-group-rows[hidden] { display: none; }
.brain-day {
  font-size: 11px; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-faint); margin: 10px 2px 2px;
}
/* The permanent transparent border keeps the card from shifting 1px when the
   expanded (.is-open) gradient border swaps in. */
.brain-ev {
  background: var(--bg-elev); border: 1px solid transparent; border-radius: 14px;
  padding: 12px 14px; transition: background var(--ease) 120ms;
}
.brain-ev.is-open {
  background:
    linear-gradient(var(--bg-elev), var(--bg-elev)) padding-box,
    linear-gradient(135deg,
      color-mix(in srgb, var(--brand-a) 65%, transparent),
      color-mix(in srgb, var(--brand-b) 65%, transparent)) border-box;
}
.brain-ev-head {
  display: flex; align-items: flex-start; gap: 11px; width: 100%;
  background: none; border: none; padding: 0; font: inherit; color: inherit; text-align: left;
}
button.brain-ev-head { cursor: pointer; }
.brain-ev-head.is-link:hover .brain-ev-main b { color: var(--text); }
.brain-ev-go { color: var(--text-faint); font-size: 17px; line-height: 1; margin-top: 3px; }
.brain-ev-ico {
  flex: none; width: 30px; height: 30px; border-radius: 10px; background: var(--bg-elev-2);
  display: grid; place-items: center; font-size: 15px;
}
.brain-ev-main { flex: 1; min-width: 0; }
.brain-ev-main b { display: block; font-size: 14px; font-weight: 600; line-height: 1.4; }
.brain-ev-main small { color: var(--text-faint); font-size: 12px; }
.brain-kind {
  flex: none; font-size: 10.5px; color: var(--text-dim); background: var(--bg-elev-2);
  border-radius: 6px; padding: 2px 8px; margin-top: 4px;
}
.brain-ev.reverted .brain-ev-main b { text-decoration: line-through; opacity: 0.6; }
.brain-ev-body { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.brain-ev-actions { display: flex; align-items: center; gap: 8px; }
.brain-ev-actions button {
  font: inherit; font-size: 12px; font-weight: 600; border: none; border-radius: 999px;
  padding: 6px 14px; cursor: pointer; background: var(--bg-elev-2); color: var(--text-dim);
}
.brain-ev-actions button:hover:not(:disabled) { color: var(--text); }
.brain-reverted-tag { font-size: 11px; color: var(--text-faint); }
.brain-commit { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); }
.brain-diff {
  margin: 0; font-family: var(--font-mono); font-size: 11px; line-height: 1.55;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; overflow: auto; max-height: 260px; white-space: pre;
}
.brain-diff .diff-add { color: var(--ok); }
.brain-diff .diff-del { color: var(--danger); }
.brain-diff .diff-meta { color: var(--text-faint); }
/* A collapsed dream night's sub-entries, indented under the group head. */
.brain-group-rows {
  display: flex; flex-direction: column; gap: 6px; margin-top: 10px;
  border-left: 2px solid var(--border); padding-left: 10px;
}
.brain-group-rows .brain-ev { background: var(--bg-elev-2); }
.brain-group-rows .brain-ev .brain-ev-ico { background: var(--bg-elev-3); }

/* Brain on mobile: the graph goes full-bleed under the 56px title bar, the
   topic/linked/inferred legend floats over it as a widget, and tapping a node slides
   its note up as a bottom sheet (matching the app's other slide-up modals). The
   .sched-intro keeps its 24px-negating side margin, so we keep the center's side
   padding and break the graph out to full width instead of zeroing it. */
@media (max-width: 720px) {
  .layout .brain-center { padding-bottom: 0; }
  .brain-center .sched-intro { margin-bottom: 0; }
  .brain-graph {
    margin: 0 -24px; /* full-bleed past .sched-center's 24px side padding */
    /* 56px title bar + ~52px lens rail (docs/BRAIN_UX.md P1) above the graph. */
    height: calc(100vh - 108px - env(safe-area-inset-top));
    height: calc(100dvh - 108px - env(safe-area-inset-top));
  }
  .brain-lenses.tabs-primary { margin: 4px 0 8px; }
  /* The legend owns the top-right corner on mobile; the status chip sits at the bottom. */
  .brain-mapstat { top: auto; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  /* Compound selectors beat the .memory-graph-canvas / .memory-graph-legend bases. */
  .brain-canvas.memory-graph-canvas {
    height: 100%; margin: 0; border: none; border-radius: 0; background: var(--bg);
  }
  .brain-legend.memory-graph-legend {
    position: absolute; top: 12px; right: 12px; z-index: 2; margin: 0;
    flex-direction: column; gap: 8px; padding: 9px 11px; border-radius: 12px;
    background: color-mix(in srgb, var(--bg-elev-2) 82%, transparent);
    border: 1px solid var(--border);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  }
  .brain-empty {
    position: absolute; inset: 0; margin: 0; padding: 0 32px;
    display: flex; align-items: center; justify-content: center;
  }
  /* Tap-to-dismiss backdrop. */
  .brain-backdrop {
    display: block; position: fixed; inset: 0; z-index: 50;
    background: color-mix(in srgb, #000 45%, transparent);
    opacity: 0; visibility: hidden;
    transition: opacity 200ms var(--ease), visibility 200ms var(--ease);
  }
  .brain-backdrop.visible { opacity: 1; visibility: visible; }
  /* The note as a bottom sheet: always laid out (display:block) but slid off-screen
     until .open, so it can animate in. */
  .brain-detail {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 51;
    margin: 0; max-height: 72vh; overflow-y: auto;
    padding: 18px 18px calc(18px + env(safe-area-inset-bottom));
    border: 1px solid var(--border); border-bottom: none;
    border-radius: 18px 18px 0 0;
    transform: translateY(101%); visibility: hidden;
    transition: transform 260ms var(--ease), visibility 260ms var(--ease);
  }
  .brain-detail.open { transform: translateY(0); visibility: visible; }
  /* Grabber affordance. */
  .brain-detail::before {
    content: ""; display: block; width: 36px; height: 4px; border-radius: 999px;
    background: var(--border); margin: -4px auto 12px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .brain-backdrop, .brain-detail { transition: none; }
}

/* ── Brain v2 — fullscreen immersive brain-scape (docs/BRAIN_UX.md §5) ─────────
   The Map IS the Brain screen: a fixed, edge-to-edge constellation with floating
   glass chrome (no nav bar over the map — owner rule). Follows the app theme (owner
   2026-07-14, "Light constellation"): dark = deep space; light overrides are at the
   end of this block + the canvas recolors itself (brainScape.ts).
   z-index 25: under the hamburger FAB (40) and the nav drawer; the hidden column
   layout sits behind. */
.brain-immerse { position: fixed; inset: 0; z-index: 25; overflow: hidden; background: #0b0a10; }
.brain-scape { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
/* Bottom HUD stack — replay pill centered ON TOP of the status pane, caption at the
   very bottom (owner). Normal flow inside, so nothing overlaps at any card height. */
.brain-hud-stack {
  position: absolute; left: 16px; right: 16px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
  z-index: 4; display: flex; flex-direction: column; align-items: center; gap: 10px;
  pointer-events: none;
}
.brain-hud-stack > * { pointer-events: auto; }
.brain-hud-bottom {
  width: 100%; text-align: center; pointer-events: none;
  font-size: 11px; color: rgba(200, 190, 230, 0.5);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Lens switcher — bare floating TEXT at top center (owner rule: NOT a nav bar,
   no capsule, no background — like the reference HUD's "CORE · IDLE" labels).
   The active lens is white with a thin gradient underline (the one sanctioned
   gradient use for the lens indicator); the inactive one is a faint whisper. */
.brain-dock {
  /* Below the hamburger row (FAB: top 12/safe+4, 48px tall) — the lens words must
     never share the app bar's line (owner). */
  position: absolute; top: calc(env(safe-area-inset-top, 0px) + 74px); left: 50%;
  /* width:max-content: an abs-positioned box at left:50% shrink-wraps to HALF the
     screen — with four dock words the CJK labels wrapped vertically on phones. */
  transform: translateX(-50%); z-index: 4; display: flex; gap: 30px;
  width: max-content; white-space: nowrap;
}
@media (max-width: 480px) {
  .brain-dock { gap: 20px; }
}
.brain-dock button {
  position: relative; border: none; background: none; cursor: pointer;
  font-weight: 600; font-size: 13px; line-height: 1; padding: 6px 2px 9px;
  color: rgba(190, 185, 215, 0.5); letter-spacing: 0.02em;
  transition: color 0.2s var(--ease);
}
.brain-dock button:hover { color: rgba(226, 220, 245, 0.85); }
.brain-dock button.is-active { color: #fff; }
.brain-dock button.is-active::after {
  content: ""; position: absolute; left: 15%; right: 15%; bottom: 0; height: 2px;
  border-radius: 2px; background: linear-gradient(90deg, var(--brand-a), var(--brand-b));
}
/* The one-sentence status card — floats above the dock, recedes once read. */
.brain-status {
  align-self: flex-start;
  max-width: 330px; text-align: left; cursor: pointer;
  border: none; border-radius: 24px; padding: 14px 16px 12px; color: #e8e8ea;
  font: inherit; font-family: inherit;
  background: color-mix(in srgb, #15141b 62%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  animation: brain-hud-recede 1.2s ease 4.5s forwards;
}
.brain-status:hover, .brain-status:focus-visible { animation: none; opacity: 1; }
@keyframes brain-hud-recede { to { opacity: 0.45; } }
.brain-status-s1 { font-size: 14.5px; line-height: 1.45; font-weight: 600; }
.brain-status-s2 { margin-top: 6px; font-size: 12px; line-height: 1.6; color: #a9a9b4; }
.brain-status-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px;
  vertical-align: 1px; background: linear-gradient(135deg, var(--brand-a), var(--brand-b));
}
.brain-status-s3 {
  display: flex; align-items: center; gap: 10px; margin-top: 10px; padding-top: 9px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.brain-status-s3 span { font-size: 11.5px; color: #8f8f9a; }
.brain-status-s3 b { color: #cfcfd8; font-weight: 650; }
.brain-pace { display: flex; align-items: flex-end; gap: 2.5px; height: 18px; }
.brain-pace i { width: 5px; border-radius: 2px; background: #3a3a42; min-height: 3px; }
.brain-pace i.hi { background: linear-gradient(180deg, var(--brand-b), var(--brand-a)); }
/* Day zero — the expectant sky (passive: no buttons, no asks). */
.brain-newborn { position: absolute; left: 24px; right: 24px; top: 36%; text-align: center; pointer-events: none; color: #e8e8ea; }
.brain-newborn b { display: block; font-size: 16px; font-weight: 650; margin-bottom: 8px; }
.brain-newborn p { margin: 0 auto; max-width: 300px; font-size: 13px; line-height: 1.6; color: rgba(210, 205, 230, 0.75); }
/* Stream + Search lenses — overlays over the DIMMED scape (owner, 2026-07-13): no
   grey glass, just darkness; the components float on the still-live sky. Anchored
   rows (widgets, tabs, input, footer) with one dedicated scroller in between. */
.brain-stream-panel {
  position: absolute; inset: 0; z-index: 3; overflow: hidden;
  display: flex; flex-direction: column;
  background: color-mix(in srgb, #05050a 62%, transparent);
  padding: calc(env(safe-area-inset-top, 0px) + 132px) 16px 0;
}
.brain-stream-panel[hidden] { display: none; }
/* Anchored status widgets (owner: "more like widgets"). */
.brain-widgets {
  flex: none; width: 100%; max-width: 640px; margin: 0 auto 10px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px;
}
.brain-widgets:empty { display: none; }
.brain-widget { border-radius: 20px; background: var(--bg-elev); padding: 12px 14px; min-width: 0; }
.brain-widget-title {
  font-weight: 600; font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase;
  color: var(--text-faint); margin-bottom: 8px;
}
.brain-widget-body {
  margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); min-width: 0;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.brain-widget-stats { display: flex; gap: 18px; }
.brain-widget-stat { display: flex; flex-direction: column; gap: 1px; }
.brain-widget-stat b { font-size: 20px; font-weight: 650; line-height: 1.15; font-variant-numeric: tabular-nums; }
.brain-widget-stat small { font-size: 11px; color: var(--text-faint); }
.brain-widget .brain-recap-btn { margin-top: 8px; }
/* Anchored day tabs — horizontal scroll; only the records below scroll vertically. */
.brain-daytabs {
  flex: none; width: 100%; max-width: 640px; margin: 0 auto; padding: 2px 0 10px;
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
}
.brain-daytabs::-webkit-scrollbar { display: none; }
.brain-daytab {
  flex: none; border: none; border-radius: 999px; background: var(--bg-elev); cursor: pointer;
  padding: 7px 14px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-dim);
}
.brain-daytab:hover { background: var(--bg-elev-1); }
.brain-daytab.is-active { background: var(--bg-elev-2); color: var(--text); }
.brain-stream-scroll {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 4px 0 calc(env(safe-area-inset-bottom, 0px) + 40px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 24px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 24px);
}
.brain-stream-panel .brain-stream { max-width: 640px; margin: 0 auto; }
.brain-recap-btn {
  font: inherit; font-size: 12px; font-weight: 600; border: none; border-radius: 999px;
  padding: 8px 16px; cursor: pointer; background: var(--bg-elev-3); color: var(--text);
}
.brain-prov { margin-top: 12px; }
.brain-ev-diffhost { margin-top: 10px; }
.brain-ev-diffhost:empty { display: none; }
.brain-about-note { margin-top: 4px; font-size: 12px; }
/* System objects (Skills / Rules / Persona / Export) — quiet pills at the bottom of
   the owner-portrait sheet (owner: the search lens shows the input and nothing else). */
.brain-about-utils { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
/* Sheets inside the immersive shell are bottom sheets on EVERY size (they slide
   over the scape); these two-class rules out-rank both the desktop base and the
   mobile media-query versions above. */
.brain-immerse .brain-backdrop {
  display: block; position: fixed; inset: 0; z-index: 50;
  background: color-mix(in srgb, #000 45%, transparent);
  opacity: 0; visibility: hidden;
  transition: opacity 200ms var(--ease), visibility 200ms var(--ease);
}
.brain-immerse .brain-backdrop.visible { opacity: 1; visibility: visible; }
.brain-immerse .brain-detail {
  display: block; position: fixed; left: 50%; bottom: 0; z-index: 51;
  width: 100%; max-width: 640px; transform: translate(-50%, 101%);
  margin: 0; max-height: 72vh; max-height: 72dvh; overflow-y: auto;
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  background: var(--bg-elev); border: none; border-radius: 24px 24px 0 0;
  visibility: hidden;
  transition: transform 260ms var(--ease), visibility 260ms var(--ease);
}
.brain-immerse .brain-detail.open { transform: translate(-50%, 0); visibility: visible; }
.brain-immerse .brain-detail::before {
  content: ""; display: block; width: 36px; height: 4px; border-radius: 999px;
  background: var(--border); margin: -4px auto 12px;
}
/* Brain Search lens (owner, 2026-07-13) — Google grammar over the dimmed sky:
   ONE clear input, a compact dropdown of top hits while typing, footer pills at
   the bottom edge. The dim + flex-column shell comes from .brain-stream-panel. */
.brain-lib-filterwrap { flex: none; width: 100%; max-width: 640px; margin: 4px auto 0; }
.brain-lib-filter {
  width: 100%; border: none; border-radius: 999px; padding: 11px 18px;
  /* 16px is the iOS no-auto-zoom floor: Safari zooms the whole page when a focused
     input's font is smaller (owner hit this on phone). Google's box is 16px too. */
  background: var(--bg-elev-1); color: var(--text); font-size: 16px; line-height: 1.4; outline: none;
}
.brain-lib-filter:focus { background: var(--bg-elev-2); }
.brain-lib-filter::placeholder { color: var(--text-faint); }
/* The dropdown — top hits only; the sky spotlight holds the full result set. */
.brain-suggest {
  margin-top: 8px; border-radius: 20px; background: var(--bg-elev); padding: 6px;
  display: flex; flex-direction: column; gap: 2px;
}
.brain-suggest[hidden] { display: none; }
.brain-suggest-row {
  display: flex; align-items: baseline; gap: 10px; min-width: 0; text-align: left; cursor: pointer;
  border: none; border-radius: 14px; background: transparent; padding: 10px 12px;
  font: inherit; color: var(--text);
}
.brain-suggest-row:hover { background: var(--bg-elev-1); }
.brain-suggest-row b { font-size: 13.5px; font-weight: 600; white-space: nowrap; }
.brain-suggest-row small {
  font-size: 11.5px; color: var(--text-faint);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.brain-suggest-none { margin: 8px 12px; }
.brain-suggest-more { margin: 4px 12px 8px; font-size: 11px; color: var(--text-faint); }
.brain-lib-util {
  border: none; border-radius: 999px; background: var(--bg-elev); cursor: pointer;
  padding: 8px 14px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-dim);
}
.brain-lib-util:hover { background: var(--bg-elev-1); color: var(--text); }

/* Review bell (B3) — a dock word like the lenses, sitting right of Search
   (owner, 2026-07-13). The .brain-dock button rule styles the text; this only
   lays out the count badge beside it. */
.brain-bell { display: inline-flex; align-items: center; gap: 6px; }
.brain-bell-badge {
  min-width: 16px; height: 16px; border-radius: 999px; padding: 0 4px;
  display: inline-grid; place-items: center;
  font-weight: 700; font-size: 10px; background: var(--bg-elev-2); color: var(--text);
}

/* Section labels shared by the Library list head and the sheets. */
.brain-sec-title {
  margin: 10px 2px 2px; font-weight: 600; font-size: 11px; line-height: 1.4; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--text-faint);
}

/* Topic sheet: quiet properties line + connection rows with their reasons. */
.brain-topic-meta {
  margin: -4px 0 10px; font-weight: 500; font-size: 11px; line-height: 1.4; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--text-faint);
}
.brain-conn {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; cursor: pointer;
  border: none; border-radius: 14px; background: var(--bg-elev-1); padding: 10px 14px;
  font: inherit; color: var(--text); margin-top: 6px;
}
.brain-conn:hover { background: var(--bg-elev-2); }
.brain-conn-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.brain-conn-main b { font-size: 14px; font-weight: 600; }
.brain-conn-main small { font-size: 12px; line-height: 1.45; color: var(--text-faint); }
.brain-conn-type {
  margin-left: auto; flex: none; font-weight: 600; font-size: 11px; line-height: 1; letter-spacing: 0.3px;
  color: var(--text-muted); background: var(--bg-elev-2); border-radius: 999px; padding: 5px 9px;
}

/* Suggested connections (C3) — approve/dismiss rows inside a recap-style card. */
.brain-sugg {
  display: flex; align-items: center; gap: 10px;
  border-radius: 14px; background: var(--bg-elev-1); padding: 10px 12px; margin-top: 8px;
}
.brain-sugg-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.brain-sugg-main b { font-size: 13.5px; font-weight: 600; }
.brain-sugg-main small { font-size: 12px; line-height: 1.45; color: var(--text-faint); }
.brain-sugg-acts { margin-left: auto; display: flex; gap: 6px; flex: none; }
.brain-sugg-btn {
  border: none; border-radius: 999px; padding: 7px 12px; cursor: pointer;
  font-weight: 600; font-size: 12px; line-height: 1;
  background: var(--bg-elev-2); color: var(--text-muted);
}
.brain-sugg-btn.is-yes { background: var(--text); color: var(--bg); }
.brain-sugg-btn:hover:not(:disabled) { filter: brightness(1.1); }
.brain-sugg-btn:disabled { opacity: 0.5; cursor: default; }

/* Growth replay (B4) — quiet centered pill riding the bottom HUD stack. */
.brain-replay {
  border: none; background: none; cursor: pointer; padding: 4px 8px;
  font-weight: 600; font-size: 12px; line-height: 1; color: var(--text-faint);
}
.brain-replay:hover { color: var(--text); }

/* Owner-curated tag (B5) — quiet trust signal on hand-edited notes. */
.brain-curated-tag { margin-left: auto; font-size: 11px; color: var(--text-faint); }

/* Forgotten (superseded) notes — struck through, quiet; Delete forever lives only here. */
.brain-note-forgotten { opacity: 0.75; }
.brain-forgot-text { text-decoration: line-through; color: var(--text-faint); }
.brain-forgot-tag { font-size: 11px; color: var(--text-faint); }

/* Per-note actions + inline editor in the topic sheet. */
.brain-note-acts { display: flex; gap: 12px; margin-top: 7px; }
.brain-note-btn {
  border: none; background: none; padding: 0; cursor: pointer;
  font-weight: 600; font-size: 12px; line-height: 1.4; color: var(--text-faint);
}
.brain-note-btn:hover:not(:disabled) { color: var(--text); }
.brain-note-btn.is-save { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.brain-note-btn.is-danger:hover:not(:disabled) { color: var(--danger); }
.brain-note-btn:disabled { opacity: 0.5; cursor: default; }
.brain-note-edit {
  width: 100%; min-height: 64px; resize: vertical; border: none; border-radius: 12px;
  background: var(--bg-elev-2); color: var(--text); padding: 10px 12px;
  font-size: 14px; line-height: 1.5; outline: none;
}
.brain-doc { white-space: pre-wrap; font-size: 12.5px; }

/* Chat memory receipt chips (docs/BRAIN_UX.md §5 B2) — tonal, quiet, inline. */
.chat-memchip-row { display: flex; justify-content: center; padding: 2px 0; }
.chat-memchip-row.is-link { cursor: pointer; }
.chat-memchip {
  display: inline-flex; align-items: center; gap: 4px; max-width: 86%;
  border-radius: 999px; padding: 7px 14px; font-size: 12px; line-height: 1.4;
  background: var(--bg-elev-1); color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-memchip-row.is-link:hover .chat-memchip { background: var(--bg-elev-2); color: var(--text); }
.chat-memchip-undo {
  border: none; background: none; padding: 0; margin: 0; cursor: pointer;
  font-weight: 600; font-size: 12px; line-height: 1.4; color: var(--text);
  text-decoration: underline; text-underline-offset: 2px;
}
.chat-memchip-undo:disabled { opacity: 0.5; cursor: default; }

/* Interrupted-reply marker: same quiet chip shape, warn-tinted so a dead-end reply
 * is explained in place (the error banner is transient). Wraps on phones — the
 * sentence + Retry must never ellipsize into an unexplained "回复中…". */
.chat-interrupt-chip {
  background: color-mix(in srgb, var(--warn) 12%, var(--bg-elev-1));
  color: var(--text);
  white-space: normal; text-align: center;
}
.session-status.interrupted { color: var(--warn); }

/* The hamburger FAB keeps contrast over the dark scape in both themes. */
body:has(.layout.brain-active) .nav-fab {
  color: #e4e4e4;
  background: color-mix(in srgb, #1c1b23 60%, transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
@media (max-width: 720px) {
  .brain-status { align-self: stretch; max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .brain-status { animation: none; }
  .brain-immerse .brain-backdrop, .brain-immerse .brain-detail { transition: none; }
}

/* Light theme (owner: "Light constellation") — the Brain screen follows the app
   theme. Dark keeps the deep-space look verbatim; light rides the app neutrals so
   the screen isn't a dark slab in a light app. The canvas recolors itself
   (brainScape.ts reads data-theme); these rules carry the fixed backdrop, the lens
   scrim, and the HUD text/chrome that was hardcoded light-on-dark. The brand-gradient
   accents (lens underline, status dot, fresh-node ring) stay — they read on both. */
:root[data-theme="light"] .brain-immerse { background: var(--bg); }
:root[data-theme="light"] .brain-stream-panel {
  background: color-mix(in srgb, var(--bg) 58%, transparent);
}
:root[data-theme="light"] .brain-status {
  background: color-mix(in srgb, var(--bg-elev) 72%, transparent);
  color: var(--text);
}
:root[data-theme="light"] .brain-status-s2 { color: var(--text-dim); }
:root[data-theme="light"] .brain-status-s3 { border-top-color: var(--border); }
:root[data-theme="light"] .brain-status-s3 span { color: var(--text-faint); }
:root[data-theme="light"] .brain-status-s3 b { color: var(--text); }
:root[data-theme="light"] .brain-pace i { background: var(--bg-elev-3); }
:root[data-theme="light"] .brain-dock button { color: var(--text-faint); }
:root[data-theme="light"] .brain-dock button:hover { color: var(--text-dim); }
:root[data-theme="light"] .brain-dock button.is-active { color: var(--text); }
:root[data-theme="light"] .brain-hud-bottom { color: var(--text-faint); }
:root[data-theme="light"] .brain-newborn { color: var(--text); }
:root[data-theme="light"] .brain-newborn p { color: var(--text-dim); }
/* The FAB glass flips to light over the now-light scape. */
:root[data-theme="light"] body:has(.layout.brain-active) .nav-fab {
  color: var(--text);
  background: color-mix(in srgb, var(--bg-elev) 70%, transparent);
}

.uset-label {
  margin: 0 0 14px; font-size: 12px; font-weight: 600; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--text-dim);
}
.uset-options { display: flex; flex-direction: column; gap: 8px; }
.uset-option {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 14px 16px; cursor: pointer; text-align: left;
  background: var(--bg-elev-2); border: none; border-radius: var(--radius-md);
  color: var(--text); font: inherit; font-size: 15px; font-weight: 500;
  transition: background 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.uset-option:hover { background: var(--bg-elev-3); }
.uset-option.is-active {
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elev-2));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 50%, transparent);
}
.uset-option-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uset-option-check { flex: none; font-size: 20px; color: var(--accent); opacity: 0; }
.uset-option.is-active .uset-option-check { opacity: 1; }
.uset-status { font-size: 12.5px; color: var(--text-faint); line-height: 1.5; margin-top: 12px; }
.uset-status.err { color: var(--danger-text); }
.uset-field {
  display: flex; align-items: center; gap: 8px;
  background: var(--bg-elev-2); border-radius: var(--radius-sm); padding: 0 14px;
  transition: box-shadow 0.15s var(--ease);
}
.uset-field:focus-within { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent); }
.uset-prefix { flex: none; color: var(--text-faint); font-size: 15px; }
.uset-input {
  flex: 1 1 auto; min-width: 0; background: transparent; border: none; outline: none;
  color: var(--text); font: inherit; font-size: 15px; padding: 12px 0;
}
.uset-input::placeholder { color: var(--text-faint); }

/* Self-improvement: Gemini toggle switches + indented sub-toggles + tonal history. */
.uset-toggle { display: flex; align-items: center; gap: 14px; padding: 11px 0; cursor: pointer; }
.uset-toggle-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.uset-toggle-label { font-size: 14px; font-weight: 500; color: var(--text); }
.uset-toggle-hint { font-size: 12px; color: var(--text-faint); line-height: 1.45; }
.uset-switch-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.uset-switch {
  flex: none; position: relative; width: 42px; height: 24px; border-radius: 999px;
  background: var(--bg-elev-3); transition: background 0.18s var(--ease);
}
.uset-switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--text-dim);
  transition: transform 0.18s var(--ease), background 0.18s var(--ease);
}
.uset-switch-input:checked + .uset-switch { background: var(--accent); }
.uset-switch-input:checked + .uset-switch::after { transform: translateX(18px); background: var(--on-accent); }
.uset-switch-input:focus-visible + .uset-switch { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
.uset-subtoggles { margin-left: 4px; padding-left: 16px; border-left: 2px solid var(--border); }
.uset-subtoggles.is-disabled { opacity: 0.45; pointer-events: none; }
.uset-actions { margin-top: 14px; }

/* Per-target apply mode: row (label/hint left, segmented control right) + the
   Gemini segmented control itself (tonal track, accent-filled active pill). */
.uset-apply { display: flex; flex-direction: column; }
.uset-apply.is-disabled { opacity: 0.45; pointer-events: none; }
.uset-apply-row {
  display: flex; align-items: center; gap: 16px; padding: 11px 0;
}
.uset-apply-row + .uset-apply-row { border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.uset-seg {
  flex: none; display: inline-flex; align-items: center; gap: 2px;
  background: var(--bg-elev-3); border-radius: 999px; padding: 3px;
}
.uset-seg-btn {
  appearance: none; border: none; cursor: pointer; font: inherit;
  font-size: 12.5px; font-weight: 500; line-height: 1; white-space: nowrap;
  color: var(--text-faint); background: transparent;
  padding: 7px 14px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  transition: background 0.18s var(--ease), color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
/* M3-style leading check on the selected segment — slides in with the tonal fill. */
.uset-seg-btn::before {
  content: "check"; font-family: "Material Symbols Rounded"; font-size: 14px; line-height: 1;
  max-width: 0; opacity: 0; overflow: hidden;
  transition: max-width 0.18s var(--ease), opacity 0.18s var(--ease);
}
.uset-seg-btn:hover { color: var(--text); }
.uset-seg-btn.is-active {
  /* Soft tonal thumb (iOS/Gemini style) instead of a full-contrast accent fill. */
  background: color-mix(in srgb, var(--accent) 16%, var(--bg-elev-2));
  color: var(--text);
  box-shadow: 0 1px 3px color-mix(in srgb, #000 25%, transparent);
}
.uset-seg-btn.is-active::before { max-width: 14px; opacity: 1; }
/* Narrow screens: the label column and the segmented control can't share a row
   (the label squeezes to one word per line) — stack them, control full-width. */
@media (max-width: 540px) {
  .uset-apply-row { flex-direction: column; align-items: stretch; gap: 10px; }
  .uset-apply-row .uset-seg { display: flex; width: 100%; }
  .uset-apply-row .uset-seg-btn { flex: 1 1 0; text-align: center; }
}
.uset-seg-btn:focus-visible {
  outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

.uset .improve-list { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 0; padding: 0; list-style: none; }
.uset .improve-item { background: var(--bg-elev-2); border: none; border-radius: var(--radius-md); padding: 13px 15px; }

/* ── Account screens (Profile / Team / Settings / Admin) ─────────────────────
   These reuse the modal section look (.settings-section / .billing-*) inside the
   standalone screen scaffold (.sched-center). Neutralize the modal-only chrome
   (side padding, inner scroll caps) so sections align with the intro. */
/* NOTE: #admin-view is intentionally excluded — the admin panel renders each
   .settings-section as a padded card (see `.admin-body .settings-section`), so it
   must NOT get this edge-to-edge, zero-horizontal-padding modal treatment. */
#profile-view .settings-section,
#team-view .settings-section,
#settings-view .settings-section {
  padding: 22px 0;
  border-bottom: 1px solid var(--border);
}
#profile-view .settings-section:last-child,
#team-view .settings-section:last-child,
#settings-view .settings-section:last-child { border-bottom: none; }
#admin-view .billing-body { max-height: none; overflow: visible; }
#admin-view .billing-note { padding: 16px 0; }

/* Copy button beside an info-row value/code (Profile + Team). */
.profile-copy {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--text-faint); cursor: pointer;
  min-width: 40px; min-height: 40px; padding: 2px; border-radius: 999px; margin-left: 2px;
  transition: color var(--ease) 120ms, background var(--ease) 120ms;
}
.profile-copy:hover { color: var(--text); background: var(--bg-elev-2); }
.profile-copy .material-symbols-rounded { font-size: 16px; }

/* ── Profile screen (profile.ui.ts) — Gemini identity hero + tonal tiles ──────
   A lettered avatar on the signature blue→purple→pink gradient (email + role
   pill) over a softly gradient-lit card (mirrors .wallet-hero), then a column of
   tonal detail tiles for the copyable user id / invite code / wallet address. */
.profile-hero-slot { margin: 8px 0 6px; }
.profile-hero {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 13px;
  padding: 34px 20px 28px;
  border-radius: var(--radius-hero);
  background:
    radial-gradient(125% 80% at 50% -10%, color-mix(in srgb, var(--grad-b) 18%, transparent), transparent 62%),
    var(--bg-elev-1);
  text-align: center;
}
.profile-avatar {
  display: grid; place-items: center;
  width: 88px; height: 88px; border-radius: 50%;
  font-family: var(--font-display);
  font-size: 38px; font-weight: 500; line-height: 1;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-a), var(--grad-b) 52%, var(--grad-c));
  box-shadow: 0 8px 26px color-mix(in srgb, var(--grad-b) 40%, transparent);
}
.profile-hero-identity {
  display: flex; flex-direction: column; align-items: center; gap: 3px; max-width: 100%;
}
.profile-hero-email {
  font-family: var(--font-display);
  font-size: 20px; font-weight: 500; letter-spacing: -0.2px;
  color: var(--text); word-break: break-all; max-width: 100%;
}
.profile-hero-sub { font-size: 13px; color: var(--text-faint); word-break: break-all; }
.profile-role-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 13px 5px 10px; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text-dim);
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.2px;
}
.profile-role-pill .material-symbols-rounded { font-size: 16px; }
.profile-role-pill.is-admin {
  background: color-mix(in srgb, var(--grad-b) 24%, var(--bg-elev-2)); color: var(--text);
}

/* Tonal detail tiles. */
.profile-detail-list { display: flex; flex-direction: column; gap: 8px; }
.profile-detail {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border-radius: var(--radius);
  background: var(--bg-elev-1);
  transition: background 0.15s var(--ease);
}
.profile-detail:hover { background: var(--bg-elev-2); }
/* Editable tile (Your name): the whole row is the tap target, not just the pencil. */
.profile-detail.is-editable { cursor: pointer; }
.profile-detail.is-editable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.profile-detail.is-editable:has(.profile-name-input) { cursor: default; }
.profile-detail-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: none; border-radius: var(--radius-tile);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elev-2)); color: var(--text-dim);
}
.profile-detail-icon .material-symbols-rounded { font-size: 20px; }
.profile-detail-main { flex: 1 1 auto; min-width: 0; }
.profile-detail-label {
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-faint);
}
.profile-detail-value { font-size: 14.5px; color: var(--text); margin-top: 2px; word-break: break-all; }
.profile-detail-value.is-mono { font-family: var(--font-mono); font-size: 13px; }
.profile-detail-sub { font-size: 12px; margin-top: 3px; }
.profile-detail-sub.is-ok { color: var(--ok); }
.profile-detail-sub.is-muted { color: var(--text-faint); }
.profile-detail-copy {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; border-radius: 50%; border: none; cursor: pointer;
  background: transparent; color: var(--text-faint);
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.profile-detail-copy:hover { color: var(--text); background: var(--bg-elev-3); }
.profile-detail-copy:disabled { opacity: 0.5; cursor: default; }
.profile-detail-copy .material-symbols-rounded { font-size: 18px; }
.profile-detail-copy.is-primary { color: var(--accent); }
.profile-detail-copy.is-primary:hover:not(:disabled) {
  color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent);
}

/* Editable display-name tile. */
.profile-detail-value.is-placeholder { color: var(--text-faint); }
.profile-name-actions { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.profile-name-input {
  width: 100%; margin-top: 3px; box-sizing: border-box;
  font-family: inherit; font-size: 14.5px; color: var(--text);
  background: var(--bg-elev-3); border: none; border-radius: 8px; padding: 7px 10px;
}
.profile-name-input::placeholder { color: var(--text-faint); }
.profile-name-input:focus {
  outline: none;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* Pending agent wallet approvals. */
.profile-actions-hint { margin: 14px 0 0; }
.profile-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.profile-action-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 14px; border-radius: var(--radius); background: var(--bg-elev-1);
}
.profile-action-what { font-size: 13.5px; color: var(--text); min-width: 0; word-break: break-word; }
.profile-action-btns { flex: none; display: inline-flex; gap: 8px; }
.profile-action-btns .skills-action { padding: 6px 14px; font-size: 12.5px; }

/* Team invite codes + create action. */
.team-code { font-family: var(--font-mono); font-size: 13.5px; color: var(--text); }
.team-actions { margin-top: 14px; }

/* Team screen — Gemini referral-count hero + tonal cards (matches profile/earn),
   replacing the old hard-bordered settings-form look. */
.team-hero {
  max-width: 560px;
  background: var(--bg-elev-1);
  border-radius: var(--radius-lg);
  padding: 22px 22px 20px;
  margin-bottom: 14px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent);
}
.team-hero-value { font-family: var(--font-display); font-size: 44px; font-weight: 700; line-height: 1.05; color: var(--text); }
.team-hero-label { font-size: 12.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-dim); margin-top: 2px; }
.team-hero-sub { display: inline-flex; align-items: center; gap: 6px; margin-top: 14px; font-size: 13px; color: var(--text-faint); }
.team-hero-sub .material-symbols-rounded { font-size: 17px; }
/* Tonal cards + borderless rows for the Team screen (standalone view + embedded tab). */
#team-view .settings-section,
.team-embed .settings-section {
  background: var(--bg-elev-1);
  border: none;
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  margin: 0 0 14px;
  max-width: 560px;
}
#team-view .settings-section h3,
.team-embed .settings-section h3 {
  margin: 0 0 6px; font-size: 12px; font-weight: 600;
  letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-dim);
}
#team-view .info-row,
.team-embed .info-row { border-bottom: none; padding: 11px 0; }
#team-view .info-row + .info-row,
.team-embed .info-row + .info-row { border-top: 1px solid var(--border); }

/* ===================================================================
   Earn screen redesign (earnings.ui.ts) — hero + referral/yield sheets.
   Reuses .earnings-topbar/.earnings-scroll/.earnings-overview/.earn-tile/
   .earn-rank-* and adds the hero, gauges, invite, team, market, search +
   chips below. The Activity feed + Leaderboard are embedded inside the
   Referral/Yield sheets (not a top-level tab), so they never duplicate the
   Wallet screen's own Activity tab. */

/* Hero: total earned + breakdown + the cash-out/reinvest pockets, all one card — the
   pockets ARE the total, just split spendable-now vs locked, not a separate surface. */
.earnings-hero {
  background: var(--bg-elev-1);
  border-radius: var(--radius-lg);
  padding: 20px 22px;
  display: flex; flex-direction: column; justify-content: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent);
}
.earnings-hero-label {
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--text-dim);
}
.earnings-hero-total {
  font-family: var(--font-display);
  font-size: 44px; font-weight: 700; line-height: 1.05; margin-top: 6px;
  background: var(--grad-h); -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Source breakdown (Referral / Yield) — TIER 3: quiet navigation into the detail sheets,
   not headline stats. Deliberately recessive (flat, muted, label-led) so it doesn't fight
   the hero total above it or the action pockets below it. */
.earnings-hero-breakdown { display: flex; gap: 8px; margin-top: 14px; }
.earnings-bd-chip {
  flex: 1; display: flex; align-items: center; gap: 8px;
  background: transparent; border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 8px 8px 8px 11px; min-width: 0;
  cursor: pointer; text-align: left; font: inherit; color: inherit;
  transition: background var(--ease) 130ms, transform var(--ease) 130ms;
}
.earnings-bd-chip:hover { background: var(--bg-elev-1); }
.earnings-bd-chip:active { transform: scale(0.985); }
.earnings-bd-main { min-width: 0; display: flex; flex-direction: column-reverse; }
.earnings-bd-chip .earnings-bd-chev { margin-left: auto; flex: none; font-size: 18px; color: var(--text-faint); }
.earnings-bd-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex: none; color: var(--text-faint);
}
.earnings-bd-icon .material-symbols-rounded { font-size: 17px; }
/* Label leads (normal weight), amount is the small secondary line. */
.earnings-bd-label { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.earnings-bd-amount { font-size: 11.5px; font-weight: 600; color: var(--text-faint); font-variant-numeric: tabular-nums; margin-top: 1px; }

/* Withdraw / claim CTA row. */
.earnings-claim {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);
}
.earnings-claim-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.earnings-claim-label { font-size: 11.5px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.5px; }
.earnings-claim-amount { font-size: 20px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.earnings-claim-pending { font-size: 11.5px; color: var(--warn); }
.earnings-claim-btn {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  border: none; cursor: pointer; border-radius: 999px; padding: 11px 20px;
  font-family: var(--font-ui); font-size: 14px; font-weight: 600;
  background: var(--grad); color: var(--on-accent);
  transition: filter var(--ease) 140ms, opacity var(--ease) 140ms;
}
.earnings-claim-btn:hover:not(:disabled) { filter: brightness(1.08); }
.earnings-claim-btn:disabled { opacity: 0.45; cursor: default; }
.earnings-claim-btn .material-symbols-rounded { font-size: 18px; }

/* ── Genesis Giveaway hero (top of the Earn screen) ─────────────────────────
   The ONE card that breaks the calm mono palette. Deep restrained violet gradient
   (premium depth), a warm champagne glow + glassy highlight for richness (not just
   text), and ONE accent — gold, reserved strictly for the prize money. */
.earn-jackpot-promo {
  position: relative; overflow: hidden;
  border-radius: var(--radius-lg); padding: 22px 22px 20px;
  display: flex; flex-direction: column; gap: 2px;
  color: #fff;
  background:
    radial-gradient(135% 120% at 14% 0%, rgba(255, 255, 255, 0.22) 0%, transparent 52%),
    linear-gradient(140deg, #2e1065 0%, #5b21b6 34%, #7c3aed 64%, #9d2d8f 100%);
  background-size: 135% 120%, 240% 240%;
  background-position: 0% 0%, 0% 50%;
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 20px 48px -18px rgba(46, 16, 101, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.16);
  animation: earn-jackpot-flow 20s ease-in-out infinite;
}
/* Diagonal shine sweep. */
.earn-jackpot-promo::before {
  content: ""; position: absolute; top: -25%; left: -75%;
  width: 45%; height: 150%; pointer-events: none; z-index: 1;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.35), transparent);
  transform: skewX(-18deg);
  animation: earn-jackpot-shine 6s var(--ease) 1.4s infinite;
}
/* Warm champagne glow in the lower-right — depth + a quiet tie to the gold money. */
.earn-jackpot-promo::after {
  content: ""; position: absolute; right: -15%; bottom: -28%;
  width: 66%; height: 78%; pointer-events: none; z-index: 0;
  background: radial-gradient(circle at center, rgba(255, 206, 120, 0.22) 0%, transparent 62%);
}
.earn-jackpot-promo > * { position: relative; z-index: 2; }
@keyframes earn-jackpot-shine { 0% { left: -75%; } 24% { left: 135%; } 100% { left: 135%; } }
@keyframes earn-jackpot-flow {
  0%, 100% { background-position: 0% 0%, 0% 50%; }
  50% { background-position: 0% 0%, 100% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .earn-jackpot-promo { animation: none; }
  .earn-jackpot-promo::before { animation: none; }
}
.earn-jackpot-promo-head { position: relative; display: flex; align-items: center; gap: 12px; }
.earn-jackpot-promo-label {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.7px; text-transform: uppercase; color: rgba(255, 244, 230, 0.78);
}
/* The headline IS the money → metallic champagne gold (with a soft drop shadow for
   depth, so it reads as an object, not flat text). The only big gold on the card. */
.earn-jackpot-promo-amount {
  position: relative; font-family: var(--font-display);
  font-size: clamp(42px, 12vw, 56px); font-weight: 800; line-height: 1.02; margin: 6px 0 2px;
  background: linear-gradient(135deg, #fff6da 0%, #ffe2a0 44%, #f4b95a 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 14px rgba(0, 0, 0, 0.28));
}
.earn-jackpot-promo .earnings-progress { position: relative; margin-top: 14px; }
.earn-jackpot-promo .earnings-progress-track { background: rgba(255, 255, 255, 0.22); }
.earn-jackpot-promo .earnings-progress-bar { background: linear-gradient(90deg, rgba(255, 255, 255, 0.85), #ffffff); }
.earn-jackpot-promo .earnings-progress-meta { color: rgba(255, 255, 255, 0.75); }
.earn-jackpot-promo .earnings-progress-meta strong { color: #fff; }

/* Premium CTA — one clean solid-white button: benefit line + the code in a single
   violet accent, a quiet copy glyph, and a soft breathing halo. No tile, no chip. */
.earn-jackpot-cta {
  position: relative; display: flex; align-items: center; gap: 12px; width: 100%;
  margin-top: 16px; padding: 12px 16px; border-radius: var(--radius-md); cursor: pointer;
  background: #fff; border: none; color: #1a1430; font: inherit; text-align: left;
  box-shadow: 0 8px 22px -12px rgba(0, 0, 0, 0.5);
  transition: transform var(--ease) 130ms, filter var(--ease) 130ms;
  animation: earn-cta-pulse 3s ease-in-out infinite;
}
.earn-jackpot-cta:hover { filter: brightness(0.98); }
.earn-jackpot-cta:active { transform: scale(0.99); }
@keyframes earn-cta-pulse {
  0%, 100% { box-shadow: 0 8px 22px -12px rgba(0, 0, 0, 0.5), 0 0 0 0 rgba(255, 255, 255, 0); }
  50% { box-shadow: 0 8px 22px -12px rgba(0, 0, 0, 0.5), 0 0 0 4px rgba(255, 255, 255, 0.16); }
}
@media (prefers-reduced-motion: reduce) { .earn-jackpot-cta { animation: none; } }
.earn-jackpot-cta-main { min-width: 0; flex: 1 1 auto; }
.earn-jackpot-cta-title { font-size: 14.5px; font-weight: 700; color: #1a1430; letter-spacing: -0.1px; }
.earn-jackpot-cta-code { display: flex; align-items: baseline; gap: 7px; margin-top: 2px; }
.earn-jackpot-cta-code-label { font-size: 10.5px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: rgba(26, 20, 48, 0.5); }
.earn-jackpot-cta-code-val { font-family: var(--font-mono); font-size: 14px; font-weight: 700; letter-spacing: 1.5px; color: #5b21b6; }
.earn-jackpot-cta-go { flex: none; display: inline-flex; align-items: center; color: #5b21b6; }
.earn-jackpot-cta-go .material-symbols-rounded { font-size: 20px; }

/* Deadline badge — quiet date chip under the amount (no days-left pill). */
.earn-jackpot-deadline {
  position: relative; display: inline-flex; align-items: center; gap: 7px; align-self: flex-start;
  margin-top: 10px; padding: 6px 12px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
  font-size: 12.5px; color: rgba(255, 255, 255, 0.92);
}
.earn-jackpot-deadline .material-symbols-rounded { font-size: 17px; color: rgba(255, 255, 255, 0.78); }
.earn-jackpot-deadline-date { font-weight: 600; }
.earn-jackpot-deadline.is-urgent { border-color: rgba(255, 209, 92, 0.55); }
.earn-jackpot-deadline.is-urgent .earn-jackpot-deadline-date { color: #ffd98a; }

/* "How it works" disclosure — admin-configured conditions + prize tiers + note. */
.earn-jackpot-how-wrap { position: relative; margin-top: 14px; }
.earn-jackpot-how-btn {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 0;
  background: none; border: none; cursor: pointer; font: inherit;
  font-size: 12px; font-weight: 600; color: rgba(255, 255, 255, 0.78);
}
.earn-jackpot-how-btn:hover { color: rgba(255, 255, 255, 0.95); }
.earn-jackpot-how-btn .material-symbols-rounded { font-size: 17px; }
.earn-jackpot-how-chev { transition: transform 0.2s var(--ease); }
.earn-jackpot-how-btn.is-open .earn-jackpot-how-chev { transform: rotate(180deg); }
.earn-jackpot-how { margin-top: 8px; }
.earn-jackpot-how.is-collapsed { display: none; }
.earn-jackpot-how-cap {
  font-size: 11px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78); margin: 10px 0 6px;
}
.earn-jackpot-how-rule { margin: 0 0 4px; font-size: 12.5px; line-height: 1.55; color: rgba(255, 255, 255, 0.86); }
.earn-jackpot-conditions { display: flex; flex-direction: column; gap: 5px; }
.earn-jackpot-condition { display: flex; align-items: flex-start; gap: 7px; font-size: 12.5px; line-height: 1.4; color: rgba(255, 255, 255, 0.92); }
.earn-jackpot-condition .material-symbols-rounded { font-size: 17px; color: #ffe08a; flex: none; margin-top: 0.5px; }
.earn-jackpot-prizes { display: flex; flex-direction: column; gap: 9px; }
.earn-jackpot-prize {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border-radius: var(--radius-md); background: rgba(0, 0, 0, 0.22);
}
/* Big champagne-gold prize figures — the exciting part. */
.earn-jackpot-prize-amt {
  flex: none; min-width: 96px;
  font-family: var(--font-display); font-weight: 800; font-size: 25px;
  line-height: 1; letter-spacing: -0.5px; font-variant-numeric: tabular-nums;
  background: linear-gradient(135deg, #fff6da 0%, #ffe2a0 44%, #f4b95a 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.earn-jackpot-prize-main { min-width: 0; }
.earn-jackpot-prize-label { font-size: 13px; font-weight: 700; color: #fff; }
.earn-jackpot-prize-desc { font-size: 12px; line-height: 1.45; color: rgba(255, 255, 255, 0.86); margin-top: 2px; }
.earn-jackpot-note { margin: 10px 0 0; font-size: 11.5px; line-height: 1.45; color: rgba(255, 255, 255, 0.74); }

/* ── Referral / yield slide-in detail sheet ────────────────────────────────
   Opened from the total-earned card's breakdown buttons. Slides in from the
   right (matches the session panel); full-width on mobile. */
.earn-sheet-overlay {
  position: fixed; inset: 0; z-index: 1100; display: flex; justify-content: flex-end;
  background: rgba(0, 0, 0, 0); transition: background 0.26s var(--ease);
}
.earn-sheet-overlay.is-open { background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(2px); }
.earn-sheet {
  display: flex; flex-direction: column;
  width: min(480px, 100%); height: 100%;
  background: var(--bg-elev); box-shadow: var(--shadow-lg);
  transform: translateX(100%); transition: transform 0.28s var(--ease);
}
.earn-sheet-overlay.is-open .earn-sheet { transform: translateX(0); }
/* Admin "Details" sheet only (admin-bi.ui.ts) — wide enough on desktop to hold a real
   data table (7 metric columns + a 4-button per-row action cell) without the actions
   collapsing into a vertical stack; the earnings screen's own sheet usage is untouched
   (stays 480px-capped). */
@media (min-width: 721px) {
  .earn-sheet.is-wide { width: min(1240px, 96vw); }
}
.earn-sheet-bar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--border); background: var(--bg-elev);
}
/* Title takes the middle so any trailing element (e.g. Power) pins to the right. */
.earn-sheet-title { flex: 1 1 auto; min-width: 0; font-size: 17px; font-weight: 600; color: var(--text); }
/* Back chevron, top-left (replaces the close X). */
.earn-sheet-back {
  flex: none; width: 36px; height: 36px; border-radius: 999px; cursor: pointer; border: none;
  background: transparent; color: var(--text-dim); display: inline-grid; place-items: center;
  transition: background var(--ease) 120ms, color var(--ease) 120ms;
}
.earn-sheet-back:hover { background: var(--bg-elev-2); color: var(--text); }
.earn-sheet-back .material-symbols-rounded { font-size: 24px; }
.earn-sheet-power { flex: none; display: inline-flex; align-items: center; }
.earn-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 18px; }
/* First section heading inside a sheet shouldn't carry the big top margin. */
.earn-sheet-body > div > .earnings-section-head:first-child { margin-top: 4px; }
/* Admin "Details" sheet tab strip (Overview/Activity/Agents) — reuses .buy-chip's
   pill-toggle look verbatim, just needs its own row layout. */
.earn-sheet-tabs { display: flex; gap: 6px; margin-bottom: 16px; }

/* Yield-agent subscription progress ring (days left out of the 30-day window). */
/* ── Yield tab: compact Power chip + taller agent cards ───────────────────── */
/* Agents-section header meta: "N agents | ⚡ 4,961". */
.yield-agents-meta { display: inline-flex; align-items: center; gap: 9px; }
.yield-meta-sep { width: 1px; height: 12px; background: var(--border-strong); flex: none; }
/* Power balance as a ⚡ + number chip (the word "Power" is never shown). Taps → Store. */
.yield-power-chip {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  border: none; background: none; font: inherit; padding: 2px 2px;
  font-size: 13px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums;
  transition: color var(--ease) 120ms;
}
.yield-power-chip:hover { color: var(--accent); }
.yield-power-chip .material-symbols-rounded { font-size: 17px; color: #f0b94a; }
.yield-power-chip.is-empty { color: var(--accent); font-weight: 600; }

/* One tall card per owned agent. */
.yield-cards { display: flex; flex-direction: column; gap: 10px; }
.yield-card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px 16px 12px; border-radius: var(--radius); background: var(--bg-elev-1);
  box-shadow: inset 0 0 0 1px var(--border);
}
.yield-card.is-lapsed { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 40%, transparent); }
.yield-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.yield-card-name { font-size: 15.5px; font-weight: 600; color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* (the star tier renders via the shared .star-pill — see src/web/star.ts) */
/* (b) the three numbers that matter */
.yield-card-stats { display: flex; gap: 8px; }
.yield-card-stat { flex: 1 1 0; min-width: 0; background: var(--bg-elev-2); border-radius: var(--radius-sm); padding: 10px 8px; text-align: center; }
.yield-card-stat-value { font-size: 16px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.yield-card-stat-label { font-size: 11px; color: var(--text-faint); margin-top: 3px; }
/* Earn-back progress (revenue model v2 §5): gross generated vs purchase price. */
.yield-payback { display: flex; flex-direction: column; gap: 5px; }
.yield-payback-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 12px; }
.yield-payback-label { font-weight: 600; color: var(--text-dim); }
.yield-payback.is-done .yield-payback-label { color: var(--ok); }
.yield-payback-nums { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.yield-payback-track { height: 5px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; }
.yield-payback-fill { height: 100%; width: 0; border-radius: inherit; background: linear-gradient(90deg, #0036ff, #c600ff); transition: width 0.6s ease; }
.yield-payback.is-done .yield-payback-fill { background: var(--ok); }
/* Appreciation line: new-agent tranche price vs what this one cost. */
.yield-apprec { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ok); }
.yield-apprec .material-symbols-rounded { font-size: 16px; }
/* (c) time left + extend */
.yield-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-top: 11px; border-top: 1px solid var(--border); }
.yield-card-status { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-dim); }
.yield-card-status .material-symbols-rounded { font-size: 18px; }
.yield-card-status.is-lapsed { color: var(--warn); }
/* Extend CTA: two segments — "+30 days" | "39 ⚡" — split by a divider. */
.yield-card-extend {
  flex: none; display: inline-flex; align-items: stretch; cursor: pointer;
  border: none; border-radius: 999px; overflow: hidden; font: inherit; font-size: 13px; font-weight: 700;
  background: var(--grad); color: var(--on-accent); transition: filter var(--ease) 130ms;
}
.yield-card-extend:hover { filter: brightness(1.06); }
.yield-card-extend-gain { display: inline-flex; align-items: center; padding: 9px 12px; }
.yield-card-extend-cost {
  display: inline-flex; align-items: center; gap: 3px; padding: 9px 13px 9px 11px;
  border-left: 1px solid color-mix(in srgb, var(--on-accent) 22%, transparent);
  font-variant-numeric: tabular-nums;
}
.yield-card-extend-cost .material-symbols-rounded { font-size: 17px; }

/* Tile icons (referral/yield dashboards). */
.earn-tile-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; margin: 0 auto 6px; border-radius: var(--radius-tile);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elev-1)); color: var(--accent);
}
.earn-tile-icon .material-symbols-rounded { font-size: 16px; }

/* Section labels + meta. */
.earnings-section-label { font-size: 12.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-dim); }
.earnings-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 22px 0 10px; }
.earnings-section-meta { font-size: 12px; color: var(--text-faint); }

/* Reward-pool label + tier-name with leading icons (scoped so partner.ui is untouched). */
.earnings-screen .earnings-overview-label,
.earnings-screen .partner-tier-name { display: inline-flex; align-items: center; gap: 7px; }
.earnings-screen .partner-tier { margin-bottom: 12px; }

/* Tier qualification gauges. */
.earnings-gauges { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.earnings-gauge-head { display: flex; align-items: baseline; justify-content: space-between; font-size: 12.5px; color: var(--text-dim); margin-bottom: 6px; }
.earnings-gauge-num { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.earnings-mini-track { height: 8px; }

/* Invite-code card. */
.earnings-invite { background: var(--bg-elev-1); border-radius: var(--radius); padding: 16px 18px; margin-top: 14px; }
.earnings-invite-main { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.earnings-invite-label { font-size: 12px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.5px; }
.earnings-invite-code { display: flex; align-items: center; gap: 6px; }
.earnings-invite-code .team-code { font-size: 18px; letter-spacing: 1px; }
.earnings-invite-hint { font-size: 12.5px; color: var(--text-dim); margin-top: 8px; line-height: 1.5; }

/* List-row leading icon + negative amounts. */
.earnings-row-icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 36px; height: 36px; border-radius: var(--radius-tile);
  background: var(--bg-elev-2); color: var(--text-dim);
}
.earnings-row-icon .material-symbols-rounded { font-size: 19px; }
/* Qualified daily-pool row: green check. */
.earnings-row-icon.is-qualified { background: color-mix(in srgb, var(--ok) 18%, var(--bg-elev-1)); color: var(--ok); }
.earnings-row-amount.is-neg { color: var(--text-faint); }

/* Explainer notes. */
.earnings-note { font-size: 12.5px; color: var(--text-dim); background: var(--bg-elev-1); border-radius: var(--radius-sm); padding: 12px 14px; margin: 12px 0; line-height: 1.5; }
.earnings-note-split { display: flex; flex-direction: column; gap: 4px; }
.earnings-note strong { color: var(--text); }

/* ====================== Node-tier badges & referral upgrades ===============
   A cohesive metal family, premium escalating: Regular (graphite) → Light (silver)
   → Master (gold) → Super (polished black-diamond). Identities fixed across themes. */
.tier-badge {
  position: relative; flex: none; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  color: #1b1b1b;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 3px 10px rgba(0, 0, 0, 0.3);
}
.tier-badge .material-symbols-rounded { position: relative; z-index: 1; font-variation-settings: "FILL" 1; }
.tier-badge-shine {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 48%);
  mix-blend-mode: screen;
}
.tier-badge--lg { width: 54px; height: 54px; border-radius: var(--radius); }
.tier-badge--lg .material-symbols-rounded { font-size: 28px; }
.tier-badge--sm { width: 38px; height: 38px; border-radius: var(--radius-tile); }
.tier-badge--sm .material-symbols-rounded { font-size: 20px; }
.tier-badge--regular { background: linear-gradient(145deg, #9aa3af, #646b78); color: #f3f4f6; }
.tier-badge--light { background: linear-gradient(145deg, #e8edf3, #a7b2c0); color: #2b333f; }
.tier-badge--master { background: linear-gradient(145deg, #f6d98b, #d2a23c); color: #4a360f; }
.tier-badge--super { background: linear-gradient(150deg, #2c3242, #0f1219); color: #dbe6ff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 0 0 1px rgba(150, 180, 255, 0.2), 0 3px 14px rgba(70, 110, 200, 0.4);
}
.tier-badge--super .material-symbols-rounded { text-shadow: 0 0 10px rgba(140, 180, 255, 0.7); }

/* Current-tier hero block: card + the "All tiers" benefits disclosure. */
.tier-block { display: flex; flex-direction: column; }
.tier-card { display: flex; flex-direction: column; }
.tier-card-top { display: flex; align-items: flex-start; gap: 14px; }
.tier-card-id { flex: 1 1 auto; min-width: 0; }
.tier-card-name { font-family: var(--font-display); font-size: 1.25rem; font-weight: 650; color: var(--text); line-height: 1.12; }
.tier-card .partner-tier-rates { margin-top: 3px; }
.tier-card--light { background: linear-gradient(180deg, color-mix(in srgb, #aeb8c4 10%, var(--bg-elev-1)), var(--bg-elev-1)); }
.tier-card--master { background: linear-gradient(180deg, color-mix(in srgb, #f2c14e 12%, var(--bg-elev-1)), var(--bg-elev-1)); }
.tier-card--super {
  background: linear-gradient(180deg, color-mix(in srgb, #6c8bff 14%, var(--bg-elev-1)), var(--bg-elev-1));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent);
}
/* Clear pill button on the badge row that opens the all-tiers comparison. */
.tier-benefits-btn {
  flex: none; display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  font: inherit; font-size: 12px; font-weight: 600;
}
.tier-benefits-btn:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.tier-benefits-btn .material-symbols-rounded { font-size: 15px; }
.tier-learn-chev { transition: transform 0.2s var(--ease); }
.tier-benefits-btn.is-open .tier-learn-chev { transform: rotate(180deg); }
/* In-card performance dashboard (3 stats) + invite code. */
.tier-divider { height: 1px; background: color-mix(in srgb, var(--text-faint) 16%, transparent); margin: 16px 0; }
.tier-perf { display: flex; gap: 8px; }
.tier-perf-stat { flex: 1 1 0; min-width: 0; text-align: center; }
.tier-perf-icon { color: var(--text-faint); }
.tier-perf-icon .material-symbols-rounded { font-size: 18px; }
.tier-perf-value { font-family: var(--font-display); font-size: clamp(15px, 4.6vw, 20px); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tier-perf-label { font-size: 11px; color: var(--text-faint); margin-top: 3px; line-height: 1.3; }
.tier-invite {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid color-mix(in srgb, var(--text-faint) 16%, transparent);
}
.tier-invite-label { font-size: 12px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.5px; }
.tier-invite-code { display: flex; align-items: center; gap: 8px; }
.tier-invite-code .team-code { font-size: 17px; letter-spacing: 1px; }

/* All-tiers benefits ladder (revealed by "Compare all tiers"). */
.tier-ladder { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.tier-ladder.is-collapsed { display: none; }
.tier-rung {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-radius: var(--radius-md); background: var(--bg-elev-1); border: 1px solid transparent;
}
.tier-rung:not(.is-current):not(.is-achieved) { opacity: 0.78; }
.tier-rung.is-current {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-elev-1));
}
.tier-rung-main { flex: 1 1 auto; min-width: 0; }
.tier-rung-name { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--text); }
.tier-rung-req { font-size: 12px; color: var(--text-faint); margin-top: 3px; }
.tier-rung-tag {
  font-size: 11px; font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent); padding: 1px 8px; border-radius: 999px;
}
.tier-rung-mark { display: inline-flex; }
.tier-rung-mark .material-symbols-rounded { font-size: 16px; }
.tier-rung-mark.is-done { color: var(--ok); }
.tier-rung-mark.is-locked { color: var(--text-faint); }
.tier-rung-rates { display: flex; gap: 14px; flex: none; }
.tier-rung-rate { text-align: right; line-height: 1.12; }
.tier-rung-rate strong { display: block; font-size: 15px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.tier-rung-rate span { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); }

/* ===== Daily dividend pool: one cohesive bounded section ===== */
.pool-section { background: var(--bg-elev-1); border-radius: var(--radius-lg); overflow: hidden; }
.pool-hero {
  position: relative; overflow: hidden; padding: 20px 22px;
  background: linear-gradient(150deg, color-mix(in srgb, var(--accent-2) 22%, var(--bg-elev-2)), var(--bg-elev-2) 72%);
}
.pool-hero-glow {
  position: absolute; top: -40%; right: -18%; width: 260px; height: 260px; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 32%, transparent), transparent 70%); pointer-events: none;
}
.pool-hero-label {
  position: relative; display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-dim);
}
.pool-hero-label .material-symbols-rounded { font-size: 18px; }
.pool-hero-amount {
  position: relative; font-family: var(--font-display); font-size: 40px; font-weight: 700; line-height: 1.05; margin-top: 8px;
  background: var(--grad-h); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.pool-hero-sub { position: relative; font-size: 12.5px; color: var(--text-dim); margin-top: 6px; }
.pool-block { padding: 14px 16px 2px; }
.pool-block-cap { font-size: 11.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-faint); margin-bottom: 10px; }
.pool-rows { display: flex; flex-direction: column; gap: 6px; }
.pool-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--bg-elev-2); }
.pool-row-icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; border-radius: var(--radius-tile); background: var(--bg-elev-3); color: var(--text-faint);
}
.pool-row-icon .material-symbols-rounded { font-size: 18px; }
/* Qualified vs locked is carried by the check/lock icon shape, not colour — keep it
   monochrome so the section reads as one calm surface. */
.pool-row-icon.is-qualified { background: color-mix(in srgb, var(--accent) 16%, var(--bg-elev-2)); color: var(--accent); }
.pool-row-main { flex: 1 1 auto; min-width: 0; }
.pool-row-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 14px; font-weight: 500; color: var(--text); }
.pool-row .earnings-type { background: var(--bg-elev-3); }
.pool-row-sub { font-size: 12px; color: var(--text-faint); margin-top: 2px; }
.pool-row-fig { text-align: right; flex: none; }
/* Pool amounts are figures, not signed ledger movements — keep them neutral text. */
.pool-row-amount { font-size: 14px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.pool-row-note { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.pool-dash { display: flex; gap: 8px; }
.pool-stat { flex: 1 1 0; min-width: 0; background: var(--bg-elev-2); border-radius: var(--radius-sm); padding: 13px 10px; text-align: center; }
/* "Today" is the live figure — emphasise it with the neutral theme accent, not colour. */
.pool-stat.is-accent { background: color-mix(in srgb, var(--accent) 12%, var(--bg-elev-2)); }
.pool-stat-value {
  font-size: clamp(15px, 4.4vw, 21px); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pool-stat-label { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* "How it works" disclosure — rules + requirements, collapsed by default. */
.pool-learn-wrap { padding: 8px 16px 14px; }
.pool-learn {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 2px;
  background: none; border: none; cursor: pointer; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
}
.pool-learn:hover { opacity: 0.85; }
.pool-learn-chev { font-size: 18px; transition: transform 0.2s var(--ease); }
.pool-learn.is-open .pool-learn-chev { transform: rotate(180deg); }
.pool-details.is-collapsed { display: none; }
.pool-details { margin-top: 8px; }
.pool-details p { font-size: 12px; color: var(--text-dim); line-height: 1.55; margin: 0 0 8px; }
.pool-details p:last-child { margin-bottom: 0; }

/* "Earnings over time" card — reuses .crm-chart* design with stacked Stream A/B bars. */
/* Extra top room so the max gridline's label clears the section header above it. */
.earn-chart-wrap { padding-top: 22px; }
.earn-chart-head { margin: 22px 0 0; }
.earn-chart-head .earnings-section-label { margin-bottom: 0; }
.earn-bar {
  width: 100%; min-height: 2px; display: flex; flex-direction: column;
  border-radius: 3px 3px 0 0; overflow: hidden; opacity: 0.9; transition: opacity 0.12s var(--ease);
}
.crm-chart-col:hover .earn-bar { opacity: 1; }
.earn-bar-seg { width: 100%; }
/* Two income streams distinguished by accent weight, not by hue — keeps the chart in
   the same monochrome system as the rest of the screen. */
.earn-bar-seg.is-commission { background: color-mix(in srgb, var(--accent) 72%, transparent); }
.earn-bar-seg.is-rebate { background: color-mix(in srgb, var(--accent) 34%, transparent); }
.earn-chart-legend { display: flex; gap: 16px; margin-top: 8px; font-size: 11.5px; color: var(--text-dim); }
.earn-legend-dot { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 6px; vertical-align: middle; }
.earn-legend-dot.is-commission { background: color-mix(in srgb, var(--accent) 72%, transparent); }
.earn-legend-dot.is-rebate { background: color-mix(in srgb, var(--accent) 34%, transparent); }

/* 3-layer team, grouped into collapsible Layer 1/2/3 sections. */
.team-layers { display: flex; flex-direction: column; gap: 10px; }
.team-layer { background: var(--bg-elev-1); border-radius: var(--radius-md); overflow: hidden; }
.team-layer-head {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 12px 14px; background: transparent; border: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
/* Layer badges are just labels (the row also says "Layer 1/2/3") — one neutral
   treatment for all, no per-layer colour coding. */
.team-layer-pill {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 30px; height: 30px; border-radius: var(--radius-tile); font-weight: 700; font-size: 14px;
  color: var(--text); background: color-mix(in srgb, var(--accent) 12%, var(--bg-elev-2));
}
.team-layer-id { flex: 1 1 auto; min-width: 0; }
.team-layer-name { font-weight: 600; color: var(--text); }
.team-layer-meta { font-size: 12px; color: var(--text-faint); margin-top: 2px; }
.team-layer-chev { font-size: 22px; color: var(--text-faint); transition: transform 0.2s var(--ease); }
.team-layer-body { display: flex; flex-direction: column; gap: 4px; padding: 0 10px 10px; }
.team-layer-body.is-collapsed { display: none; }
.team-layer-more { font-size: 12px; color: var(--text-faint); padding: 8px 6px; }

/* Yield market / buy. */
.earnings-market { background: var(--bg-elev-1); border-radius: var(--radius); padding: 16px 18px; margin-top: 4px; }
.earnings-market-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.earnings-market-grid { display: flex; gap: 10px; margin-bottom: 14px; }
.earnings-mini { flex: 1; background: var(--bg-elev-2); border-radius: var(--radius-sm); padding: 10px 12px; text-align: center; min-width: 0; }
.earnings-mini-value { font-size: 15px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.earnings-mini-label { font-size: 11px; color: var(--text-faint); margin-top: 2px; }
.earnings-buy-btn { width: 100%; justify-content: center; display: inline-flex; align-items: center; gap: 7px; }
.earnings-buy-btn .material-symbols-rounded { font-size: 18px; }

/* Ranks: "you're #N" banner. */
.earnings-rank-you {
  font-size: 13px; font-weight: 600; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elev-1));
  border-radius: var(--radius-sm); padding: 10px 14px; margin-bottom: 8px;
}

/* Error-state retry. */
.earnings-retry-wrap { margin-top: 16px; display: flex; justify-content: center; }

/* ===== Earn: two-pocket rail + reinvest & gacha ===========================
   Builds on the Gemini tokens. The reinvest/gacha surfaces are the one place the
   multi-colour gradient stops (--grad-a/b/c) earn their keep — a "premium pull" accent
   reserved for the locked-funded growth actions, so they read as special vs the calm
   monochrome of the rest of the screen. Rarity hues: common=neutral, rare=blue,
   epic=purple, legendary=gold. */
/* align-items: start (not the grid default stretch) — Cash out can grow taller once its
   withdrawal-progress fold is open without forcing Reinvest to stretch to match. */
.earn-pockets { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; gap: 10px; margin-top: 14px; }
.earn-pocket {
  display: flex; flex-direction: column; gap: 3px;
  background: var(--bg-elev-2); border-radius: var(--radius-md); padding: 13px 14px 14px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
}
.earn-pocket--reinvest {
  background:
    linear-gradient(150deg, color-mix(in srgb, var(--grad-a) 16%, transparent), color-mix(in srgb, var(--grad-b) 16%, transparent)),
    var(--bg-elev-2);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--grad-b) 32%, transparent);
}
/* Row form for Cash out's head, so the Wallet cross-link pill can sit opposite the label
   instead of stacking under it. Reinvest has no pill, so it just uses .earn-pocket-head
   alone (inline-flex, sized to content). */
.earn-pocket-head-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.earn-pocket-head {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: var(--text-dim);
}
.earn-pocket-head .material-symbols-rounded { font-size: 16px; }
/* Cross-link pill between the two USDT surfaces (Earn ⇄ Wallet) — same shape as the
   Wallet screen's own corner shortcut, so the bridge reads the same from both sides. */
.earn-wallet-pill {
  flex: none; display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px 4px 7px; border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font: inherit; font-size: 11px; font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.earn-wallet-pill:hover { background: var(--bg-elev-1); color: var(--text); }
.earn-wallet-pill .material-symbols-rounded { font-size: 14px; }
/* In-flight withdrawal count riding the pill — the request list itself lives in the
   Wallet's Activity → Withdrawals view the pill opens. */
.earn-wallet-pill-badge {
  min-width: 15px; height: 15px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  font-size: 9.5px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 18%, transparent);
}
.earn-pocket-amount { font-size: 22px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; line-height: 1.1; }
.earn-pocket-sub { font-size: 11px; color: var(--text-faint); margin-bottom: 10px; min-height: 14px; }
.earn-pocket-btn {
  margin-top: auto; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; padding: 10px 12px; border: none; border-radius: 999px; cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 650;
  background: var(--accent); color: var(--on-accent);
  transition: transform var(--ease) 130ms, opacity var(--ease) 130ms, box-shadow var(--ease) 130ms;
}
.earn-pocket-btn .material-symbols-rounded { font-size: 18px; }
.earn-pocket-btn:not(:disabled):hover { opacity: 0.92; }
.earn-pocket-btn:not(:disabled):active { transform: scale(0.97); }
.earn-pocket-btn:disabled { opacity: 0.4; cursor: default; }
.earn-pocket-btn--alt {
  background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); color: #fff;
  box-shadow: 0 6px 18px -8px color-mix(in srgb, var(--grad-b) 80%, transparent);
}

/* The locked-balance chip pinned top-right in the Reinvest sheet bar. */
.earn-lock-chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 11px; border-radius: 999px;
  font-size: 13px; font-weight: 650; font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--grad-b) 18%, var(--bg-elev-1)); color: var(--text);
}
.earn-lock-chip .material-symbols-rounded { font-size: 15px; color: var(--grad-b); }

/* Reinvest sheet — portrait store-cards (the locked balance lives in the top-bar chip). One
   accent hue PER CARD keeps the palette cohesive (Gemini) while the tier ramp + top-tier glow
   bring the gamified hype. Tier ramp: silver → blue → purple → gold; the Genie agent card is
   the hero and wears the brand gradient (the one multi-colour accent on the screen). */
.reinvest-tab { display: flex; flex-direction: column; }
.reinvest-agent-section { margin-top: 6px; }
/* auto-fit (not auto-fill) — .pack-grid now only wraps the single Buy-a-Genie card, and
   auto-fill reserves as many minmax tracks as geometrically fit regardless of item count,
   so a lone card sat in one ~half-width track with an invisible empty second track eating
   the rest of the row. auto-fit collapses that empty track to 0, letting the card's 1fr
   track take the full row — this is what was silently shrinking it below the pack-rail's
   full-width cards. */
/* 2/3 width, matching .pack-rail .pack-card below so the Buy-a-Genie card and the pack
   cards stay the same size as each other. */
.pack-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); max-width: 66.6%; }
/* Card packs — a horizontal rail, not a wrapped grid: the most-promoted (top-tier) pack
   leads leftmost, the rest are a swipe away. No bleed/negative-margin trick — the rail
   stays inset exactly like .store-grid above it, so cards line up at the same edges
   (a bled rail computed its 100%-width cards against a slightly different box, which is
   what was throwing the two cards off by a few px). */
.pack-rail {
  display: flex; gap: 12px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.pack-rail::-webkit-scrollbar { display: none; }
/* 2/3 of the rail's width — matches .pack-grid's cap on the Buy-a-Genie card above (same
   fraction of the same container width, so both stay the same size). The remaining third
   (minus the gap) peeks the next card at the edge, inviting a swipe. */
.pack-rail .pack-card { flex: 0 0 66.6%; scroll-snap-align: start; }
.pack-card {
  --pack: #9aa0a6; gap: 8px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pack) 22%, transparent);
}
.pack-card--t1 { --pack: #9aa0a6; }
.pack-card--t2 { --pack: #38bdf8; }
.pack-card--t3 { --pack: #c084fc; }
.pack-card--t4 { --pack: #ffce5a; }
/* Top tier = the jackpot pack: brighter ring + a soft outer glow. */
.pack-card.is-top { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--pack) 65%, transparent), 0 0 34px -14px var(--pack); }
.pack-emblem {
  display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--pack) 20%, var(--bg-elev-2)); color: var(--pack);
}
.pack-emblem .material-symbols-rounded { font-size: 22px; }
.pack-name { font-size: 14px; font-weight: 700; color: var(--text); }
/* "N-card pack" — signals the booster-pack hand size under the pack name. */
.pack-slots { font-size: 11px; font-weight: 600; color: var(--gtc-accent, var(--accent)); letter-spacing: 0.2px; margin-top: -1px; }
.pack-badge { background: var(--pack); color: #0a0b0d; }
/* Featured odds — dim text with tier-hue dots (no per-rarity colour rainbow). */
/* Free-pull grants — gift chips above the store, each opens its pack for free. Brand-gradient
   so they read as a reward, distinct from the frosted paid pack cards. */
.gacha-grants { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.gacha-grant {
  display: inline-flex; align-items: center; gap: 9px; padding: 8px 12px; border: 0; cursor: pointer;
  border-radius: 14px; color: #fff; text-align: left;
  background: linear-gradient(135deg, #0036ff, #c600ff);
  box-shadow: 0 6px 16px -8px rgba(120, 40, 255, 0.7);
  transition: transform 0.12s ease, filter 0.12s ease;
}
.gacha-grant:hover { filter: brightness(1.06); }
.gacha-grant:active { transform: translateY(1px); }
.gacha-grant:disabled { opacity: 0.55; cursor: default; filter: none; transform: none; }
.gacha-grant-ic { display: inline-flex; }
.gacha-grant-ic .material-symbols-rounded { font-size: 22px; }
.gacha-grant-body { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.gacha-grant-title { font-size: 12.5px; font-weight: 800; }
.gacha-grant-reason { font-size: 10.5px; font-weight: 600; opacity: 0.85; }
.gacha-grant-open {
  margin-left: 4px; font-size: 11px; font-weight: 800; letter-spacing: 0.3px; text-transform: uppercase;
  background: rgba(255, 255, 255, 0.2); border-radius: 999px; padding: 3px 9px;
}
.pack-odds { display: flex; flex-wrap: wrap; gap: 4px 10px; margin: 0 0 2px; font-size: 11.5px; color: var(--text-dim); }
.pack-odd { display: inline-flex; align-items: center; gap: 5px; }
.pack-odd-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--pack); }
.pack-odd-pct { color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* Tier-tinted CTA: a calm tint for entry tiers, a solid fill on the top tier (extra pop). */
.pack-card .store-buy {
  background: color-mix(in srgb, var(--pack) 20%, var(--bg-elev-2)); color: var(--pack);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pack) 42%, transparent);
}
.pack-card.is-top .store-buy { background: var(--pack); color: #0a0b0d; box-shadow: none; }
.pack-card .store-buy:disabled { opacity: 0.5; }
/* The Genie agent card — the hero purchase, so it wears the brand gradient.
   Scoped to .pack-card so it can't leak into the agent-switcher dropdown,
   whose rows also use the .agent-card class. */
.pack-card.agent-card { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--grad-b) 34%, transparent); }
.pack-card.agent-card .pack-emblem { background: linear-gradient(135deg, var(--grad-a), var(--grad-b), var(--grad-c)); color: #fff; }
.pack-card.agent-card .store-buy { background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); color: #fff; box-shadow: none; }
.agent-card .store-buy:disabled { opacity: 0.5; }
.agent-tag { font-size: 11.5px; color: var(--text-faint); line-height: 1.4; margin: 0 0 2px; }

/* Upgrade card collection (Wallet screen's Cards tab) — a fixed 2-up grid of collectible
   tiles styled like the gacha reveal card itself (rarity-tinted border, dark radial
   interior, a glowing circular icon as the "art"), just shrunk down and static — so a card
   in your collection visually matches the moment you won it, not a generic list row.
   Fixed 2 columns per the design brief (not auto-fit) — a lone tile stays half-width,
   left-aligned, rather than stretching to fill the row. */
/* minmax(0,1fr) — NOT plain 1fr — so the columns can shrink below the cards' min-content
   instead of blowing the grid wider than its container (which let the mobile Wallet scroll
   sideways). The page then only ever scrolls vertically. */
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 720px) { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.card-tile {
  --card-accent: #9aa0a6;
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  /* Same proportions as the actual gacha reveal card (.gacha-spinner is 300×416) — width
     comes from the 3-up grid column, height follows the ratio (grows past it if content
     needs more, never shrinks below it), so a tile reads as a scaled-down replica, not a
     generic square box. */
  aspect-ratio: 300 / 416;
  padding: 16px 10px 12px;
  border-radius: var(--radius-lg);
  border: 2px solid transparent;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--card-accent) 20%, #0a0b0d), #0a0b0d 78%) padding-box,
    linear-gradient(140deg, var(--card-accent), color-mix(in srgb, var(--card-accent) 55%, #000) 50%, var(--card-accent)) border-box;
  box-shadow: 0 14px 28px -14px rgba(0, 0, 0, 0.6), 0 0 22px -12px var(--card-accent);
}
.card-tile--mid { --card-accent: #ffce5a; }
.card-tile--high { --card-accent: #c084fc; }
.card-tile-head { position: absolute; top: 10px; left: 11px; right: 11px; display: flex; align-items: center; justify-content: space-between; }
.card-tile-rank { font-size: 10.5px; font-weight: 800; letter-spacing: 1px; color: var(--card-accent); }
.card-tile-gem { display: inline-flex; color: var(--card-accent); filter: drop-shadow(0 0 4px var(--card-accent)); }
.card-tile-gem .material-symbols-rounded { font-size: 13px; }
/* Progress-toward-next-star chip in the tile head — a target for the collection. */
.card-tile-prog {
  font-size: 10px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: 0.2px;
  color: var(--card-accent); background: color-mix(in srgb, var(--card-accent) 15%, transparent);
  border-radius: 999px; padding: 2px 7px;
}
.card-tile-prog.is-ready { color: #fff; background: var(--card-accent); }
.card-tile-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; margin-top: 6px; border-radius: 50%; color: #fff;
  background: color-mix(in srgb, var(--card-accent) 18%, rgba(0, 0, 0, 0.5));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--card-accent) 55%, transparent), 0 0 18px -4px var(--card-accent);
}
.card-tile-icon .material-symbols-rounded { font-size: 26px; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); }
/* Upgrade card art: the same upgrade.svg used in the gacha reveal, scaled for the collectible
   tile (a mini replica of the reveal card) instead of the old glyph-in-a-circle. */
.card-tile-art { display: block; width: 120px; height: 120px; margin: 6px 0 2px; filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.5)); }
.card-tile-count { font-size: 12.5px; font-weight: 700; color: rgba(255, 255, 255, 0.82); }
/* Same explainer copy as the reveal card / old list row — always visible, not a hover-only
   title attribute, so what the card is FOR doesn't require discovering a tooltip. */
.card-tile-desc { font-size: 11.5px; line-height: 1.42; text-align: center; color: rgba(255, 255, 255, 0.64); }
/* The general outcome of an upgrade — accent-tinted so it reads as the "why". */
.card-tile-result { font-size: 11.5px; line-height: 1.42; text-align: center; font-weight: 600; margin-top: 5px; color: color-mix(in srgb, var(--card-accent) 82%, #fff); }
.card-tile-prog {
  font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.62);
  background: rgba(255, 255, 255, 0.1); border-radius: 999px; padding: 3px 10px;
}
.card-tile-prog.is-ready { color: #0a0b0d; background: var(--card-accent); font-weight: 800; }

/* Gacha card (Wallet → Cards) — an openable .card-tile mirroring the gacha reveal's card BACK
   (the mystery card + white Superpower wordmark). Each PACK TIER gets its own colour palette via
   --pack, matching the Store/Reinvest pack-card hues (t1 silver → t4 gold): a tier-hued shimmer
   over a deep base + a tier glow, so a Cosmic pack reads distinctly from a free pull at a glance. */
.card-tile--gacha {
  --pack: #9aa0a6; /* default = Free pull / tier 1 */
  cursor: pointer; font: inherit; text-align: center;
  -webkit-appearance: none; appearance: none;
  border: 2px solid color-mix(in srgb, var(--pack) 60%, transparent);
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--pack) 58%, #16102a),
    color-mix(in srgb, var(--pack) 30%, #100a1e) 54%,
    color-mix(in srgb, var(--pack) 64%, #1d1436) 100%);
  background-size: 200% 200%;
  animation: buy-card-aurora 9s ease-in-out infinite;
  box-shadow: 0 14px 28px -14px rgba(0, 0, 0, 0.6), 0 0 26px -12px var(--pack);
  transition: transform 0.12s var(--ease), filter 0.12s var(--ease);
}
/* Per-tier palette (same hues as .pack-card--t1..t4 in the Store/Reinvest packs). */
.card-tile--gacha.gacha-t1 { --pack: #9aa0a6; }
.card-tile--gacha.gacha-t2 { --pack: #38bdf8; }
.card-tile--gacha.gacha-t3 { --pack: #c084fc; }
.card-tile--gacha.gacha-t4 { --pack: #ffce5a; box-shadow: 0 14px 28px -14px rgba(0, 0, 0, 0.6), 0 0 34px -10px var(--pack); }
.card-tile--gacha:hover { filter: brightness(1.06); }
.card-tile--gacha:active { transform: translateY(1px); }
.card-tile--gacha:disabled { opacity: 0.55; cursor: default; }
.card-tile--gacha .card-tile-rank { color: #fff; }
.card-tile--gacha .card-tile-gem { color: #fff; filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.6)); }
/* Superpower wordmark emblem — matches .gacha-back-emblem / .gacha-back-logo on the reveal. */
.gacha-tile-emblem {
  display: flex; align-items: center; justify-content: center;
  width: 62%; aspect-ratio: 1 / 1; border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 0, 0, 0.12) 30%, transparent 66%);
}
.gacha-tile-logo { width: 92%; opacity: 0.94; filter: brightness(0) invert(1) drop-shadow(0 0 12px rgba(255, 255, 255, 0.35)); }
/* "What's inside" loot overview — tiny frosted chips under the emblem teasing the drop CATEGORIES
   (upgrade cards / Credits / rare agent). Keep the emblem full-size (the wordmark is the hero); the
   loot chips tuck under it. */
.card-tile--gacha:has(.gacha-loot) .gacha-tile-emblem { width: 64%; }
/* Card layout: emblem+loot form one bottom-aligned block at the top, the price sits vertically
   centred, and the CTA pins to the bottom — so price + CTA land at the SAME height on every card no
   matter how many loot chips a pack shows. */
.card-tile--gacha { justify-content: space-between; }
.gacha-tile-body {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  gap: 8px; width: 100%; padding-top: 14px;
}
.gacha-loot { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px 5px; padding: 0 2px; }
.gacha-loot-chip {
  display: inline-flex; align-items: center; gap: 2.5px;
  font-size: 9px; font-weight: 700; line-height: 1; white-space: nowrap;
  color: rgba(255, 255, 255, 0.92);
  background: rgba(0, 0, 0, 0.28); border-radius: 999px; padding: 2.5px 7px 2.5px 5px;
}
.gacha-loot-chip .material-symbols-rounded { font-size: 11px; opacity: 0.95; }
/* Fungible stack count (×N) — white + bold so a stacked pack reads at a glance, like the star-up ×N. */
.card-tile--gacha .card-tile-count { color: #fff; font-weight: 800; font-size: 15px; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }
/* Store version: the Power price sits where the ×N stack count does on a Wallet card. */
.gacha-tile-price { color: #fff; font-weight: 800; font-size: 15px; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }
.gacha-tile-price .cur-icon { font-size: 17px; }
/* Reinvest version: a lock + $ price (opened with locked earnings, not Power). */
.gacha-tile-price--lock { display: inline-flex; align-items: center; gap: 4px; }
.gacha-tile-price--lock .material-symbols-rounded { font-size: 14px; opacity: 0.85; }
/* Reinvest sheet: the gacha packs wrap in a 2-col grid (like the Shop on mobile) — the sheet is
   ≤480px, so 2-up keeps the cards a good size (2+1 for the three packs). */
.reinvest-pack-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

/* Store currency card (Power / Credits) — SAME layout as the Gacha card: an absolute head
   (name + a currency gem, top-right), the amount as the centred hero (where the Gacha logo
   sits), then a chip + price + white CTA. Reuses the .card-tile gradient/glow, themed via
   --card-accent (Power = electric blue, Credits = gold). */
.card-tile--store {
  cursor: pointer; font: inherit; -webkit-appearance: none; appearance: none;
  container-type: inline-size; /* size the hero amount to the card width (cqw) so long numbers never truncate */
  transition: transform 0.12s var(--ease), filter 0.12s var(--ease);
}
.card-tile--store.is-top { box-shadow: 0 14px 28px -14px rgba(0, 0, 0, 0.6), 0 0 36px -10px var(--card-accent); }
.card-tile--store:hover { filter: brightness(1.07); }
.card-tile--store:active { transform: translateY(1px); }
.card-tile--store .card-tile-rank { color: rgba(255, 255, 255, 0.72); }
.card-tile--store .card-tile-gem { color: var(--card-accent); filter: drop-shadow(0 0 4px color-mix(in srgb, var(--card-accent) 55%, transparent)); }
/* "Best value" sits BOTTOM-CENTER, in the empty band below the centred CTA — top-right (image
   22/24) collided with the pack name. A ribbon-style pill anchored to the card's lower edge. */
.card-tile--store .store-badge {
  top: auto; right: auto; bottom: 10px; left: 50%; transform: translateX(-50%);
  background: var(--card-accent); color: #0a0b0d;
}
/* Hero occupies the EXACT footprint of the gacha card's emblem (62% width, 1:1) — same box, so
   the price + Buy below it land at the identical positions as the gacha card's. */
.store-hero { width: 62%; aspect-ratio: 1 / 1; margin: 0 auto; display: grid; place-items: center; }
/* The amount is a single unbroken unit sized to the card width — big on desktop, shrinking on
   narrow cards so even ◉3,000 fits without truncating. */
.store-hero .cur { font-weight: 800; color: #fff; white-space: nowrap; max-width: 100%; }
.store-hero .cur-val { font-size: min(44px, 21cqw); line-height: 1; }
.store-hero .cur-icon { font-size: min(30px, 15cqw); color: var(--card-accent); }
.card-tile--store .store-price { color: #fff; }
.card-tile--store .store-price .cur { color: #fff; }
.card-tile--store .store-price .cur-icon { color: var(--card-accent); font-size: 15px; }
/* Discount / bonus chip floats at TOP-CENTER (in the empty band under the name row), out of the
   flow so it never shifts the number/button. */
.store-chip {
  position: absolute; top: 34px; left: 50%; transform: translateX(-50%);
  font-size: 11px; font-weight: 800; white-space: nowrap;
  color: color-mix(in srgb, var(--card-accent) 82%, #fff);
  background: color-mix(in srgb, var(--card-accent) 16%, transparent);
  border-radius: 999px; padding: 3px 11px;
}
.card-tile-open {
  margin-top: 6px; font-size: 12px; font-weight: 800; letter-spacing: 0.3px;
  padding: 6px 18px; border-radius: 999px;
  color: #1a1030; background: #fff; box-shadow: 0 6px 16px -8px rgba(0, 0, 0, 0.5);
}
@media (prefers-reduced-motion: reduce) { .card-tile--gacha { animation: none; } }

/* Star-up strip on a yield agent card — ONE loud number (the relative yield jump) with
   the star target under it and an x/x cards pill. The same strip in both states: lit
   (gradient, tappable) when a full card set is held, muted .is-locked otherwise. */
.yield-card-starup {
  display: flex; align-items: center; gap: 11px; width: 100%; margin-top: 10px;
  padding: 10px 12px; border: none; border-radius: var(--radius-md); cursor: pointer; font: inherit; text-align: left; color: #fff;
  background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c));
  box-shadow: 0 6px 18px -10px color-mix(in srgb, var(--grad-b) 90%, transparent);
  transition: transform var(--ease) 130ms, filter var(--ease) 130ms;
}
.yield-card-starup:hover { filter: brightness(1.06); }
.yield-card-starup:active { transform: scale(0.985); }
.starup-emblem {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  border-radius: 50%; background: rgba(255, 255, 255, 0.18);
}
.starup-emblem .material-symbols-rounded { font-size: 18px; }
.starup-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.starup-headline { font-size: 16.5px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.15; letter-spacing: 0.1px; }
.starup-sub { font-size: 11px; font-weight: 500; opacity: 0.85; margin-top: 1px; }
/* Card progress fill under the headline — thin, so "almost there" reads at a glance. */
.starup-bar {
  margin-top: 5px; height: 4px; border-radius: 999px; overflow: hidden;
  background: rgba(0, 0, 0, 0.24);
}
.starup-bar-fill {
  display: block; height: 100%; border-radius: 999px;
  background: rgba(255, 255, 255, 0.9); transition: width 0.5s ease;
}
.starup-bar.is-full .starup-bar-fill { background: #fff; box-shadow: 0 0 8px rgba(255, 255, 255, 0.7); }
.yield-card-starup.is-locked .starup-bar { background: color-mix(in srgb, var(--text) 8%, transparent); }
.yield-card-starup.is-locked .starup-bar-fill { background: color-mix(in srgb, var(--text) 34%, transparent); box-shadow: none; }
/* x/x cards pill — frosted dark so it reads on the gradient; dimmer when locked. */
.starup-prog {
  flex: none; font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: #fff; background: rgba(0, 0, 0, 0.28); border-radius: 999px; padding: 4px 10px;
}
/* Locked variant: the full card set isn't held yet — same layout, non-interactive,
   flat elevation; the pill shows how far along the set is. */
.yield-card-starup.is-locked {
  cursor: default; color: var(--text-dim);
  background: var(--bg-elev-2); box-shadow: none;
}
.yield-card-starup.is-locked:hover { filter: none; }
.yield-card-starup.is-locked:active { transform: none; }
.yield-card-starup.is-locked .starup-emblem { background: color-mix(in srgb, var(--text) 9%, transparent); color: var(--text-faint); }
.yield-card-starup.is-locked .starup-sub { color: var(--text-faint); opacity: 1; }
.yield-card-starup.is-locked .starup-prog {
  color: var(--text-dim); background: color-mix(in srgb, var(--text) 9%, transparent);
}

/* Trial banner (v53): reinvest/gacha agents earn LOCKED scrip only until the first fee is
   paid. Warm amber "gated — unlock me" treatment, deliberately distinct from the brand-
   gradient star-up strip so it reads as a state to resolve, not a reward. The whole strip is
   the subscribe CTA (paying the fee clears the trial → cashable USDT + weekly credits switch on). */
.yield-trial {
  display: flex; align-items: center; gap: 11px; width: 100%; margin-top: 10px;
  padding: 10px 12px; border-radius: var(--radius-md); cursor: pointer; font: inherit; text-align: left;
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, var(--bg-elev-1));
  transition: transform var(--ease) 130ms, filter var(--ease) 130ms;
}
.yield-trial:hover { filter: brightness(1.04); }
.yield-trial:active { transform: scale(0.985); }
.yield-trial-emblem {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  border-radius: 50%; background: color-mix(in srgb, var(--warn) 22%, transparent); color: var(--warn);
}
.yield-trial-emblem .material-symbols-rounded { font-size: 18px; }
.yield-trial-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.yield-trial-headline { font-size: 14px; font-weight: 750; line-height: 1.15; color: var(--text); }
.yield-trial-sub { font-size: 11px; font-weight: 500; color: var(--text-dim); margin-top: 1px; }
/* Power cost to unlock — a gradient pill mirroring the extend CTA's spend affordance. */
.yield-trial-cost {
  flex: none; display: inline-flex; align-items: center; gap: 3px; font-size: 12.5px; font-weight: 800;
  font-variant-numeric: tabular-nums; color: var(--on-accent); border-radius: 999px; padding: 5px 11px;
  background: var(--grad);
}
.yield-trial-cost .material-symbols-rounded { font-size: 15px; }

/* Upline (sponsor) card on the Referral tab. */
.upline-card { display: flex; align-items: center; gap: 12px; background: var(--bg-elev-1); border-radius: var(--radius-md); padding: 13px 15px; margin-bottom: 12px; }
.upline-avatar {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elev-2)); color: var(--accent);
}
.upline-avatar .material-symbols-rounded { font-size: 21px; }
.upline-main { min-width: 0; flex: 1; }
.upline-label { font-size: 11px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: var(--text-faint); }
.upline-name { font-size: 14.5px; font-weight: 650; color: var(--text); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upline-tag { flex: none; font-size: 11px; font-weight: 600; color: var(--text-dim); padding: 3px 10px; border-radius: 999px; background: var(--bg-elev-2); }

/* ===== Gacha reveal: swipe-up → flashy spin/flip → prize (the dopamine moment) ===== */
.gacha-stage {
  /* Above the reinvest/yield slide-in sheet (z 1100) + nav menus (1300/1400) — the reveal
     is a full-screen takeover moment, nothing should sit over it. */
  position: fixed; inset: 0; z-index: 1500; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
  /* Dark theatrical takeover (theme-independent) so the card, rays + particles all glow. The
     backdrop is near-opaque + heavily blurred so the earn page behind dissolves into a soft
     vignette (a lighter blur let the hero card bleed through as an obvious rectangle). */
  background:
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--grad-b) 40%, transparent), transparent 66%),
    rgba(7, 8, 11, 0.95);
  /* Light blur only — the 0.95-opaque backdrop already hides the page, and a large full-screen
     backdrop-filter re-composites every frame behind the animating card (a real FPS cost). */
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); opacity: 0; transition: opacity 220ms var(--ease);
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.gacha-stage.is-open { opacity: 1; }
.gacha-stage.flash::before {
  content: ""; position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none;
  animation: gacha-flash 720ms var(--ease) forwards;
}
@keyframes gacha-flash { 0% { opacity: 0; } 10% { opacity: 0.6; } 100% { opacity: 0; } }
/* Reveal impact: the whole card jolts (screen-shake feel) and the prize icon punches in. */
.gacha-stage.shake .gacha-card-wrap { animation: gacha-shake 520ms var(--ease); }
@keyframes gacha-shake {
  0% { transform: translate(0, 0) rotate(0deg); }
  12% { transform: translate(-10px, 6px) rotate(-1.6deg); }
  26% { transform: translate(9px, -7px) rotate(1.4deg); }
  40% { transform: translate(-7px, 5px) rotate(-1deg); }
  56% { transform: translate(6px, -4px) rotate(0.8deg); }
  72% { transform: translate(-4px, 3px) rotate(-0.5deg); }
  88% { transform: translate(2px, -1px) rotate(0.2deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}
.gacha-stage.is-revealed .gacha-reveal-icon { animation: gacha-pop 540ms var(--ease); }
@keyframes gacha-pop { 0% { transform: scale(1); } 32% { transform: scale(1.3); } 66% { transform: scale(0.94); } 100% { transform: scale(1); } }

.gacha-card-wrap { position: relative; z-index: 1; perspective: 1200px; cursor: grab; touch-action: none; }
.gacha-card-wrap:active { cursor: grabbing; }
/* The spinner rotates BOTH stacked cards as one plane; we crossfade mystery→prize at the
   edge-on midpoint (no preserve-3d / backface-visibility — those are fragile and a `filter`
   silently breaks them). Spin ends at 720° (= upright) so the prize text never mirrors. */
/* 1.5× bigger (was 196×264) so the prize reads like a real collectible card. */
.gacha-spinner { --glow: rgba(255, 255, 255, 0.9); position: relative; width: 300px; height: 416px; animation: gacha-float 3s ease-in-out infinite; }
.gacha-spinner.is-spinning { animation: gacha-spin 1s var(--ease) forwards; }
.gacha-stage.is-revealed .gacha-spinner { animation: none; transform: none; }
/* BorderGlow (reactbits.dev/components/border-glow) — a bright arc that travels around the
   card's border, autoplaying. Masked conic gradient = border ring only; drop-shadow = bloom.
   The glow tints to the prize value once revealed (stays white on a common pull). */
@property --gborder { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
.gacha-spinner::before {
  content: ""; position: absolute; inset: -3px; z-index: 5; pointer-events: none;
  border-radius: calc(var(--radius-lg) + 3px); padding: 3px;
  background: conic-gradient(from var(--gborder), transparent 52%, var(--glow) 76%, #fff 82%, var(--glow) 88%, transparent 96%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  filter: drop-shadow(0 0 6px var(--glow));
  animation: gborder-spin 3s linear infinite;
}
@keyframes gborder-spin { to { --gborder: 360deg; } }
.gacha-stage--rare.is-revealed .gacha-spinner { --glow: #7dd3fc; }
.gacha-stage--epic.is-revealed .gacha-spinner { --glow: #e879f9; }
.gacha-stage--legendary.is-revealed .gacha-spinner { --glow: #ffce5a; }
@keyframes gacha-float { 0%, 100% { transform: translateY(0) rotateY(0deg); } 50% { transform: translateY(-10px) rotateY(0deg); } }
@keyframes gacha-spin { 0% { transform: rotateY(0deg) scale(0.92); } 60% { transform: rotateY(540deg) scale(1.16); } 100% { transform: rotateY(720deg) scale(1); } }
.gacha-card {
  position: absolute; inset: 0; border-radius: var(--radius-lg); overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; text-align: center;
  box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65); transition: opacity 180ms linear;
}
/* Pre-reveal mystery card: the default agent-card look (the store buy-card's animated aurora
   gradient) with the translucent LetterGlitch matrix (a `.gtc-glitch` canvas) over it. The 3px
   border matches the glow ring + the revealed prize card's border. */
.gacha-card--back {
  opacity: 1; border: 3px solid rgba(255, 255, 255, 0.55);
  background: linear-gradient(125deg, var(--grad-a), var(--grad-b) 42%, var(--grad-c) 72%, var(--grad-a) 100%);
  background-size: 240% 240%;
  box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65);
  animation: buy-card-aurora 9s ease-in-out infinite;
}
.gacha-stage.show-front .gacha-card--back { opacity: 0; }
.gacha-stage.show-front .gacha-card--front { opacity: 1; }
/* Glyph alpha lives in the colours (≈30% white); the canvas itself stays fully opaque. */
.gtc-glitch { position: absolute; inset: 0; z-index: 0; opacity: 1; }
/* Superpower wordmark over a very soft scrim (dimmed — no hard black circle).
   The logo art is a DARK wordmark → brightness(0) invert(1) makes it white (matches .buy-card-logo). */
.gacha-back-emblem {
  position: relative; z-index: 1; display: flex; align-items: center; justify-content: center;
  width: 64%; aspect-ratio: 1 / 1; border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 0, 0, 0.1) 30%, transparent 66%);
}
.gacha-back-logo { width: 82%; opacity: 0.92; filter: brightness(0) invert(1) drop-shadow(0 0 12px rgba(255, 255, 255, 0.3)); }
.gacha-shimmer {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 36%, rgba(255, 255, 255, 0.4) 50%, transparent 64%);
  transform: translateX(-120%); animation: gacha-sheen 2.4s var(--ease) infinite;
}
@keyframes gacha-sheen { 0% { transform: translateX(-120%); } 55%, 100% { transform: translateX(120%); } }

/* ----- Prize as a collectible (Pokémon-style) trading card ----- */
/* One per-rarity accent var drives the holographic border, gem, art glow, and icon tint. */
.gacha-card--front {
  --gtc-border: linear-gradient(140deg, #e2e8f0, #94a3b8 50%, #e2e8f0);
  --gtc-accent: #b8c2cf;
  opacity: 0; align-items: stretch; padding: 11px; border: 3px solid transparent;
  /* Dark rarity-tinted interior so the full-bleed pixels glow over it (screen blend). */
  background:
    radial-gradient(circle at 50% 42%, color-mix(in srgb, var(--gtc-accent) 18%, #0a0b0d), #0a0b0d 80%) padding-box,
    var(--gtc-border) border-box;
  box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65);
}
/* Radiating light shafts behind the card — a canvas burst (earnings.ui.ts createRays) whose
   rays vary in LENGTH + WIDTH so they fill the surrounding space, esp. on desktop. The burst
   is value-tinted + slow-spun in JS; this radial mask dissolves the far ends into the dark
   before the viewport edges (no hard rectangle / no edge-to-edge "hyperspace" wall). */
.gacha-rays {
  position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  transition: opacity 700ms var(--ease);
  -webkit-mask: radial-gradient(circle at 50% 50%, #000 30%, transparent 72%);
  mask: radial-gradient(circle at 50% 50%, #000 30%, transparent 72%);
}
.gacha-rays.is-on { opacity: 1; }
.gtc-art-glow {
  position: absolute; inset: 0; z-index: 1; margin: auto; width: 62%; height: 46%; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--gtc-accent) 55%, transparent), transparent 70%); filter: blur(16px);
}
/* Light-pillar card background (reactbits.dev/backgrounds/light-pillar, reimplemented in CSS
   to avoid a Three.js dependency): a value-tinted vertical beam with a slow volumetric shimmer,
   revealed with the prize. Deliberately DIM (screen blend + low-opacity layers) so it never
   competes with the icon / prize text above it (z-index 2). */
.gtc-pillar {
  position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none;
  border-radius: inherit; opacity: 0; transition: opacity 700ms var(--ease);
  mix-blend-mode: screen; --pillar: var(--gtc-accent);
}
.gacha-stage.is-revealed .gtc-pillar { opacity: 1; }
/* the beam: bright-top → value → dark-bottom, with a soft column falloff to the card edges */
.gtc-pillar::before {
  content: ""; position: absolute; left: 50%; top: -22%; bottom: -22%; width: 78%;
  transform: translateX(-50%);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--pillar) 55%, #fff) 0%,
    var(--pillar) 46%,
    color-mix(in srgb, var(--pillar) 50%, #000) 100%);
  -webkit-mask: radial-gradient(56% 94% at 50% 46%, #000 6%, rgba(0, 0, 0, 0.4) 40%, transparent 74%);
  mask: radial-gradient(56% 94% at 50% 46%, #000 6%, rgba(0, 0, 0, 0.4) 40%, transparent 74%);
  filter: blur(16px); opacity: 0.4;
}
/* slow volumetric shimmer drifting up the beam */
.gtc-pillar::after {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 78%;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent 0%, rgba(255, 255, 255, 0.85) 50%, transparent 100%);
  background-repeat: no-repeat; background-size: 100% 42%; background-position: 50% 50%;
  -webkit-mask: linear-gradient(90deg, transparent, #000 50%, transparent);
  mask: linear-gradient(90deg, transparent, #000 50%, transparent);
  filter: blur(12px); opacity: 0.22;
  animation: gtc-pillar-flow 6s ease-in-out infinite;
}
@keyframes gtc-pillar-flow { 0% { background-position: 50% 128%; } 100% { background-position: 50% -28%; } }
/* Prismatic-burst card background (reactbits.dev/backgrounds/prismatic-burst via the shared
   WebGL2 port in prismaticBurst.ts) — agent-prize cards only: tier-tinted rays radiating out
   from behind the mascot, revealed with the prize. Replaces the pillar on those cards. The
   canvas clears to opaque black, so screen-blend it like the pillar: black is neutral under
   screen, leaving the card's rarity-tinted interior visible with the rays added on top. */
.gtc-burst {
  position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none;
  border-radius: inherit; opacity: 0; transition: opacity 700ms var(--ease);
  mix-blend-mode: screen;
}
.gacha-stage.is-revealed .gtc-burst { opacity: 1; }
/* Scrim over the ray field: dims the top (rarity chip + name) and bottom (description)
   bands so the text stays legible, while the centre — behind the mascot — stays hot.
   Painted INSIDE the screen-blended host, so darkening here just fades the rays out. */
.gtc-burst::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.62), transparent 34%, transparent 60%, rgba(0, 0, 0, 0.68));
}
/* The minted agent's live mascot, where the icon medallion sits on other prizes. Sized so
   the resting ball (~48% of the short side) reads close to the 100px medallion while keeping
   the headroom the reference jump/celebrate arcs need. */
.gtc-mascot { display: block; width: 172px; height: 172px; }
/* Currency prize art — the branded animated SVGs (credit coin / power bolt / XP badge),
   free-floating like the agent card's mascot rather than boxed in the icon medallion. */
.gtc-prize-img { display: block; width: 116px; height: 116px; filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.45)); }
/* The upgrade card's crown/star pop (upgrade.svg) — its artwork sits smaller inside a
   500x500 viewBox than the currency icons, so it gets a larger box to match their weight. */
.gtc-prize-img--upgrade { width: 168px; height: 168px; }
.gtc-foil {
  position: absolute; inset: 0; z-index: 4; pointer-events: none; border-radius: var(--radius-lg);
  background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, 0.14) 49%, transparent 56%);
  transform: translateX(-120%); animation: gacha-sheen 3s ease-in-out infinite;
}
/* Prize CONTENT — its own layer, hidden until the card lands (the shake), then it pops in.
   Three rows: NAME centered in the top half, ICON dead-centre, DESCRIPTION in the bottom
   half. The rarity chip is pinned to the top corners, out of the row flow. */
.gtc-content {
  position: relative; z-index: 2; flex: 1; width: 100%;
  display: grid; grid-template-rows: 1fr auto 1fr; align-items: center; justify-items: center;
  opacity: 0; transition: opacity 260ms var(--ease);
}
.gacha-stage.is-revealed .gtc-content { opacity: 1; }
.gtc-header { position: absolute; top: 0; left: 0; right: 0; z-index: 1; display: flex; align-items: center; justify-content: space-between; padding: 1px 3px; }
.gacha-card--front .gacha-reveal-rarity { font-size: 11px; font-weight: 800; letter-spacing: 1.6px; text-transform: uppercase; color: var(--gtc-accent); }
.gtc-gem { display: inline-flex; color: var(--gtc-accent); filter: drop-shadow(0 0 6px var(--gtc-accent)); }
.gtc-gem .material-symbols-rounded { font-size: 19px; }
/* NAME — the card's headline, centred in the top half. Theme-independent light: the card is
   always dark, so the theme --text token would vanish against it. */
.gtc-name { align-self: center; text-align: center; padding: 0 8px; font-size: 25px; font-weight: 800; line-height: 1.15; letter-spacing: -0.3px; color: #f4f6fb; }
/* ICON — dead-centre of the card. */
.gtc-icon-wrap { display: flex; align-items: center; justify-content: center; }
.gacha-card--front .gacha-reveal-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100px; height: 100px; margin: 0; border-radius: 50%; color: #fff;
  background: color-mix(in srgb, var(--gtc-accent) 16%, rgba(0, 0, 0, 0.5));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--gtc-accent) 60%, transparent), 0 0 30px -4px var(--gtc-accent);
}
.gacha-card--front .gacha-reveal-icon .material-symbols-rounded { font-size: 56px; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5)); }
/* DESCRIPTION — one-sentence use case, centred in the bottom half. */
.gtc-desc { align-self: center; text-align: center; max-width: 90%; font-size: 12px; line-height: 1.45; color: rgba(255, 255, 255, 0.62); }
/* Card-progress line on a card reveal — "7/9 to ★4 · name" (or a "ready" call-out). Frosted
   pill in the card accent so the win means something on the spot. */
.gtc-progress {
  align-self: center; margin-top: 8px; padding: 4px 11px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: 0.1px;
  color: #fff; background: rgba(255, 255, 255, 0.14); border: 1px solid rgba(255, 255, 255, 0.22);
}
.gtc-progress.is-ready { background: color-mix(in srgb, var(--gtc-accent, #38bdf8) 34%, transparent); border-color: transparent; }
/* Rarity → accent var + outer glow (icon gold-fills on legendary). */
.gacha-stage--rare .gacha-card--front { --gtc-border: linear-gradient(140deg, #7dd3fc, #0ea5e9 50%, #7dd3fc); --gtc-accent: #38bdf8; box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65), 0 0 48px -8px #0ea5e9; }
.gacha-stage--epic .gacha-card--front { --gtc-border: linear-gradient(140deg, #e879f9, #a855f7 50%, #e879f9); --gtc-accent: #c084fc; box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65), 0 0 54px -6px #a855f7; }
.gacha-stage--legendary .gacha-card--front { --gtc-border: linear-gradient(140deg, #fde047, #f5a623 50%, #fde047); --gtc-accent: #ffce5a; box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65), 0 0 72px -6px #ffce5a; }
.gacha-stage--legendary .gacha-card--front .gacha-reveal-icon { background: linear-gradient(135deg, #ffce5a, #f5a623); color: #1a1400; }
/* Hints. */
.gacha-hint, .gacha-continue { position: relative; z-index: 1; }
.gacha-hint { display: inline-flex; flex-direction: column; align-items: center; gap: 1px; color: rgba(255, 255, 255, 0.82); font-size: 13px; font-weight: 600; }
.gacha-hint-arrow { font-size: 26px !important; color: #fff; animation: gacha-bob 1.2s var(--ease) infinite; }
@keyframes gacha-bob { 0%, 100% { transform: translateY(0); opacity: 0.65; } 50% { transform: translateY(-8px); opacity: 1; } }
.gacha-continue { font-size: 12px; color: rgba(255, 255, 255, 0.6); opacity: 0; transition: opacity 240ms var(--ease); }
.gacha-stage.is-revealed .gacha-continue { opacity: 1; }
.gacha-stage.is-revealing .gacha-hint, .gacha-stage.is-revealed .gacha-hint { display: none; }
/* Booster-pack progress dots — one per card in the hand; fills as each is flipped. */
.gacha-progress { position: relative; z-index: 1; display: inline-flex; gap: 7px; align-items: center; }
.gacha-dot { width: 7px; height: 7px; border-radius: 50%; background: rgba(255, 255, 255, 0.22); transition: background 220ms var(--ease), transform 220ms var(--ease); }
.gacha-dot.is-done { background: #fff; transform: scale(1.15); }
/* Confetti on reveal reuses the shared new-agent-deploy shower (fireConfetti / .confetti-canvas). */

/* ===== Star-up confirm — an agent card: name/star head, the mascot idling over a
   prismatic-burst backdrop, the three stats the upgrade moves (+% gains in green),
   and Cancel / Upgrade (x/x). The burst canvas fills the card behind everything. ===== */
.upg-overlay {
  /* Above the Yield sheet (z 1100) it's launched from — matches .confirm-overlay (1400). */
  position: fixed; inset: 0; z-index: 1500; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: color-mix(in srgb, var(--bg) 70%, transparent); backdrop-filter: blur(5px);
  opacity: 0; transition: opacity 200ms var(--ease);
}
.upg-overlay.is-open { opacity: 1; }
.upg-card {
  position: relative; overflow: hidden;
  /* Same footprint + radius + throw as the gacha prize card (300×416) so the two
     card moments in the reinvest loop read as one family. */
  width: 300px; height: 416px; max-width: 100%;
  display: flex; flex-direction: column;
  border-radius: var(--radius-lg); padding: 16px;
  /* Near-black base so the burst's additive rays carry the card's color story. */
  background: #08080c;
  box-shadow: 0 30px 56px -14px rgba(0, 0, 0, 0.65), inset 0 0 0 1.5px rgba(255, 255, 255, 0.14);
  transform: translateY(12px) scale(0.96); opacity: 0; transition: transform 280ms var(--ease), opacity 200ms var(--ease);
}
.upg-overlay.is-open .upg-card { transform: none; opacity: 1; }
/* Success beat 1 — crescendo shake: barely a tremor at first, vibrant by the end
   (amplitude + tilt grow through the keyframes; ends mid-throw, hidden by the flash). */
.upg-card.is-shaking { animation: upg-shake-card 500ms linear; }
@keyframes upg-shake-card {
  0% { transform: translate(0, 0) rotate(0deg); }
  15% { transform: translate(-0.5px, 0.5px) rotate(-0.1deg); }
  30% { transform: translate(1px, -1px) rotate(0.15deg); }
  45% { transform: translate(-2px, 1.5px) rotate(-0.3deg); }
  60% { transform: translate(3px, -2.5px) rotate(0.5deg); }
  72% { transform: translate(-4.5px, 3.5px) rotate(-0.8deg); }
  84% { transform: translate(6px, -4.5px) rotate(1.1deg); }
  92% { transform: translate(-7px, 5px) rotate(-1.3deg); }
  100% { transform: translate(7px, -5px) rotate(1.4deg); }
}
/* Success beat 2 — the card bounces up and spins a full turn, landing upright
   (360° = upright, so the face never ends mirrored). Perspective comes from the
   overlay so the spin reads as 3D. */
.upg-overlay { perspective: 1200px; }
.upg-card.is-spinning { animation: upg-card-spin 750ms var(--ease); }
@keyframes upg-card-spin {
  0% { transform: rotateY(0deg) translateY(0) scale(1); }
  45% { transform: rotateY(200deg) translateY(-16px) scale(1.1); }
  100% { transform: rotateY(360deg) translateY(0) scale(1); }
}
/* Well dimmed — an ambient glow behind the mascot, not a light show; the mascot
   + stats stay the brightest things on the card. On upgrade success it flares
   to full brightness (.is-bright + a live intensity/speed bump) to celebrate. */
.upg-burst { position: absolute; inset: 0; pointer-events: none; opacity: 0.5; transition: opacity 600ms var(--ease); }
.upg-burst.is-bright { opacity: 1; }
.upg-card-head { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 10px; color: #fff; }
.upg-card-name {
  font-family: var(--font-display); font-size: 18px; font-weight: 600;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.upg-star-slot { display: inline-flex; flex: none; }
/* Star glaze — when the badge steps up: a scale pop plus a shine sweeping across it. */
.upg-star-glaze { position: relative; overflow: hidden; border-radius: 999px; animation: upg-star-pop 620ms var(--ease); }
.upg-star-glaze::after {
  content: ""; position: absolute; inset: -40%;
  background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.95) 50%, transparent 68%);
  transform: translateX(-130%); animation: upg-star-shine 900ms var(--ease) 140ms forwards;
}
@keyframes upg-star-pop { 0% { transform: scale(1); } 35% { transform: scale(1.4); } 70% { transform: scale(0.94); } 100% { transform: scale(1); } }
@keyframes upg-star-shine { to { transform: translateX(130%); } }
/* The mascot floats over the burst's bright core; its canvas is transparent.
   80% of the stage (not full-bleed) so the card keeps air around it. */
.upg-stage { position: relative; z-index: 1; flex: 1; min-height: 0; }
.upg-mascot { position: absolute; inset: 0; margin: auto; display: block; width: 80%; height: 80%; cursor: pointer; }
/* Stat tiles — Gemini-style hierarchy: the display-face number leads, the green
   gain chip is the second read, the label whispers. Perspective is for the
   post-upgrade flip, where each tile turns edge-on and lands on its new value. */
.upg-stats { position: relative; z-index: 1; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; perspective: 640px; }
.upg-stat.is-flipping { animation: upg-stat-flip 460ms var(--ease); }
@keyframes upg-stat-flip { 0% { transform: rotateX(0deg); } 50% { transform: rotateX(92deg); } 100% { transform: rotateX(0deg); } }
.upg-stat {
  display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px 8px;
  border-radius: var(--radius-md); text-align: center; color: #fff;
  background: rgba(0, 0, 0, 0.45); backdrop-filter: blur(3px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}
.upg-stat-value { font-family: var(--font-display); font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.upg-stat-delta {
  font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ok);
  background: color-mix(in srgb, var(--ok) 16%, transparent); border-radius: 999px; padding: 1.5px 8px;
}
.upg-stat-label { font-size: 10px; color: rgba(255, 255, 255, 0.6); }
/* Placeholder delta (previewStarUp) — reserves the chip's height so a stat with no % gain still
   lines its value/label up with the others. */
.upg-stat-delta--ghost { visibility: hidden; }
/* Preview-only bits (previewStarUp): a subtitle under the name + a card-progress bar above the
   actions. Reuse the ceremony's card chrome; these just fill the "not ready yet" state. */
.upg-preview-sub {
  position: relative; z-index: 1; text-align: center; margin: -2px 0 8px;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.2px;
  color: color-mix(in srgb, var(--grad-b) 60%, #fff);
}
.upg-prog { position: relative; z-index: 1; margin-top: 12px; }
.upg-prog-bar { height: 8px; border-radius: 999px; background: var(--bg-elev-3); overflow: hidden; }
.upg-prog-fill { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); transition: width 0.3s var(--ease); }
.upg-prog-label { margin-top: 7px; text-align: center; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text-dim); }
/* Clear breathing room between the progress message and the action buttons (they were crowded). */
.upg-prog + .upg-actions { margin-top: 18px; }
.upg-actions { position: relative; z-index: 1; display: flex; gap: 8px; margin-top: 12px; }
/* The 300px card leaves ~130px per button — tighten so "Upgrade (x/x)" stays one line. */
.upg-actions .upg-btn { padding: 11px 6px; font-size: 13.5px; white-space: nowrap; }
.upg-actions .upg-btn--go { flex: 1.35; }
/* The screen joins the card's crescendo — same subtle→vibrant ramp, smaller travel. */
.upg-screen-shake { animation: upg-shake-screen 550ms linear both; }
@keyframes upg-shake-screen {
  0% { transform: translate(0, 0); }
  25% { transform: translate(0.5px, -0.5px); }
  45% { transform: translate(-1px, 1px); }
  62% { transform: translate(2px, -1.5px); }
  76% { transform: translate(-3px, 2px); }
  88% { transform: translate(4px, -3px); }
  100% { transform: translate(0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .upg-card.is-shaking, .upg-card.is-spinning, .upg-screen-shake,
  .upg-star-glaze, .upg-star-glaze::after, .upg-stat.is-flipping { animation: none; }
}
.upg-btn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 12px; border: none; border-radius: 999px; cursor: pointer; font: inherit; font-size: 14px; font-weight: 650; transition: transform var(--ease) 120ms, filter var(--ease) 120ms; }
.upg-btn:active { transform: scale(0.97); }
.upg-btn--ghost { background: var(--bg-elev-3); color: var(--text); }
.upg-btn--go { background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); color: #fff; }
.upg-btn--go:hover { filter: brightness(1.06); }
.upg-btn--go .material-symbols-rounded { font-size: 18px; }

@media (prefers-reduced-motion: reduce) {
  .gacha-spinner, .gacha-spinner.is-spinning { animation: none; }
  .gacha-spinner::before { animation: none; }
  .gacha-shimmer, .gacha-hint-arrow, .gtc-pillar::after { animation: none; }
  .gacha-stage.flash::before { animation: none; }
  .gacha-stage.shake .gacha-card-wrap { animation: none; }
  .gacha-stage.is-revealed .gacha-reveal-icon { animation: none; }
  .upg-card { transition: opacity 200ms var(--ease); transform: none; }
}
@media (max-width: 480px) { .earn-pockets { grid-template-columns: 1fr; } }

/* Hide the authed app shell until boot.ts confirms the user is signed in. The Tasks
   view is static HTML, so without this it paints for the ~0.5–1s the /api/me check is
   in flight before the sign-in screen comes up (a flash of authenticated content for
   logged-out visitors). index.html sets .is-booting before first paint; boot.ts clears
   it the moment auth passes. visibility (not display) avoids a reflow on reveal. */
.is-booting .layout { visibility: hidden; }

/* Full-page boot splash (index.html #app-splash): covers the base layout until
   the home is composed. Torn down via html.is-app-ready (boot.ts gates, main.ts
   first task paint, or the 10s inline failsafe). DELIBERATELY below every gate/
   overlay (.auth-overlay 1000, welcome 9999): a user-blocking gate must never be
   masked by the loader while its promise pends. */
#app-splash {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
}
/* Two-act splash, "charge-up → burst" (owner note: make it exciting). The mascot
   is BUILT in CSS — no JS, no asset fetch, it must animate in the pre-bundle window.
   ACT 1 · CHARGE: the camera lives inside the face (bg = head color, scale(3.4)):
   big glossy eyes act — saccade darts, squash-stretch blinks, focus squints — while
   energy motes get pulled INTO the face, an aura breathes, and the camera sways.
   ACT 2 · BURST (html.is-app-ready, from main.ts): eyes flash white → shockwave
   rings → the camera slams back with overshoot → radial particle burst as ring/
   sparks ignite → the mascot does a 360° flip-jump with a squash landing → smile
   eyes + "Ready!" bubble slams in → overlay fades. Background eases to a DIM scrim
   at t=0 (not transparent): the home is visible-but-dimmed so the celebration reads
   against it, and stays interactive (pointer-events: none). The outro fade
   (splash-gone) lifts the dim together with the mascot.
   Gate paths (sign-in / agent picker / failsafe) use is-splash-skip: instant hide. */
#app-splash { background: #0b0b0f; overflow: hidden; }
html.is-splash-skip #app-splash { display: none; }
html.is-app-ready #app-splash {
  /* flat rgba (not a gradient) so the 0.4s background transition from the opaque
     act-1 color actually interpolates — background-image swaps are instant */
  background: rgba(11, 11, 15, 0.65);
  pointer-events: none;
  animation: splash-gone 0.25s ease 1.5s forwards;
  transition: background 0.4s ease;
}
.splash-scan {
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 40%, rgba(120, 80, 255, 0.09) 50%, transparent 60%);
  background-size: 300% 100%;
  animation: splash-scan 3.2s ease-in-out infinite;
  pointer-events: none;
}
html.is-app-ready .splash-scan { display: none; }
.splash-flash {
  /* act-2 opener: a full-screen white/violet pop, the "ignition" */
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.95), rgba(198, 0, 255, 0.3) 42%, transparent 72%);
  opacity: 0;
  pointer-events: none;
}
html.is-app-ready .splash-flash { animation: splash-flash 0.5s ease-out forwards; }
.splash-zoom { transform: scale(5); animation: splash-creep 9s ease-in-out infinite alternate; }
html.is-app-ready .splash-zoom { animation: splash-slam-out 0.55s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
.splash-sway { animation: splash-sway 6.5s ease-in-out infinite alternate; }
html.is-app-ready .splash-sway { animation: none; }
.splash-mascot {
  position: relative;
  width: 118px;
  height: 118px;
  animation: splash-bob 2.4s ease-in-out infinite;
}
html.is-app-ready .splash-mascot { animation: splash-flip-land 0.7s cubic-bezier(0.34, 1.3, 0.5, 1) 0.28s both; }
.splash-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #0036ff, #c600ff, #0036ff);
  opacity: 0; /* act 1: inside the face — no ring */
  animation: splash-spin 2.2s linear infinite;
}
html.is-app-ready .splash-ring {
  animation: splash-spin 2.2s linear infinite, splash-bloom 0.3s ease 0.28s forwards;
}
.splash-ring::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  filter: blur(16px);
  opacity: 0.55;
  z-index: -1;
}
.splash-head {
  position: absolute;
  inset: 9px;
  border-radius: 50%;
  /* act 1: TRANSPARENT — the screen must hold nothing but the two eyes; any
     face-colored disc silhouettes against ambient light (owner: no face pane).
     The head fills in under the ignition flash, before the zoom-out reveals it. */
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
html.is-app-ready .splash-head { animation: splash-headfill 0.2s ease 0.08s forwards; }
.splash-gaze {
  /* saccade layer: fast darts move BOTH eyes; blinks live on the eyes themselves */
  display: flex;
  align-items: center;
  gap: 16px;
  animation: splash-gaze 4.6s ease-in-out infinite;
}
html.is-app-ready .splash-gaze { animation: none; }
.splash-eye {
  width: 13px;
  height: 34px;
  border-radius: 99px;
  background: linear-gradient(180deg, #ffffff 0%, #cfcfda 100%);
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.28);
  position: relative;
  transform-origin: 50% 30%;
  animation: splash-eyes-act 4.6s ease-in-out infinite;
}
.splash-eye::after {
  /* gloss dot — the eyes read shiny/alive at close-up */
  content: "";
  position: absolute;
  top: 8%;
  left: 20%;
  width: 42%;
  height: 15%;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.95);
}
html.is-app-ready .splash-eye { animation: splash-smile 0.3s ease 0.88s forwards; }
.splash-halo {
  position: absolute;
  inset: -70px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(98, 54, 255, 0.3), transparent 62%);
  opacity: 0; /* act 1: OFF — a head-shaped glow silhouetted the circle (owner: no circle) */
  animation: splash-halo 2.4s ease-in-out infinite;
  pointer-events: none;
}
html.is-app-ready .splash-halo {
  animation: splash-halo 2.4s ease-in-out infinite, splash-bloom 0.3s ease 0.28s forwards;
}
/* ACT 1 · charge motes: energy pulled INTO the face from all directions */
.splash-charge {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  animation: splash-charge 1.35s ease-in infinite;
}
html.is-app-ready .splash-charge { display: none; }
.ch1 { --cx: -128px; --cy: -54px; background: #6aa6ff; }
.ch2 { --cx: 134px; --cy: -38px; background: #c600ff; animation-delay: 0.22s; }
.ch3 { --cx: 92px; --cy: 108px; background: #e4e4e4; animation-delay: 0.45s; width: 4px; height: 4px; }
.ch4 { --cx: -104px; --cy: 92px; background: #0036ff; animation-delay: 0.65s; }
.ch5 { --cx: -8px; --cy: -140px; background: #c08bf5; animation-delay: 0.9s; width: 4px; height: 4px; }
.ch6 { --cx: 16px; --cy: 136px; background: #6aa6ff; animation-delay: 1.1s; width: 4px; height: 4px; }
.ch7 { --cx: 142px; --cy: 42px; background: #e4e4e4; animation-delay: 1.32s; width: 3px; height: 3px; }
.ch8 { --cx: -140px; --cy: -8px; background: #c600ff; animation-delay: 1.5s; width: 3px; height: 3px; }
/* ACT 2 · orbit sparks (ignite with the ring) */
.splash-spark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
.sp1 { --r: 74px; background: #6aa6ff; }
.sp2 { --r: 74px; background: #c08bf5; width: 4px; height: 4px; }
.sp3 { --r: 74px; background: #e4e4e4; width: 4px; height: 4px; }
.sp4 { --r: 92px; background: #c600ff; }
.sp5 { --r: 92px; background: #0091ff; width: 4px; height: 4px; }
.sp6 { --r: 92px; background: #e4e4e4; width: 3px; height: 3px; }
html.is-app-ready .sp1 { animation: splash-orbit 3.8s linear infinite, splash-bloom 0.3s ease 0.34s forwards; }
html.is-app-ready .sp2 { animation: splash-orbit 3.8s linear infinite, splash-bloom 0.3s ease 0.34s forwards; animation-delay: -1.27s, 0.34s; }
html.is-app-ready .sp3 { animation: splash-orbit 3.8s linear infinite, splash-bloom 0.3s ease 0.34s forwards; animation-delay: -2.53s, 0.34s; }
html.is-app-ready .sp4 { animation: splash-orbit-r 5.6s linear infinite, splash-bloom 0.3s ease 0.34s forwards; }
html.is-app-ready .sp5 { animation: splash-orbit-r 5.6s linear infinite, splash-bloom 0.3s ease 0.34s forwards; animation-delay: -2.8s, 0.34s; }
html.is-app-ready .sp6 { animation: splash-orbit-r 5.6s linear infinite, splash-bloom 0.3s ease 0.34s forwards; animation-delay: -4.2s, 0.34s; }
/* ACT 2 · radial particle burst on ignition */
.splash-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
html.is-app-ready .splash-burst { animation: splash-burst 0.7s cubic-bezier(0.17, 0.67, 0.35, 1) 0.3s forwards; }
.b1  { --a: 0deg;   --d: 118px; background: #c600ff; }
.b2  { --a: 30deg;  --d: 98px;  background: #e4e4e4; width: 5px; height: 5px; }
.b3  { --a: 60deg;  --d: 126px; background: #0036ff; }
.b4  { --a: 90deg;  --d: 96px;  background: #6aa6ff; width: 5px; height: 5px; }
.b5  { --a: 120deg; --d: 120px; background: #c08bf5; }
.b6  { --a: 150deg; --d: 100px; background: #e4e4e4; width: 4px; height: 4px; }
.b7  { --a: 180deg; --d: 124px; background: #c600ff; }
.b8  { --a: 210deg; --d: 94px;  background: #6aa6ff; width: 5px; height: 5px; }
.b9  { --a: 240deg; --d: 118px; background: #0036ff; }
.b10 { --a: 270deg; --d: 102px; background: #e4e4e4; width: 4px; height: 4px; }
.b11 { --a: 300deg; --d: 122px; background: #c08bf5; }
.b12 { --a: 330deg; --d: 98px;  background: #c600ff; width: 5px; height: 5px; }
.b13 { --a: 15deg;  --d: 140px; background: #e4e4e4; width: 4px; height: 4px; }
.b14 { --a: 105deg; --d: 136px; background: #c600ff; width: 4px; height: 4px; }
.b15 { --a: 195deg; --d: 142px; background: #6aa6ff; width: 4px; height: 4px; }
.b16 { --a: 285deg; --d: 138px; background: #c08bf5; width: 4px; height: 4px; }
/* ACT 2 · shockwave rings */
.splash-wave {
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.55);
  opacity: 0;
  pointer-events: none;
}
.splash-wave.w2 { border-color: rgba(198, 0, 255, 0.6); }
html.is-app-ready .splash-wave.w1 { animation: splash-wave 0.6s ease-out 0.06s forwards; }
html.is-app-ready .splash-wave.w2 { animation: splash-wave 0.65s ease-out 0.16s forwards; }
.splash-wave.w3 { border-width: 2px; border-color: rgba(106, 166, 255, 0.5); }
html.is-app-ready .splash-wave.w3 { animation: splash-wave 0.7s ease-out 0.26s forwards; }
.splash-wave.w4 { border-width: 2px; }
html.is-app-ready .splash-wave.w4 { animation: splash-wave 0.5s ease-out 0.82s forwards; } /* landing echo */
.splash-bubble {
  position: absolute;
  top: calc(50% + 96px);
  left: 50%;
  transform: translateX(-50%) scale(0.4) rotate(-5deg);
  opacity: 0;
  background: #1e1e26;
  border: 1px solid #2f2f3a;
  border-radius: 18px;
  padding: 9px 20px;
  font-size: 15px;
  font-weight: 600;
  color: #ececf1;
  white-space: nowrap;
}
.splash-bubble::after { content: " ✨"; }
html.is-app-ready .splash-bubble {
  animation: splash-bubble-slam 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.92s forwards;
}
@keyframes splash-spin {
  to { transform: rotate(360deg); }
}
@keyframes splash-bob {
  50% { transform: translateY(-7px); }
}
@keyframes splash-sway {
  0% { transform: rotate(-1.4deg) translate(-3px, 2px); }
  100% { transform: rotate(1.4deg) translate(3px, -2px); }
}
/* act-1 saccades + a power-surge beat: darts, then a rapid jitter with a flare */
@keyframes splash-gaze {
  0%, 12%, 30%, 56%, 68%, 82%, 100% { transform: translate(0, 0); filter: brightness(1); }
  15%, 24% { transform: translate(-8px, 0); filter: brightness(1); }
  33%, 42% { transform: translate(8px, -2px); filter: brightness(1); }
  60%, 64% { transform: translate(3px, 5px); filter: brightness(1); }
  70% { transform: translate(-2px, 1px); filter: brightness(1.7); }
  72% { transform: translate(2px, -1px); filter: brightness(2.1); }
  74% { transform: translate(-2px, 0); filter: brightness(1.7); }
  76% { transform: translate(1px, 1px); filter: brightness(1.3); }
  86%, 94% { transform: translate(-4px, -4px); filter: brightness(1); }
}
/* act-1 eye acting: snap blinks (squash), pop-open overshoot, focus squint, surprise */
@keyframes splash-eyes-act {
  0%, 5%, 9%, 34%, 50%, 54%, 58%, 84%, 100% { transform: scaleY(1) scaleX(1); }
  7% { transform: scaleY(0.08) scaleX(1.25); }
  11% { transform: scaleY(1.14) scaleX(0.94); }
  38%, 46% { transform: scaleY(0.42) scaleX(1.06); }
  52%, 56% { transform: scaleY(0.08) scaleX(1.25); }
  62% { transform: scaleY(1.22) scaleX(1.06); }
  66%, 82% { transform: scaleY(1) scaleX(1); }
  90% { transform: scaleY(0.08) scaleX(1.25); }
  93% { transform: scaleY(1.1) scaleX(0.96); }
}
@keyframes splash-smile {
  to { transform: translateY(-28%) scaleY(0.36) scaleX(1.12); }
}
@keyframes splash-halo {
  50% { transform: scale(1.14); }
}
@keyframes splash-bloom {
  to { opacity: 1; }
}
@keyframes splash-charge {
  0% { transform: translate(var(--cx), var(--cy)) scale(1.1); opacity: 0; }
  22% { opacity: 0.95; }
  85% { opacity: 0.5; }
  100% { transform: translate(0, 0) scale(0.15); opacity: 0; }
}
@keyframes splash-flash {
  0% { opacity: 0; }
  14% { opacity: 0.95; }
  100% { opacity: 0; }
}
@keyframes splash-wave {
  0% { transform: scale(0.25); opacity: 0.85; }
  100% { transform: scale(3); opacity: 0; }
}
@keyframes splash-slam-out {
  0% { transform: scale(5.2); }
  55% { transform: scale(0.85); }
  78% { transform: scale(1.07); }
  100% { transform: scale(1); }
}
@keyframes splash-flip-land {
  0% { transform: translateY(0) rotate(0) scale(1); }
  34% { transform: translateY(-40px) rotate(200deg) scale(1.02); }
  60% { transform: translateY(2px) rotate(360deg) scaleX(1.16) scaleY(0.8); }
  74% { transform: translateY(-12px) rotate(360deg) scale(1); }
  88% { transform: translateY(0) rotate(360deg) scaleX(1.05) scaleY(0.95); }
  100% { transform: translateY(0) rotate(360deg) scale(1); }
}
@keyframes splash-burst {
  0% { transform: rotate(var(--a)) translateY(-46px) scale(1); opacity: 1; }
  100% { transform: rotate(var(--a)) translateY(calc(-1 * var(--d))) scale(0.2); opacity: 0; }
}
@keyframes splash-bubble-slam {
  0% { transform: translateX(-50%) scale(0.4) rotate(-5deg); opacity: 0; }
  70% { transform: translateX(-50%) scale(1.12) rotate(1.5deg); opacity: 1; }
  100% { transform: translateX(-50%) scale(1) rotate(0); opacity: 1; }
}
@keyframes splash-headfill {
  to { background-color: #0b0b0f; }
}
@keyframes splash-creep {
  from { transform: scale(5); }
  to { transform: scale(5.45); }
}
@keyframes splash-gone {
  to { opacity: 0; visibility: hidden; }
}
@keyframes splash-scan {
  0%, 100% { background-position: 0% 0; }
  50% { background-position: 100% 0; }
}
@media (prefers-reduced-motion: reduce) {
  #app-splash * { animation: none !important; }
  /* No animated outro under reduced motion — `forwards` never fires, so hide directly. */
  html.is-app-ready #app-splash { display: none; }
}

/* ── Post-purchase welcome / "deploying your agent" loading screen ─────────── */
/* Post-purchase reload: hide the app shell until the loading overlay is up, so the
   empty tasks screen never flashes before it (body's var(--bg) shows behind, matching
   the overlay). main.ts clears .is-welcoming the moment showWelcome() runs. */
.is-welcoming .layout { visibility: hidden; }
.welcome-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  opacity: 1;
  transition: opacity 0.5s var(--ease);
}
.welcome-overlay.done {
  opacity: 0;
  pointer-events: none;
}
.welcome-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 24px;
  max-width: 360px;
  text-align: center;
}
.welcome-mark {
  font-size: 56px;
  color: var(--accent);
  font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 48;
  animation: welcome-pulse 1.6s var(--ease) infinite;
}
@keyframes welcome-pulse {
  0%, 100% { transform: scale(1); opacity: 0.8; }
  50% { transform: scale(1.12); opacity: 1; }
}
.welcome-title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
  color: var(--text);
}
.welcome-sub {
  font-size: 14px;
  color: var(--text-dim);
  min-height: 20px;
}
.welcome-bar {
  width: 240px;
  height: 6px;
  border-radius: 999px;
  background: var(--bg-elev-2);
  overflow: hidden;
}
.welcome-bar-fill {
  width: 0%;
  height: 100%;
  border-radius: 999px;
  background-color: var(--accent);
  background-image: linear-gradient(90deg, transparent, color-mix(in srgb, var(--bg) 45%, transparent), transparent);
  background-size: 200% 100%;
  transition: width 0.3s var(--ease);
  animation: welcome-shimmer 1.4s linear infinite;
}
@keyframes welcome-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.confetti-canvas {
  position: fixed;
  inset: 0;
  z-index: 10000;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .welcome-mark, .welcome-bar-fill { animation: none; }
}

/* ============================================================================
   Landing page (pre-auth marketing, superpower.io) — landing.ts
   Minimalist, Gemini Material-3. The hero is an abstract WebGL "organic sphere"
   (orb.ts) standing in for the agent; everything else is quiet and spacious.
   Prefixed .lp-*; reuses the design tokens, no landing-only palette.
   ========================================================================== */
html.lp-open, html.lp-open body { overflow: hidden; }

.lp-host {
  position: fixed;
  inset: 0;
  z-index: 3000;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-padding-top: 64px;
  scroll-behavior: smooth;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
}
.lp-root { min-height: 100%; display: flex; flex-direction: column; }
.lp-main { position: relative; z-index: 1; display: flex; flex-direction: column; }

/* ---- scroll-driven background (fixed; cross-fades + scales with --bgP) ---- */
.lp-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.lp-bg span { position: absolute; inset: -8%; display: block; will-change: opacity, transform; }
.lp-bg-a {
  opacity: calc(1 - var(--bgP, 0));
  transform: scale(calc(1 + var(--bgP, 0) * 0.22));
  background: radial-gradient(1000px 660px at 50% -10%, color-mix(in srgb, #9b5cff 20%, transparent), transparent 60%);
}
.lp-bg-b {
  opacity: var(--bgP, 0);
  transform: scale(calc(1.22 - var(--bgP, 0) * 0.22));
  background:
    radial-gradient(860px 680px at 12% 6%, color-mix(in srgb, #ff6a1e 24%, transparent), transparent 56%),
    radial-gradient(980px 760px at 90% 94%, color-mix(in srgb, #2f7bff 30%, transparent), transparent 58%);
}

/* ---- nav ---- */
.lp-nav {
  position: sticky;
  top: 0;
  z-index: 30;
  backdrop-filter: saturate(140%) blur(14px);
  background: color-mix(in srgb, var(--bg) 64%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.lp-nav-stuck { border-bottom-color: var(--border); background: color-mix(in srgb, var(--bg) 86%, transparent); }
.lp-nav-inner { max-width: 1100px; margin: 0 auto; padding: 13px 24px; display: flex; align-items: center; gap: 18px; }
.lp-brand { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; color: var(--text); font-family: var(--font-display); font-weight: 700; font-size: 19px; letter-spacing: -0.3px; }
.lp-mark { font-variation-settings: "FILL" 1, "wght" 500, "GRAD" 0, "opsz" 24; font-size: 24px; }
.lp-mark-grad { background: linear-gradient(135deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.lp-nav-links { display: flex; gap: 4px; }
.lp-nav-links a { color: var(--text-dim); text-decoration: none; font-size: 14px; font-weight: 500; padding: 8px 12px; border-radius: 999px; transition: color 0.15s var(--ease), background 0.15s var(--ease); }
.lp-nav-links a:hover { color: var(--text); background: var(--bg-elev-1); }
.lp-nav-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.lp-icon-btn { display: inline-flex; align-items: center; gap: 5px; height: 38px; padding: 0 12px; border: 1px solid var(--border); background: transparent; color: var(--text-dim); border-radius: 999px; cursor: pointer; font-family: var(--font-ui); font-size: 13px; font-weight: 600; transition: color 0.15s var(--ease), border-color 0.15s var(--ease), background 0.15s var(--ease); }
.lp-icon-btn:hover { color: var(--text); border-color: var(--border-strong); background: var(--bg-elev-1); }
.lp-icon-btn .material-symbols-rounded { font-size: 19px; }

/* ---- buttons ---- */
.lp-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; border: 1px solid transparent; border-radius: 999px; padding: 12px 22px; min-height: 44px; font-family: var(--font-ui); font-size: 14.5px; font-weight: 600; cursor: pointer; white-space: nowrap; transition: transform 0.13s var(--ease), filter 0.15s var(--ease), background 0.15s var(--ease), border-color 0.15s var(--ease); }
.lp-btn:active { transform: scale(0.97); }
.lp-btn .material-symbols-rounded { font-size: 19px; }
.lp-btn-primary { background: var(--accent); color: var(--on-accent); }
.lp-btn-primary:hover { filter: brightness(1.08); }
.lp-btn-ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.lp-btn-ghost:hover { background: var(--bg-elev-1); }
.lp-btn-lg { min-height: 52px; padding: 15px 28px; font-size: 15.5px; }
.lp-btn-xl { min-height: 58px; padding: 17px 38px; font-size: 17px; }
.lp-btn-sm { min-height: 36px; padding: 8px 16px; font-size: 13px; }
.lp-nav-cta { padding: 9px 18px; min-height: 38px; }
.lp-btn-spotlight {
  color: #fff; border: none;
  background: linear-gradient(120deg, var(--grad-a), var(--grad-b) 52%, var(--grad-c));
  background-size: 180% 180%;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--grad-b) 36%, transparent);
  animation: lp-shift 9s ease-in-out infinite;
}
.lp-btn-spotlight:hover { filter: brightness(1.05); transform: translateY(-1px); }
.lp-btn-spotlight:active { transform: scale(0.98); }

/* ---- shared primitives ---- */
.lp-eyebrow { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; letter-spacing: 0.2px; color: var(--text-dim); padding: 7px 14px; border: 1px solid var(--border); border-radius: 999px; background: color-mix(in srgb, var(--bg-elev-1) 70%, transparent); }
.lp-eyebrow .lp-mark { font-size: 17px; }
.lp-eyebrow-center { margin: 0 auto; }
.lp-h1 { margin: 26px 0 0; font-family: var(--font-display); font-weight: 700; font-size: clamp(38px, 6vw, 66px); line-height: 1.03; letter-spacing: -1.6px; color: var(--text); }
.lp-h2 { margin: 16px 0 0; font-family: var(--font-display); font-weight: 700; font-size: clamp(27px, 3.6vw, 42px); line-height: 1.08; letter-spacing: -0.8px; color: var(--text); }
.lp-h2-center { text-align: center; }
.lp-lede { margin: 18px 0 0; max-width: 50ch; font-size: clamp(16px, 1.5vw, 18.5px); line-height: 1.6; color: var(--text-dim); }
.lp-lede-center { margin-left: auto; margin-right: auto; text-align: center; }

/* ---- the organic-sphere orb ---- */
.lp-orb { position: relative; display: grid; place-items: center; }
/* Hero orb is a big, immersive backdrop the copy overlays (absolute-centered). */
/* The hero orb scales up + fades as you scroll (you "dive into" it) via --heroP. */
.lp-orb-hero { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(calc(1 + var(--heroP, 0) * 1.45)); opacity: calc(1 - var(--heroP, 0) * 1.15); width: min(820px, 96vw, 90vh); aspect-ratio: 1; z-index: 0; pointer-events: none; will-change: transform, opacity; }
.lp-orb-portal { width: min(620px, 82vw, 58vh); aspect-ratio: 1; margin: 0 auto 2px; }
.lp-orb-canvas { position: relative; z-index: 1; width: 100%; height: 100%; display: block; }
/* Faint ambient behind the canvas; the orb's own WebGL bloom carries the glow. */
.lp-orb-halo {
  position: absolute; inset: 2%; z-index: 0; border-radius: 50%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, #2f74ff 30%, transparent),
    color-mix(in srgb, #ff5a24 12%, transparent) 56%,
    transparent 72%);
  filter: blur(46px);
  opacity: 0.5;
  animation: lp-breathe 7s ease-in-out infinite;
}
/* CSS fallback sphere — only when WebGL is unavailable (.is-fallback set by JS) */
.lp-orb-fallback { display: none; }
.lp-orb.is-fallback .lp-orb-canvas { display: none; }
.lp-orb.is-fallback .lp-orb-fallback {
  display: block; position: absolute; z-index: 1; inset: 12%; border-radius: 50%;
  background:
    radial-gradient(circle at 36% 28%, #cde6ff 0%, transparent 38%),
    conic-gradient(from 210deg, var(--grad-a), var(--grad-b) 40%, var(--grad-c) 66%, var(--grad-a));
  box-shadow: inset -16px -22px 40px rgba(0,0,0,0.4), 0 20px 60px color-mix(in srgb, var(--grad-b) 40%, transparent);
  filter: saturate(1.05);
  animation: lp-spin 18s linear infinite;
}

/* ---- hero: copy overlays the orb, sitting in its dark hollow center ---- */
.lp-hero {
  position: relative; width: 100%; margin: 0 auto;
  min-height: 100svh;
  padding: 84px 24px 64px;
  display: grid; place-items: center; text-align: center; overflow: hidden;
}
.lp-hero-copy { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; max-width: 540px; transform: translateY(calc(var(--heroP, 0) * -40px)) scale(calc(1 - var(--heroP, 0) * 0.08)); opacity: calc(1 - var(--heroP, 0) * 1.55); will-change: transform, opacity; }
/* Theme-aware scrim so the headline stays legible over the orb's bright ribbons. */
.lp-hero-copy::before {
  content: ""; position: absolute; z-index: -1; inset: -44% -16%;
  background: radial-gradient(ellipse 60% 54% at 50% 50%,
    color-mix(in srgb, var(--bg) 92%, transparent) 0%,
    color-mix(in srgb, var(--bg) 80%, transparent) 44%,
    transparent 76%);
}
.lp-hero-copy .lp-h1, .lp-hero-copy .lp-lede { text-shadow: 0 2px 26px color-mix(in srgb, var(--bg) 78%, transparent); }
.lp-cta-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 30px; }
.lp-scroll-hint {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--border); background: color-mix(in srgb, var(--bg-elev-1) 70%, transparent);
  color: var(--text-faint); cursor: pointer; transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
  animation: lp-bob 2.4s ease-in-out infinite;
}
.lp-scroll-hint:hover { color: var(--text); border-color: var(--border-strong); }
.lp-scroll-hint .material-symbols-rounded { font-size: 24px; }

.lp-section-head { display: flex; flex-direction: column; align-items: center; text-align: center; max-width: 760px; margin: 0 auto; }

/* ---- what is Genie + how it delivers value (narrative + Works/Learns/Earns) ---- */
.lp-what { max-width: 1100px; width: 100%; margin: 0 auto; padding: clamp(72px, 11vw, 140px) 24px clamp(56px, 9vw, 110px); display: flex; flex-direction: column; align-items: center; scroll-margin-top: 72px; }
/* Scrubbed entrance: the section scales + fades UP into focus as you scroll in
   (fallback 1 = fully visible, so reduced-motion / no-JS shows it normally). */
.lp-what .lp-section-head, .lp-what .lp-values {
  transform: scale(calc(0.78 + var(--whatP, 1) * 0.22));
  opacity: calc(var(--whatP, 1) * 1.15 - 0.15);
  transform-origin: center center;
  will-change: transform, opacity;
}
.lp-values { list-style: none; margin: clamp(40px, 6vw, 64px) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; width: 100%; }
.lp-value { position: relative; padding: 30px 26px 28px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: color-mix(in srgb, var(--bg-elev) 55%, transparent); transition: border-color 0.2s var(--ease), transform 0.2s var(--ease), background 0.2s var(--ease); }
.lp-value:hover { border-color: var(--border-strong); transform: translateY(-3px); background: var(--bg-elev); }
.lp-value-n { position: absolute; top: 22px; right: 24px; font-family: var(--font-display); font-weight: 700; font-size: 13px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.lp-value-icon { font-size: 34px; font-variation-settings: "FILL" 1, "wght" 500; background: linear-gradient(135deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.lp-value-kicker { margin: 16px 0 0; font-family: var(--font-display); font-weight: 700; font-size: 21px; letter-spacing: -0.3px; color: var(--text); }
.lp-value-body { margin: 9px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--text-dim); }

/* ---- closing CTA ---- */
.lp-portal { position: relative; width: 100%; padding: clamp(64px, 10vw, 130px) 24px clamp(72px, 11vw, 150px); display: flex; flex-direction: column; align-items: center; text-align: center; scroll-margin-top: 64px; }
.lp-portal-inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; max-width: 640px; }
.lp-portal .lp-btn-xl { margin-top: 30px; }

/* ---- footer ---- */
.lp-footer { border-top: 1px solid var(--border); }
.lp-footer-grid { max-width: 1100px; margin: 0 auto; padding: clamp(40px, 5vw, 60px) 24px clamp(24px, 3vw, 32px); display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 28px; }
.lp-footer-tag { margin: 12px 0 0; color: var(--text-faint); font-size: 13.5px; max-width: 28ch; line-height: 1.5; }
.lp-footer-tag-strong { color: var(--text); font-weight: 600; margin-top: 6px; }
.lp-footer-col h4 { margin: 0 0 12px; font-size: 13px; font-weight: 600; color: var(--text); }
.lp-footer-col a { display: block; color: var(--text-faint); text-decoration: none; font-size: 13.5px; padding: 5px 0; transition: color 0.15s var(--ease); }
.lp-footer-col a:hover { color: var(--text); }
.lp-footer-base { max-width: 1100px; margin: 0 auto; padding: 16px 24px 28px; border-top: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--text-faint); font-size: 13px; }

/* ---- reveal on scroll (fade + scale-in) ---- */
.lp-host [data-reveal] { opacity: 0; transform: translateY(28px) scale(0.965); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); will-change: opacity, transform; }
.lp-host [data-reveal].in { opacity: 1; transform: none; }

/* ---- keyframes ---- */
@keyframes lp-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
@keyframes lp-breathe { 0%, 100% { opacity: 0.6; transform: scale(0.97); } 50% { opacity: 0.95; transform: scale(1.03); } }
@keyframes lp-bob { 0%, 100% { transform: translateX(-50%) translateY(0); } 50% { transform: translateX(-50%) translateY(5px); } }
@keyframes lp-spin { to { transform: rotate(360deg); } }

/* ---- responsive ---- */
@media (max-width: 900px) {
  .lp-values { grid-template-columns: 1fr; max-width: 460px; margin-left: auto; margin-right: auto; gap: 16px; }
}
@media (max-width: 860px) { .lp-nav-links { display: none; } }
@media (max-width: 600px) {
  .lp-nav-inner { padding: 11px 16px; }
  .lp-hero, .lp-what, .lp-portal { padding-left: 16px; padding-right: 16px; }
  /* immersive full-bleed orb on phones (bleeds past the edges) */
  .lp-orb-hero { width: min(680px, 138vw); top: 46%; }
  .lp-h1 { font-size: clamp(34px, 11vw, 46px); }
  .lp-footer-grid { grid-template-columns: 1fr 1fr; padding-left: 16px; padding-right: 16px; }
  .lp-footer-brand { grid-column: 1 / -1; }
  .lp-cta-row .lp-btn { flex: 1 1 auto; }
  .lp-btn-xl { width: 100%; }
  .lp-lang-label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-host { scroll-behavior: auto; }
  .lp-host [data-reveal] { opacity: 1; transform: none; transition: none; }
  .lp-orb-halo, .lp-btn-spotlight, .lp-scroll-hint, .lp-orb.is-fallback .lp-orb-fallback { animation: none; }
  /* hold the scroll-driven scale/fade static (JS also stops setting --heroP/--bgP) */
  .lp-orb-hero { transform: translate(-50%, -50%) !important; opacity: 1 !important; }
  .lp-hero-copy { transform: none !important; opacity: 1 !important; }
  .lp-bg-a { opacity: 1 !important; transform: none !important; }
  .lp-bg-b { opacity: 0 !important; }
  .lp-what .lp-section-head, .lp-what .lp-values { transform: none !important; opacity: 1 !important; }
}

/* ---- login modal overlays the landing (kept mounted behind as a blurred backdrop) ---- */
html.lp-login-open .auth-overlay {
  z-index: 3500;
  background: color-mix(in srgb, var(--bg) 52%, transparent);
  -webkit-backdrop-filter: saturate(130%) blur(11px);
  backdrop-filter: saturate(130%) blur(11px);
  animation: lp-fade-in 0.28s var(--ease);
}
html.lp-login-open .auth-card { animation: lp-card-in 0.34s var(--ease); }
@keyframes lp-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes lp-card-in { from { opacity: 0; transform: translateY(14px) scale(0.96); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  html.lp-login-open .auth-overlay, html.lp-login-open .auth-card { animation: none; }
}

/* ---- mascot rage-tap explode: full-screen companion effects (mascotExplodeFx.ts) ----
   The mascot's own WebGL scene stays self-contained to its canvas; these three
   pieces are what make the home hero's explode read as hitting the WHOLE
   screen instead: a full-viewport flash, a shake on the entire page, and a
   shower of blocky "pixel" confetti. z-index sits above every other overlay
   (the highest prior in this file is the welcome/confetti overlay at 10000). */
.mascot-explode-flash {
  position: fixed;
  inset: 0;
  z-index: 10500;
  background: #fff;
  pointer-events: none;
  animation: mascot-explode-flash-pop 550ms ease-out forwards;
}
@keyframes mascot-explode-flash-pop {
  0% { opacity: 0; }
  10% { opacity: 1; }
  100% { opacity: 0; }
}

/* Shakes the whole page (not just the mascot's own head) — applied to <body>
   so every fixed-position element (composer, nav, banners…) rattles together
   as one rigid unit instead of the shake being scoped to a sub-tree. */
.mascot-explode-shake { animation: mascot-explode-shake 600ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
@keyframes mascot-explode-shake {
  10%, 90% { transform: translate3d(-2px, 0, 0); }
  20%, 80% { transform: translate3d(5px, -3px, 0); }
  30%, 50%, 70% { transform: translate3d(-10px, 5px, 0); }
  40%, 60% { transform: translate3d(10px, -5px, 0); }
  100% { transform: translate3d(0, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .mascot-explode-shake { animation: none; }
}

.mascot-confetti-layer {
  position: fixed;
  inset: 0;
  z-index: 10400;
  overflow: hidden;
  pointer-events: none;
}
/* Plain solid squares, straight fall, no rotation — deliberately blocky/
   "pixel" rather than the fluttering-paper look of the welcome screen's
   rotating rect confetti (welcome.ts's fireConfetti). */
.mascot-confetti-pixel {
  position: absolute;
  top: -12px;
  animation-name: mascot-confetti-fall;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
@keyframes mascot-confetti-fall {
  0% { transform: translate(0, 0); opacity: 1; }
  100% { transform: translate(var(--drift, 0px), 110vh); opacity: 0.85; }
}

/* ============================================================================
   Pre-auth CINEMATIC landing (landing.ts). Fully namespaced under `.lp-cine`
   with `lpc-` classes + `#lpc*` ids so nothing here collides with the app's
   global styles. Dark GSAP scroll-film; the mascot is the app's own
   createMascot(). Typography: Inter + JetBrains Mono (loaded in index.html).
   The host sits at z-index 900 — below the Privy auth overlay (z 1000).
   ============================================================================ */
.lp-cine{
  --lpc-bg:#0f0f10;--lpc-ink:#f3f3f4;--lpc-dim:#b9bcc0;--lpc-faint:#83878d;--lpc-line:rgba(255,255,255,.10);
  position:fixed;inset:0;z-index:900;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;
  color:var(--lpc-ink);font-family:'Inter',system-ui,sans-serif;-webkit-font-smoothing:antialiased;
  background:
    radial-gradient(1100px 720px at 50% 30%, rgba(99,27,255,.16), transparent 60%),
    radial-gradient(820px 620px at 74% 64%, rgba(198,0,255,.10), transparent 60%),
    var(--lpc-bg);
}
.lp-cine *{box-sizing:border-box;margin:0;padding:0}
.lp-cine h1{font-size:clamp(2rem,5.6vw,4.6rem);line-height:1.03;letter-spacing:-.032em;font-weight:700;text-shadow:0 2px 50px rgba(0,0,0,.9)}
.lpc-g{background:linear-gradient(110deg,#4d7bff,#8a6cff 55%,#c600ff);-webkit-background-clip:text;background-clip:text;color:transparent}

.lpc-nav{position:fixed;top:0;left:0;right:0;z-index:40;display:flex;align-items:center;justify-content:space-between;padding:26px 40px}
.lpc-logo{height:34px;filter:brightness(0) invert(1) drop-shadow(0 1px 10px rgba(0,0,0,.5))}
.lpc-nav-r{display:flex;align-items:center;gap:16px}
.lpc-lang{font-family:'JetBrains Mono',monospace;font-size:12px;letter-spacing:.05em;color:var(--lpc-faint);border:1px solid var(--lpc-line);border-radius:100px;padding:7px 12px;cursor:pointer;user-select:none;transition:border-color .2s}
.lpc-lang:hover{border-color:var(--lpc-ink)}
.lpc-lang b{color:var(--lpc-ink);font-weight:700}
.lpc-socials{display:flex;align-items:center;gap:6px}
.lpc-social{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:100px;color:var(--lpc-dim);text-decoration:none;transition:color .2s,background .2s}
.lpc-social:hover{color:var(--lpc-ink);background:rgba(255,255,255,.07)}
.lpc-social svg{width:18px;height:18px;display:block}

.lpc-scroller{height:500vh;position:relative}
.lpc-stage{position:sticky;top:0;height:100vh;width:100%;overflow:hidden}
.lpc-bg{position:absolute;inset:-10%;z-index:0;opacity:0;background:radial-gradient(1100px 720px at 50% 62%, rgba(66,120,255,.15), transparent 60%)}
.lpc-world{position:absolute;inset:0;z-index:2;will-change:transform}

.lpc-net{position:absolute;inset:0;width:100%;height:100%;overflow:visible;z-index:1}
.lpc-net line{stroke:rgba(150,165,255,.30);stroke-width:1;vector-effect:non-scaling-stroke;stroke-dasharray:2 3;animation:lpc-march 2.4s linear infinite}
@keyframes lpc-march{to{stroke-dashoffset:-5}}

.lpc-agent{position:absolute;transform:translate(-50%,-50%);pointer-events:none;filter:saturate(.62) brightness(.92);transition:filter .7s ease}
.lpc-agent.is-ack{filter:saturate(.95) brightness(1.04)}

#lpcHero{position:absolute;left:50%;top:70%;width:150vmin;height:150vmin;pointer-events:none;z-index:4;filter:saturate(.9) brightness(.96);transition:filter .7s ease}
#lpcHero.is-verified{filter:saturate(1.12) brightness(1.12) drop-shadow(0 0 34px rgba(90,150,255,.55))}

.lpc-ring{position:absolute;left:50%;top:70%;width:15vmin;height:15vmin;transform:translate(-50%,-50%) rotate(-90deg);z-index:3;opacity:0;pointer-events:none}
.lpc-ring circle{fill:none;stroke:#78c8ff;stroke-width:2.4;stroke-linecap:round;filter:drop-shadow(0 0 6px rgba(120,200,255,.85))}

.lpc-beam{position:absolute;left:50%;bottom:32%;width:2px;height:0;transform:translateX(-50%);z-index:18;
  background:linear-gradient(to top,rgba(120,200,255,.12),#7fc4ff);box-shadow:0 0 14px rgba(120,200,255,.9);opacity:0}
.lpc-beam::before{content:"";position:absolute;top:-4px;left:50%;transform:translateX(-50%);width:9px;height:9px;border-radius:50%;background:#cfe6ff;box-shadow:0 0 14px rgba(150,210,255,1),0 0 26px rgba(90,160,255,.7)}
.lpc-hbeam{position:absolute;top:70%;height:2px;width:0;z-index:5;transform:translateY(-50%);opacity:0;box-shadow:0 0 14px rgba(120,200,255,.9)}
.lpc-hbeam.left{right:53%;background:linear-gradient(to left,rgba(120,200,255,.12),#7fc4ff)}
.lpc-hbeam.right{left:53%;background:linear-gradient(to right,rgba(120,200,255,.12),#7fc4ff)}
.lpc-hbeam::after{content:"";position:absolute;top:50%;transform:translateY(-50%);width:9px;height:9px;border-radius:50%;background:#cfe6ff;box-shadow:0 0 14px rgba(150,210,255,1),0 0 26px rgba(90,160,255,.7)}
.lpc-hbeam.left::after{left:-4px}
.lpc-hbeam.right::after{right:-4px}

.lpc-record{position:absolute;left:50%;top:37%;transform:translate(-50%,14px);z-index:22;opacity:0;pointer-events:none;
  width:min(430px,88vw);padding:18px 20px;border-radius:16px;font-family:'JetBrains Mono',monospace;
  background:rgba(18,20,26,.74);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,.12);box-shadow:0 30px 80px rgba(0,0,0,.55), inset 0 0 0 1px rgba(90,150,255,.08)}
.lpc-record-hd{display:flex;align-items:center;gap:8px;font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--lpc-faint);margin-bottom:15px}
.lpc-record-hd .lpc-chain{margin-left:auto;color:#6b7078;font-weight:700;letter-spacing:.14em;transition:color .4s}
.lpc-record-hd .lpc-chain.on{color:#5aa0ff}
.lpc-record ul{list-style:none;display:grid;gap:12px}
.lpc-record li{display:flex;align-items:center;gap:12px;font-size:13.5px;color:#6b7078;transition:color .4s ease}
.lpc-record li.done{color:#e7e9ee}
.lpc-record .lpc-chk{flex:none;width:18px;height:18px;border-radius:50%;border:1.5px solid #3a3f48;display:grid;place-items:center;transition:.35s ease}
.lpc-record li.done .lpc-chk{background:#22c55e;border-color:#22c55e;box-shadow:0 0 12px rgba(34,197,94,.55)}
.lpc-record .lpc-chk::after{content:"✓";font-size:11px;line-height:1;color:#04210f;opacity:0;transition:opacity .3s ease}
.lpc-record li.done .lpc-chk::after{opacity:1}

.lpc-beat{position:absolute;left:0;right:0;z-index:20;text-align:center;padding:0 8vw;pointer-events:none}
.lpc-heroCopy{top:30%;transform:translateY(-50%)}
.lpc-heroCopy .lpc-cta{pointer-events:auto}
.lpc-problemCopy,.lpc-solutionCopy{top:15%;opacity:0}
#lpcSolutionCopy{top:9%}
#lpcSolutionCopy h1{font-size:clamp(2.4rem,5.2vh,4.6rem);line-height:1.04}
.lpc-eyebrow{font-family:'JetBrains Mono',monospace;font-size:12px;font-weight:700;letter-spacing:.28em;text-transform:uppercase;color:var(--lpc-dim);margin-bottom:26px}
.lpc-heroCopy h1{font-size:clamp(2.4rem,6.4vw,5.4rem);line-height:1.0}
.lpc-heroCopy h1 .lpc-g{background:linear-gradient(110deg,#0036ff,#631bff 52%,#c600ff);-webkit-background-clip:text;background-clip:text;color:transparent}
.lpc-sub{margin:22px auto 0;max-width:50ch;font-size:clamp(1rem,1.5vw,1.2rem);line-height:1.6;color:var(--lpc-dim);text-shadow:0 2px 30px rgba(0,0,0,.9)}
.lpc-cta{display:flex;gap:14px;justify-content:center;margin-top:40px}
.lpc-btn{font-family:'JetBrains Mono',monospace;font-size:.84rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:14px 24px;border-radius:100px;text-decoration:none;border:0;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s}
.lpc-btn:hover{transform:translateY(-1px)}
.lpc-btn-p{background:#fff;color:#0f0f10}

.lpc-cue{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:40;font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.3em;color:var(--lpc-faint);text-transform:uppercase;animation:lpc-bob 1.8s ease-in-out infinite}
@keyframes lpc-bob{0%,100%{opacity:.35;transform:translate(-50%,0)}50%{opacity:1;transform:translate(-50%,5px)}}

.lpc-finale{position:relative;z-index:1;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
  padding:12vh 8vw 11vh;background:radial-gradient(1000px 720px at 50% 42%, rgba(99,27,255,.16), transparent 62%),var(--lpc-bg)}
.lpc-genieWrap{position:relative;width:31vmin;height:31vmin;max-width:260px;max-height:260px;margin-bottom:-14px;will-change:transform}
.lpc-genieWrap::before{content:"";position:absolute;inset:8%;border-radius:50%;z-index:0;
  background:radial-gradient(closest-side, rgba(90,150,255,.34), transparent 72%);filter:blur(4px)}
.lpc-finale canvas{position:relative;z-index:1;width:100%;height:100%;filter:saturate(1.12) brightness(1.1) drop-shadow(0 0 34px rgba(90,150,255,.5))}
.lpc-finale .lpc-eyebrow{margin-bottom:20px}
.lpc-finale h1{max-width:15ch}
.lpc-finale .lpc-sub{max-width:52ch}
.lpc-finale .lpc-cta{margin-top:38px}
.lpc-finale .lpc-btn{padding:16px 30px;font-size:.9rem}

.lpc-bloom{position:absolute;left:50%;top:70%;width:200vmax;height:200vmax;transform:translate(-50%,-50%);border-radius:50%;z-index:36;opacity:0;pointer-events:none;
  background:radial-gradient(closest-side, rgba(244,246,255,.99), rgba(172,140,255,.95) 30%, rgba(99,27,255,.6) 55%, rgba(15,15,16,0) 72%)}
.lpc-stage .lpc-finale{position:absolute;inset:0;min-height:0;z-index:30;opacity:0;pointer-events:none}
.lpc-stage .lpc-finale.is-live{pointer-events:auto}

/* static fallback: no GSAP / prefers-reduced-motion — page reads at rest */
.lp-cine.is-static .lpc-scroller{height:auto}
.lp-cine.is-static .lpc-stage{height:auto;min-height:100vh;overflow:visible;padding:16vh 0}
.lp-cine.is-static .lpc-heroCopy{position:static;transform:none;opacity:1}
.lp-cine.is-static .lpc-problemCopy,.lp-cine.is-static .lpc-solutionCopy{display:none}
.lp-cine.is-static .lpc-cue{display:none}

@media (max-width:640px){
  .lpc-nav{padding:15px 16px}
  .lpc-logo{height:24px}
  .lpc-nav-r{gap:9px}
  .lpc-lang{padding:6px 9px;font-size:10.5px;white-space:nowrap}
  .lpc-socials{gap:2px} .lpc-social{width:30px;height:30px} .lpc-social svg{width:17px;height:17px}
  #lpcHero{width:124vmin;height:124vmin;top:76%}
  .lpc-ring{top:76%} .lpc-hbeam{top:76%} .lpc-bloom{top:76%} .lpc-beam{bottom:24%}
  .lpc-eyebrow{margin-bottom:15px;font-size:10.5px;letter-spacing:.2em}
  .lp-cine h1{font-size:clamp(2rem,8.6vw,2.7rem)}
  .lpc-heroCopy h1{font-size:clamp(2.3rem,10.5vw,3.1rem);line-height:1.03}
  .lpc-sub{font-size:1rem;max-width:30ch;margin-top:18px}
  .lpc-beat{padding:0 6vw}
  .lpc-heroCopy{top:38%}
  .lpc-problemCopy,.lpc-solutionCopy{top:23%}
  #lpcSolutionCopy{top:22%}
  #lpcSolutionCopy h1{font-size:clamp(2rem,8.6vw,2.6rem);line-height:1.05}
  .lpc-cta{flex-direction:column;align-items:center;gap:11px;margin-top:26px}
  .lpc-btn{width:min(300px,84vw);text-align:center;padding:15px 24px}
  .lpc-finale .lpc-btn{padding:15px 24px;font-size:.85rem}
  .lpc-record{width:90vw;padding:16px 18px;top:52%}
  .lpc-record li{font-size:12.5px}
  .lpc-finale{padding:12vh 7vw 12vh}
  .lpc-finale h1{font-size:clamp(2.4rem,12vw,3.4rem)}
  .lpc-finale .lpc-sub{font-size:1rem;max-width:32ch}
  .lpc-cue{bottom:15px}
}

/* ── 聪明钱 Smart Money screen (docs/SMART_MONEY.md) ─────────────────────────
   Robinhood-minimal: hairline rows + whitespace, big numbers; --ok/--err are the
   only color, reserved for PnL and the candles. All data comes from the
   control-plane cache — this screen never spends a token. */
/* ── 聪明钱 scanner boot sequence (src/web/smartmoneyScanner.ts) ──────────────
   A full-height square targeting grid that plays IN FRONT of the board while it
   loads: a beam sweeps top→bottom, wallet blips light with asset logos, the bot
   floor drops, and the #1 target locks — then it fades to the real board. Theme-
   adaptive instrument surface (light overrides at the end of this block; the canvas
   palette flips in smartmoneyScanner.ts). Full cinematic plays once per app
   session; warm reopens are load-gated. Skipped under reduced motion. */
.smx {
  position: relative; height: 78vh; border-radius: 16px; overflow: hidden;
  background: #0b0c0d;
  transition: opacity .5s ease, transform .6s cubic-bezier(.4, 0, .2, 1), filter .5s ease;
}
.smx.smx-done { opacity: 0; transform: scale(1.04); filter: blur(6px); pointer-events: none; }
.smx.smx-shake { animation: smx-shake .34s cubic-bezier(.36, .07, .19, .97); }
@keyframes smx-shake {
  0%, 100% { transform: translate(0, 0); } 15% { transform: translate(-2px, 1px); }
  30% { transform: translate(3px, -1px); } 45% { transform: translate(-2px, 2px); }
  60% { transform: translate(2px, -2px); } 75% { transform: translate(-1px, 1px); } 90% { transform: translate(1px, 0); }
}
.smx-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 2; }

/* CRT scanlines + vignette + animated grain */
.smx-crt { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.smx-lines { position: absolute; inset: 0; background: repeating-linear-gradient(0deg, rgba(0, 0, 0, .20) 0 1px, transparent 1px 3px); mix-blend-mode: multiply; }
.smx-vig { position: absolute; inset: 0; box-shadow: inset 0 0 80px 20px rgba(0, 0, 0, .55); border-radius: 16px; }
.smx-grain {
  position: absolute; inset: -50%; pointer-events: none; z-index: 3; opacity: .08;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: smx-grain .5s steps(4) infinite;
}
.smx-full .smx-grain { opacity: .09; }
@keyframes smx-grain {
  0% { transform: translate(0, 0); } 25% { transform: translate(-4%, 3%); } 50% { transform: translate(3%, -2%); }
  75% { transform: translate(-2%, -3%); } 100% { transform: translate(2%, 2%); }
}

/* power-on line-expand flash (full cinematic only) */
.smx-power {
  position: absolute; inset: 0; z-index: 6; pointer-events: none; transform-origin: center; opacity: 0;
  background: radial-gradient(closest-side, rgba(220, 255, 240, .95), rgba(0, 212, 126, .4) 55%, transparent);
}
.smx-power.smx-power-play { animation: smx-power .55s ease-out forwards; }
@keyframes smx-power {
  0% { opacity: 1; transform: scaleY(.006); } 16% { opacity: 1; transform: scaleY(.006); }
  38% { opacity: .95; transform: scaleY(1); } 100% { opacity: 0; transform: scaleY(1); }
}

/* centered readout */
.smx-read { position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%); text-align: center; pointer-events: none; z-index: 4; }
.smx-count { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 46px; font-weight: 700; color: #eaf7f0; letter-spacing: -.02em; line-height: 1; text-shadow: 0 0 24px rgba(0, 212, 126, .5); font-variant-numeric: tabular-nums; }
.smx-lbl { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 9.5px; letter-spacing: .26em; color: #7e8c84; text-transform: uppercase; margin-top: 6px; }

/* bottom telemetry strip */
.smx-telem { position: absolute; left: 0; right: 0; bottom: 0; padding: 20px 16px 18px; z-index: 4; background: linear-gradient(transparent, rgba(11, 12, 13, .55) 34%, rgba(11, 12, 13, .92)); }
.smx-status { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; font-weight: 700; letter-spacing: .12em; color: #00d47e; text-transform: uppercase; }
.smx-detail { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; color: #7e8a84; margin-top: 7px; min-height: 14px; letter-spacing: .02em; }
.smx-bar { height: 3px; border-radius: 3px; background: rgba(255, 255, 255, .06); margin-top: 11px; overflow: hidden; }
.smx-bar-fill { display: block; height: 100%; width: 0%; border-radius: 3px; background: linear-gradient(90deg, #00d47e, #7fe9c0); box-shadow: 0 0 10px rgba(0, 212, 126, .6); }
@media (prefers-reduced-motion: reduce) { .smx-grain, .smx-power { animation: none; } }

/* Light-theme scan (owner 2026-07-17: the scan adapts too) — same instrument in
   daylight. The canvas palette flips in smartmoneyScanner.ts; the chrome here. */
:root[data-theme="light"] .smx { background: #f3f6f3; }
:root[data-theme="light"] .smx-lines { background: repeating-linear-gradient(0deg, rgba(0, 0, 0, .05) 0 1px, transparent 1px 3px); }
:root[data-theme="light"] .smx-vig { box-shadow: inset 0 0 80px 20px rgba(22, 62, 38, .12); }
:root[data-theme="light"] .smx-grain { opacity: .05; }
:root[data-theme="light"] .smx-full .smx-grain { opacity: .06; }
:root[data-theme="light"] .smx-power { background: radial-gradient(closest-side, rgba(255, 255, 255, .95), rgba(0, 143, 4, .3) 55%, transparent); }
:root[data-theme="light"] .smx-count { color: #10271a; text-shadow: 0 0 24px rgba(0, 143, 4, .3); }
:root[data-theme="light"] .smx-lbl { color: #5f6e64; }
:root[data-theme="light"] .smx-telem { background: linear-gradient(transparent, rgba(243, 246, 243, .55) 34%, rgba(243, 246, 243, .95)); }
:root[data-theme="light"] .smx-status { color: #008f04; }
:root[data-theme="light"] .smx-detail { color: #5f6e64; }
:root[data-theme="light"] .smx-bar { background: rgba(0, 0, 0, .08); }
:root[data-theme="light"] .smx-bar-fill { background: linear-gradient(90deg, #008f04, #35b24b); box-shadow: 0 0 10px rgba(0, 143, 4, .35); }

/* 聪明钱 palette matched to the 预测市场 screen (owner call 2026-07-17): the
   board sits on the app's neutral ground and paints its cards/borders as a thin
   wash IN the text color (color-mix), exactly like predict's --s1/--s2. That
   auto-themes — a light wash on the dark ground, a dark wash on the light
   ground — so no per-theme hex overrides are needed (the old committed-dark
   block is gone). The boot scanner keeps its own instrument surface (smx). */
.sm-root {
  --bg-elev-1: color-mix(in srgb, var(--text) 5%, transparent);
  --bg-elev-2: color-mix(in srgb, var(--text) 9%, transparent);
  --bg-elev-3: color-mix(in srgb, var(--text) 14%, transparent);
  --border: color-mix(in srgb, var(--text) 9%, transparent);
  --border-strong: color-mix(in srgb, var(--text) 16%, transparent);
  /* Active-chip wash, matching predict's --chip-on (text 16%). */
  --sm-chip-on: color-mix(in srgb, var(--text) 16%, transparent);
  /* Card surface — the tonal wash in DARK, pure WHITE in light (owner call:
     light-theme cards should be white, not grey). Separate from the chip fill
     so nav chips stay tonal pills while cards go white. */
  --sm-card: var(--bg-elev-1);
  /* Same type foundation as predict (M3/Robinhood): SF-Pro-first system stack,
     antialiased, tabular numerals everywhere — NOT the app's Google Sans. */
  font-family: -apple-system, "SF Pro Text", "Segoe UI", Roboto, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums;
  max-width: 960px; margin: 0 auto; padding: 6px 4px 28px; display: flex; flex-direction: column; container-type: inline-size;
  /* TRANSPARENT root (like predict's .pd-root): the board rides the container's
     elevated surface instead of sinking a darker --bg (#131314) well inside it.
     Measured: predict sits on #1e1f20; the old `background: var(--bg)` made the
     board a darker panel-in-a-panel. Cards come from the text-color washes. */
  background: transparent; color: var(--text);
}
/* Light theme: cards are WHITE, not the grey text-wash (owner call). Chips keep
   their tonal fill (they stay --bg-elev-1). */
:root[data-theme="light"] .sm-root { --sm-card: #ffffff; }
.sm-mount { width: 100%; }
/* Pull-to-refresh: the live Genie mascot (no text) descends from the top edge
   as you drag — a random face each pull, "thinking" while the board refetches
   underneath. The content stays put, so the refresh is barely noticeable. */
/* Overlays ABOVE the sticky tab row (owner 2026-08-05: "the pull anim should
   be on top of the tab section") — under the snackbar layer (10400+) only. */
.sm-ptr { position: absolute; top: 0; left: 50%; transform: translate(-50%, -100%); opacity: 0; width: 72px; height: 72px; pointer-events: none; z-index: 10300; }
.sm-ptr-canvas { width: 72px; height: 72px; display: block; }
/* Flat mockup sections (owner: match the approved mockup, not chat-card boxes). */
.sm-flat { margin-top: 4px; }
.sm-body { flex: 1 1 auto; }
.sm-sec { font-size: 13px; font-weight: 650; color: var(--text-dim); padding: 18px 0 4px; }
.sm-sec-flex { display: flex; justify-content: space-between; align-items: baseline; }
.sm-cap { font-size: 12px; color: var(--text-faint); padding: 2px 0 8px; }
.sm-cap-inline { font-size: 12px; font-weight: 400; color: var(--text-faint); }
.sm-empty { color: var(--text-faint); font-size: 14px; padding: 28px 0; text-align: center; }

.sm-list { display: flex; flex-direction: column; }
.sm-row { display: flex; align-items: center; gap: 12px; padding: 19px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
.sm-row:last-child { border-bottom: none; }
.sm-avatar { width: 34px; height: 34px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px; color: #eceef4; }
.sm-row-main { flex: 1; min-width: 0; }
.sm-row-name { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-row-sub { display: flex; align-items: center; gap: 12px; font-size: 12px; color: var(--text-faint); margin-top: 3px; font-variant-numeric: tabular-nums; }
.sm-sub-bit { display: inline-flex; align-items: center; gap: 4px; }
.sm-sub-ico { font-size: 15px; line-height: 1; }
/* PnL owns a right-aligned fixed slot so the numbers line up down the list. */
.sm-row-pnl { flex: none; min-width: 62px; text-align: right; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; color: var(--sm-up); font-variant-numeric: tabular-nums; }
.sm-row-pnl-cap { font-size: 10px; font-weight: 500; letter-spacing: 0; color: var(--text-faint); margin-top: 1px; white-space: nowrap; }
/* Fixed-width pill so Follow/Following don't shift the columns. */
.sm-row .sm-follow-pill { min-width: 84px; text-align: center; margin-top: 0; padding: 7px 10px; }
/* The sparkline is decoration — the first thing to go when width runs out. */
@media (max-width: 390px) { .sm-row .sm-mini { display: none; } }

.sm-alert { border: 1px solid var(--border); border-radius: 14px; padding: 11px 13px; margin-bottom: 8px; }
/* Instagram anatomy for move cards: header (who/what · when), chart media,
   one fact line, actions. */
.sm-ig-head { display: flex; align-items: center; gap: 10px; }
.sm-ig-ava { flex: none; width: 34px; height: 34px; border-radius: 999px; display: grid; place-items: center; font-size: 16px; font-weight: 800; }
.sm-ig-ava.long { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 14%, transparent); }
.sm-ig-ava.short { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 13%, transparent); }
.sm-ig-id { min-width: 0; }
.sm-ig-act { font-size: 15px; font-weight: 650; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-ig-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 1px; font-variant-numeric: tabular-nums; }
/* The chart is the hero: full-bleed to the card edges (card pads 11px 13px). */
.sm-ig-chart { display: block; width: calc(100% + 26px); height: auto; margin: 10px -13px 6px; }
.sm-ig-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }
.sm-alert.is-new { border-color: color-mix(in srgb, var(--sm-up) 35%, transparent); }
.sm-alert-line { font-size: 14px; }
.sm-alert-line b { font-weight: 650; }
.sm-alert-meta { font-size: 11.5px; color: var(--text-faint); margin: 3px 0 8px; }
.sm-mini-btn { font-size: 12.5px; font-weight: 600; color: var(--on-accent); background: var(--accent); border: none; border-radius: 999px; padding: 6px 13px; cursor: pointer; }

.sm-back-row { display: flex; align-items: center; gap: 6px; background: none; border: none; color: var(--text-dim); font-size: 13px; padding: 8px 0; cursor: pointer; }
.sm-hero { padding: 6px 0 2px; display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.sm-hero-main { min-width: 0; }
/* Follow pill at the dashboard's top right (persona). */
.sm-follow-pill { flex: none; margin-top: 2px; font-size: 12.5px; font-weight: 650; color: var(--sm-up); background: none; border: 1px solid color-mix(in srgb, var(--sm-up) 42%, transparent); border-radius: 999px; padding: 6px 14px; cursor: pointer; white-space: nowrap; }
.sm-follow-pill.done { color: var(--text-faint); border-color: var(--border); }
.sm-hero-name { font-size: 13px; color: var(--text-dim); }
.sm-hero-pnl { font-size: 38px; font-weight: 600; letter-spacing: -0.03em; color: var(--sm-up); line-height: 1.12; font-variant-numeric: tabular-nums; }
.sm-hero-cap { font-size: 12.5px; color: var(--text-faint); margin-top: 3px; }
.sm-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 14px 10px; padding: 14px 0; border-bottom: 1px solid var(--border); }
.sm-cell-k { font-size: 11px; color: var(--text-faint); }
.sm-cell-v { font-size: 14px; font-weight: 650; margin-top: 2px; }
.sm-cell-v.up { color: var(--sm-up); }
.sm-cell-v.down { color: var(--sm-down); }
.sm-replay { display: block; width: 100%; height: auto; margin-top: 6px; }
.sm-trades { display: flex; flex-direction: column; margin-top: 4px; }
/* (legacy .sm-trade block removed — superseded by the coin-dot history rows
   below; its inset box-shadow was the stray green bar clipping the coin pfp) */
.sm-trade-v { font-variant-numeric: tabular-nums; }
.sm-cta { display: block; width: 100%; margin-top: 14px; background: var(--sm-up); color: var(--sm-on-up); font-size: 14.5px; font-weight: 640; border: none; border-radius: 999px; padding: 12px; cursor: pointer; }
.sm-cta.done { background: var(--bg-elev-2); color: var(--text-dim); }
.sm-cta.ghost { background: none; border: 1px solid var(--border); color: var(--sm-up); font-weight: 600; margin-top: 10px; }


.home-chip-badge { background: var(--sm-up); color: var(--sm-on-up); border-radius: 999px; font-size: 10.5px; font-weight: 700; padding: 1px 6px; margin-left: 2px; }

/* PR6: 打法 playbook + 如果你跟 calculator (persona cards) */
.sm-kv { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.sm-kv:last-of-type { border-bottom: none; }
.sm-kv-k { color: var(--text-faint); flex: none; }
.sm-kv-v { font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.sm-kv-v.up { color: var(--sm-up); }
.sm-kv-v.down { color: var(--sm-down); }
.sm-risk { margin-top: 10px; border: 1px solid color-mix(in srgb, var(--sm-down) 25%, transparent); background: color-mix(in srgb, var(--sm-down) 7%, transparent); border-radius: 12px; padding: 9px 12px; font-size: 12.5px; color: var(--text-dim); line-height: 1.55; }
.sm-chips { display: flex; gap: 8px; padding: 8px 0 2px; }
.sm-chip { font-size: 12.5px; border: 1px solid var(--border); border-radius: 999px; padding: 5px 12px; background: none; color: var(--text-dim); cursor: pointer; font-variant-numeric: tabular-nums; }
.sm-chip.sel { color: var(--text); border-color: var(--border-strong); background: var(--bg-elev-1); font-weight: 650; }
.sm-calc-big { font-size: 21px; font-weight: 600; letter-spacing: -0.01em; padding-top: 8px; }


/* PR7: mockup parity — tabs, hero sparkline, row minis, footnote, cell subs */
/* Top spacing matches predict exactly (pd-root pad-top 6 + pd-chips margin-top
   2 = 32px clearance from the session header): enough to clear the header's drop
   shadow, no pull-up. */
/* Pill-chip navigation, matching predict's .pd-chips/.pd-chip exactly (M3 tonal
   chips, not the old green-underline tabs) — 13px/560, wash bg, active = the
   neutral chip-on wash + 620, no green accent (predict parity). */
.sm-tabs { display: flex; gap: 8px; margin: 2px 2px 0; padding-bottom: 2px; overflow-x: auto; scrollbar-width: none; }
.sm-tabs::-webkit-scrollbar { display: none; }
.sm-tab { flex: 0 0 auto; white-space: nowrap; font-size: 13px; font-weight: 560; color: var(--text-dim); background: var(--bg-elev-1); border: none; border-radius: 999px; padding: 9px 15px; line-height: 1; cursor: pointer; transition: background .14s, color .14s, transform .1s; }
.sm-tab:hover { background: var(--bg-elev-2); }
.sm-tab:active { transform: scale(0.96); }
.sm-tab.on { background: var(--sm-chip-on); color: var(--text); font-weight: 620; }
.sm-tab-new { font-size: 8.5px; font-weight: 680; letter-spacing: 0.06em; color: var(--sm-on-up); background: var(--sm-up); border-radius: 4px; padding: 1.5px 5px; margin-left: 6px; vertical-align: 1px; }
.sm-flow-eyebrow { font-size: 12px; color: var(--text-dim); margin-bottom: 2px; }
.sm-flow-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 3px; font-variant-numeric: tabular-nums; }
.sm-flow-sub.up { color: var(--sm-up); font-weight: 650; }
.sm-flow-sub.down { color: var(--sm-down); font-weight: 650; }
.sm-spark-wrap { padding: 10px 0 2px; }
.sm-spark { display: block; width: 100%; height: 92px; }
.sm-mini { flex: none; width: 56px; height: 24px; }
.sm-cell-sub { font-size: 11px; color: var(--text-faint); margin-top: 1px; }
/* Methodology footer under the board list: a concise 'what qualifies' line +
   this sweep's numbers. Its own hairline separates it from the last row. */
.sm-foot { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.sm-foot .sm-cap:not(:first-child) { padding-top: 2px; }
.sm-hero-pnl { font-size: 40px; }

/* PR8: alert-card context lines + compact CTAs + chase note */
.sm-alert-ctx { font-size: 13px; color: var(--text-dim); padding: 2px 0; }

/* F3: price×OI quadrant chip (docs/SMART_MONEY_RECEIPTS.md §3) — colored text
   line, not a pill; green/red only for fresh conviction, neutral otherwise. */
.sm-ctx { display: inline-block; font-size: 11.5px; font-weight: 600; padding: 3px 0 0; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.sm-ctx.up { color: var(--sm-up); }
.sm-ctx.down { color: var(--sm-down); }

/* Persona mockup parity: identity header, hero delta, range row, history rows */
.sm-pid { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.sm-pid-name { font-size: 15px; font-weight: 640; letter-spacing: -0.01em; }
.sm-pid-name .sm-pid-alink { color: inherit; text-decoration: none; }
.sm-pid-sub .sm-pid-alink { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.sm-verify { font-size: 16px; color: var(--sm-up); margin-left: 5px; vertical-align: -3px; }
.sm-pid-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 1px; font-variant-numeric: tabular-nums; }
.sm-hero-delta { font-size: 13px; font-weight: 650; margin-top: 3px; font-variant-numeric: tabular-nums; }
.sm-hero-delta.up { color: var(--sm-up); }
.sm-hero-delta.down { color: var(--sm-down); }
/* Realized / unrealized split under the hero (owner call 2026-07-17): breaks
   the headline into booked vs open-position paper. */
.sm-hero-split { font-size: 12px; margin-top: 5px; display: flex; flex-wrap: wrap; gap: 4px 12px; font-variant-numeric: tabular-nums; }
.sm-split { font-weight: 650; }
.sm-split.up { color: var(--sm-up); }
.sm-split.down { color: var(--sm-down); }
.sm-split.mut { color: var(--text-dim); font-weight: 500; }
.sm-split-k { color: var(--text-faint); font-weight: 500; }
.sm-trade { display: flex; align-items: center; gap: 10px; width: 100%; background: none; border: none; border-bottom: 1px solid var(--border); color: inherit; text-align: left; font: inherit; padding: 12px 2px; cursor: pointer; }
.sm-trade.sel { border-bottom-color: transparent; background: color-mix(in srgb, var(--sm-up) 6%, transparent); border-radius: 10px; }
.sm-trade-main { flex: 1; min-width: 0; }
.sm-trade-t { display: block; font-size: 15px; font-weight: 680; letter-spacing: -0.01em; }
.sm-trade-sub { display: block; font-size: 11.5px; color: var(--text-faint); margin-top: 1px; font-variant-numeric: tabular-nums; }
.sm-trade-right { flex: none; display: flex; align-items: center; gap: 8px; }
.sm-trade-v { font-size: 15.5px; font-weight: 680; letter-spacing: -0.01em; }
.sm-trade-v.up { color: var(--sm-up); }
.sm-trade-v.down { color: var(--sm-down); }
.sm-trade-v.mut { color: var(--text-dim); font-weight: 650; }
.sm-tag { font-style: normal; font-size: 9px; font-weight: 680; letter-spacing: 0.05em; border-radius: 4px; padding: 2.5px 6px; }
.sm-tag.win { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 14%, transparent); }
.sm-tag.loss { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 13%, transparent); }
.sm-tag.open { color: var(--text-faint); border: 1px dashed var(--border-strong); }
/* Human-gate receipt on the persona identity line (VENUES PRD §3). */
.sm-tag.human { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); }
.sm-trade-host:has(.sm-trade-panel) { border-bottom: 1px solid var(--border); padding: 2px 0 12px; }
.sm-trade-panel .sm-replay { margin-top: 4px; }
.sm-avs { display: inline-flex; margin-right: 6px; vertical-align: -1px; }
.sm-avs i { width: 11px; height: 11px; border-radius: 50%; border: 1.5px solid var(--bg); margin-left: -4px; display: inline-block; }
.sm-avs i:first-child { margin-left: 0; }

/* Persona §1: benchmark chart legend (his ROI vs board average) */
.sm-bench-legend { display: flex; gap: 16px; font-size: 11px; color: var(--text-dim); padding: 6px 0 0; font-variant-numeric: tabular-nums; }
.sm-bench-dot { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.sm-bench-dot.him { background: var(--sm-up); }
.sm-bench-dot.avg { background: var(--text-faint); }

/* F2: follow-record badge on Winners rows (docs/SMART_MONEY_RECEIPTS.md §2) */
.sm-fr { font-size: 11px; font-weight: 640; margin-top: 1px; font-variant-numeric: tabular-nums; }
.sm-fr.up { color: var(--sm-up); }
.sm-fr.down { color: var(--sm-down); }
.sm-fr.thin { color: var(--text-faint); font-weight: 500; }

/* F1: 资金流向 Flow tab (docs/SMART_MONEY_RECEIPTS.md §1) */
.sm-flow-hero { padding: 14px 0 4px; }
.sm-flow-net { font-size: 32px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; }
.sm-flow-net.up { color: var(--sm-up); }
.sm-flow-net.down { color: var(--sm-down); }
.sm-flow-bar { display: flex; align-items: center; gap: 14px; border-bottom: 1px solid var(--border); margin-top: 8px; }
.sm-flow-win { border: none; background: none; color: var(--text-dim); font-size: 12.5px; font-weight: 700; padding: 6px 0 9px; position: relative; cursor: pointer; }
.sm-flow-win.on { color: var(--sm-up); }
.sm-flow-win.on::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--sm-up); border-radius: 2px; }
.sm-flow-hint { margin-left: auto; font-size: 10.5px; color: var(--text-faint); padding-bottom: 6px; }
.sm-flow-row { border-bottom: 1px solid var(--border); }
.sm-flow-row:last-child { border-bottom: none; }
/* No-consensus rows stay full-brightness (owner call 2026-07-17: dimming made
   the common single-wallet case read as broken) — the caption + sunk order
   carry the distinction. */
.sm-flow-head { display: grid; grid-template-columns: 32px 1fr auto; gap: 12px; align-items: center; width: 100%; border: none; background: none; color: inherit; text-align: left; padding: 13px 2px; cursor: pointer; font: inherit; }
.sm-flow-coin { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-size: 10.5px; font-weight: 700; color: var(--text); }
.sm-flow-sym { font-weight: 640; font-size: 14.5px; }
.sm-flow-who { font-size: 12px; color: var(--text-dim); margin-top: 1px; }
.sm-flow-right { text-align: right; }
.sm-flow-amt { font-weight: 640; font-size: 14.5px; }
.sm-flow-amt.up { color: var(--sm-up); }
.sm-flow-amt.down { color: var(--sm-down); }
.sm-flow-right .sm-ctx { display: block; }
/* ★ display:flex DEFEATS [hidden] (its display:none loses to a later rule), so the
   collapsed state must be restated. This bit the board once already via
   .sm-secline; here it rendered every row expanded. */
.sm-flow-receipts { margin: 0 8px 9px; padding: 12px 14px; background: var(--bg-elev-1); border-radius: 12px; display: flex; flex-direction: column; gap: 10px; }
.sm-flow-receipts[hidden] { display: none; }
/* Kept for news headline dates (asset read → Fundamentals). */
.sm-flow-r-ago { flex: none; font-size: 11px; color: var(--text-faint); white-space: nowrap; }

/* ── Wallet receipt as an M3 two-line list item (owner 2026-07-27) ─────────
   Primary line: who + when. Supporting line: tonal verb chip + price + amount.
   Names lose the link underline — weight and color carry the affordance. */
.sm-rc { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.sm-rc + .sm-rc { border-top: 1px solid var(--border); padding-top: 10px; }
.sm-rc-top { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.sm-rc-top .sm-name, .sm-rc-top .sm-strong { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; font-size: 13px; font-weight: 600; border-bottom: none; }
.sm-rc-ago { flex: none; font-size: 11px; color: var(--text-faint); white-space: nowrap; }
.sm-rc-sub { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sm-rc-verb { flex: none; font-size: 11px; font-weight: 650; letter-spacing: 0.02em; white-space: nowrap; border-radius: 999px; padding: 3px 10px; }
.sm-rc-verb.up { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); }
.sm-rc-verb.down { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 12%, transparent); }
.sm-rc-px { flex: none; font-size: 11.5px; color: var(--text-faint); white-space: nowrap; font-variant-numeric: tabular-nums; }
.sm-rc-amt { flex: none; margin-left: auto; font-size: 13px; font-weight: 640; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sm-rc-amt.up { color: var(--sm-up); }
.sm-rc-amt.down { color: var(--sm-down); }
.sm-rc-sub .sm-copy-pill { margin-left: 8px; }
/* Inside an asset-read category body there is no gapped container — the item
   carries its own list rhythm so receipts align with the kv rows around them. */
.sm-acat-b .sm-rc { padding: 7px 0; }
.sm-acat-b .sm-rc + .sm-rc { padding-top: 10px; }
/* Entry price is context, not a decision input (the ticket restates it) — it is
   the first thing to go when the row runs out of width. */
@container (max-width: 460px) { .sm-rc-px { display: none; } }

/* ── Flow tab v2 (2026-07-25, restyled 2026-07-25 to M3 — owner pick B) ───────
   The comparison across windows IS the signal (buying 1h / selling 24h = you are
   the exit), but three same-size numbers in three columns gave the eye nowhere to
   land and scaled their bars against three different maxima, so nothing could be
   compared across them. Now: the LONG window is one dominant figure at tile scale
   (matching .sm-tile-v), the short windows live in a sparkline, and rows sit in a
   tonal surface-container.

   House idiom (see PR12 above): Gemini/M3 surfaces — tonal card, 16dp radius,
   state layers, error-container — with ROBINHOOD numerals: the figures keep their
   heavy weight and tight tracking rather than M3's lighter 400/500 defaults,
   because density is the point on a tab built for scanning many coins. */
.sm-flow-venue { display: inline-flex; gap: 2px; background: var(--sm-card); border: 1px solid var(--border); border-radius: 10px; padding: 2px; margin: 12px 0 2px; }
.sm-flow-vbtn { border: none; background: none; color: var(--text-dim); font: inherit; font-size: 13px; padding: 8px 15px; border-radius: 8px; cursor: pointer; min-height: 36px; }
.sm-flow-vbtn.on { background: color-mix(in srgb, var(--sm-up) 14%, transparent); color: var(--sm-up); font-weight: 650; }
/* headline-small, M3 weight — the surrounding chrome is where M3 shows, not the data */
.sm-flow-q { font-size: 20px; font-weight: 550; letter-spacing: -0.01em; margin: 14px 0 3px; text-wrap: balance; }
.sm-flow-qsub { font-size: 12px; color: var(--text-dim); margin-bottom: 12px; }
/* surface-container */
.sm-flow-card { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 4px; overflow: hidden; }
.sm-flow-row + .sm-flow-row { border-top: 1px solid var(--border); }
.sm-flow-head3 { display: grid; grid-template-columns: minmax(0, 1fr) 72px minmax(80px, auto); gap: 10px; align-items: center; width: 100%; border: none; background: none; color: inherit; text-align: left; padding: 12px 10px; cursor: pointer; font: inherit; border-radius: 12px; min-height: 64px; transition: background 0.12s ease; }
/* M3 state layers — the rows are buttons and previously gave no press feedback */
@media (hover: hover) { .sm-flow-head3:hover { background: var(--bg-elev-1); } }
.sm-flow-head3:active { background: var(--bg-elev-2); }
.sm-flow-tok { display: flex; align-items: center; gap: 11px; min-width: 0; }
.sm-flow-coin { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; font-size: 11.5px; font-weight: 700; color: #000; flex: none; }
.sm-flow-stack { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sm-flow-sym { font-weight: 600; font-size: 16px; letter-spacing: 0.005em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* label-small */
.sm-flow-sub2 { font-size: 10.5px; color: var(--text-faint); letter-spacing: 0.04em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Direction of travel across the windows; colour follows the dominant figure. */
.sm-flow-spark { display: block; }
.sm-flow-lead { text-align: right; min-width: 0; }
.sm-flow-leadv { font-size: 19px; font-weight: 680; letter-spacing: -0.02em; line-height: 1.1; white-space: nowrap; }
.sm-flow-leadv.up { color: var(--sm-up); }
.sm-flow-leadv.down { color: var(--sm-down); }
.sm-flow-leadv.zero { color: var(--text-faint); }
.sm-flow-leadk { font-size: 9.5px; color: var(--text-faint); letter-spacing: 0.08em; text-transform: uppercase; margin-top: 3px; white-space: nowrap; }
.sm-flow-row.dim .sm-flow-leadv { color: var(--text-dim); }
/* error-container: this is the most consequential sentence on the screen, and as
   bare ink it read like a rendering accident. Only rendered where the windows
   actually disagree — a warning on every row is a warning nobody reads. */
.sm-flow-warn { display: flex; gap: 9px; align-items: flex-start; margin: 0 8px 9px; padding: 11px 13px; border-radius: 12px; background: color-mix(in srgb, var(--warn) 13%, transparent); color: var(--warn); font-size: 11.5px; line-height: 1.45; }
.sm-flow-warn svg { flex: none; margin-top: 1px; }
/* filled tonal button at a real touch target (was a 24px outline pill) */
.sm-copy-pill { flex: none; margin-left: 6px; min-height: 36px; padding: 0 12px; font-size: 12px; font-weight: 650; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 18%, transparent); border: none; border-radius: 999px; cursor: pointer; }
.sm-copy-pill:active { background: color-mix(in srgb, var(--sm-up) 28%, transparent); }
/* Loading rows shaped like real ones (same sm-skel pulse as the Overview hero).
   A cold digest is a live ~20s Hyperliquid walk, and "Nobody moved" during it
   would state as fact something we haven't finished checking. */
.sm-flow-skel { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 4px; }
.sm-flow-skel-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) 72px 80px; gap: 10px; align-items: center; padding: 12px 10px; min-height: 64px; }
.sm-flow-skel-row + .sm-flow-skel-row { border-top: 1px solid var(--border); }
.sm-flow-skel-dot { width: 38px; height: 38px; border-radius: 50%; }
.sm-flow-skel-name { height: 14px; width: 58%; }
.sm-flow-skel-cell { height: 14px; }

/* ── 跟这单 copy ticket ──────────────────────────────────────────────────────
   A bottom sheet on phones, a centred card once there's room. The user places
   the order themselves; this only prefills it. */
/* SELF-CONTAINED TOKENS. The sheet mounts on <body>, not inside .sm-root, because
   .sm-root sets `container-type: inline-size` — containment makes it the containing
   block for position:fixed descendants, so a scrim nested there would be trapped
   inside the card instead of covering the screen. Mounting outside costs us every
   token .sm-root declares (--border, --sm-card, --bg-elev-*), so they are restated
   here from the root-level primitives (--text/--bg/--sm-up ARE on :root). */
.sm-sheet-scrim {
  --border: color-mix(in srgb, var(--text) 9%, transparent);
  --sm-card: color-mix(in srgb, var(--text) 5%, transparent);
  position: fixed; inset: 0; z-index: 90; background: rgba(0, 0, 0, 0.55);
  display: flex; align-items: flex-end; justify-content: center;
  font-variant-numeric: tabular-nums;
  /* Whatever the on-screen keyboard is covering, measured from visualViewport
     and set by the sheet that opened. The bottom-aligned sheet lands above it
     instead of behind it. 0 when there is no keypad. */
  padding-bottom: var(--kb, 0px);
  /* And the scrim ENDS above the session's composer, rather than dimming it:
     an overlay over a control is the same as removing the control. 0 on any
     screen that has no composer parked at the bottom. */
  bottom: var(--dock, 0px);
}
/* The composer's own 32px fade (.composer-wrap::before) can never reach a
   sheet — the scrim paints in a higher layer — so a docked sheet ended in a
   hard cut at the composer's edge (owner report 2026-08-01). Repaint the same
   gradient on the scrim itself. Height rides the dock: 0 when no composer is
   parked below, so an undocked sheet keeps its clean bottom. */
.sm-sheet-scrim::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: min(32px, var(--dock, 0px));
  background: linear-gradient(to top, var(--bg-elev), transparent);
  pointer-events: none;
}
/* Opaque on purpose: --sm-card and friends are translucent washes, and a sheet
   you can read the page through is how the ticket ended up double-printed over
   its own receipts row in testing. */
.sm-sheet { width: 100%; max-width: 460px; max-height: min(92vh, 100%); overflow-y: auto; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 18px 18px 0 0; padding-bottom: 18px; animation: sm-sheet-in 0.24s cubic-bezier(0.3, 0.9, 0.3, 1); }
/* Always full height, top edge at the top of the usable screen — the scrim
   already ends above the composer (--dock), so "100%" can never cover it. */
.sm-sheet.tall { height: 100%; }
@keyframes sm-sheet-in { from { transform: translateY(14px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .sm-sheet { animation: none; } }
@media (min-height: 720px) and (min-width: 520px) {
  .sm-sheet-scrim { align-items: center; }
  .sm-sheet { border-radius: 18px; }
}
/* Desktop: the sheet floats mid-screen, so it can never sit on the composer —
   the mobile bottom clearance (--dock) only carved a bright full-width strip
   out of the dimmed page, cutting task rows in half under the sheet (owner
   screenshot 2026-08-03). Dim everything; a modal may cover the composer. */
@media (min-height: 720px) and (min-width: 900px) {
  .sm-sheet-scrim { bottom: 0; }
  .sm-sheet-scrim::after { content: none; }
  /* The add-strategy picker gets desktop room — 460px is a phone width, and
     the strategy cards inside were clipped mid-label. Recent activity too
     (owner screenshot 2026-08-06): its cards are paragraphs, and at phone
     width every one wrapped into a dense block. */
  .sm-sheet:has(.st-picker),
  .sm-sheet:has(.qn-ck-in-sheet) { max-width: 720px; }
}
/* Taller since 2026-08-04 (owner: "nav bar could be taller") — M3 top-bar
   proportions: more breathing room, larger title. */
.sm-sheet-head { display: flex; align-items: center; gap: 8px; padding: 20px 20px 13px; position: sticky; top: 0; background: var(--bg); z-index: 1; }
.sm-sheet-head b { font-size: 18px; }
.sm-sheet-x { margin-left: auto; border: none; background: none; color: var(--text-dim); font-size: 22px; line-height: 1; padding: 0 4px; cursor: pointer; }
.sm-ticket-src { margin: 0 16px; background: var(--sm-card); border: 1px solid var(--border); border-radius: 12px; padding: 11px 12px; }
.sm-ticket-who { display: flex; align-items: center; gap: 8px; font-size: 13px; flex-wrap: wrap; }
.sm-hexav { width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center; flex: none; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 8.5px; color: var(--sm-on-up); }
.sm-ticket-name { font-weight: 620; }
.sm-ticket-rec { margin-left: auto; font-size: 11.5px; color: var(--text-dim); }
.sm-ticket-act { margin-top: 7px; font-size: 14.5px; }
.sm-ticket-act b.up { color: var(--sm-up); }
.sm-ticket-act b.down { color: var(--sm-down); }
.sm-ticket-play { margin-top: 6px; font-size: 11.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.sm-ticket-sec { padding: 15px 16px 0; }
.sm-ticket-lbl { display: block; font-size: 11px; color: var(--text-faint); letter-spacing: 0.05em; margin-bottom: 8px; }
.sm-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.sm-chip { border: 1px solid var(--border); background: none; color: var(--text-dim); border-radius: 9px; padding: 7px 14px; font: inherit; font-size: 13.5px; cursor: pointer; }
.sm-chip.on { border-color: var(--sm-up); color: var(--text); font-weight: 640; background: color-mix(in srgb, var(--sm-up) 8%, transparent); }
.sm-ticket-lev { display: flex; align-items: center; gap: 12px; margin-top: 11px; font-size: 13px; color: var(--text-dim); }
.sm-stepper { display: flex; align-items: center; border: 1px solid var(--border); border-radius: 9px; overflow: hidden; }
.sm-stepper button { width: 34px; height: 30px; border: none; background: none; color: var(--text); font-size: 16px; cursor: pointer; }
.sm-stepper span { min-width: 44px; text-align: center; font-weight: 640; color: var(--text); }
.sm-ticket-hint { font-size: 11px; color: var(--text-faint); }
.sm-ticket-kv { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; color: var(--text-dim); padding: 5px 0; }
.sm-ticket-kv b { color: var(--text); font-weight: 620; }
.sm-ticket-sl { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.sm-ticket-slbox { border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; }
.sm-ticket-slt { font-size: 11px; color: var(--text-faint); }
.sm-ticket-slpx { font-size: 15.5px; font-weight: 640; margin-top: 3px; }
.sm-ticket-sleff { font-size: 11.5px; margin-top: 3px; }
.sm-ticket-sleff.down { color: var(--sm-down); }
.sm-ticket-sleff.up { color: var(--sm-up); }
.sm-ticket-risk { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin: 14px 16px 0; padding: 10px 12px; border-radius: 12px; background: var(--sm-card); font-size: 13px; }
.sm-ticket-risk b.down { color: var(--sm-down); font-weight: 700; }
.sm-ticket-cta { margin: 14px 16px 0; }
.sm-ticket-go { width: 100%; background: var(--sm-up); color: var(--sm-on-up); border: none; border-radius: 12px; padding: 14px; font: inherit; font-size: 15.5px; font-weight: 700; cursor: pointer; }
.sm-ticket-trust { margin: 10px 18px 0; font-size: 11px; color: var(--text-faint); line-height: 1.65; text-align: center; }
.sm-ticket-ask { display: block; margin: 12px auto 0; border: none; background: none; color: var(--text-dim); font: inherit; font-size: 12.5px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
/* Alert-card CTAs: compact side-by-side pills (mockup parity), not full-width slabs. */
.sm-cta.compact { display: inline-block; width: auto; padding: 8px 18px; font-size: 12.5px; margin: 10px 8px 0 0; }
.sm-chase { font-size: 12.5px; color: var(--text-dim); padding-top: 8px; }
.sm-chase.warn { color: var(--warn); }

/* PR9: board polish — mockup-exact follow text-chip + hex avatars */
.sm-avatar.hex { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; letter-spacing: 0.5px; }

/* PR12: full-width gen-UI widget — a hero band (plain-language verdict + glance
   tiles) over a two-column grid: the 24h SIGNAL LIST (owner: a list, not prose)
   on the left, the leaderboard on the right. Gemini surfaces (tiles, rounding,
   tonal) with Robinhood density (hairlines, tabular nums, green/red only).
   Container queries collapse it to one column on a narrow pane. */
.sm-hero-band { display: flex; flex-direction: column; gap: 14px; padding: 10px 0 4px; }
.sm-hero-head { min-width: 0; }
.sm-tldr-eyebrow { font-size: 11px; font-weight: 600; color: var(--text-faint); letter-spacing: 0.07em; text-transform: uppercase; }
.sm-tldr-verdict { font-size: 31px; font-weight: 600; letter-spacing: -0.03em; margin-top: 6px; line-height: 1.12; color: var(--text); }
.sm-tldr-verdict.up { color: var(--sm-up); }
.sm-tldr-verdict.down { color: var(--sm-down); }
/* Glance tiles — four Gemini cards, Robinhood numerals (2×2 on a narrow pane). */
.sm-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 22px; }
.sm-tile { min-width: 0; background: none; border: none; border-radius: 0; padding: 2px 0; }
.sm-tile-k { font-size: 10px; font-weight: 640; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); }
.sm-tile-v { font-size: 19px; font-weight: 680; margin-top: 4px; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-tile-v.up { color: var(--sm-up); }
.sm-tile-v.down { color: var(--sm-down); }
.sm-tile-v.mut { color: var(--text-faint); font-weight: 650; }
/* Vertical sections: ① TLDR hero ② movers breakdown ③ board. */
.sm-main { min-width: 0; margin-top: 16px; }
.sm-main.gap { margin-top: 14px; }
/* Card blocks: each tab is a stack of white/elevated cards (owner call
   2026-07-18) — the 24h digest and the winners list are separate cards on
   Overview; your traders + recent moves on Following. */
.sm-block { background: color-mix(in srgb, var(--text) 5%, transparent); border-radius: 20px; padding: 16px; }
.sm-block + .sm-block { margin-top: 12px; }
:root[data-theme="light"] .sm-block { background: #fff; border: 1px solid var(--border); }
.sm-block-head { font-size: 11px; font-weight: 640; color: var(--text-faint); letter-spacing: 0.07em; text-transform: uppercase; padding: 0 0 12px; }
.sm-block > .sm-hero-band:first-child { padding-top: 0; }
.sm-block .sm-foot { margin-top: 10px; }
.sm-block .sm-alert:last-child { margin-bottom: 0; }
/* display:flex/grid on the class beats the UA [hidden] rule — restate it. */
.sm-secline[hidden], .sm-movers[hidden] { display: none; }
/* Digest loading: pulse placeholders shaped like the verdict/tiles/signals
   (same idiom as .home-earn-skel). */
.sm-skel { background: var(--bg-elev-2); border-radius: 8px; animation: sm-skel-pulse 1.3s ease-in-out infinite; }
.sm-skel.verdict { height: 26px; width: min(320px, 72%); margin-top: 4px; }
.sm-skel.tile { min-width: 0; min-height: 56px; border-radius: var(--radius-md); }
.sm-skel.sig { height: 14px; }
@keyframes sm-skel-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) { .sm-skel { animation: none; } }
.sm-secline { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 16px 0 6px; }
.sm-secline.gap { padding-top: 22px; }
.sm-secline-t { font-size: 13px; font-weight: 640; color: var(--text-dim); }
.sm-secline-h { font-size: 11.5px; color: var(--text-faint); }
/* §2 mover cards — who → what he did → where he stands (Gemini tonal card). */
.sm-movers { display: grid; grid-template-columns: 1fr; gap: 10px; }
.sm-mover { background: none; border: 1px solid var(--border); border-radius: 16px; padding: 13px 15px; min-width: 0; }
.sm-mover.tap { cursor: pointer; }
.sm-mover.tap:hover { border-color: var(--border-strong); }
.sm-mover-head { display: flex; align-items: center; gap: 10px; padding-bottom: 2px; }
.sm-avatar.sm { width: 30px; height: 30px; font-size: 11px; }
.sm-mover-main { flex: 1; min-width: 0; }
.sm-mover-name { font-size: 14.5px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-mover-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 1px; }
.sm-mover-right { flex: none; text-align: right; }
.sm-mover-pnl { font-size: 14.5px; font-weight: 640; color: var(--sm-up); font-variant-numeric: tabular-nums; }
.sm-mover-mv { padding-top: 10px; }
.sm-mover-mv + .sm-mover-mv { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 12px; }
.sm-mover-move { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 13.5px; }
.sm-mover-move-k { min-width: 0; }
.sm-mover-move-k .sm-verb { font-weight: 650; }
.sm-mover-move-k .sm-coin { color: var(--text); font-weight: 700; }
.sm-mover-move-v { flex: none; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.sm-mover-amt { flex: none; font-size: 14px; font-weight: 640; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.sm-mover-amt.up { color: var(--sm-up); }
.sm-mover-amt.down { color: var(--sm-down); }
.sm-mover-meta { display: flex; justify-content: space-between; gap: 10px; font-size: 11.5px; color: var(--text-faint); margin-top: 5px; font-variant-numeric: tabular-nums; }
/* Persona: dashboard | replay, then playbook | calculator; one CTA at the bottom. */
.sm-persona { display: flex; flex-direction: column; }
.sm-p-grid { display: grid; grid-template-columns: 1fr; min-width: 0; }
.sm-p-grid:empty { display: none; }
.sm-cta.analyze { max-width: 440px; margin: 22px auto 6px; width: 100%; }
.sm-sig { display: flex; flex-direction: column; }
.sm-sig-row { display: flex; align-items: center; gap: 11px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.sm-sig-row:last-child { border-bottom: none; }
.sm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--text-faint); }
.sm-dot.up { background: var(--sm-up); }
.sm-dot.down { background: var(--sm-down); }
.sm-sig-txt { flex: 1; min-width: 0; font-size: 14px; color: var(--text-dim); line-height: 1.4; }
.sm-sig-txt .sm-coin { color: var(--text); font-weight: 700; }
.sm-sig-txt .sm-strong { color: var(--text); font-weight: 650; font-variant-numeric: tabular-nums; }
.sm-sig-txt .sm-verb { font-weight: 650; }
.sm-sig-amt { flex: none; font-size: 14.5px; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--text); letter-spacing: -0.01em; }
.sm-sig-amt.up { color: var(--sm-up); }
.sm-sig-amt.down { color: var(--sm-down); }
.sm-sig-amt.mut { color: var(--text-faint); font-weight: 600; }
.sm-sig-quiet { font-size: 14px; color: var(--text-faint); padding: 14px 0; }
.sm-name { font: inherit; font-weight: 700; color: var(--text); background: none; border: none; padding: 0; cursor: pointer; border-bottom: 1px solid var(--border-strong); }
.sm-name:hover { border-bottom-color: var(--text); }
.sm-verb.up { color: var(--sm-up); }
.sm-verb.down { color: var(--sm-down); }
.sm-rank { width: 20px; flex: none; text-align: center; font-size: 12.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* Wide pane: headline + tiles share a row; mover cards tile side-by-side. */
@container (min-width: 680px) {
  .sm-hero-band { flex-direction: row; align-items: center; justify-content: space-between; gap: 28px; }
  .sm-hero-head { flex: 1; }
  .sm-tldr-verdict { font-size: 30px; }
  .sm-tiles { flex: 0 1 300px; gap: 14px 26px; }
  .sm-tile { padding: 2px 0; }
  .sm-tile-v { font-size: 18px; }
  .sm-movers { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
  /* Persona goes two-up; flat sections gain card chrome so columns read apart.
     Default stretch (no align-items:start) keeps each row's two cards the SAME
     height (owner call: blocks of the same size). */
  .sm-p-grid { grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 10px; }
  .sm-p-grid > .sm-flat { background: var(--sm-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px 16px; margin-top: 0; min-width: 0; }
  .sm-p-grid > .sm-flat:only-child { grid-column: 1 / -1; }
}

.sm-alert-head { display: flex; align-items: center; gap: 8px; font-size: 16.5px; font-weight: 640; letter-spacing: -0.01em; margin-bottom: 2px; }
.sm-side { flex: none; font-size: 11px; font-weight: 680; border-radius: 6px; padding: 1.5px 6px; }
.sm-side.long { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 15%, transparent); }
.sm-side.short { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 15%, transparent); }
.sm-alert-pos { font-size: 13px; color: var(--text-dim); margin-top: 2px; font-variant-numeric: tabular-nums; }

/* Same chip row, session flavor: rides inside .composer-wrap above the composer. */
.session-chips { margin: 0; padding: 2px 2px 8px; }

/* ------------------------------------------------------------------ */
/* Add-to-Home-Screen install nudge (install.ts) — mascot-led bottom  */
/* sheet. Above modals (1400/1500), below the toast (20000) so the    */
/* "Genie is on your Home Screen" confirmation can land over it.      */
/* ------------------------------------------------------------------ */
.a2hs-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  z-index: 1600;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.25s var(--ease);
}
.a2hs-overlay.is-open { opacity: 1; }
.a2hs-sheet {
  width: min(440px, 100%);
  background: var(--bg-elev);
  border-radius: var(--radius-hero) var(--radius-hero) 0 0;
  border-top: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  padding: 10px 24px calc(22px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transform: translateY(100%);
  transition: transform 0.3s var(--ease);
}
.a2hs-overlay.is-open .a2hs-sheet { transform: none; }
.a2hs-handle { width: 36px; height: 4px; border-radius: 4px; background: var(--border); margin-bottom: 8px; }
/* The live mascot (createMascot canvas) — the sheet's hero; big enough that its
   tap-reactions invite touching without needing a hint. */
.a2hs-mascot { width: 168px; height: 168px; cursor: pointer; touch-action: manipulation; }
.a2hs-title {
  margin: 8px 0 2px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  color: var(--text);
  text-align: center;
  letter-spacing: -0.01em;
}
.a2hs-sub {
  margin: 0 0 12px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-faint);
  text-align: center;
  max-width: 32ch;
}
.a2hs-install {
  width: 100%;
  border: none;
  border-radius: 999px;
  padding: 13px 0;
  min-height: 46px;
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.15s var(--ease);
}
.a2hs-install:hover { filter: brightness(1.08); }
.a2hs-install:disabled { opacity: 0.6; cursor: default; }
.a2hs-later {
  border: none;
  background: none;
  color: var(--text-faint);
  font: inherit;
  font-size: 13px;
  padding: 10px 16px;
  cursor: pointer;
}
.a2hs-later:hover { color: var(--text-dim); }
/* iOS two-step instruction card, share-sheet flavored. */
.a2hs-steps {
  width: 100%;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: 4px;
}
.a2hs-step {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  font-size: 13.5px;
  color: var(--text);
}
.a2hs-step + .a2hs-step { border-top: 1px solid var(--border); }
.a2hs-step-n {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--bg-elev-3);
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
}
.a2hs-step svg { flex: none; width: 20px; height: 20px; }

/* ------------------------------------------------------------------ */
/* Early-access trial funnel (docs/EARLY_ACCESS_TRIAL.md, trial.ui.ts) */
/* ------------------------------------------------------------------ */
.trial-choice-panel,
.trial-activate-panel {
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.trial-choice-head { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 6px; padding-top: 6px; }
/* Landing-style bottom hero: the mascot rises out of the panel's bottom edge, top
   half visible. Negative margins cancel the panel padding so the clip line IS the
   panel edge; overflow hides the lower half. */
.trial-mascot-hero {
  /* Fixed to the SCREEN bottom behind the panel — the landing-page stage: a big
     mascot rising from the viewport edge, top half visible. */
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  height: min(200px, 24dvh);
  overflow: hidden;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.trial-mascot-hero-canvas {
  width: min(400px, 90vw);
  height: min(400px, 90vw);
  flex: none;
  pointer-events: auto;
}
.trial-choice-title { font-size: 1.25rem; font-weight: 700; text-align: center; }
.trial-choice-sub { color: var(--text-dim); font-size: 0.85rem; text-align: center; max-width: 42ch; margin: 0 auto; }
.trial-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev-1);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* The hero card commits to the brand gradient (owner call 2026-07-16: this funnel
   surface overrides the gradient-is-Brain-only rule — the mockup won). */
.trial-card-instant {
  border-color: rgba(110, 90, 255, 0.55);
  background:
    linear-gradient(160deg, rgba(0, 54, 255, 0.14), rgba(198, 0, 255, 0.07)),
    var(--bg-elev-1);
}
.trial-card-tag { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.trial-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  border: 1px solid rgba(90, 130, 255, 0.4);
  background: rgba(0, 84, 255, 0.16);
  color: #9db4ff;
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}
.trial-pill-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.trial-cta-primary {
  background: linear-gradient(90deg, #0036ff 0%, #7b2bff 60%, #c600ff 100%);
  color: #fff;
  border: none;
  font-weight: 700;
}
.trial-cta-primary:hover { filter: brightness(1.1); }
.trial-card-heading { font-weight: 700; }
.trial-card-price { display: flex; align-items: baseline; gap: 8px; font-variant-numeric: tabular-nums; }
.trial-price-now { font-size: 1.6rem; font-weight: 800; }
.trial-price-was { text-decoration: line-through; color: var(--text-dim); }
.trial-price-off { color: var(--ok); font-weight: 700; font-size: 0.85rem; }
.trial-card-note { color: var(--text-dim); font-size: 0.8rem; }
.trial-cta { width: 100%; }
.trial-decay-strip { display: flex; gap: 6px; font-variant-numeric: tabular-nums; }
.trial-decay-tile {
  flex: 1;
  border-radius: 10px;
  padding: 7px 4px;
  text-align: center;
  background: var(--bg-elev-2);
  font-size: 0.7rem;
  color: var(--text-dim);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.trial-decay-tile b { font-size: 0.85rem; color: var(--text); }
.trial-decay-tile.is-past { opacity: 0.45; }
.trial-decay-tile.is-now { outline: 1px solid var(--accent); background: var(--accent-dim); }
.trial-decay-full b { color: var(--danger-text); }
.trial-keeps { display: flex; flex-direction: column; gap: 6px; font-size: 0.85rem; }
.trial-fee-line { color: var(--text-dim); font-size: 0.75rem; text-align: center; }
.trial-grace-warn {
  border: 1px solid var(--danger);
  background: var(--danger-bg);
  color: var(--danger-text);
  border-radius: var(--radius);
  padding: 10px 12px;
  font-size: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* Launch banner: shown ONCE per app launch (owner call — the trial clock must not
   squat in the titlebar over normal use), then it slides itself away. The standing
   access point is the hamburger row (.nav-trial-row) below. */
.trial-banner {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 10px);
  left: 50%;
  transform: translate(-50%, -180%);
  opacity: 0;
  transition: transform 0.34s var(--ease), opacity 0.34s var(--ease);
  z-index: 1050;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  max-width: calc(100vw - 28px);
  border: 1px solid var(--danger);
  border-radius: 999px;
  background: var(--bg-elev-2);
  color: var(--danger-text);
  padding: 7px 8px 7px 14px;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--shadow-lg);
}
.trial-banner.is-in { transform: translate(-50%, 0); opacity: 1; }
.trial-banner.is-grace { background: var(--danger-bg); }
.trial-banner-cta {
  background: rgba(128, 128, 160, 0.16);
  color: inherit;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 0.7rem;
  font-weight: 700;
}
.trial-banner.is-grace .trial-banner-cta { background: var(--danger); color: #fff; }
.trial-badge-dot {
  width: 6px;
  height: 6px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--danger-text);
  animation: trial-pulse 1.6s ease-in-out infinite;
}
@keyframes trial-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  .trial-badge-dot { animation: none; }
}
.trial-urgency { color: var(--danger-text); font-size: 0.8rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Persistent trial row in the hamburger drawer (the standing access point) —
   pinned to the bottom, directly ABOVE the profile footer. The slot absorbs the
   free space (margin-top:auto); when it's present the footer drops its own auto so
   the two sit adjacent (else the space splits and a gap opens between them). */
.nav-trial-slot { margin-top: auto; }
.nav-trial-slot:not([hidden]) + .nav-drawer-foot { margin-top: 0; }
.nav-trial-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--danger);
  border-radius: 12px;
  background: var(--danger-bg);
  color: var(--text);
  cursor: pointer;
  text-align: left;
}
.nav-trial-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.nav-trial-title { font-size: 0.86rem; font-weight: 700; color: var(--danger-text); }
.nav-trial-sub { font-size: 0.75rem; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-trial-chev { font-size: 18px; color: var(--text-faint); flex: 0 0 auto; }

/* Grace-state activation sheet (owner mockup §7): loss-framed — a red recycle
   countdown + a "deleted forever" list — with a danger "Save it" CTA. */
.trial-grace-head {
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--danger);
  border-radius: 999px;
  padding: 4px 12px;
  color: var(--danger-text);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.06em;
}
.trial-recycle-hero {
  border: 1px solid var(--danger);
  border-radius: 16px;
  background: var(--danger-bg);
  padding: 18px;
  text-align: center;
}
.trial-recycle-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--text-dim);
}
.trial-recycle-clock {
  margin-top: 6px;
  font-size: 2.6rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--danger-text);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.trial-loss {
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.trial-loss-head {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text-faint);
}
.trial-loss-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.9rem;
  line-height: 1.35;
  color: var(--text);
}
.trial-loss-x { color: var(--danger-text); font-weight: 800; flex: 0 0 auto; }
.trial-cta.is-danger {
  background: linear-gradient(100deg, #e0483a, #c0362b);
  color: #fff;
}
:root[data-theme="light"] .trial-pill {
  color: #2447cc;
  background: rgba(0, 54, 255, 0.08);
  border-color: rgba(0, 54, 255, 0.3);
}
/* Start free trial: deliberately QUIET next to the gradient Buy CTA (the buy is the
   hero; the trial is the fallback). */
.trial-cta.ghost {
  background: var(--bg-elev-2);
  color: var(--text-dim);
  border: 1px solid var(--border);
  font-weight: 600;
}
.trial-cta.ghost:hover { color: var(--text); }
/* Mobile: the mascot is the landing stage — let it dominate the bottom edge. */
.trial-mascot-hero { width: 100vw; height: min(250px, 30dvh); }
.trial-mascot-hero-canvas { width: min(500px, 125vw); height: min(500px, 125vw); }
/* Desktop: the 560px modal read as tiny on a big screen — scale the whole stage. */
@media (min-width: 900px) {
  .settings-panel.trial-choice-panel { width: min(700px, 100%); padding: 32px 36px; gap: 18px; }
  .trial-choice-title { font-size: 1.7rem; }
  .trial-choice-sub { font-size: 0.95rem; max-width: 52ch; }
  .trial-price-now { font-size: 2.2rem; }
  .trial-keeps { font-size: 0.95rem; }
  .trial-card { padding: 20px; }
  .trial-cta { padding: 15px 16px; font-size: 1.05rem; }
  .trial-decay-tile { padding: 10px 6px; font-size: 0.8rem; }
  .trial-decay-tile b { font-size: 1.05rem; }
  .trial-pill { font-size: 0.78rem; }
  .trial-mascot-hero { height: 300px; }
  .trial-mascot-hero-canvas { width: 600px; height: 600px; }
}
/* Price ON the CTA: strike + discount ride the button. */
.trial-cta-primary { display: flex; gap: 8px; align-items: baseline; justify-content: center; }
.trial-cta-was { text-decoration: line-through; opacity: 0.65; font-weight: 600; font-size: 0.85em; }
.trial-cta-off { color: #7dffb0; font-weight: 700; font-size: 0.85em; }
/* Scarcity block: sold-at-this-price bar + next tranche price + total supply. */
.trial-supply { display: flex; flex-direction: column; gap: 6px; font-variant-numeric: tabular-nums; }
.trial-supply-row, .trial-supply-sub { display: flex; justify-content: space-between; align-items: baseline; }
.trial-supply-row { font-size: 0.8rem; color: var(--text-dim); }
.trial-supply-count { color: var(--text); font-weight: 700; }
.trial-supply-bar { height: 5px; border-radius: 3px; background: var(--bg-elev-3); overflow: hidden; }
.trial-supply-bar i { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg, #0036ff, #c600ff); }
.trial-supply-sub { font-size: 0.72rem; color: var(--text-dim); }
/* The choice screen's own overlay (deliberately NOT .settings-overlay: the buy modal's
   double-open guard checks that class, and the buy sheet stacks on top of this). */
.trial-choice-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 1090; /* just under .settings-overlay (1100) so the buy sheet covers it */
}
/* Roomier instant card: 4 short lines, generous rhythm — less brochure, more poster. */
.trial-card-instant { gap: 14px; padding: 18px; }
.trial-keeps-roomy { gap: 11px; line-height: 1.5; }
.trial-supply { margin-top: 2px; }
@media (min-width: 900px) {
  .trial-card-instant { gap: 16px; padding: 24px; }
  .trial-keeps-roomy { gap: 13px; }
}
/* Quiet emphasis (v2 — the animated ring/glow/shine was unreadable): a static soft
   bloom and a slightly stronger gradient edge. No motion, full text contrast. */
.trial-card-instant {
  border-color: rgba(123, 43, 255, 0.65);
  box-shadow: 0 0 36px rgba(123, 43, 255, 0.18), 0 0 90px rgba(0, 54, 255, 0.1);
}
/* Merged funnel bits inside the buy modal (EARLY_ACCESS_TRIAL.md §2). */
.buy-earn-props { display: flex; flex-direction: column; gap: 8px; }
.buy-earn-prop { font-size: 0.86rem; line-height: 1.45; }
/* Trial fallback (owner call v4) — a quiet secondary pill right under the buy CTA;
   opens the rules confirm sheet (owner call v3). */
.buy-trial-btn {
  width: 100%;
  padding: 13px 22px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  font-size: 14.5px;
  font-weight: 700;
  cursor: pointer;
}
.buy-trial-btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.09); }
.buy-trial-btn:disabled { opacity: 0.6; cursor: default; }
/* Poker-deck fan: ghost cards behind the live mascot card + a ×N chip. */
.buy-card { z-index: 1; }
.buy-card-ghost {
  position: absolute;
  inset: 10px; /* = .buy-card-stage padding, so ghosts mirror the live card's box exactly */
  border-radius: var(--radius-lg);
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
}
.buy-card-ghost-1 { transform: rotate(-5deg) translate(-12px, 8px); }
.buy-card-ghost-2 { transform: rotate(4deg) translate(12px, 10px); }
.buy-card-ghost-3 { transform: rotate(-9deg) translate(-22px, 18px); }
.buy-deck-count {
  position: absolute;
  top: -8px;
  right: 2px;
  z-index: 2;
  background: linear-gradient(90deg, #0036ff, #c600ff);
  color: #fff;
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 0.78rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
/* ---- Buy-modal drop stage (owner redesign v4) — single column everywhere. ---- */
.buy-card-stage {
  background:
    radial-gradient(60% 50% at 50% 42%, rgba(0, 54, 255, 0.14), transparent 70%),
    radial-gradient(50% 40% at 55% 60%, rgba(198, 0, 255, 0.1), transparent 70%);
  border-radius: 24px;
  padding: 10px;
}
/* Name row: name + the 🎲 reroll die (random names, rerollable — no name field). */
.buy-card-name-wrap { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.buy-card-reroll {
  background: rgba(255, 255, 255, 0.1);
  border: none;
  border-radius: 8px;
  font-size: 0.85rem;
  line-height: 1;
  padding: 4px 6px;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.buy-card-reroll:hover { transform: rotate(-20deg) scale(1.15); }
.buy-card-reroll:active { transform: rotate(180deg) scale(0.9); }
/* Poke hint: fades after the first poke (the mascot reacts to taps). */
.buy-poke-hint {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  font-size: 0.72rem;
  color: var(--text-dim);
  background: rgba(0, 0, 0, 0.45);
  border-radius: 999px;
  padding: 4px 12px;
  pointer-events: none;
  white-space: nowrap;
  transition: opacity 0.5s ease;
}
.buy-poke-hint.is-gone { opacity: 0; }
.buy-card-stage { position: relative; }
/* ---- Money block (owner redesign v4): ONE white number, one payback sub-line,
   one rotating value-prop line. Rides over the sticky deck on mobile (z 3). ---- */
.buy-agent-card-col .buy-money { position: relative; z-index: 3; }
.buy-money { text-align: center; display: flex; flex-direction: column; gap: 5px; width: min(430px, 100%); }
.buy-hero-num {
  font-size: 43px; /* ~80% of the old 54px (owner call) */
  font-weight: 800;
  letter-spacing: -1.6px;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
/* Promo glint (owner call): a slow metallic sheen sweeps the money. The gradient
   paints only glyphs with no color of their own ($ + digits) — "Earns" and "/day"
   keep their explicit dim ink over it. */
@supports (-webkit-background-clip: text) {
  .buy-hero-num {
    background: linear-gradient(110deg, #fff 35%, #cdb6ff 46%, #96a4ff 50%, #cdb6ff 54%, #fff 65%);
    background-size: 240% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: buy-hero-glint 3.6s ease-in-out infinite;
  }
}
@keyframes buy-hero-glint {
  0%, 45% { background-position: 115% 0; }
  90%, 100% { background-position: -15% 0; }
}
@media (prefers-reduced-motion: reduce) { .buy-hero-num { animation: none; } }
.buy-hero-unit { font-size: 0.42em; font-weight: 700; color: var(--text-dim); letter-spacing: 0; }
/* "Earns" eyebrow ABOVE the number + the "for training the network" line BELOW it —
   the two quiet ends of the headline sentence, thin/light (owner call). */
.buy-hero-eyebrow,
.buy-hero-why { color: var(--text-faint); font-size: 12px; font-weight: 500; }
.buy-hero-eyebrow { margin-bottom: -3px; }
.buy-hero-why { margin-top: -2px; }
.buy-hero-soon {
  align-self: center;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
}
/* Feature rotation = the Genie's chat bubbles over the card (replaced the money-block
   ticker, owner call). Pop in, hold, fade — sides alternate like a conversation. No
   backdrop-filter (compositor lags it behind momentum scroll in this sheet). */
.buy-card-bubble {
  position: absolute;
  bottom: 14%; /* under the mascot orb, above the poke hint — never over the face */
  right: -8px;
  z-index: 3;
  max-width: 78%;
  padding: 9px 13px;
  border-radius: 16px;
  border-top-right-radius: 4px;
  /* M3 secondary-container tone (owner call: too many competing gradients) — a soft
     lavender surface, not a saturated gradient. Reads as the Genie's speech bubble
     while leaving the CTA gradient as the only vivid call to action. */
  background: color-mix(in srgb, #7b5bff 16%, var(--bg-elev-1));
  border: 1px solid color-mix(in srgb, #7b5bff 30%, transparent);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  text-align: left;
  opacity: 0;
  pointer-events: none;
}
.buy-card-bubble.is-left {
  right: auto;
  left: -8px;
  border-radius: 16px;
  border-top-left-radius: 4px;
}
.buy-card-bubble.is-in { animation: buy-bubble-pop 3.2s ease forwards; }
@keyframes buy-bubble-pop {
  0% { opacity: 0; transform: translateY(8px) scale(0.9); }
  7%, 82% { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-6px) scale(0.96); }
}
@media (prefers-reduced-motion: reduce) {
  .buy-card-bubble.is-in { animation: none; opacity: 1; }
}

/* ---- Price ladder: one quiet line + a thin bar (scarcity = "it only rises"). ---- */
.buy-ladder { width: 100%; }
.buy-ladder-line { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 12.5px; color: var(--text-faint); }
.buy-ladder-line b { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.buy-ladder-bar { height: 3px; border-radius: 4px; background: var(--bg-elev-2); overflow: hidden; margin-top: 6px; }
.buy-ladder-fill { height: 100%; border-radius: 4px; background: var(--text); transition: width 0.7s cubic-bezier(0.2, 0.7, 0.2, 1); }
.buy-ladder-sold { font-variant-numeric: tabular-nums; }
/* A staged buy briefly flashes the fill as it ticks up. */
.buy-ladder-fill.is-tick { animation: buy-ladder-tick 0.6s ease; }
@keyframes buy-ladder-tick {
  0%, 100% { filter: none; }
  40% { filter: brightness(1.5) saturate(1.3); }
}

/* ---- Purchase theatre toast (billing.ui.ts): a staged "others are buying / using"
   feed. Body-mounted + position:fixed so it drops from the TOP OF THE SCREEN over the
   modal titlebar, JS-aligned to the panel width. `.is-buy` is the hyped variant. ---- */
.buy-toast {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 12px); /* top of the SCREEN, below any notch */
  z-index: 1200; /* above .settings-overlay (1100) */
  display: flex;
  align-items: center;
  gap: 10px;
  border-radius: 14px;
  padding: 9px 12px;
  overflow: hidden;
  transform: translateY(-220%);
  opacity: 0;
  transition: transform 0.42s cubic-bezier(0.2, 0.85, 0.25, 1), opacity 0.42s;
}
.buy-toast.is-show { transform: none; opacity: 1; }
.buy-toast-body { flex: 1; min-width: 0; position: relative; z-index: 2; }
.buy-toast-name { font-size: 12.5px; font-weight: 700; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
.buy-toast-sub { display: block; color: var(--text-faint); font-size: 10px; }
.buy-toast-plus {
  flex: 0 0 auto; position: relative; z-index: 2;
  font-size: 12px; font-weight: 800; color: #04120a;
  background: var(--good, #37d67a); border-radius: 999px; padding: 2px 8px;
  display: none;
}
/* Quiet usage toast — recedes into the app surface. */
.buy-toast.is-use { background: var(--bg-elev); border: 1px solid var(--border); box-shadow: var(--shadow-lg); }
/* Hyped buy toast — brand-tint glass, bouncy wiggle entrance + a color-cycling border. */
.buy-toast.is-buy {
  background: linear-gradient(135deg, rgba(43, 74, 255, 0.36), rgba(150, 28, 255, 0.34)), var(--bg-elev);
  border: 1.6px solid rgba(170, 120, 255, 0.85);
}
.buy-toast.is-buy .buy-toast-name { font-weight: 800; }
.buy-toast.is-buy .buy-toast-plus { display: block; }
.buy-toast.is-buy::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 60px; left: -80px; z-index: 3;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  animation: buy-toast-shine 1.05s ease 0.12s;
}
@keyframes buy-toast-shine { to { left: 120%; } }
.buy-toast.is-buy.is-show { animation: buy-toast-pop 0.74s cubic-bezier(0.2, 1.5, 0.3, 1), buy-toast-flash 1.15s ease-in-out 0.15s infinite; }
@keyframes buy-toast-pop {
  0% { transform: translateY(-220%) scale(0.9); }
  50% { transform: translateY(0) scale(1.06); }
  64% { transform: rotate(-2.4deg); }
  76% { transform: rotate(1.8deg); }
  88% { transform: rotate(-0.9deg); }
  100% { transform: none; }
}
@keyframes buy-toast-flash {
  /* The color-cycling BORDER carries the "flashy" feel; the ambient bloom is kept
     tight + dim so it no longer bleeds a violet wash onto the modal titlebar below. */
  0%, 100% { border-color: rgba(170, 120, 255, 0.9); box-shadow: 0 0 0 1px rgba(198, 0, 255, 0.18), 0 8px 20px rgba(70, 40, 160, 0.28); }
  50% { border-color: rgba(0, 225, 255, 0.95); box-shadow: 0 0 10px 1px rgba(0, 200, 255, 0.4), 0 8px 20px rgba(110, 40, 210, 0.3); }
}
/* Confetti sparks on a buy (left side, where the emoji leads). */
.buy-toast-spark { position: absolute; top: 13px; left: 16px; width: 5px; height: 5px; border-radius: 1px; pointer-events: none; opacity: 0; z-index: 3; }
.buy-toast.is-buy.is-show .buy-toast-spark { animation: buy-toast-spark 0.7s ease-out 0.05s; }
.buy-toast-spark.s1 { background: #ffd23f; transform: rotate(20deg); }
.buy-toast-spark.s2 { background: #37d67a; left: 34px; }
.buy-toast-spark.s3 { background: #c600ff; left: 54px; }
.buy-toast-spark.s4 { background: #00c2ff; left: 74px; }
@keyframes buy-toast-spark {
  0% { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-18px) scale(0.4); }
}
@media (prefers-reduced-motion: reduce) {
  .buy-ladder-fill, .buy-toast { transition: opacity 0.2s; }
  .buy-toast.is-buy.is-show, .buy-toast.is-buy::after, .buy-toast.is-buy.is-show .buy-toast-spark, .buy-ladder-fill.is-tick { animation: none; }
  .buy-toast { transform: none; }
}

/* Quantity row: naked chips + stepper, centered, no label (v4). */
.buy-qty-center { justify-content: center; }

/* ---- Fine print row: payouts fold, coupon fold, trial link on ONE quiet line;
   an opened fold wraps onto its own full-width line. ---- */
.buy-fineline {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px 18px;
  font-size: 12.5px;
  color: var(--text-faint);
}
/* Accordion toggles: the links keep ONE line forever; the open pane renders in
   .buy-finedetail below the row (owner call — <details> folds stacked the links
   and crunched the opened form mid-row). */
.buy-fine-toggle {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.buy-fine-toggle:hover,
.buy-fine-toggle.is-open { color: var(--text-dim); }
.buy-finedetail { width: 100%; margin-top: -6px; }
.buy-finedetail .buy-earn-rows { max-width: 340px; margin: 8px auto 0; text-align: left; }
.buy-finedetail .buy-coupon-row { max-width: 340px; margin: 8px auto 0; }
.buy-earn-percap { text-align: center; font-size: 12px; color: var(--text-faint); }
/* The pane content came from old panel homes — strip any inherited divider. */
.buy-finedetail .buy-earn-pane { margin-top: 0; border-top: none; padding-top: 0; }

/* ---- Trial rules confirm sheet (owner call v3): the gate before the trial starts —
   numbered rules, one red-marked warning, one confirm tap. ---- */
.trial-rules-cover {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(5, 5, 8, 0.72);
  display: grid;
  place-items: center;
  padding: 20px;
}
.trial-rules {
  width: min(420px, 100%);
  background: var(--bg-elev-1);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 24px 24px 16px;
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.7);
}
.trial-rules h3 { margin: 0 0 4px; font-size: 18px; }
.trial-rules-sub { color: var(--text-dim); font-size: 13.5px; margin-bottom: 16px; }
.trial-rule { display: flex; gap: 11px; align-items: flex-start; font-size: 14px; line-height: 1.5; margin-bottom: 12px; color: var(--text-dim); }
.trial-rule b { color: var(--text); }
.trial-rule-n {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid var(--border);
  color: var(--text-faint);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 800;
  margin-top: 2px;
}
.trial-rule.is-warn { border-left: 2px solid #ff6a6a; padding-left: 12px; margin-left: 3px; }
.trial-rule.is-warn b { color: #ff8484; }
.trial-rules-start {
  width: 100%;
  border: none;
  border-radius: 999px;
  padding: 14px;
  background: var(--text);
  color: var(--bg);
  font-size: 15.5px;
  font-weight: 800;
  cursor: pointer;
  margin-top: 8px;
}
.trial-rules-start:disabled { opacity: 0.6; }
.trial-rules-back {
  width: 100%;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 14px;
  font-weight: 600;
  padding: 10px 8px 4px;
  cursor: pointer;
}
.trial-rules-back:hover { color: var(--text); }
/* Hero parallax (owner call): on the single-column sheet the card deck holds just
   under the titlebar while the descriptive content scrolls faster and slides up
   OVER it — sticky is scoped to the card column, so once the value card has ridden
   over the deck the whole column departs together. CSS-only: sticky runs on the
   compositor, so it stays in sync with iOS momentum scroll. */
@media (max-width: 679.98px) {
  .buy-agent-card-col .buy-card-stage {
    position: sticky;
    top: 76px; /* tucked under the sticky titlebar (~73px) */
    z-index: 0;
  }
}
/* Dark veil over the deck as content rides over it (--deck-dim set per scroll frame
   in billing.ui.ts). Oversized inset so the fan's spilling ghost corners dim too;
   z-index 5 tops everything inside the stage's own stacking context (card z1,
   poke hint / ×N chip z2). Invisible at 0 — desktop never sets it. */
.buy-card-stage::after {
  content: "";
  position: absolute;
  inset: -20px;
  border-radius: 32px;
  background: #0a0a0e;
  opacity: var(--deck-dim, 0);
  pointer-events: none;
  z-index: 5;
}
/* (Squarer-card experiment reverted — owner call: keep the 5/7 poker aspect,
   just smaller overall.) */

/* ---- "You're invited" — the login card's invite step (EARLY_ACCESS_TRIAL §2 step 1).
   Rides the normal .auth-card overlay on the landing; .auth-body supplies the gaps. ---- */
.ig-code-box {
  border: 1px solid rgba(155, 122, 255, 0.45);
  border-radius: 16px;
  padding: 11px 14px 8px;
  background: rgba(120, 80, 255, 0.07);
  text-align: center;
}
.ig-code-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 2.5px;
  text-transform: uppercase;
  color: var(--text-faint);
}
.ig-code {
  width: 100%;
  background: none;
  border: none;
  outline: none;
  color: var(--text);
  text-align: center;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 21px;
  font-weight: 700;
  letter-spacing: 5px;
  text-transform: uppercase;
  padding: 6px 0 2px;
}
.ig-code::placeholder { color: var(--text-faint); letter-spacing: 3px; }
.ig-chip {
  align-self: center;
  width: fit-content;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-dim);
  background: rgba(128, 128, 160, 0.14);
  border-radius: 999px;
  padding: 5px 12px;
}
.auth-btn.ig-btn { font-weight: 700; }
.ig-alt { text-align: center; font-size: 13px; color: var(--text-faint); margin-top: 2px; }
.ig-alt-link { color: var(--text-dim); text-decoration: underline; text-underline-offset: 3px; }
.ig-alt-link:hover { color: var(--text); }

/* ---- smart money: asset-first tile grid + detail (docs/AUTO_QUANT.md §8) ---- */
.sm-flowbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.sm-flowbar .sm-flow-venue { margin: 0; }
.sm-flowlist-wrap[hidden] { display: none; }
.sm-gridhost { min-height: 120px; }
.sm-gridhost[hidden] { display: none; }
/* Fill the pane: the map is the page here, so it takes the height it can get
   (owner 2026-07-26 — the 4:3 box left half the screen empty on desktop). */
.sm-grid { position: relative; width: 100%; aspect-ratio: 4 / 3; max-height: none; min-height: 340px; }
@media (min-width: 720px) { .sm-grid { aspect-ratio: 3 / 2; min-height: 520px; } }
@media (min-height: 900px) { .sm-grid { min-height: 620px; aspect-ratio: auto; height: min(72vh, 780px); } }
.sm-gt {
  position: absolute; border: 1px solid rgba(255,255,255,0.07); border-radius: 10px; padding: 7px 9px;
  color: var(--text); font: inherit; cursor: pointer; text-align: left;
  display: flex; flex-direction: column; justify-content: space-between; min-width: 0; overflow: hidden;
}
.sm-gt:focus-visible { outline: 2px solid var(--accent, #5b7cfa); outline-offset: 1px; z-index: 2; }
.sm-gt:hover { border-color: rgba(255,255,255,0.22); }
/* Trap keeps its own channel — a ring, not a hue: colour is stance only. */
.sm-gt.trap { border-color: color-mix(in srgb, var(--sm-warn, #d2a13a) 70%, transparent); }
.sm-gt-more {
  display: block; width: 100%; margin-top: 6px; padding: 9px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--border); background: var(--sm-card); color: var(--text-dim);
  font: inherit; font-size: 11.5px; font-weight: 650;
}
.sm-gt-top { display: flex; align-items: center; gap: 5px; width: 100%; min-width: 0; }
.sm-gt-sym { font-weight: 750; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #fff; }
.sm-gt.small .sm-gt-sym { font-size: 10.5px; }
.sm-gt.mid .sm-gt-sym { font-size: 12.5px; }
.sm-gt.big .sm-gt-sym { font-size: 18px; }
.sm-gt-badge { margin-left: auto; flex: none; font-size: 9px; font-weight: 700; background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.82); border-radius: 999px; padding: 2px 6px; white-space: nowrap; }
.sm-gt-spark { width: 100%; height: 20px; opacity: 0.95; flex: 1 1 auto; min-height: 0; }
.sm-gt.big .sm-gt-spark { height: 46px; }
.sm-gt-sub { font-size: 9.5px; font-weight: 600; color: rgba(255,255,255,0.62); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sm-gt.big .sm-gt-sub { font-size: 11.5px; }
.sm-gt-net { font-weight: 750; }
.sm-gridcap { font-size: 10.5px; color: var(--text-faint); margin: 8px 2px 0; }

.sm-spotflow-h { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 8px; }
.sm-spotflow { display: flex; flex-direction: column; gap: 7px; }
.sm-sfrow { display: flex; align-items: center; gap: 9px; }
.sm-sfk { font-size: 11px; font-weight: 700; color: var(--text-dim); width: 30px; flex: none; }
.sm-sftrack { flex: 1; height: 14px; background: color-mix(in srgb, var(--text) 6%, transparent); border-radius: 7px; overflow: hidden; }
.sm-sffill { height: 100%; border-radius: 7px; }
.sm-sffill.up { background: var(--sm-up); }
.sm-sffill.down { background: var(--sm-down); }
.sm-sfv { font-size: 11.5px; font-weight: 700; width: 66px; text-align: right; flex: none; }
.sm-sfv.up { color: var(--sm-up); }
.sm-sfv.down { color: var(--sm-down); }

/* Material 3: 8dp spacing scale, 16dp corners on containers, tonal surfaces
   with no hard shadows. Layout is two real columns (see sm-asset-cols) rather
   than grid-placed singletons, which left holes when a row didn't fill. */
.sm-asset { display: flex; flex-direction: column; gap: 16px; }
.sm-body { container-type: inline-size; }
.sm-asset-cols { display: flex; flex-direction: column; gap: 16px; }
.sm-asset-main, .sm-asset-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* Desktop layout (owner 2026-07-27): NO side column — the chart runs FULL
   WIDTH under the verdict, and the six-pillar read follows beneath it. The
   old ≥900px two-column split (chart left, read right) is retired. */
.sm-asset-skel { height: 72px; border-radius: 14px; }
.sm-asset-head { display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center; padding: 8px 0 2px; }
.sm-asset-sym { font-size: 34px; font-weight: 800; letter-spacing: -0.02em; }
.sm-asset-pill { font-size: 11px; font-weight: 800; letter-spacing: 0.04em; padding: 3px 9px; border-radius: 999px; }
.sm-asset-pill.long { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 15%, transparent); }
.sm-asset-pill.short { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 15%, transparent); }
.sm-asset-pill.spot { color: var(--text-dim); background: color-mix(in srgb, var(--text) 8%, transparent); }
.sm-asset-px { margin-left: 0; text-align: center; }
.sm-asset-pxv { font-size: 24px; font-weight: 750; font-variant-numeric: tabular-nums; }
.sm-asset-pxc { font-size: 13px; font-weight: 700; }
.sm-asset-pxc.up { color: var(--sm-up); }
.sm-asset-pxc.down { color: var(--sm-down); }

/* A CHAT LINE (owner 2026-08-02): the mascot beside the message, the tint on
   the BUBBLE — the old shape tinted a whole box with the face floating inside
   it. The clipped top-left corner points at the sender, M3 chat style. And
   clear air below (margin), so Genie's take never crowds the strategy cards —
   the take is an opinion, the cards are the controls (owner: "genie's
   suggestion should not block user from accessing these strategies"). */
.sm-verd { display: flex; flex-direction: row; align-items: flex-start; gap: 10px; background: none; border: none; border-radius: 0; padding: 4px 2px 2px; margin-bottom: 16px; }
.sm-verd-bub { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; background: var(--bg-elev-1); border-radius: 4px 16px 16px 16px; padding: 11px 14px; }
/* Same type scale as the picker's receipt bubble (the owner's reference):
   13px, quiet — a chat message, not a headline over a paragraph. */
.sm-verd-title { font-size: 13px; font-weight: 700; letter-spacing: -0.01em; }
.sm-verd.warn .sm-verd-bub { background: var(--bg-elev-1); }
.sm-verd.wait .sm-verd-bub { background: var(--bg-elev-1); }
.sm-verd-dot { width: 26px; height: 26px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 12px; font-weight: 800; background: linear-gradient(135deg, #0036FF, #C600FF); margin-top: 1px; }
/* The animated mascot as the bubble's avatar — the canvas draws its own face,
   so no disc behind it. Slightly larger than the lettered dot it replaces:
   a face needs a few more pixels to read as one. */
.sm-verd-m { width: 38px; height: 38px; flex: none; margin-top: -2px; }
.sm-verd-t { color: var(--text-dim); font-size: 13px; line-height: 1.5; max-width: 62ch; }

.sm-asset-chart { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 12px; }
/* Hero redesign (owner 2026-07-27): full-bleed chart, no card chrome. */
.sm-chart-hero .sm-asset-chart { background: none; border: none; border-radius: 0; padding: 0; }
.sm-chart-hero { margin: 2px 0 6px; }
.sm-chart-hero .sm-tfrow { display: flex; justify-content: space-evenly; gap: 0; margin: 10px 0 2px; }
.sm-chart-hero .sm-tfbtn { background: none; border: none; }
.sm-chart-hero .sm-tfbtn.on { background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text); border-radius: 8px; }
.sm-asset-svg { display: block; width: 100%; height: auto; }
/* Trade-window view (owner 2026-08-07): the svg draws wider than the screen
   and this scroller pans it; scrollbars hidden, momentum scrolling on. */
.sm-chart-scroll { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.sm-chart-scroll::-webkit-scrollbar { display: none; }
.sm-tfbtn.trade.on { background: color-mix(in srgb, var(--sm-up) 20%, transparent); color: var(--sm-up); }
/* Chart typography is owned by the DESIGN SYSTEM, not by per-element SVG
   attributes: CSS beats presentation attributes, so this one rule pins every
   axis price, level tag and indicator label to M3 labelSmall in the app font
   (owner 2026-07-27: "not following Material 3 and inconsistent with the UI"). */
.sm-asset-svg text, .sm-osc-svg text {
  font-family: inherit;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.02em;
}
/* Axis prices are secondary information — one step quieter than the labels. */
.sm-asset-svg text.ax { fill: var(--text-faint); }
.sm-tfrow { display: flex; gap: 6px; margin-bottom: 12px; }
.sm-tfbtn { min-width: 40px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: none; color: var(--text-dim); font: inherit; font-size: 10.5px; font-weight: 500; letter-spacing: 0.02em; cursor: pointer; padding: 0 10px; transition: background 0.16s, color 0.16s, border-color 0.16s; }
.sm-tfbtn.on { background: color-mix(in srgb, var(--sm-up) 14%, transparent); border-color: transparent; color: var(--sm-up); }

.sm-aflags { display: flex; gap: 6px; flex-wrap: wrap; }
.sm-aflag { font-size: 11.5px; font-weight: 650; color: var(--text-dim); background: var(--sm-card); border: 1px solid var(--border); border-radius: 999px; padding: 5px 11px; }
.sm-aflag.dim { opacity: 0.7; border-style: dashed; }

.sm-aread-h { display: flex; align-items: baseline; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); margin: 2px 2px -4px; }
.sm-aread-badge { margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: 0; text-transform: none; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); border-radius: 999px; padding: 3px 9px; }
.sm-acats { border: 1px solid var(--border); border-radius: 16px; overflow: hidden; background: var(--sm-card); }
.sm-acat { border-bottom: 1px solid var(--border); }
.sm-acat:last-child { border-bottom: none; }
.sm-acat-h { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 10px 12px; background: none; border: none; color: inherit; font: inherit; cursor: pointer; min-height: 46px; }
.sm-acat-h:focus-visible { outline: 2px solid var(--accent, #5b7cfa); outline-offset: -2px; }
.sm-acat-h:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
.sm-acat-h { transition: background 0.12s; }
@media (prefers-reduced-motion: reduce) { .sm-acat-h { transition: none; } }
.sm-amark { width: 21px; height: 21px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 800; }
.sm-amark.strong { background: var(--sm-up); color: #fff; }
.sm-amark.ok { background: color-mix(in srgb, var(--sm-up) 16%, transparent); color: var(--sm-up); }
.sm-amark.neutral { background: color-mix(in srgb, #a8842c 20%, transparent); color: #a8842c; }
.sm-amark.bad { background: color-mix(in srgb, var(--sm-down) 16%, transparent); color: var(--sm-down); }
.sm-amark.na { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text-faint); }
.sm-acat-t { min-width: 0; flex: 1; }
.sm-acat-n { display: block; font-size: 13px; font-weight: 700; line-height: 1.25; }
.sm-acat-s { display: block; font-size: 11.5px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.35; margin-top: 1px; }
.sm-acat-c { margin-left: auto; color: var(--text-faint); flex: none; transition: transform 0.15s; }
.sm-acat-h.open .sm-acat-c { transform: rotate(90deg); }
.sm-acat-b { padding: 0 12px 11px 43px; }
.sm-akv { display: flex; font-size: 12px; color: var(--text-dim); padding: 3px 0; gap: 10px; }
.sm-akv b { color: var(--text); font-variant-numeric: tabular-nums; margin-left: auto; text-align: right; font-weight: 700; }
.sm-akv b.up { color: var(--sm-up); }
.sm-akv b.down { color: var(--sm-down); }
@media (prefers-reduced-motion: reduce) { .sm-acat-c { transition: none; } }

/* asset detail: TA legend + news rows */
.sm-talegend { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px; }
.sm-taregime { font-size: 10px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 13%, transparent); border-radius: 999px; padding: 3px 9px; }
.sm-aflag.good { color: var(--sm-up); border-color: color-mix(in srgb, var(--sm-up) 35%, transparent); }
.sm-aflag.bad { color: var(--sm-down); border-color: color-mix(in srgb, var(--sm-down) 45%, transparent); background: color-mix(in srgb, var(--sm-down) 10%, var(--sm-card)); }
.sm-newsrow { display: flex; align-items: baseline; gap: 7px; padding: 4px 0; font-size: 12px; }
.sm-newstag { flex: none; font-size: 9px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; border-radius: 4px; padding: 2px 5px; background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text-dim); }
.sm-newstag.exploit, .sm-newstag.regulatory, .sm-newstag.delisting, .sm-newstag.unlock, .sm-newstag.team, .sm-newstag.depeg { background: color-mix(in srgb, var(--sm-down) 18%, transparent); color: var(--sm-down); }
.sm-newstag.etf, .sm-newstag.listing, .sm-newstag.upgrade, .sm-newstag.partnership, .sm-newstag.supply { background: color-mix(in srgb, var(--sm-up) 15%, transparent); color: var(--sm-up); }
.sm-newslink { color: var(--text); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sm-newslink:hover { text-decoration: underline; }

/* the quiet day — a designed state, not an error (docs/AUTO_QUANT.md §8) */
.sm-quiet { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 22px 18px; text-align: center; }
.sm-quiet-h { font-size: 15.5px; font-weight: 700; margin-bottom: 6px; }
.sm-quiet-b { font-size: 13px; color: var(--text-dim); max-width: 42ch; margin: 0 auto; line-height: 1.5; }
.sm-quiet-r { font-size: 11.5px; font-weight: 650; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); border-radius: 999px; padding: 5px 12px; display: inline-block; margin-top: 12px; }
.sm-osc-svg { display: block; width: 100%; height: 46px; margin-top: 4px; border-top: 1px solid var(--border); padding-top: 4px; }
.sm-taswatch { display: inline-block; width: 12px; height: 0; border-top: 2px solid var(--sw, var(--text-dim)); margin-right: 5px; vertical-align: middle; border-radius: 2px; }
.sm-taswatch.dash { border-top-style: dashed; }
.sm-newstag.none { background: color-mix(in srgb, var(--text) 7%, transparent); color: var(--text-faint); }

.sm-nochart { text-align: center; padding: 22px 16px; }
.sm-nochart-t { font-size: 14px; font-weight: 700; margin-bottom: 5px; }
.sm-nochart-s { font-size: 12.5px; color: var(--text-dim); max-width: 40ch; margin: 0 auto; line-height: 1.5; }
.sm-newsbox { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 12px 16px; }
.sm-newsbox-h { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 7px; }
/* newsbox follows the stack — no column placement (owner 2026-07-27) */

/* ---- Strategies tab (AUTO_QUANT §10.4 A2b/A3): tabs, play carousel, ticket ---- */
.sm-atabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin: 14px 0 12px; }
.sm-atab { appearance: none; background: none; border: none; cursor: pointer; padding: 9px 15px; font: inherit; font-size: 13.5px; font-weight: 600; color: var(--text-dim); border-bottom: 2.5px solid transparent; }
.sm-atab.on { color: var(--accent, #4c8dff); border-color: var(--accent, #4c8dff); }
/* Live-count chip on the Positions tab. */
.sm-atab-n { display: inline-block; margin-left: 6px; min-width: 16px; padding: 0 4px; border-radius: 999px; background: var(--sm-up); color: var(--sm-on-up, #fff); font-size: 10.5px; font-weight: 700; line-height: 16px; text-align: center; }
/* POSITIONS TAB (owner 2026-08-06): this asset's plays; a tapped row lights
   its mark on the chart. */
.sm-pos { display: flex; flex-direction: column; gap: 8px; padding: 2px 0 12px; }
.sm-pos-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; background: var(--sm-card); border: 1px solid transparent; border-radius: 14px; padding: 12px 14px; font: inherit; color: var(--text); cursor: pointer; }
.sm-pos-row.on { border-color: color-mix(in srgb, var(--text) 30%, transparent); }
.sm-pos-dir { flex: none; font-size: 13px; }
.sm-pos-dir.long { color: var(--sm-up); }
.sm-pos-dir.short { color: var(--sm-down); }
.sm-pos-dir.grid { color: var(--text-dim); }
/* Long/Short word in the title — same colours as the home row's (owner
   2026-08-06: "long / short color in sync"). */
.sm-pos-dirw { font-size: 12.5px; font-weight: 650; }
.sm-pos-dirw.long { color: var(--sm-up); }
.sm-pos-dirw.short { color: var(--sm-down); }
.sm-pos-main { flex: 1 1 auto; min-width: 0; }
.sm-pos-t { font-size: 13.5px; font-weight: 650; display: flex; align-items: center; gap: 7px; }
.sm-pos-sub { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.sm-pos-chip { font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; border-radius: 999px; padding: 1.5px 7px; }
.sm-pos-chip.ok { background: color-mix(in srgb, var(--sm-up) 18%, transparent); color: var(--sm-up); }
.sm-pos-chip.warn { background: color-mix(in srgb, #e6a23c 20%, transparent); color: #e6a23c; }
.sm-pos-chip.dim { background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text-dim); }
.sm-pos-pnl { flex: none; font-size: 13px; font-weight: 650; font-variant-numeric: tabular-nums; }
.sm-pos-pnl.up { color: var(--sm-up); }
.sm-pos-pnl.down { color: var(--sm-down); }
.sm-pos-r { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.sm-pos-pct { font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.sm-pos-pct.up { color: var(--sm-up); }
.sm-pos-pct.down { color: var(--sm-down); }
.sm-pos-empty { color: var(--text-dim); font-size: 13px; padding: 14px 2px; }
/* Share button + PnL-card modal (owner 2026-08-06). The scrim mounts on
   <body>, outside .sm-root's token scope — restate what it needs (same
   reason as .sm-sheet-scrim). */
.sm-asset-head { position: relative; }
.sm-share-btn { position: absolute; top: 4px; right: 2px; width: 38px; height: 38px; display: grid; place-items: center; background: var(--sm-card); border: 1px solid var(--border); border-radius: 999px; color: var(--text-dim); cursor: pointer; }
.sm-share-btn:hover { color: var(--text); }
.sm-share-scrim {
  --border: color-mix(in srgb, var(--text) 9%, transparent);
  position: fixed; inset: 0; z-index: 95; background: rgba(0, 0, 0, 0.6);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.sm-share-box { display: flex; flex-direction: column; gap: 14px; width: min(420px, 92vw); }
.sm-share-box canvas { width: 100%; height: auto; display: block; border-radius: 18px; border: 1px solid var(--border); }
.sm-share-row { display: flex; gap: 10px; }
.sm-share-act { flex: 1; padding: 13px 0; border-radius: 999px; border: 1px solid var(--border); background: var(--bg); color: var(--text); font: inherit; font-size: 14px; font-weight: 650; cursor: pointer; }
.sm-share-act.primary { background: var(--sm-up, #2ecc71); color: var(--sm-on-up, #08130a); border-color: transparent; }
.sm-strat-car { display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 2px 2px 8px; scrollbar-width: none; }
.sm-strat-car::-webkit-scrollbar { display: none; }
.sm-strat-card { flex: 0 0 min(320px, 86%); scroll-snap-align: start; background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 16px; }
/* The standing build-your-own slot at the carousel's end (owner 2026-08-04):
   a dashed add-card — tonal plus, centered pitch, one CTA. The whole card is
   the button; the CTA is paint. */
.sm-strat-new { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; text-align: center; cursor: pointer; background: transparent; border: 1.5px dashed color-mix(in srgb, var(--text) 24%, transparent); font: inherit; color: var(--text); }
.sm-strat-new:hover { background: var(--bg-elev-1); }
.sm-strat-new-plus { width: 42px; height: 42px; border-radius: 50%; background: var(--bg-elev-2); display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 500; color: var(--text-dim); }
.sm-strat-new-t { font-size: 15px; }
.sm-strat-new .sm-strat-blurb { text-align: center; }
.sm-strat-new-cta { pointer-events: none; width: auto; padding: 10px 18px; }
/* Deep-linked from a Home tile: a brief ring marks the card the tap promised. */
.sm-strat-card.sm-strat-focus { border-color: color-mix(in srgb, var(--sm-up) 55%, var(--border)); box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--sm-up) 40%, transparent); transition: border-color 0.5s, box-shadow 0.5s; }
.sm-strat-card.hi { border-color: color-mix(in srgb, var(--accent, #4c8dff) 45%, var(--border)); }
/* The owner's own rule in the asset list — the same violet its board tiles wear. */
.sm-strat-card.mine { border-color: color-mix(in srgb, #a06bff 40%, var(--border)); background: linear-gradient(170deg, color-mix(in srgb, #a06bff 8%, transparent), transparent 55%), var(--sm-card); }
.sm-strat-minechip { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: color-mix(in srgb, #b58cff 75%, var(--text)); background: color-mix(in srgb, #a06bff 16%, transparent); border-radius: 999px; padding: 2px 8px; margin-left: auto; white-space: nowrap; }
.sm-strat-card.mine .sm-strat-rej { margin-left: 0; }
/* M3 titleMedium for the name; the direction chip carries the only colour. */
.sm-strat-head { display: flex; align-items: center; gap: 8px; font-size: 15px; letter-spacing: 0.01em; flex-wrap: wrap; }
/* Leverage sits with the title, not in the fine print: the headline return is
   a LEVERAGED number and must never be read without its multiplier. Amber —
   it qualifies the number rather than approving it (owner 2026-07-28). */
.sm-strat-lev { font-size: 10px; font-weight: 700; letter-spacing: 0.04em; color: var(--warn, #e2a03f); background: color-mix(in srgb, var(--warn, #e2a03f) 14%, transparent); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.sm-strat-blurb { font-size: 12.5px; color: var(--text-dim); margin: 5px 0 0; line-height: 1.45; }
/* Grouped disclosure (owner 2026-07-27: "more organized, easier to read").
   A labelSmall header + its rows, separated by whitespace and one hairline —
   no dashed boxes, no nested cards. */
.sm-strat-grp { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.sm-strat-glbl { font-size: 10.5px; font-weight: 500; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 6px; }
/* Foldable section header (owner 2026-08-05): the label IS the toggle — a
   full-width button wearing the label's exact look, chevron flipping open. */
.sm-strat-fold { display: flex; width: 100%; justify-content: space-between; align-items: center; background: none; border: 0; padding: 0; margin-bottom: 0; cursor: pointer; font: inherit; font-size: 10.5px; font-weight: 500; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-faint); }
.sm-strat-fold.open { margin-bottom: 6px; }
.sm-strat-foldc { transition: transform 0.15s ease; font-size: 11px; }
.sm-strat-fold.open .sm-strat-foldc { transform: rotate(180deg); }
.sm-strat-grp .sm-akv { font-size: 11.5px; padding: 4px 0; align-items: baseline; }
.sm-strat-grp .sm-akv b { font-weight: 600; text-align: right; max-width: 64%; line-height: 1.4; }
.sm-strat-see { appearance: none; background: none; border: none; cursor: pointer; font: inherit; font-size: 12px; font-weight: 600; color: var(--accent, #4c8dff); padding: 2px; white-space: nowrap; }
.sm-outpair { display: flex; justify-content: center; align-items: baseline; gap: 10px; margin: 10px 0 2px; font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.sm-outpair .down { color: var(--sm-down); }
.sm-outpair .up { color: var(--sm-up); }
.sm-outpair .sep { color: var(--text-faint); font-weight: 400; }
.sm-strat-cap { text-align: center; font-size: 11px; color: var(--text-faint); }
.sm-strat-detail { margin-top: 4px; }
.sm-strat-detail .sm-akv { font-size: 11.5px; padding: 3px 0; }
.sm-strat-see { margin-top: 6px; }
.sm-ticket { border-top: 1px solid var(--border); margin-top: 12px; padding-top: 10px; }
/* A level that came from the card, not from the user. Same rhythm as the input
   it replaced, none of the affordance — these are facts (the record was earned
   with them), so they must not look typeable. */
/* Composing: the strategy's own exits, set on the card where its tested levels
   and its record already are. */
/* M3 spacing: the exits editor is a form, not a footnote — 12px between its
   rows, 20px of air from the record above it (owner 2026-08-01). */
.sm-exits { display: flex; flex-direction: column; gap: 12px; margin-top: 20px; }
.sm-exmodes { display: flex; gap: 8px; padding-bottom: 2px; }
.sm-expill { appearance: none; border: none; border-radius: 999px; padding: 9px 16px; font: inherit; font-size: 11.5px; font-weight: 620; background: var(--bg-elev-2); color: var(--text-dim); cursor: pointer; }
.sm-expill.on { background: color-mix(in srgb, var(--sm-up) 18%, transparent); color: var(--sm-up); }
.sm-exbad { font-size: 11.5px; line-height: 1.45; color: var(--sm-down); }
/* Steppers, and what the numbers add up to. Every tap is a known increment and
   the lines under it say what the combination MEANS — a free number box asks
   the creator to invent a value and tells them nothing about it. */
.sm-exrow { display: flex; align-items: center; gap: 12px; min-height: 40px; }
.sm-exlab { flex: 1 1 auto; font-size: 12.5px; color: var(--text-dim); }
.sm-exctl { display: flex; align-items: center; gap: 2px; }
/* The value is a real INPUT now (owner 2026-08-02: type it or step it), styled
   as a Material 3 filled field only while it has focus — at rest it reads as
   the same quiet number it always was, same type ramp as every figure here. */
.sm-exval { min-width: 52px; width: 52px; text-align: center; font-size: 15px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; font-family: inherit; color: var(--text); background: none; border: none; border-radius: 10px; padding: 6px 2px; }
.sm-exval:focus { outline: none; background: var(--bg-elev-2); }
.sm-exunit { font-size: 12.5px; color: var(--text-dim); margin-right: 2px; min-width: 14px; }
.sm-exfb { display: flex; flex-direction: column; gap: 3px; padding-top: 4px; font-size: 11.5px; line-height: 1.45; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.sm-exfb .warn { color: var(--warn, #d2a13a); }
.sm-exfb .bad { color: var(--sm-down); }
/* The tested-baseline row, as an M3 tonal surface (owner 2026-08-02: "this
   part needs a better layout"): supporting text left, a TEXT button right —
   a disabled pill next to a two-line caption read as two orphans. */
.sm-exbase { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; padding: 10px 14px; background: var(--bg-elev-1); border-radius: 12px; }
.sm-exbase .sm-strat-cap { margin: 0; }
.sm-exreset { appearance: none; border: none; background: none; padding: 8px 10px; margin: -8px -10px -8px 0; border-radius: 10px; font: inherit; font-size: 12.5px; font-weight: 650; color: var(--sm-up); cursor: pointer; white-space: nowrap; }
.sm-exreset:hover:not(:disabled) { background: color-mix(in srgb, var(--sm-up) 12%, transparent); }
.sm-exreset:disabled { color: var(--text-faint); cursor: default; }
/* ── The strategy COPY screen (owner 2026-08-02): copying one strategy works
   like copying a Stack — a full screen over everything, amount first. ── */
/* A VIEW inside the session pane, not an overlay: the pane owns it, the
   composer keeps its own layer below (no z-index games to lose), and the
   sticky CTA anchors to the pane's visible bottom the same way the Stack
   copy screen's does — because it uses the same qn-bcta mechanism. Height
   fills the pane's viewport so the CTA has a bottom to stick to. */
.sm-copyscr { display: flex; flex-direction: column; min-height: calc(100dvh - 230px); }
.sm-copyscr-h { display: flex; align-items: center; gap: 10px; padding: 6px 2px 8px; }
.sm-copyscr-t { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
.sm-copyscr-b { flex: 1 1 auto; display: flex; flex-direction: column; padding: 8px 2px 0; }
/* Same vertical rhythm as the Stack copy screen: the amount group floats
   centered in the free space, the CTA anchors at the bottom. */
.sm-copyscr .qn-cf-amt { flex: 1 1 auto; justify-content: center; }
/* Wider, roomier (owner 2026-08-02: "make it wider, more space") — the card
   spans the pane up to a readable cap instead of shrink-wrapping its rows,
   and the rows breathe. The centered amount column would otherwise hug it
   to content width. */
/* Full pane width, like the Stack review it mirrors (owner 2026-08-03) — a
   centered 420px column read as an inset card floating in the pane. */
.sm-copyrev { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 0; }
.sm-copyrev .sm-arm { margin-top: 14px; }
.sm-copyrev .sm-strat-cap.bad { color: var(--sm-down); }
.sm-expill:disabled { opacity: 0.35; cursor: default; }
.sm-tfact { margin-left: auto; font-size: 12.5px; color: var(--text-dim); padding: 5px 0; }
.sm-tin { width: 110px; margin-left: auto; background: color-mix(in srgb, var(--sm-card) 60%, #000 8%); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font: inherit; font-size: 12.5px; padding: 5px 9px; text-align: right; }
/* Leverage: a swipe bar + a typeable number, so the whole 1..max range is
   visible in one row and nothing truncates on a narrow card. */
.sm-levblk { padding: 2px 0 4px; }
.sm-levval { margin-left: auto; display: inline-flex; align-items: baseline; gap: 1px; }
.sm-levin { width: 62px; padding: 4px 6px; text-align: right; }
.sm-levx { font-size: 12px; font-weight: 600; color: var(--text-dim); }
.sm-levbar { -webkit-appearance: none; appearance: none; display: block; width: 100%; height: 20px; margin: 6px 0 0; background: none; cursor: pointer; }
.sm-levbar::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--text) 14%, transparent); }
.sm-levbar::-moz-range-track { height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--text) 14%, transparent); }
.sm-levbar::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%; background: var(--accent, #4c8dff); border: none; }
.sm-levbar::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%; background: var(--accent, #4c8dff); border: none; }
.sm-levbar:focus-visible { outline: 2px solid var(--accent, #4c8dff); outline-offset: 3px; border-radius: 4px; }
.sm-levends { display: flex; justify-content: space-between; font-size: 10.5px; color: var(--text-faint); margin-top: 2px; }
/* The card's record belongs to the tested rung — say so when you're on it. */
.sm-levblk.tested .sm-levends span:first-child { color: var(--accent, #4c8dff); }
/* What the chosen rung actually costs — never a blocker, always visible. */
.sm-levnote { font-size: 11px; line-height: 1.45; color: var(--text-faint); display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }
.sm-levnote.warn { color: var(--sm-warn, #d2a13a); }
.sm-levnote.bad { color: var(--sm-down); }
.sm-arm { display: block; width: 100%; margin-top: 20px; appearance: none; cursor: pointer; font: inherit; font-size: 13.5px; font-weight: 700; padding: 11px; border-radius: 999px; border: none; background: var(--accent, #4c8dff); color: #071120; }
.sm-arm:disabled { opacity: 0.7; cursor: default; }
.sm-arm.quiet { background: color-mix(in srgb, var(--text-dim) 14%, transparent); color: var(--text-dim); }
/* Four-layer flow (§10.4c): each surface leads with its question. */
.sm-layer-q { font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; font-weight: 700; color: var(--text-faint); margin: 2px 2px 8px; }
.sm-layer-q-sub { text-transform: none; letter-spacing: 0; font-weight: 600; }
/* ---- Quant screen (four-layer home, AUTO_QUANT §10.4c) ---- */
/* max-width WITHOUT an auto inline margin pins the column to the left edge, so
   on a wide pane (and worse with the task list collapsed) every row sat left of
   a big empty gutter while the composer below it stayed centred — the mismatch
   is what read as broken (owner 2026-07-29). width:100% keeps it flush on
   phones, where max-width never binds. */
/* Auto on = the reveal's green settled over the WHOLE screen: strongest at
   the nav bar, gone by the composer. Sits above the opaque session bar
   (z2), below sheets/toasts; never catches taps. */
.qn-autocast {
  position: absolute; inset: 0; pointer-events: none; z-index: 3;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--sm-up) 13%, transparent) 0%,
    color-mix(in srgb, var(--sm-up) 6%, transparent) 34%,
    transparent 70%
  );
}
.qn-wrap { display: flex; flex-direction: column; gap: 14px; padding: 0 2px 24px; width: 100%; max-width: 980px; margin-inline: auto; min-height: calc(100vh - 220px); container-type: inline-size; container-name: qnwrap; }
/* Autopilot-on reveal: a brand-tinted wash in a circle growing OUT OF the
   Auto pill until it covers the WHOLE screen — nav bar to composer — then
   fading (owner 2026-08-05, reference: react-native-nitro-theme-transition's
   circular theme reveal; replaced the top→bottom scan band). Geometry (center,
   radius, clip) is animated from quant.ts per tap — the pill moves with
   layout, a keyframe can't know where it is. Fixed overlay on <body>, under
   the snackbar layer (10400+), above all content chrome. */
/* Feed on desktop = Instagram's answer: one centered column at phone
   width. Full-pane cards blew the charts up ~3x (the SVG scales with
   width) and made every label shout. */
.sm-feed { width: 100%; max-width: 540px; margin-inline: auto; }

.qn-scanfx { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 10300; }
/* The wash: brightest at the pill, thinning outward — same subtle palette
   the scan band used ("more transparent and subtle", owner 2026-08-04). */
.qn-scanfx-wash {
  position: absolute; inset: 0;
  background: radial-gradient(
    circle at var(--cx, 50%) var(--cy, 20%),
    rgba(0, 217, 7, 0.18) 0%,
    rgba(0, 217, 7, 0.10) 45%,
    rgba(0, 143, 4, 0.07) 100%
  );
  filter: blur(2px);
}
/* The crisp wavefront riding the circle's edge. */
.qn-scanfx-ring {
  position: absolute; border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0 0 24px rgba(0, 217, 7, 0.3), inset 0 0 18px rgba(0, 217, 7, 0.15);
  mix-blend-mode: screen;
}
@media (prefers-reduced-motion: reduce) { .qn-scanfx { display: none; } }

/* ── Home layout: one column on a phone, two on a wide pane ─────────────────
   Owner 2026-07-29: "on desktop, under the dashboard hero, show the list of
   strategies; on the right of the dashboard, show open positions, and recent
   activities in 2 tabs" — then, having used it: "on mobile keep them in 2 tabs
   as in desktop".

   So the TABS are unconditional and only the COLUMNS are conditional. (The first
   cut showed both panels stacked on a phone, on the theory that a screen which
   scrolls needn't hide anything; the owner tried it and preferred one thing at a
   time everywhere. Their call — and it keeps one behaviour to reason about.)

   A CONTAINER query, not a media query: this pane's width depends on the task
   list (and the desktop collapse toggle), so viewport width is the wrong
   question — at 1700px the pane can be 1090 or 1640. DOM order is the phone
   order; the grid re-places the same three blocks for the wide case. */
.qn-cols { display: flex; flex-direction: column; gap: 14px; }
/* WHAT YOUR MONEY IS DOING — four tonal cards between the curve and the Stacks.
   auto-fit so they land 4-up with room and 2×2 on a narrow phone rather than
   squeezing four numbers into one line. */
.qn-snap { display: grid; grid-template-columns: repeat(auto-fit, minmax(74px, 1fr)); gap: 8px; }
.qn-snapc { background: var(--bg-elev-1); border-radius: 16px; padding: 11px 10px; text-align: center; min-width: 0; }
.qn-snapv { font-size: 15.5px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.qn-snapv.up { color: var(--sm-up); } .qn-snapv.down { color: var(--sm-down); } .qn-snapv.none { color: var(--text-faint); }
.qn-snapl { font-size: 10.5px; color: var(--text-faint); margin-top: 2px; }
/* Supporting facts under the hero P&L: at-work + unrealized. */
.qn-hero-work { display: flex; align-items: baseline; gap: 6px; font-size: 12px; color: var(--text-faint); margin-top: 3px; font-variant-numeric: tabular-nums; }
.qn-hero-work .up { color: var(--sm-up); }
.qn-hero-work .down { color: var(--sm-down); }
/* Manual / Autopilot — M3 segmented-button reading of what Robinhood
   would ship: hairline-outlined track, tonal fill on the selected side
   with a leading check (the M3 signature), the green fill earned only by
   Autopilot. Emphasized easing, no glow, no shouting. */
.qn-modeseg { position: relative; display: grid; grid-template-columns: 1fr 1fr; background: var(--bg-elev-1); border: 1px solid var(--border); border-radius: 999px; padding: 4px; margin-top: 10px; }
.qn-modeseg-thumb { position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc(50% - 4px); border-radius: 999px; background: var(--bg-elev-2); transition: transform 0.35s cubic-bezier(0.2, 0, 0, 1), background 0.35s; }
.qn-modeseg.auto .qn-modeseg-thumb { transform: translateX(100%); background: var(--sm-up); }
.qn-modeseg-b { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 7px; background: none; border: none; font: inherit; font-size: 13.5px; font-weight: 600; letter-spacing: 0.01em; color: var(--text-dim); padding: 10px 0; cursor: pointer; border-radius: 999px; transition: color 0.2s; }
.qn-modeseg-b.on { color: var(--text); font-weight: 650; }
.qn-modeseg.auto .qn-modeseg-b.on { color: var(--sm-on-up); }
@media (prefers-reduced-motion: reduce) { .qn-modeseg-thumb { transition: none; } }
/* Under the zeros of an empty dashboard — says what would fill them. */
/* THE CHECKUP SCREEN: the findings timeline. Rows are cards like the positions
   sheet's; needs-you rows carry the down-accent edge; muted rows sit dim. */
.qn-ck { padding: 8px 16px 28px; display: flex; flex-direction: column; gap: 12px; }
/* Inside the Recent-activity sheet nothing else pads — the sheet body is a
   bare flex column — so the cards sat flush against the modal border (owner
   screenshot 2026-08-06). M3 insets: 16px on phones, 24px in the desktop
   modal. */
.qn-ck-in-sheet { padding: 4px 16px 16px; }
@media (min-height: 720px) and (min-width: 900px) {
  .qn-ck-in-sheet { padding: 4px 24px 20px; }
}
.qn-ck-head { padding: 4px 2px 0; }
/* Stamp + "look now" CTA on ONE line; category chips + search under them
   (owner 2026-08-06 redesign of the Recent-activity header). */
.qn-ck-filter { display: flex; flex-direction: column; gap: 8px; padding: 2px 2px 0; }
.qn-ck-search { width: 100%; box-sizing: border-box; background: var(--sm-card); border: 1px solid var(--border); border-radius: 999px; padding: 9px 14px; font: inherit; font-size: 13px; color: var(--text); }
.qn-ck-search::placeholder { color: var(--text-faint); }
.qn-ck-search:focus { outline: none; border-color: color-mix(in srgb, var(--text) 25%, transparent); }
.qn-ck-cats { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; }
.qn-ck-cats::-webkit-scrollbar { display: none; }
.qn-ck-cat { flex: none; border: 1px solid var(--border); background: none; color: var(--text-dim); border-radius: 999px; padding: 5px 12px; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; }
.qn-ck-cat.on { background: var(--text); color: var(--bg); border-color: transparent; }
.qn-ck-title { font-size: 18px; font-weight: 700; letter-spacing: -0.01em; }
.qn-ck-stamp { font-size: 12px; color: var(--text-faint); margin-top: 3px; }
.qn-ck-ask {
  align-self: flex-start; background: var(--sm-card); color: var(--text);
  border: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  border-radius: 999px; padding: 9px 16px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.qn-ck-ask:disabled { color: var(--text-faint); cursor: default; }
/* The look-in-progress spinner inside the ask button. */
.qn-ck-askspin {
  display: inline-block; width: 12px; height: 12px; margin-right: 7px; vertical-align: -1px;
  border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
.qn-ck-list { display: flex; flex-direction: column; gap: 10px; }
.qn-ck-row { background: var(--sm-card); border-radius: 18px; padding: 12px 14px 14px; }
.qn-ck-row.need { box-shadow: inset 3px 0 0 var(--sm-down); }
.qn-ck-row.is-muted { opacity: 0.55; display: flex; align-items: center; gap: 10px; }
.qn-ck-row.is-muted .qn-ck-text { flex: 1 1 auto; }
.qn-ck-row-h { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.qn-ck-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); }
.qn-ck-dot.need { background: var(--sm-down); }
.qn-ck-dot.agent { background: var(--sm-up); }
.qn-ck-dot.eng { background: #5b8def; }
/* The decision bubble — the one attention signal, riding the Recent-activity
   tab since the positions circle retired (2026-08-04). */
.qn-posbtn { position: relative; }
.qn-tab-need {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; margin-left: 6px; padding: 0 4px;
  border-radius: 999px; background: var(--sm-down); color: #fff;
  font-size: 10px; font-weight: 700; vertical-align: 1px; box-sizing: border-box;
}
.qn-ck-when { font-size: 11px; color: var(--text-faint); flex: 1 1 auto; }
.qn-ck-x { background: none; border: none; color: var(--text-faint); font-size: 16px; line-height: 1; cursor: pointer; padding: 2px 4px; }
.qn-ck-text { font-size: 13.5px; line-height: 1.5; }
/* Outcomes wear the money's color, same code as the bell: green won, red lost. */
.qn-ck-text.up { color: var(--sm-up); }
.qn-ck-text.down { color: var(--sm-down); }
.qn-ck-act {
  margin-top: 10px; background: var(--bg-elev-2); color: var(--text); border: none;
  border-radius: 999px; padding: 7px 14px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.qn-ck-mh { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); margin-top: 6px; }
.qn-ck-empty { color: var(--text-faint); font-size: 13.5px; line-height: 1.5; padding: 8px 2px; }
.qn-live-review { font-size: 11.5px; color: var(--text-faint); padding: 2px 2px 0 18px; }

/* THE NEWS DECK: a horizontal row of dismissible cards. As a vertical list it
   pushed Your portfolios off the first screen; a deck costs one row's height
   whatever the news volume is. Snap so a swipe lands on a card, not between two. */
.qn-ndeck { display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; padding: 2px 0 4px; scrollbar-width: none; }
.qn-ndeck::-webkit-scrollbar { display: none; }
.qn-ncard { flex: 0 0 auto; width: min(78%, 300px); scroll-snap-align: start; display: flex; flex-direction: column; gap: 8px; background: var(--bg-elev-1); border-radius: 18px; padding: 14px; min-width: 0; }
.qn-nhead { display: flex; align-items: center; gap: 7px; }
.qn-nsym { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; color: var(--text-dim); }
/* A coin you actually hold wears the dot — money at risk right now reads
   differently from one a Stack is merely watching. */
.qn-nsym.held::before { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--sm-up); margin-right: 6px; vertical-align: middle; }
.qn-ntag { font-size: 10px; font-weight: 620; border-radius: 999px; padding: 2px 7px; text-transform: lowercase; white-space: nowrap; }
.qn-ntag.flag { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 14%, transparent); }
.qn-ntag.catalyst { color: var(--text-dim); background: var(--bg-elev-2); }
.qn-nago { font-size: 10.5px; color: var(--text-faint); margin-left: auto; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Three lines, then stop: a deck of equal cards beats four ragged ones. */
.qn-ntitle { font-size: 13.5px; line-height: 1.38; color: var(--text); flex: 1 1 auto; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.qn-nfoot { display: flex; align-items: center; justify-content: space-between; margin-top: 2px; }
.qn-nx { appearance: none; border: none; background: none; color: var(--text-faint); font-size: 19px; line-height: 1; padding: 4px 6px; margin-left: -6px; border-radius: 50%; cursor: pointer; }
.qn-nx:hover { color: var(--text); background: var(--bg-elev-2); }
.qn-ngo { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--bg-elev-2); color: var(--text); text-decoration: none; }
.qn-ngo:hover { background: var(--bg-elev-3); }
.qn-ngo svg { width: 17px; height: 17px; }
.qn-side { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
/* On a phone the positions live in a MODAL (owner 2026-07-30) — the home is the
   performance story, and a table under the chart made them share the first
   screen. The same element is a sticky side column once there's room for one,
   so nothing is hidden on a pane wide enough to just show it. */
/* Inline Home section on narrow screens (owner 2026-08-04) — the wide
   container query below re-places it as the sticky right column. */
.qn-cols > .qn-side { display: flex; }
.qn-side-panels { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.qn-side-panel { display: flex; flex-direction: column; min-width: 0; }
/* With tabs at every width a blank panel reads as a failure, so the empty states
   are always on. */
.qn-side-empty { display: block; font-size: 12.5px; color: var(--text-faint); padding: 10px 2px; }
.qn-side-tabs { display: flex; gap: 8px; padding-top: 4px; }
/* ~40dp pills since 2026-08-04 (owner: "tab pill could be taller"). */
.qn-side-tab { flex: 1 1 auto; white-space: nowrap; font-size: 13px; font-weight: 560; color: var(--text-dim); background: var(--bg-elev-1); border: none; border-radius: 999px; padding: 12px 14px; line-height: 1; cursor: pointer; transition: background .14s, color .14s; }
.qn-side-tab:hover { background: var(--bg-elev-2); }
.qn-side-tab.on { background: var(--sm-chip-on); color: var(--text); font-weight: 620; }
/* Either/or — display, not [hidden]: a flex/grid display rule beats [hidden] and
   the panel would stay visible anyway (the lesson .qn-pane already carries). */
.qn-side[data-active="positions"] > .qn-side-panels > .qn-side-panel[data-tab="activity"],
.qn-side[data-active="activity"] > .qn-side-panels > .qn-side-panel[data-tab="positions"] { display: none; }
@container qnwrap (min-width: 860px) {
  /* FIVE rows, and the last one is the flexible one, for a reason worth keeping
     (owner 2026-08-02, staging screenshot): the positions rail spans the whole
     reading column, so when the rail is TALLER than the four sections beside it
     — twelve open positions against a fresh account's short left column — the
     grid hands that surplus to the tracks the rail spans. With four auto rows
     that meant 140px of dead air between the chart and the cards, another 140
     between the cards and the news, and Your portfolios shoved off the bottom
     of the screen. A flexible fifth row is where the surplus goes now: `fr`
     tracks absorb a spanning item's excess, so the content rows stay at their
     content height and the sections stay stacked as written. */
  .qn-cols { display: grid; grid-template-columns: minmax(0, 1fr) 318px; column-gap: 18px; row-gap: 14px; align-items: start; grid-template-rows: auto auto auto auto 1fr; }
  .qn-cols > .qn-hero { grid-column: 1; grid-row: 1; }
  .qn-cols > .qn-sec-snap { grid-column: 1; grid-row: 2; }
  .qn-cols > .qn-sec-news { grid-column: 1; grid-row: 3; }
  .qn-cols > .qn-sec-port { grid-column: 1; grid-row: 4; }
  /* Spans the reading column and sticks: Your portfolios scrolls for a long
     time, and positions are what you keep glancing at while it does.
     It is CAPPED to the viewport and scrolls inside itself, because a sticky
     column taller than the screen pins its top at 4px and leaves its bottom
     rows permanently unreachable — on staging that was positions 10 through 12,
     visible in no scroll position at all. The 220px is the same allowance
     .qn-wrap already makes for the chrome above and the composer below (the
     composer measures 209px on a desktop pane), so a stuck rail ends just
     above it rather than under it. */
  .qn-cols > .qn-side { display: flex; grid-column: 2; grid-row: 1 / span 5; position: sticky; top: 4px; max-height: calc(100dvh - 220px); overflow-y: auto; overscroll-behavior: contain; }
}
/* The unread badge overhangs the bell button on purpose, but .sm-tabs is a
   scroll container (overflow-x: auto), and a scroll container CLIPS anything
   outside its content box — the badge lost its top-right corner (owner
   2026-07-29). overflow-y can't be `visible` while overflow-x scrolls, so the
   fix is clearance INSIDE the scrollport: padding is part of the padding box and
   renders, so the overhang has room without escaping. */
.qn-tabs { margin: 0 2px; align-items: center; padding: 5px 6px 2px 0; }
/* Quant's tab bar rides the scroll on its own (owner 2026-08-04, the
   composer stays put): sticky at the scrollport top; once the page is
   scrolled it FLOATS — solid backdrop, shadow, rounded base. Swiping up
   (scrolling down through content) slides it out via .hide; swiping back
   down slides it in. At the very top it sits in flow, never hidden. */
.qn-tabs {
  position: sticky;
  top: 0;
  z-index: 6;
  transition: transform 0.24s ease, box-shadow 0.24s ease, background 0.24s ease;
}
.qn-tabs.float {
  background: var(--bg-elev);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  border-radius: 0 0 14px 14px;
  padding-bottom: 6px;
}
.qn-tabs.hide { transform: translateY(-130%); }
/* ── The bell: liveness + updates in one tap (owner 2026-07-27) ─────────── */
.qn-bellwrap { margin-left: auto; position: relative; flex: none; }
.qn-bell { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 999px; border: none; background: var(--bg-elev-1); color: var(--text-dim); cursor: pointer; transition: background .14s; }
.qn-bell:hover { background: var(--bg-elev-2); }
.qn-bellic { width: 18px; height: 18px; }
.qn-bell-badge { position: absolute; top: -3px; right: -4px; background: var(--sm-up); color: var(--sm-on-up); border-radius: 999px; font-size: 9.5px; font-weight: 700; padding: 1.5px 5px; pointer-events: none; }
.qn-bellbub { position: absolute; right: 2px; top: 53px; z-index: 40; width: min(360px, calc(100vw - 28px)); background: var(--bg-elev); border: 1px solid var(--border); border-radius: 16px; padding: 12px 14px; box-shadow: 0 14px 40px rgba(0,0,0,0.35); }
.qn-bellbub-h { font-size: 12px; font-weight: 620; color: var(--text-dim); padding-bottom: 8px; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.qn-bellbub-h.warn { color: var(--warn); }
.qn-bellbub-empty { font-size: 12px; color: var(--text-faint); padding: 12px 0 4px; }
.qn-bellbub-sum { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; padding: 4px 0 2px; border-bottom: 1px solid color-mix(in srgb, var(--text) 7%, transparent); }
.qn-bellbub-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.qn-bellbub-row:last-child { border-bottom: none; padding-bottom: 2px; }
.qn-bellbub-t { font-size: 12.5px; font-weight: 560; min-width: 0; }
.qn-bellbub-t.up { color: var(--sm-up); }
.qn-bellbub-t.down { color: var(--sm-down); }
.qn-bellbub-ago { flex: none; font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* ── Picked by AI: a screen, not advice (owner 2026-07-27) ──────────────── */
.qn-picks { display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; padding: 2px 2px 4px; }
.qn-picks::-webkit-scrollbar { display: none; }
.qn-pick { flex: 0 0 240px; scroll-snap-align: start; text-align: left; background: var(--bg-elev-1); border: 1px solid var(--border); border-radius: 14px; padding: 12px 14px; cursor: pointer; color: var(--text); font: inherit; transition: background .14s; }
.qn-pick:hover { background: var(--bg-elev-2); }
.qn-pick-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.qn-pick-sym { display: flex; align-items: baseline; gap: 5px; font-size: 15px; font-weight: 680; letter-spacing: -0.01em; }
.qn-pick-left { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.qn-pick-right { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; flex: none; }
.qn-pick-ch { font-size: 11px; font-weight: 640; font-variant-numeric: tabular-nums; }
.qn-pick-ch.up { color: var(--sm-up); }
.qn-pick-ch.down { color: var(--sm-down); }
.qn-pick-px { font-size: 12.5px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.qn-pick-fact { font-size: 10.5px; color: var(--text-faint); line-height: 1.45; font-variant-numeric: tabular-nums; }
/* Strategies tab — one M3 card per coin, its surviving strategies stacked
   inside. Same surface/radius/typographic scale as .qn-pick so the two
   surfaces read as one family; the divider between rows is the only new
   structure (it separates strategies WITHIN a coin, never between coins). */
.qn-sgroup { display: block; width: 100%; background: var(--bg-elev-1); border: 1px solid var(--border); border-radius: 20px; padding: 6px 0 14px; color: var(--text); margin-bottom: 16px; }
.qn-sgroup-h { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 8px; padding: 14px 16px 12px; background: none; border: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.qn-sgroup-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
.qn-sgroup-ic { width: 20px; height: 20px; border-radius: 50%; flex: none; }
.qn-sgroup-sym { font-size: 15px; font-weight: 680; letter-spacing: -0.01em; }
.qn-sgroup-right { display: flex; align-items: baseline; gap: 8px; flex: none; }
.qn-sgroup-px { font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.qn-sgroup-ch { font-size: 11px; font-weight: 640; font-variant-numeric: tabular-nums; }
.qn-sgroup-ch.up { color: var(--sm-up); }
.qn-sgroup-ch.down { color: var(--sm-down); }
/* Sort control — secondary to the tab bar, same M3 pill grammar, CENTRED
   (owner 2026-07-28). Reorders the coins AND the tiles inside each card. */
/* Breathing room above (owner 2026-08-02: "leave some margin before the
   tabs"), and the SAME pill language as the screen's main tabs — outlined
   ghosts beside filled tabs read as a second, unrelated control. */
.qn-sortbar { display: flex; justify-content: flex-start; gap: 8px; padding: 14px 2px 18px; overflow-x: auto; scrollbar-width: none; }
.qn-sortbar::-webkit-scrollbar { display: none; }
.qn-sortpill { flex: none; font-size: 13px; font-weight: 560; padding: 9px 15px; border-radius: 999px; border: none; color: var(--text-dim); background: var(--bg-elev-1); font-family: inherit; white-space: nowrap; cursor: pointer; transition: background .14s, color .14s; }
.qn-sortpill:hover { background: var(--bg-elev-2); }
.qn-sortpill.on { background: var(--sm-chip-on); color: var(--text); font-weight: 620; }
/* The "Your rules" pill selected — the same violet its tiles wear. */
.qn-sortpill.mine.on { background: color-mix(in srgb, #a06bff 26%, var(--bg-elev-1)); }
/* The mascot's chat line — shared by the picker receipt and anywhere else a
   sentence is Genie speaking. Clipped top-left corner points at the sender. */
.qn-chatline { display: flex; align-items: flex-start; gap: 10px; margin: 4px 2px 6px; }
.qn-chatface { width: 38px; height: 38px; flex: 0 0 auto; }
.qn-chatbub { flex: 1 1 auto; min-width: 0; background: var(--bg-elev-1); border-radius: 4px 16px 16px 16px; padding: 12px 14px; font-size: 13px; line-height: 1.5; color: var(--text-dim); }

/* Strategy tiles — three things to CHOOSE BETWEEN, not three rows to read. The
   strip runs to the card's edge so a partly-visible tile reads as "more this
   way" instead of a clipping bug, and snaps so it lands cleanly. */
.qn-strip { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-left: 14px; padding: 2px 14px 11px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.qn-strip::-webkit-scrollbar { display: none; }
/* 222px is load-bearing: the four-fact stat line is ~170px and must never wrap
   (owner 2026-07-28 picked the wide tile over the wrapped one). */
.qn-stile { flex: none; scroll-snap-align: start; width: 226px; background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 18px; padding: 14px 14px 16px; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.qn-stile.t-gain { background: linear-gradient(170deg, color-mix(in srgb, var(--sm-up) 10%, transparent), transparent 60%), var(--bg-elev-2); border-color: color-mix(in srgb, var(--sm-up) 26%, transparent); }
.qn-stile.t-cons { background: linear-gradient(170deg, color-mix(in srgb, var(--info, #7896ff) 12%, transparent), transparent 60%), var(--bg-elev-2); border-color: color-mix(in srgb, var(--info, #7896ff) 28%, transparent); }
.qn-stile.t-act { background: linear-gradient(170deg, color-mix(in srgb, var(--warn) 10%, transparent), transparent 60%), var(--bg-elev-2); border-color: color-mix(in srgb, var(--warn) 26%, transparent); }
/* The owner's own rule on the board — violet, so "mine" never reads as one of
   the sweep's own axes. */
.qn-stile.t-mine { background: linear-gradient(170deg, color-mix(in srgb, #a06bff 12%, transparent), transparent 60%), var(--bg-elev-2); border-color: color-mix(in srgb, #a06bff 30%, transparent); }
.qn-stile:hover .qn-stile-n { text-decoration: underline; text-underline-offset: 3px; }
.qn-stile-eb { font-size: 9px; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; min-height: 11px; color: var(--text-faint); }
.qn-stile-eb.gain { color: color-mix(in srgb, var(--sm-up) 62%, var(--text)); }
.qn-stile-eb.cons { color: color-mix(in srgb, var(--info, #7896ff) 68%, var(--text)); }
.qn-stile-eb.act { color: color-mix(in srgb, var(--warn) 72%, var(--text)); }
.qn-stile-eb.mine { color: color-mix(in srgb, #b58cff 70%, var(--text)); }
.qn-stile-n { font-size: 12px; font-weight: 640; line-height: 1.25; margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qn-stile-dir { font-weight: 800; }
.qn-stile-dir.long { color: var(--sm-up); }
.qn-stile-dir.short { color: var(--sm-down); }
/* The grid glyph is NEUTRAL — red/green claims a direction the ladder
   doesn't have (owner 2026-08-03). */
.qn-stile-dir.grid { color: var(--text-faint); }
.qn-stile-r { font-size: 22px; font-weight: 700; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; margin-top: 8px; }
.qn-stile-r.up { color: var(--sm-up); }
.qn-stile-r.down { color: var(--sm-down); }
/* The line must never wrap. Measured at 430px: the widest real line needs
   ~161px of the tile's 196px, and the widest line that could ever exist
   ("90 days | 128 trades | 100% wins | 10×") needs ~178px — so it fits with
   room to spare at 10px. Headroom is bought in separator padding, never by
   widening the tile: at 222px only two tiles are visible already, so every
   extra pixel of width costs the peek. */
.qn-stile-stat { font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; margin-top: 12px; white-space: nowrap; }
.qn-stile-sep { color: color-mix(in srgb, var(--text) 26%, transparent); padding: 0 3px; }
/* Trending's Top-strategies list (owner 2026-08-04): the SAME card language
   as Picked by AI — it reuses .qn-pick's anatomy, stacked full-width. */
.qn-sec-topstrat { gap: 8px; margin-top: 16px; }
.qn-sec-topstrat > .qn-q { margin-bottom: 2px; }
/* Roomier than the AI cards on purpose (owner 2026-08-04: "each card can be
   a bit taller, like what it was last week") — the second fact line plus
   these insets restores that height. */
.qn-topstrat { flex: none; width: 100%; padding: 15px 16px; }
.qn-topstrat .qn-pick-left { gap: 4px; }
/* The category chips: a scrollable row of the same pills the sheets use. */
.qn-topcats { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding: 2px 0 4px; }
.qn-topcats::-webkit-scrollbar { display: none; }
.qn-topcats .qn-side-tab { flex: none; }
.qn-ts-r { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* The fact line starts under the direction mark, not under the rank. */
.qn-topstrat .qn-pick-fact { padding-left: 23px; }
.qn-ts-r.up { color: var(--sm-up); }
.qn-ts-r.down { color: var(--sm-down); }
.qn-sdots { display: flex; justify-content: center; gap: 5px; padding: 0 0 8px; }
.qn-sdot { width: 5px; height: 5px; border-radius: 50%; background: color-mix(in srgb, var(--text) 20%, transparent); }
.qn-sdot.on { background: color-mix(in srgb, var(--text) 62%, transparent); }

/* ── Stacks: Featured + Top performers (docs/QUANT_BASKETS.md §3-5) ─────────
   Material 3 minimal throughout, as the owner asked for on this screen: tonal
   surfaces instead of outlines, no component borders anywhere, pill controls,
   and hairline dividers only where a list genuinely needs them. */
.qn-sec { display: flex; flex-direction: column; }
.qn-sechead { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 4px 2px 8px; }
.qn-sech-right { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* The activity bell: icon button with the needs-you bubble riding it. */
.qn-bellbtn { position: relative; flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 999px; background: var(--bg-elev-1); border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; padding: 0; }
.qn-bellbtn .material-symbols-rounded { font-size: 17px; }
.qn-bellbtn .qn-tab-need { position: absolute; top: -4px; right: -4px; }
/* An overline sits closer to what it labels than a title does — the same 2px
   the hero's eyebrow uses above the balance. */
.qn-sechead.eb { padding: 2px 2px 10px; }
.qn-sech-t { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.qn-bwins { margin: 0; justify-content: flex-end; }

/* Featured card — identity ONLY (owner 2026-07-31). No number, no curve: a
   showcase that leads with a percent is an ad for whatever is green today. */
.qn-bcard { position: relative; overflow: hidden; flex: none; scroll-snap-align: start; width: 232px; min-height: 96px; display: flex; align-items: flex-end; background: var(--bg-elev-2); border: none; border-radius: 20px; padding: 12px 13px; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.qn-bcard.on { background: linear-gradient(170deg, color-mix(in srgb, var(--sm-up) 12%, transparent), transparent 62%), var(--bg-elev-2); }
/* The backdrop: a wash of the Stack's own palette plus an oversized glyph
   watermark, both faded well under the text they sit behind. */
.qn-bart { position: absolute; inset: 0; opacity: 0.22; pointer-events: none; }
.qn-bartg { position: absolute; right: -6px; bottom: -18px; font-size: 88px; line-height: 1; opacity: 0.16; pointer-events: none; filter: saturate(0.8); }
.qn-bhead { position: relative; z-index: 1; display: flex; align-items: center; gap: 9px; width: 100%; min-width: 0; }
.qn-bpfp { flex: none; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; font-size: 19px; }
.qn-bpfp.sm { width: 34px; height: 34px; font-size: 17px; }
.qn-bpfp.big { width: 48px; height: 48px; font-size: 24px; }
.qn-btxt { min-width: 0; display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; }
.qn-bname { font-size: 13.5px; font-weight: 660; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qn-bby { font-size: 11px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qn-bfol { position: absolute; z-index: 1; top: 10px; right: 10px; font-size: 10px; font-weight: 680; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 15%, transparent); border-radius: 999px; padding: 3px 7px; }

/* Top performers — a LIST, taller tonal rows, and the only place a Stack's
   number appears on the home. Losers stay in it, at the bottom, in red. */
.qn-tplist { display: flex; flex-direction: column; gap: 8px; }
.qn-tprow { display: flex; align-items: center; gap: 10px; background: var(--bg-elev-1); border: none; border-radius: 16px; padding: 13px; color: inherit; font: inherit; text-align: left; cursor: pointer; transition: background .14s; }
.qn-tprow:hover { background: var(--bg-elev-2); }
.qn-tprow.on { background: linear-gradient(170deg, color-mix(in srgb, var(--sm-up) 9%, transparent), transparent 62%), var(--bg-elev-1); }
.qn-bspark { flex: none; width: 76px; height: 30px; }
.qn-bspark-l { fill: none; stroke-width: 1.7; vector-effect: non-scaling-stroke; }
.qn-bspark-l.up { stroke: var(--sm-up); } .qn-bspark-l.down { stroke: var(--sm-down); }
.qn-bpaused { flex: none; font-size: 10px; font-weight: 680; color: var(--warn, #d2a13a); background: color-mix(in srgb, var(--warn, #d2a13a) 15%, transparent); border-radius: 999px; padding: 3px 7px; }
.qn-tpret { flex: none; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; min-width: 58px; text-align: right; }
.qn-tpret.up { color: var(--sm-up); } .qn-tpret.down { color: var(--sm-down); }

/* The positions button — a tonal circle at the chart's top-right, and now the
   hero's only control. The search that shared this row moved to the Search tab
   (owner 2026-08-01), taking its expand/collapse choreography with it: the
   wrapper no longer has to span the hero to animate a width, but it costs
   nothing to leave it spanning and it keeps the button's offset in one place. */
/* A right-anchored COLUMN since 2026-08-04: positions circle on top, the
   Auto pill under it. */
.qn-heroacts { position: absolute; top: 0; right: 2px; z-index: 3; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; pointer-events: none; }
.qn-heroacts > * { pointer-events: auto; }
/* Its own name: .qn-autobtn was already the legacy auto-pilot bar's class
   further down, whose bordered 13px rule overrode this pill's geometry —
   the twins looked different for exactly that reason (owner asked twice). */
.qn-heroauto { letter-spacing: 0.01em; }
.qn-heroauto.on { background: var(--sm-up); color: var(--sm-on-up); }
.qn-heroauto.on:hover { background: var(--sm-up); }
/* The positions pill and the Auto pill are TWINS (owner 2026-08-04: "make
   these 2 icons same size and style") — one explicit geometry, centered
   content, identical width; the lit state may only change color. */
.qn-posbtn { display: flex; align-items: center; justify-content: center; gap: 5px; height: 32px; min-width: 68px; padding: 0 12px; border: none; border-radius: 999px; background: var(--bg-elev-1); color: var(--text-dim); font-size: 12px; font-weight: 680; cursor: pointer; transition: background .14s; }
.qn-posbtn:hover { background: var(--bg-elev-2); }
.qn-posbtn svg { width: 16px; height: 16px; }
.qn-posbtn-n { font-size: 12px; font-weight: 680; font-variant-numeric: tabular-nums; }
/* Wide: the positions column is already on screen, so its button is redundant.
   Declared AFTER the base rule on purpose: same specificity, source order wins. */
/* Wide panes show positions inline in the side column, so the circle
   folds away — but the Auto pill is the ONLY autopilot control and stays
   (owner 2026-08-04: "where's auto button in desktop"). */
@container qnwrap (min-width: 860px) { .qn-posbtn:not(.qn-heroauto) { display: none; } }

/* ── The detail's three tabs (owner 2026-08-01) ──────────────────────────── */
/* Same pill row as the positions panel and Your portfolios — a third tab style
   on the same screen would be three ways to say "pick one". */
.qn-bstabs { padding: 14px 0 12px; }
/* Overview stats wrap to two rows at four items rather than shrinking to fit. */
.qn-bstat-v.up { color: var(--sm-up); } .qn-bstat-v.down { color: var(--sm-down); }
.qn-bstat-v.none { color: var(--text-faint); }
/* BREAKDOWN — one hue, ramped by weight. Distinct colours would imply the picks
   belong to categories; they don't, they differ only in size. */
/* The %/$ switch and the two cuts, sized like controls rather than headings. */
.qn-bd-unit { display: flex; gap: 4px; background: var(--bg-elev-1); border-radius: 999px; padding: 3px; }
.qn-bd-ub { appearance: none; border: none; background: none; color: var(--text-faint); font: inherit; font-size: 12px; font-weight: 680; min-width: 28px; padding: 4px 8px; border-radius: 999px; cursor: pointer; }
.qn-bd-ub.on { background: var(--sm-chip-on); color: var(--text); }
.qn-bdtabs { padding-bottom: 10px; }
.qn-bd-bar { display: flex; gap: 3px; height: 34px; margin: 2px 0 12px; }
.qn-bd-seg { min-width: 3px; border-radius: 5px; background: color-mix(in srgb, var(--sm-up) 82%, var(--text)); }
.qn-bd-list { display: flex; flex-direction: column; }
.qn-bd-row { display: flex; align-items: center; gap: 10px; padding: 9px 2px; }
.qn-bd-dot { flex: none; width: 11px; height: 11px; border-radius: 3px; background: color-mix(in srgb, var(--sm-up) 82%, var(--text)); }
.qn-bd-pct { flex: none; font-size: 13px; font-weight: 660; font-variant-numeric: tabular-nums; }

/* ── The copy flow: amount → review → slide (owner 2026-08-01) ───────────── */
/* Pushed screens inside the same sheet, so a back arrow rather than a second
   scrim: stacking modals over modals is how people lose the thread of what
   they were doing. */
.qn-cf-head { display: flex; align-items: center; gap: 8px; padding: 6px 0 14px; }
.qn-cf-back { appearance: none; border: none; background: none; color: var(--text-dim); width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; cursor: pointer; flex: none; }
.qn-cf-back:hover { background: var(--bg-elev-2); color: var(--text); }
.qn-cf-back svg { width: 22px; height: 22px; }
.qn-cf-title { font-size: 15px; font-weight: 680; letter-spacing: -0.01em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qn-cf-amt { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 0 4px; }
/* FULL SCREEN once you're past the detail (owner 2026-08-01: "the screen
   should be full height / screen"). The amount stops being a sheet laid over a
   list and becomes the page you are on — figure in the middle of it, bar at the
   bottom. A half-height sheet asking for a number leaves the thing you were
   reading visible above it, which invites you to keep reading instead of
   answering. The back arrow is the way out, so nothing is lost by filling. */
/* The copy steps fill the screen they are on, and the figure sits in the
   MIDDLE of it (owner 2026-08-01: "the number input should be around center
   vertically"). The body has to grow for that to mean anything — without it the
   whole screen collects at the top and the CTA's `margin-top: auto` has no free
   space to push against, which is only visible on a short step like this one. */
.qn-screen > .qn-bsheet { flex: 1 1 auto; }
.qn-screen .qn-cf-amt { flex: 1 1 auto; justify-content: center; }
.qn-cf-sub { font-size: 13px; color: var(--text-dim); }
.qn-cf-sub2 { font-size: 11.5px; color: var(--text-faint); }
/* The figure IS the screen. An input, not a display + keypad: the OS keyboard
   is better than any numpad we would draw, and on desktop there is a real one. */
.qn-cf-big { position: relative; display: flex; align-items: baseline; justify-content: center; gap: 2px; padding: 26px 0 8px; max-width: 100%; }
.qn-cf-cur { font-size: 34px; font-weight: 700; letter-spacing: -0.03em; color: var(--text-dim); }
/* Width is set from the SIZER, per keystroke: a fixed box left-aligns "$0" a
   third of the way across the screen, because the row centres the box and the
   text sits at one end of it (owner 2026-08-01: "numbers to enter should be
   centered"). Measuring beats arithmetic here — `ch` is the advance of "0",
   which the letter-spacing and the "." both break. */
.qn-cf-in { appearance: none; background: none; border: none; outline: none; font: inherit; font-size: 46px; font-weight: 700; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; color: var(--text); width: 1ch; max-width: 100%; text-align: left; padding: 0; }
.qn-cf-sizer { position: absolute; left: 0; top: 0; visibility: hidden; pointer-events: none; white-space: pre; font-size: 46px; font-weight: 700; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; }
.qn-cf-in::placeholder { color: var(--text-faint); }
/* What the Stack will really deploy, under the number that decides it. */
.qn-cf-use { font-size: 11.5px; color: var(--text-dim); text-align: center; min-height: 16px; padding-top: 2px; }
.qn-cf-err { font-size: 11.5px; color: var(--sm-down); min-height: 15px; text-align: center; }
/* The shortcuts are a separate decision from the number, and sat close
   enough to read as its underline. */
.qn-cf-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; padding-top: 28px; }
.qn-cf-chip { appearance: none; border: none; border-radius: 999px; background: var(--bg-elev-2); color: var(--text); font: inherit; font-size: 12.5px; font-weight: 620; padding: 9px 16px; cursor: pointer; transition: background .14s; }
.qn-cf-chip:hover { background: var(--bg-elev-3); }
.qn-cf-note.left { text-align: left; padding: 4px 0 0; }
.qn-cf-note.bad { color: var(--sm-down); }
.qn-cf-note { font-size: 10.5px; line-height: 1.5; color: var(--text-faint); text-align: center; padding: 2px 6px 0; }
/* The review card: label/value rows, one prose paragraph, no invented holdings. */
.qn-cf-card { display: flex; flex-direction: column; gap: 9px; background: var(--bg-elev-1); border-radius: 16px; padding: 14px; }
/* The exits control sits under the figure it belongs beside. A FIXED width in
   both states — the card used to shrink-wrap around the two pills and then
   jump to full width when "Set my own" added its rows (owner 2026-08-02:
   "keep the width of the trade setting area the same"). AFTER .qn-cf-card:
   same specificity, so order decides the padding/gap. */
.qn-cf-exits { margin: 18px auto 0; width: 100%; max-width: 420px; text-align: left; padding: 16px 18px; gap: 12px; }
.qn-cf-exits .sm-exrow { min-height: 44px; }
.qn-cf-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.qn-cf-k { font-size: 12.5px; color: var(--text-dim); }
.qn-cf-v { font-size: 13px; font-weight: 660; font-variant-numeric: tabular-nums; text-align: right; }
.qn-cf-v.money { color: var(--sm-up); }
.qn-cf-v.risk { color: var(--sm-down); }
.qn-cf-body { font-size: 12px; line-height: 1.55; color: var(--text-faint); border-top: 1px solid var(--border); padding-top: 10px; }
.qn-cf-picks { display: flex; flex-direction: column; gap: 2px; }
.qn-cf-pick { display: flex; align-items: center; gap: 10px; padding: 8px 2px; }
.qn-cf-terms { flex: none; font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
/* SLIDE TO CONFIRM. A button underneath, so the keyboard and a screen reader
   get the same action the gesture does. */
.qn-slide { position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 52px; padding: 0; border: none; border-radius: 999px; background: var(--bg-elev-2); color: var(--text); font: inherit; cursor: pointer; overflow: hidden; }
/* A PILL, not a bar (owner 2026-08-01: "should not have a square progress,
   needs to be rounded"). Inset to the knob's own 4px so the fill reads as the
   trail the knob is dragging behind it, with a leading edge the same shape as
   the knob's — a square edge under a round knob is the thing that looks unfinished. */
.qn-slide-fill { position: absolute; left: 4px; top: 4px; bottom: 4px; width: 44px; border-radius: 999px; background: color-mix(in srgb, var(--sm-up) 26%, transparent); pointer-events: none; }
.qn-slide-t { position: relative; font-size: 13.5px; font-weight: 680; pointer-events: none; padding: 0 56px; text-align: center; }
.qn-slide-knob { position: absolute; left: 4px; top: 4px; width: 44px; height: 44px; border-radius: 50%; background: var(--sm-up); color: var(--sm-on-up); display: grid; place-items: center; touch-action: none; cursor: grab; }
.qn-slide-knob:active { cursor: grabbing; }
.qn-slide-knob svg { width: 22px; height: 22px; pointer-events: none; }
.qn-slide.snap .qn-slide-knob, .qn-slide.snap .qn-slide-fill { transition: transform .18s ease, width .18s ease; }
.qn-slide.done { background: color-mix(in srgb, var(--sm-up) 22%, transparent); }
.qn-slide:disabled { cursor: default; }
@media (prefers-reduced-motion: reduce) { .qn-slide.snap .qn-slide-knob, .qn-slide.snap .qn-slide-fill { transition: none; } }

/* ── The Stack detail sheet (§5) ─────────────────────────────────────────── */
.qn-bsheet { display: flex; flex-direction: column; padding: 6px 16px 4px; }
/* One height for three tabs: the tallest is measured on open and held here, so
   switching tabs never resizes the modal under your thumb. Still a flex column
   — its children used to be the sheet's own. */
.qn-btabbody { display: flex; flex-direction: column; }
/* M3 assist chips. They rendered as three bare lines of body text: the class
   had no rule at all after the "waiting" block was deleted (owner 2026-08-01:
   "the text here doesn't fit the rest of material 3 design principles"). */
.qn-wchips { display: flex; flex-wrap: wrap; gap: 8px; padding: 2px 0 2px; }
.qn-wchip { border-radius: 999px; background: var(--bg-elev-1); color: var(--text-dim); font-size: 12.5px; font-weight: 560; line-height: 1.35; padding: 7px 13px; }
.qn-bsh-head { display: flex; align-items: center; gap: 11px; padding: 8px 0 12px; }
.qn-bsh-name { font-size: 18px; font-weight: 700; letter-spacing: -0.02em; }
/* THE BIGGEST THING ON THE SCREEN (owner 2026-08-01: "show performance chart
   (big real estate)"). It can be, now that there is a real series behind it —
   the durable track record — instead of an empty array. */
.qn-bsh-chart { width: 100%; height: clamp(140px, calc(var(--avail, 100vh) * 0.42), 208px); display: block; margin: 2px 0 6px; }
/* The headline, ABOVE the chart: the number is the answer, the chart is the
   working. Left-aligned under the name like the reference, not centred. */
.qn-bsh-ret { font-size: 26px; font-weight: 700; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; padding-top: 4px; }
/* The one-line description, between the name and the number. */
.qn-bsh-desc { font-size: 12.5px; color: var(--text-dim); padding-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qn-bsh-ret.up { color: var(--sm-up); } .qn-bsh-ret.down { color: var(--sm-down); }
/* The caveat under a replay is not a footnote — it is the difference between
   the chart above it being evidence and being a drawing. */
.qn-bsh-h { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); padding: 18px 0 8px; }
/* Four stats since the Overview gained an all-time return (owner 2026-08-01):
   auto-fit wraps to 2×2 on a phone rather than squeezing four into one row. */
.qn-bstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); gap: 8px; margin-top: 6px; }
.qn-bstat { background: var(--bg-elev-1); border-radius: 16px; padding: 11px 10px; text-align: center; }
.qn-bstat-v { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.qn-bstat-l { font-size: 10.5px; color: var(--text-faint); margin-top: 2px; }
.qn-bmine { background: color-mix(in srgb, var(--sm-up) 10%, transparent); border-radius: 14px; padding: 10px 12px; margin-top: 10px; }
.qn-bmine-t { font-size: 12px; font-weight: 560; color: var(--text-dim); }
/* Two-line M3 list items, no dividers — whitespace does the separation. */
/* A DAY AT A TIME: a rail down the left, a dot per day, the sentence, then the
   trades. The rail is what makes it read as a history rather than a table. */
.qn-days { display: flex; flex-direction: column; gap: 2px; }
.qn-day { position: relative; padding: 6px 0 10px 20px; }
.qn-day::before { content: ""; position: absolute; left: 4px; top: 14px; bottom: -6px; width: 1px; background: var(--border); }
.qn-day:last-child::before { display: none; }
.qn-day-h { display: flex; align-items: center; gap: 8px; }
.qn-day-dot { position: absolute; left: 0; top: 7px; width: 9px; height: 9px; border-radius: 50%; box-shadow: 0 0 0 3px var(--bg-elev); }
.qn-day-dot.up { background: var(--sm-up); } .qn-day-dot.down { background: var(--sm-down); }
.qn-day-d { font-size: 12.5px; font-weight: 680; font-variant-numeric: tabular-nums; }
.qn-day-d.up { color: var(--sm-up); } .qn-day-d.down { color: var(--sm-down); }
.qn-day-s { font-size: 13.5px; color: var(--text); padding: 3px 0 6px; }
.qn-day-r { margin-left: auto; font-size: 12.5px; font-weight: 660; font-variant-numeric: tabular-nums; }
.qn-day-r.up { color: var(--sm-up); } .qn-day-r.down { color: var(--sm-down); }
.qn-day-dot.flat { background: var(--text-faint); }
/* The coins, overlapped like a stack of chips — the reference's shape, and it
   keeps a six-coin day to one line. */
.qn-day-coins { display: flex; align-items: center; }
.qn-day-coins > .qn-cdisc { margin-right: -8px; box-shadow: 0 0 0 2px var(--bg-elev); }
.qn-day-coins > .qn-cdisc:last-of-type { margin-right: 0; }
.qn-day-more { font-size: 11.5px; color: var(--text-faint); margin-left: 14px; }
.qn-btrades { display: flex; flex-direction: column; gap: 2px; }
.qn-btrade { display: flex; align-items: center; gap: 11px; padding: 7px 0; }
.qn-cdisc { flex: none; width: 28px; height: 28px; border-radius: 50%; background: var(--bg-elev-2); color: var(--text-dim); display: grid; place-items: center; font-size: 9.5px; font-weight: 700; letter-spacing: 0.02em; }
.qn-btrade-c { min-width: 0; flex: 1 1 auto; }
.qn-btrade-n { font-size: 13.5px; font-weight: 600; }
.qn-btrade-s { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; margin-top: 1px; }
.qn-btrade-r { text-align: right; flex: none; }
.qn-btrade-z { font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.qn-btrade-z.up { color: var(--sm-up); } .qn-btrade-z.down { color: var(--sm-down); }
.qn-btrade-t { font-size: 11px; color: var(--text-faint); margin-top: 1px; }
.qn-balloc { display: flex; flex-direction: column; gap: 9px; }
.qn-ballocr { display: flex; align-items: center; gap: 10px; }
.qn-balloc-s { flex: none; width: 52px; font-size: 12px; font-weight: 620; }
.qn-balloc-bar { flex: 1 1 auto; height: 7px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; }
.qn-balloc-fill { height: 100%; border-radius: 999px; background: color-mix(in srgb, var(--sm-up) 66%, transparent); }
.qn-balloc-bar.cash .qn-balloc-fill { background: color-mix(in srgb, var(--text) 26%, transparent); }
.qn-balloc-p { flex: none; width: 38px; text-align: right; font-size: 12px; font-variant-numeric: tabular-nums; color: var(--text-dim); }
/* THE STACK AS A SCREEN (owner 2026-08-01: "should be a screen can go back
   with, not a modal"). It takes the Quant pane's place rather than covering it,
   so the composer stays where it is and the session header carries the name and
   the arrow back. min-height comes from JS — the scroller's height, so the
   anchored bar has something to sit against on a short Stack. */
.qn-screen { display: flex; flex-direction: column; }
/* The transcript already insets its content; the sheet's own 16px would double
   it. Bottom padding goes too — the bar carries its own. */
.qn-screen > .qn-bsheet { padding: 0; }
/* The composer's fade would otherwise land on the anchored button and read as a
   cut-off control. Nothing needs dissolving under an opaque bar. */
body.qn-screen-open .composer-wrap::before { opacity: 0; }
/* The transcript's 44px bottom padding exists to clear that fade. Under an
   anchored bar it becomes a strip of content scrolling past BELOW the button —
   sticky can't leave its containing block, so the bar stops at the content box.
   No fade, no padding. */
body.qn-screen-open .transcript { padding-bottom: 0; }

/* THE ACTION BAR IS ANCHORED (owner 2026-08-01, two reference shots: one
   full-width "Copy" before you copy, a pair after). The decision this screen
   asks for must not depend on how far you happened to scroll — a 208px chart,
   three tabs and a trade list push a button clean off a phone, and a sheet
   whose only action is somewhere below the fold reads as something to browse
   rather than something to act on. Sticky, not fixed: the SHEET is the
   scroller, and a fixed bar would escape it to hang over the page behind. */
/* `margin-top: auto` is what holds it down on a screen whose content doesn't
   fill it — sticky only ever pulls an element UP into view, so on the review,
   which is shorter than the display, bottom:0 alone left the bar floating in
   the middle of the page. It collapses to 0 the moment the content does fill,
   which is why the bar carries its own top padding. */
.qn-bcta { position: sticky; bottom: 0; z-index: 2; display: flex; flex-direction: column; gap: 8px; margin-top: auto; padding: 10px 0 max(10px, env(safe-area-inset-bottom)); background: var(--bg-elev); }
/* Content has to stop VISIBLY at the bar. Under a hard edge the last row reads
   as the end of the list; the fade says there is more of it up there. */
.qn-bcta::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 16px; background: linear-gradient(to top, var(--bg-elev), transparent); pointer-events: none; }
/* The sheet's own bottom padding would sit UNDER the stuck bar and then appear
   as an 18px jump the moment you hit the true bottom and it unsticks. The bar
   carries the bottom inset itself, safe-area included. */
.sm-sheet.qn-anch { padding-bottom: 0; }
.sm-sheet.qn-anch > .qn-bsheet { padding-bottom: 0; }
/* The follower's allocation: a stepper, so 35% is sayable. */
.qn-allocrow { display: flex; align-items: center; gap: 6px; }
.qn-allocval { min-width: 62px; text-align: center; font-size: 19px; font-weight: 720; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
/* What "the Stack's exits" actually ARE — read off its picks, never described
   in the abstract. */
.qn-exlist { display: flex; flex-direction: column; gap: 3px; padding: 2px 0 2px; }
.qn-exline { font-size: 11.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.qn-exline.dim { color: var(--text-faint); }
.qn-exrow { display: flex; align-items: center; gap: 10px; }
.qn-exlab { flex: 1 1 auto; font-size: 12.5px; color: var(--text-dim); }
.qn-cs-ein { width: 88px; appearance: none; border: none; border-radius: 10px; background: var(--bg-elev-2); color: var(--text); font: inherit; font-size: 12.5px; padding: 7px 10px; text-align: right; font-variant-numeric: tabular-nums; }
.qn-cs-ein:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 1px; }
/* Two ways forward, sharing the width: leaving is not a footnote to staying. */
.qn-bcta-row { display: flex; align-items: center; gap: 8px; }
/* `.ghost` is declared further down with the same specificity and would win on
   order, so it is named here too rather than left to a coin toss. */
.qn-bcta-row > .qn-bgo, .qn-bcta-row > .qn-bgo.ghost { flex: 1 1 0; min-width: 0; padding: 13px 14px; font-size: 14px; font-weight: 680; }
/* The hard exit is an OPTION, not the sheet's call to action — and it belongs
   beside the positions it closes, not in the bar. Right-aligned and quiet. */
.qn-close-all { align-self: flex-end; margin-top: 10px; }
/* The creator's edit: a header icon opposite the Stack's face, quiet enough
   that a reader who can't use it doesn't read it as the screen's action. */
.qn-bsh-edit { flex: none; margin-left: auto; width: 38px; height: 38px; border: none; border-radius: 50%; background: var(--bg-elev-1); color: var(--text-dim); display: grid; place-items: center; cursor: pointer; }
.qn-bsh-edit:hover { background: var(--bg-elev-2); color: var(--text); }
.qn-bsh-edit svg { width: 19px; height: 19px; }
/* How far back the Overview's numbers go — the record's own span, next to the
   heading that reports it. */
.qn-bsh-since { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.qn-bsh-hrow { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.qn-bfol-on { font-size: 12.5px; font-weight: 660; color: var(--sm-up); }
.qn-brules { display: flex; gap: 6px; }
.qn-brules-l { font-size: 11.5px; color: var(--text-faint); padding-top: 6px; }
.qn-bgo { appearance: none; border: none; border-radius: 999px; padding: 13px 18px; font: inherit; font-size: 14px; font-weight: 680; background: var(--sm-up); color: var(--sm-on-up); cursor: pointer; }
/* A CTA that can't fire has to look like it. The amount screen opens at $0 with
   Review dead, and a full-strength green pill that does nothing on tap is worse
   than no button. */
.qn-bgo:disabled { opacity: 0.42; cursor: default; }
.qn-bgo.ghost { flex: none; background: var(--bg-elev-2); color: var(--text); padding: 10px 15px; font-size: 12.5px; font-weight: 620; }
/* The hand-off dial (owner 2026-08-03): Autopilot toggles — dim until lit,
   green when Genie has the whole lifecycle; Advanced opens the manual hub. */
.qn-mode-row { margin-top: 12px; }
.qn-mode-auto { background: var(--bg-elev-2); color: var(--text); }
.qn-mode-auto.on { background: var(--sm-up); color: var(--sm-on-up); }
.qn-mode-adv.on { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text) 32%, transparent); }
/* The Advanced sheet body, on the M3 8dp grid: 16px side padding, 16px
   between groups, and no duplicate headline — the sheet title already names
   the place, so the section's own "Your portfolios" header hides here. The
   one action row keeps the 48dp touch minimum. */
.qn-mode-body { display: flex; flex-direction: column; gap: 16px; padding: 0 16px 16px; }
.qn-mode-body .qn-sechead { display: none; }
.qn-mode-body .qn-pbtn { min-height: 48px; }
/* Bottom padding clears the bar's fade: at full scroll the bar unsticks and
   the gradient would otherwise wash the last line of the one paragraph on the
   screen that has to be read. */
.qn-bfoot { font-size: 11px; line-height: 1.45; color: var(--text-faint); padding: 12px 0 17px; }

/* ── The Stack screen (§8c) ─────────────────────────────────────────────────
   A real screen, so it gets a screen's rhythm: an identity row you type into,
   a card per strategy carrying its own record and its weight, and one way to
   add more. Material 3 minimal — tonal surfaces, no borders, big tabular
   numbers doing the talking. */
.st-wrap { display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 620px; margin-inline: auto; padding: 4px 2px 28px; }
.st-id { display: flex; align-items: center; gap: 12px; padding: 6px 0 10px; }
.st-face { flex: none; width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; font-size: 26px; }
.st-name { flex: 1 1 auto; min-width: 0; appearance: none; border: none; background: var(--bg-elev-1); color: var(--text); border-radius: 16px; padding: 14px 16px; font: inherit; font-size: 17px; font-weight: 660; letter-spacing: -0.01em; }
.st-name::placeholder { color: var(--text-faint); font-weight: 500; }
.st-name:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 1px; }
.st-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 6px 2px 2px; }
.st-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); }
.st-cash { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.st-list { display: flex; flex-direction: column; gap: 10px; }
.st-empty { font-size: 12.5px; color: var(--text-faint); padding: 14px 2px; }

/* One composed pick: which strategy, and how much of the Stack. Nothing else —
   the price, the win rate and the trade breakdown live on the strategy's own
   screen, one tap away through this card. */
.st-card { background: var(--bg-elev-1); border-radius: 20px; padding: 4px 14px 12px; }
.st-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; width: 100%; padding: 12px 0 10px; background: none; border: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.st-card-top:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 2px; border-radius: 12px; }
.st-card-id { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.st-card-nm { min-width: 0; }
.st-sym { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.st-strat { font-size: 12.5px; color: var(--text-dim); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The weight — the one decision that belongs to the Stack rather than the
   strategy, so it gets its own line under a hairline. */
.st-wrow { display: flex; align-items: center; gap: 10px; padding-top: 11px; border-top: 1px solid color-mix(in srgb, var(--text) 7%, transparent); }
.st-wlab { flex: 1 1 auto; font-size: 12px; color: var(--text-faint); }
.st-wctl { display: flex; align-items: center; gap: 2px; }
.st-wval { min-width: 50px; text-align: center; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.st-add { display: flex; align-items: center; justify-content: center; gap: 8px; appearance: none; border: none; border-radius: 999px; background: var(--bg-elev-2); color: var(--text); font: inherit; font-size: 13.5px; font-weight: 640; padding: 14px; cursor: pointer; transition: background .14s; }
.st-add:hover { background: var(--bg-elev-3, #333537); }
.st-add svg { width: 15px; height: 15px; }
.st-lab { font-size: 11.5px; color: var(--text-faint); padding: 14px 2px 0; }
.st-pills { display: flex; gap: 6px; padding: 0 2px; }
.st-cta { display: flex; flex-direction: column; gap: 8px; padding-top: 18px; }
.st-cta-row { display: flex; gap: 8px; }
.st-cta-row .qn-bgo { flex: 1 1 auto; }
.st-picker { padding: 8px 16px 20px; }

/* ── The composer (§8c) ─────────────────────────────────────────────────────
   "+" beside the bell. It takes the auto margin so the two controls stay
   grouped at the right instead of the bell alone flying off. */
.qn-newstack { margin-left: auto; }
.qn-newstack + .qn-bellwrap { margin-left: 6px; }
.qn-csheet { display: flex; flex-direction: column; padding: 2px 16px 4px; }
.qn-cs-id { display: flex; align-items: center; gap: 11px; padding: 4px 0 14px; }
.qn-cs-name { flex: 1 1 auto; min-width: 0; appearance: none; border: none; background: var(--bg-elev-1); color: var(--text); border-radius: 14px; padding: 12px 14px; font: inherit; font-size: 15px; font-weight: 620; }
.qn-cs-name::placeholder { color: var(--text-faint); font-weight: 500; }
.qn-cs-name:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 1px; }
/* The picker IS the board — its own strips scroll, so it needs no chrome. */
.qn-cs-picker { display: flex; flex-direction: column; gap: 10px; padding-bottom: 6px; }
.qn-cs-picker .qn-sgroup-h:disabled { cursor: default; opacity: 1; }
/* A picked tile: tinted surface + a filled check. Selection has to survive the
   tile's own "best at" tint, so it wins on both background and outline. */
.qn-stile { position: relative; }
.qn-stile.picked { background: linear-gradient(170deg, color-mix(in srgb, var(--sm-up) 22%, transparent), transparent 70%), var(--bg-elev-2); border-color: color-mix(in srgb, var(--sm-up) 60%, transparent); }
/* The pick control. The tile itself opens the strategy, so this has to look
   like its own target: a 26px disc with real contrast in both states, and a
   36px hit area around it (the tile behind it is the other tap). */
.qn-stile-check { position: absolute; top: 6px; right: 6px; z-index: 1; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--bg-elev-3, #333537); color: var(--text-dim); font-size: 15px; font-weight: 700; line-height: 1; cursor: pointer; transition: background .14s, color .14s, transform .1s; }
.qn-stile-check::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
.qn-stile-check:hover { background: color-mix(in srgb, var(--sm-up) 26%, var(--bg-elev-3, #333537)); color: var(--text); }
.qn-stile-check:active { transform: scale(0.92); }
.qn-stile-check.on { background: var(--sm-up); color: var(--sm-on-up); font-size: 13px; }
.qn-stile-check:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 70%, transparent); outline-offset: 2px; }
/* The eyebrow runs under the control, so it stops short of it — and stays on
   ONE line: a two-line "BIGGEST GAIN · MOST CONSISTENT" pushes that tile's
   numbers down and the row of tiles stops lining up. */
.qn-cs-picker .qn-stile-eb { padding-right: 24px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qn-bgo.danger { color: var(--sm-down); background: color-mix(in srgb, var(--sm-down) 12%, transparent); }
/* The weights list: what each pick is worth, and what's left in cash. */
.qn-cs-note { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; padding: 8px 0 2px; line-height: 1.45; }
.qn-cs-weights { display: flex; flex-direction: column; gap: 8px; }
/* A card, not a list row: it's a tap target that opens the strategy (where the
   exits live), so it has to look like one. Same tonal treatment as a Top
   performers row. */
.qn-cs-wrow { display: flex; align-items: center; gap: 11px; padding: 13px; background: var(--bg-elev-1); border-radius: 16px; cursor: pointer; transition: background .14s; }
.qn-cs-wrow:hover { background: var(--bg-elev-2); }
.qn-cs-wrow:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 2px; }
.qn-wctl { flex: none; display: flex; align-items: center; gap: 2px; }
/* Steppers, not a slider: a weight is a number people mean exactly, and 5%
   steps on a 44px target beat chasing a thumb on a phone. */
.qn-wstep { appearance: none; border: none; width: 32px; height: 32px; border-radius: 50%; background: var(--bg-elev-2); color: var(--text-dim); font: inherit; font-size: 16px; font-weight: 700; line-height: 1; cursor: pointer; transition: background .14s, color .14s; }
.qn-wstep:hover { background: var(--bg-elev-3, #333537); color: var(--text); }
.qn-cs-wrow:hover .qn-wstep { background: var(--bg-elev-3, #333537); }
.qn-wstep:active { transform: scale(0.92); }
.qn-wstep:disabled { opacity: 0.32; cursor: default; }
.qn-wstep:disabled:hover { background: var(--bg-elev-2); color: var(--text-dim); }
.qn-wval { min-width: 46px; text-align: center; font-size: 13px; font-weight: 660; font-variant-numeric: tabular-nums; }

/* Pull to refresh — height is driven from JS, so it collapses to nothing at
   rest and never occupies a row of its own. */
.qn-pull { height: 0; overflow: hidden; display: flex; align-items: center; justify-content: center; gap: 8px; transition: height 0.18s ease; }
.qn-pull-m { width: 34px; height: 34px; flex: 0 0 auto; }
.qn-pull-t { font-size: 10.5px; letter-spacing: 0.03em; color: var(--text-faint); }
.qn-pull.armed .qn-pull-t { color: var(--text-dim); }
/* Pane keeps the wrap's own rhythm so Trending's children space exactly as
   they did when they were direct children of .qn-wrap. Panes are hidden via
   inline display:none (inline beats this rule; clearing it restores flex). */
.qn-pane { display: flex; flex-direction: column; gap: 14px; }
/* Home's children are the (collapsed) pull strip and the layout grid, and a flex
   `gap` applies around a zero-height child just the same — so the pane was paying
   14px for a strip that isn't there. The grid brings its own gaps, so drop this
   one and the dashboard starts right under the tabs (owner 2026-07-29). */
.qn-pane-strategies { gap: 0; }
/* LOAD-BEARING: .sm-root pairs `margin: 0 auto` with `container-type:
   inline-size`. In a flex COLUMN parent, auto cross-axis margins turn off
   stretching, and inline-size containment computes the intrinsic width as if
   the board had no children — the whole pane collapsed to a ~56px sliver
   (owner bug report 2026-07-27: "leaderboard is acting up"). An explicit
   width restores the block-parent geometry the board was designed against. */
.qn-pane > .sm-root { width: 100%; }
.qn-q { font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; font-weight: 700; color: var(--text-faint); margin: 6px 2px -6px; display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
/* The sweep denominator beside the shelf title — quieter than the title,
   no uppercase shouting, right-aligned. */
.qn-q-n { font-weight: 500; text-transform: none; letter-spacing: 0; font-size: 11px; color: var(--text-faint); opacity: 0.85; white-space: nowrap; }
/* Rank inside the Top-strategies ticker line: dim ordinal, fixed slot so
   tickers align down the list ("1  PUMP" over "10 XRP"). */
.qn-ts-rank { color: var(--text-faint); font-weight: 600; min-width: 18px; display: inline-block; }
/* Ticker search — the way into the ~230 perps the treemap can't show.
   M3 filled search field: 28px radius, tonal surface, leading icon. */
.qn-search { position: relative; margin: 2px 2px 0; }
.qn-searchbox { display: flex; align-items: center; gap: 10px; background: color-mix(in srgb, var(--text) 6%, transparent); border: 1px solid transparent; border-radius: 28px; padding: 0 16px; height: 48px; }
.qn-searchbox:focus-within { border-color: var(--accent, #4c8dff); background: color-mix(in srgb, var(--text) 4%, transparent); }
.qn-searchic { width: 20px; height: 20px; flex: 0 0 auto; color: var(--text-faint); }
.qn-searchin { flex: 1 1 auto; min-width: 0; appearance: none; background: none; border: none; outline: none; font: inherit; font-size: 14px; color: var(--text); }
.qn-searchin::placeholder { color: var(--text-faint); }
.qn-searchin::-webkit-search-cancel-button { -webkit-appearance: none; }
/* Results float over the map rather than pushing it down — the map must not
   jump every keystroke. */
.qn-results:not(:empty) { position: absolute; z-index: 30; left: 0; right: 0; top: 52px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: 16px; padding: 6px; box-shadow: 0 12px 32px rgb(0 0 0 / 0.32); max-height: 320px; overflow-y: auto; }

/* The Search tab's own bar: full width, at the top of the tab named after it
   (owner 2026-08-01). It replaced the hero's expanding circle, so the overrides
   that made a 34px pill grow into the balance row are gone with it. */
.qn-sec-find { padding-bottom: 2px; }
.qn-sec-find .qn-search { margin: 0; }
.qn-result { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; appearance: none; background: none; border: none; cursor: pointer; font: inherit; text-align: left; padding: 9px 10px; border-radius: 10px; color: var(--text); }
.qn-result:hover, .qn-result:focus-visible { background: color-mix(in srgb, var(--text) 7%, transparent); outline: none; }
.qn-result-l { display: flex; flex-direction: column; min-width: 0; }
.qn-result-sym { font-size: 14px; font-weight: 600; }
.qn-result-vol { font-size: 10.5px; color: var(--text-faint); margin-top: 1px; }
.qn-result-r { display: flex; flex-direction: column; align-items: flex-end; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.qn-result-px { font-size: 13px; font-weight: 600; }
.qn-result-ch { font-size: 11px; margin-top: 1px; }
.qn-result-ch.up { color: var(--sm-up); }
.qn-result-ch.down { color: var(--sm-down); }
.qn-noresult { font-size: 12px; color: var(--text-dim); padding: 12px 10px; }
/* Hosts the Top-assets LIST now (2026-08-04) — no reserved tall box. */
.qn-map { position: relative; width: 100%; }
.qn-ta-symline { display: flex; align-items: center; gap: 7px; }
.qn-ta-ic { width: 18px; height: 18px; border-radius: 50%; flex: none; }
.qn-tile { position: absolute; appearance: none; border: 1px solid rgba(255,255,255,0.05); cursor: pointer; border-radius: 10px; padding: 3px; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 0; font: inherit; text-align: center; overflow: hidden; transition: filter 0.12s; }
.qn-tile-head { display: flex; align-items: center; gap: 5px; max-width: 100%; }
.qn-tile-ic { width: calc(var(--sym) * 0.85); height: calc(var(--sym) * 0.85); border-radius: 50%; flex: none; }
.qn-tile-px { font-size: var(--stat, 10px); font-weight: 600; opacity: 0.95; line-height: 1.3; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qn-tile-ch { font-size: var(--stat, 10px); font-weight: 700; line-height: 1.3; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qn-tile-mc { font-size: calc(var(--stat, 10px) * 0.86); opacity: 0.62; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qn-tile:hover { filter: brightness(1.18); z-index: 2; }

.qn-tile.up { background: color-mix(in srgb, var(--sm-up) 14%, #10141a); color: var(--sm-up); }
.qn-tile.down { background: color-mix(in srgb, var(--sm-down) 14%, #10141a); color: var(--sm-down); }


.qn-cap { width: 100%; font-size: 11px; color: var(--text-faint); margin-top: 2px; }

/* ---- Paper hero board (owner 2026-07-28: paper first, mockup A). One board:
   P&L hero → curve → Running → Today → New today. M3-tonal, hero leads. ---- */
/* `relative` so the positions button can sit at the chart's top-right without
   taking a row of its own (owner 2026-07-30). */
.qn-hero { position: relative; display: flex; flex-direction: column; gap: 4px; width: 100%; }
.qn-board { display: flex; flex-direction: column; gap: 10px; width: 100%; }
.qn-hero-ebrow { display: flex; align-items: center; gap: 8px; margin-top: 2px; }
/* THE HOME'S ONE HEADING STYLE — an M3 overline, not a title (owner
   2026-08-01: "total balance / your portfolios should be of the same style…
   the home page should be very material 3 style minimal design"). Two selectors
   rather than two rules: the hero and the portfolios block were 11px/700
   uppercase-faint and 15px/700 sentence-case respectively, which read as two
   different screens stacked. It is also the treatment the owner's own reference
   used ("YOUR PORTFOLIOS", "YOUR WATCHLIST"), and the one .qn-run-grp and
   .qn-pgt already speak inside the cards. */
.qn-hero-eb, .qn-sech-eb { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); }
.qn-chip-paper { font-size: 9.5px; font-weight: 800; letter-spacing: 0.06em; color: var(--sm-warn, #d2a13a); background: color-mix(in srgb, var(--sm-warn, #d2a13a) 16%, transparent); border-radius: 6px; padding: 2px 7px; }
.qn-hero-bigrow { display: flex; align-items: baseline; gap: 8px; }
.qn-hero-big { font-size: 42px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.12; font-variant-numeric: tabular-nums; color: var(--text); }
.qn-hero-big.up { color: var(--sm-up); } .qn-hero-big.down { color: var(--sm-down); }
/* Free-to-trade paper, beside the equity total it's part of. Dim and secondary:
   it's the number you check before arming, not the headline. */
.qn-hero-free { font-size: 13px; font-weight: 650; color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The loop's terms, shown only when they aren't the defaults — an icon can say ON
   but not "$100 a play until Thursday 02:14". Taps through to the sheet. */
.qn-hero-auto { display: block; appearance: none; border: none; background: none; padding: 2px 0 0; margin: 0; text-align: left; font: inherit; font-size: 11.5px; font-weight: 560; color: var(--text-faint); cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.qn-hero-auto:hover { color: var(--text-dim); }
.qn-hero-sub { font-size: 13px; font-weight: 600; color: var(--text-dim); font-variant-numeric: tabular-nums; }
/* Now a button (tap swaps money ⇄ percent) — strip the UA chrome so it still
   reads as the line of text it was, and keep a hit area a thumb can find. */
.qn-hero-pnl { display: block; appearance: none; border: none; background: none; padding: 2px 0; margin-top: 0; text-align: left; font: inherit; font-size: 13.5px; font-weight: 600; color: var(--text-dim); font-variant-numeric: tabular-nums; cursor: pointer; }
.qn-hero-pnl:active { opacity: 0.7; }
.qn-hero-pnl.up { color: var(--sm-up); } .qn-hero-pnl.down { color: var(--sm-down); }
/* The curve is the point of the dashboard, so it takes roughly half the band
   between the tabs and the composer instead of a 92px sliver (owner 2026-07-29).
   svh, not vh: a mobile URL bar that hides and shows must not resize the chart
   mid-scroll. The clamp keeps it readable on a short laptop window and stops it
   swallowing a tall desktop pane. Stretching is safe — the SVG is drawn with
   preserveAspectRatio: none and the line uses non-scaling-stroke below, so a
   taller box shows more detail rather than a fatter line. */
/* THE TRADE LOCK POSTER (owner 2026-08-06: "make the sales in your face"):
   full-pane, giant type, the \$10,000 as the centrepiece, the chart drawing
   itself across the top, brand glows behind. */
.qn-lock-host { min-height: 74vh; display: flex; align-items: stretch; justify-content: center; padding: 10px; }
.qn-lockcard.sales {
  position: relative; overflow: hidden; width: 100%; max-width: 560px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
  gap: 12px; padding: 34px 24px 30px; border-radius: 24px;
  background: var(--sm-card); border: 1px solid var(--border); box-shadow: 0 16px 50px rgba(0, 0, 0, 0.4);
}
.qn-lockcard.sales::before, .qn-lockcard.sales::after {
  content: ""; position: absolute; width: 340px; height: 340px; border-radius: 50%; pointer-events: none;
}
/* Tonal glows, ONE hue family (M3, owner 2026-08-06): the app's primary
   green at two low alphas — no second saturated hue on the poster. */
.qn-lockcard.sales::before { top: -160px; right: -120px; background: radial-gradient(circle, color-mix(in srgb, var(--sm-up) 16%, transparent), transparent 65%); }
.qn-lockcard.sales::after { bottom: -170px; left: -130px; background: radial-gradient(circle, color-mix(in srgb, var(--sm-up) 10%, transparent), transparent 65%); }
.qn-lockcard.sales > * { position: relative; }
.qn-lock-anim { width: 100%; max-width: 400px; height: auto; display: block; margin-bottom: 4px; color: var(--text); }
/* One 4.2s master loop: candles pop → lines draw + comet rides → chips
   float → peak flare → everything breathes out and the loop restarts. */
.qn-anim-candle { transform-box: fill-box; transform-origin: 50% 100%; animation: qn-candle 4.2s ease-in-out infinite; }
@keyframes qn-candle { 0% { transform: scaleY(0); opacity: 0; } 8% { transform: scaleY(1); opacity: 1; } 88% { transform: scaleY(1); opacity: 1; } 100% { transform: scaleY(1); opacity: 0; } }
.qn-anim-ghost { stroke-dasharray: 420; stroke-dashoffset: 420; animation: qn-ghost 4.2s ease-in-out infinite; }
@keyframes qn-ghost { 0%, 6% { stroke-dashoffset: 420; } 46% { stroke-dashoffset: 0; } 88% { stroke-dashoffset: 0; opacity: 0.14; } 100% { stroke-dashoffset: 0; opacity: 0; } }
.qn-lock-line-path { stroke-dasharray: 460; stroke-dashoffset: 460; animation: qn-lock-draw 4.2s ease-in-out infinite; filter: drop-shadow(0 0 6px color-mix(in srgb, var(--sm-up) 55%, transparent)); }
@keyframes qn-lock-draw { 0%, 12% { stroke-dashoffset: 460; opacity: 1; } 58% { stroke-dashoffset: 0; opacity: 1; } 88% { stroke-dashoffset: 0; opacity: 1; } 100% { stroke-dashoffset: 0; opacity: 0; } }
.qn-anim-area { opacity: 0; animation: qn-area 4.2s ease-in-out infinite; }
@keyframes qn-area { 0%, 20% { opacity: 0; } 58% { opacity: 1; } 88% { opacity: 1; } 100% { opacity: 0; } }
.qn-anim-comet { opacity: 0; animation: qn-comet 4.2s linear infinite; filter: drop-shadow(0 0 8px #fff); }
@keyframes qn-comet { 0%, 12% { opacity: 0; } 14%, 56% { opacity: 1; } 60%, 100% { opacity: 0; } }
.qn-lock-animdot { opacity: 0; animation: qn-lock-dot 4.2s ease-in-out infinite; filter: drop-shadow(0 0 10px var(--sm-up)); }
@keyframes qn-lock-dot { 0%, 55% { opacity: 0; } 60% { opacity: 1; } 66% { opacity: 0.5; } 72%, 88% { opacity: 1; } 100% { opacity: 0; } }
.qn-anim-chip { opacity: 0; transform-box: fill-box; animation: qn-chip 4.2s ease-out infinite; }
.qn-anim-chip2 { animation-delay: 0.5s; }
@keyframes qn-chip { 0%, 42% { opacity: 0; transform: translateY(10px); } 52% { opacity: 1; transform: translateY(0); } 82% { opacity: 1; transform: translateY(-4px); } 92%, 100% { opacity: 0; transform: translateY(-10px); } } }
.qn-lockcard.sales .qn-lock-t { font-size: clamp(24px, 6.4vw, 32px); font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; }
.qn-lock-big {
  font-size: clamp(52px, 15vw, 76px); font-weight: 800; letter-spacing: -0.03em; line-height: 1;
  /* M3: the display figure stays in the primary's tonal range — a subtle
     green→mint ramp, not a three-hue neon (owner 2026-08-06). */
  background: linear-gradient(100deg, var(--sm-up), #9fe8bd); -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
}
.qn-lockcard.sales .qn-lock-s { font-size: clamp(14px, 4vw, 17px); font-weight: 700; color: var(--text); }
.qn-lock-line { font-size: 13px; color: var(--text-dim); margin-top: 4px; }
.qn-lock-alt { font-size: 11.5px; color: var(--text-faint); }
.qn-lockcard.sales .qn-lock-cta {
  margin-top: 8px; width: min(300px, 86%); padding: 15px 0; border: none; border-radius: 999px;
  font: inherit; font-size: 16px; font-weight: 800; background: var(--sm-up); color: var(--sm-on-up, #08130a); cursor: pointer;
  animation: qn-cta-breathe 2.4s ease-in-out infinite;
}
@keyframes qn-cta-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.035); } }
.qn-lock-cta { border: none; border-radius: 999px; padding: 12px 30px; font: inherit; font-size: 14px; font-weight: 700; background: var(--sm-up); color: var(--sm-on-up, #08130a); cursor: pointer; }
@media (prefers-reduced-motion: reduce) {
  .qn-lock-line-path, .qn-anim-ghost { animation: none; stroke-dashoffset: 0; }
  .qn-lock-animdot, .qn-anim-area { animation: none; opacity: 1; }
  .qn-anim-candle { animation: none; }
  .qn-anim-comet, .qn-anim-chip { display: none; }
  .qn-lockcard.sales .qn-lock-cta { animation: none; }
}
}
.qn-lock-t { font-size: 15px; font-weight: 700; }
.qn-lock-s { font-size: 13px; line-height: 1.5; color: var(--text-dim); }
.qn-lock-cta { margin-top: 4px; border: none; border-radius: 999px; padding: 10px 22px; font: inherit; font-size: 13.5px; font-weight: 700; background: var(--sm-up); color: var(--sm-on-up, #08130a); cursor: pointer; }

.qn-hero-chart { width: 100%; height: clamp(120px, 24vh, 260px); height: clamp(120px, 24svh, 260px); margin-top: 8px; display: block; }
/* FINGER-ON-THE-CHART scrub (owner 2026-08-06). The wrapper takes over the
   chart's top margin so the HTML overlay's coordinates line up with the svg
   exactly (a child margin would collapse out of the positioned box). pan-y
   keeps the page scrollable while a horizontal drag scrubs. */
.qn-scrub { position: relative; margin-top: 8px; touch-action: pan-y; cursor: crosshair; }
.qn-scrub > .qn-hero-chart { margin-top: 0; }
/* The line is a GRADIENT on a real 2px box, not a dashed border on a 0-width
   one: iOS Safari dropped the border version entirely (owner screenshot
   2026-08-06 — pill and dot painted, line didn't). Backgrounds paint
   everywhere the dot already does. */
.qn-scrub-line {
  position: absolute; top: 0; bottom: 0; width: 2px; transform: translateX(-1px);
  background: repeating-linear-gradient(to bottom, color-mix(in srgb, var(--text) 60%, transparent) 0 5px, transparent 5px 9px);
  pointer-events: none;
}
.qn-scrub-dot { position: absolute; width: 9px; height: 9px; border-radius: 50%; transform: translate(-50%, -50%); background: var(--text); border: 2px solid var(--bg); pointer-events: none; }
.qn-scrub-dot.up { background: var(--sm-up); }
.qn-scrub-dot.down { background: var(--sm-down); }
.qn-scrub-tag { position: absolute; top: 4px; font-size: 11px; white-space: nowrap; color: var(--text-dim); background: var(--bg); border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px; pointer-events: none; }
.qn-hero-line { fill: none; stroke-width: 2; vector-effect: non-scaling-stroke; }
.qn-hero-line.up { stroke: var(--sm-up); } .qn-hero-line.down { stroke: var(--sm-down); }
.qn-hero-gtop.up { stop-color: var(--sm-up); stop-opacity: 0.24; }
.qn-hero-gtop.down { stop-color: var(--sm-down); stop-opacity: 0.24; }
.qn-hero-gbot.up { stop-color: var(--sm-up); stop-opacity: 0; }
.qn-hero-gbot.down { stop-color: var(--sm-down); stop-opacity: 0; }
.qn-range { display: flex; gap: 4px; margin-top: 2px; justify-content: center; }
.qn-range-b { appearance: none; border: none; background: none; cursor: pointer; font: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; color: var(--text-faint); border-radius: 999px; padding: 4px 10px; }
.qn-range-b.on { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 14%, transparent); }
/* Claimable paper — the weekly gacha and the bounty board (VIBE_TRADING §5a),
   the one place a little shine is earned.

   NAMED `qn-claim`, NOT `qn-pull`: it used to share the pull-to-refresh strip's
   class, and since this block comes later it was silently dressing that strip in
   a brand border, a gradient and 12+22px of box — a ~36px phantom band above the
   dashboard that read as "too much gap" (owner 2026-07-29). Two unrelated
   components, two names. */
.qn-claim { display: flex; align-items: center; gap: 10px; margin-top: 12px; padding: 11px 12px; border-radius: 14px; cursor: pointer; border: 1px solid color-mix(in srgb, var(--sm-up) 38%, transparent); background: linear-gradient(100deg, color-mix(in srgb, var(--sm-up) 15%, transparent), transparent); }
.qn-claim:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.qn-claim.busy { opacity: 0.6; pointer-events: none; }
.qn-claim-l { min-width: 0; flex: 1; }
.qn-claim-name { font-size: 13.5px; font-weight: 650; }
.qn-claim-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 2px; }
.qn-claim-cta { flex: none; font-size: 12px; font-weight: 700; padding: 6px 12px; border-radius: 999px; background: var(--sm-up); color: var(--sm-on-up); }

/* Auto-pilot — an icon in the dashboard's top-right corner (owner 2026-07-29),
   where a full-width card used to spend ~60px of a phone screen saying what a
   colour says. The toggle carries state in its fill; the small gear beside it
   opens the settings the card's tap used to. */
/* A LABELLED button, not a glyph: under the one-loop rule this is also how you
   close a running loop, and "close this loop" isn't something an icon can say. */
.qn-autobar { display: flex; align-items: stretch; gap: 6px; }
.qn-autobtn { flex: 1 1 auto; min-width: 0; appearance: none; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 11px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--bg-elev-1); color: var(--text-dim); font: inherit; font-size: 13px; font-weight: 620; cursor: pointer; transition: background .14s, color .14s, border-color .14s; }
.qn-autobtn svg { width: 19px; height: 19px; flex: none; }
.qn-autobtn-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qn-autobtn:hover { background: var(--bg-elev-2); color: var(--text); }
.qn-autobtn.running { background: color-mix(in srgb, var(--sm-up) 14%, transparent); border-color: color-mix(in srgb, var(--sm-up) 40%, transparent); color: var(--sm-up); }
.qn-autobtn.idle { background: color-mix(in srgb, var(--warn, #b8860b) 14%, transparent); border-color: color-mix(in srgb, var(--warn, #b8860b) 38%, transparent); color: var(--warn, #b8860b); }
.qn-autobtn.busy { opacity: 0.55; pointer-events: none; }
.qn-autobtn:focus-visible, .qn-autoi-set:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.qn-autoi-set { appearance: none; display: grid; place-items: center; flex: none; width: 42px; border-radius: 999px; border: 1px solid var(--border); background: none; color: var(--text-faint); cursor: pointer; transition: background .14s, color .14s; }
.qn-autoi-set svg { width: 18px; height: 18px; }
.qn-autoi-set:hover { background: var(--bg-elev-1); color: var(--text-dim); }

.qn-auto-sheet { padding: 0 16px; }
.qn-auto-lede { font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
.qn-auto-h { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); margin-top: 18px; }
/* "What's set now" — the concierge card's own four rows, read back from storage
   so the loop's terms outlive the chat bubble that proposed them. */
.qn-auto-now { display: flex; flex-direction: column; margin-top: 8px; border-radius: 12px; background: var(--surface); overflow: hidden; }
.qn-auto-nrow { display: flex; align-items: baseline; gap: 12px; padding: 9px 12px; font-size: 12.5px; }
.qn-auto-nrow + .qn-auto-nrow { border-top: 1px solid color-mix(in srgb, var(--text) 7%, transparent); }
.qn-auto-nlabel { flex: none; color: var(--text-faint); min-width: 84px; }
.qn-auto-nval { flex: 1; min-width: 0; display: flex; align-items: baseline; justify-content: space-between; gap: 8px; color: var(--text); font-weight: 560; text-align: right; }
.qn-auto-nval > span { flex: 1; }
.qn-auto-nclear { flex: none; appearance: none; border: none; background: none; padding: 0; font: inherit; font-size: 11.5px; color: var(--text-faint); text-decoration: underline; cursor: pointer; }
.qn-auto-nclear:hover { color: var(--text-dim); }
/* Your rules — each with the verdict it was admitted on and the loop switch. */
.qn-auto-rulerow { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 12px; background: var(--surface); margin-top: 6px; }
.qn-auto-rule-l { flex: 1; min-width: 0; }
.qn-auto-rule-n { font-size: 13px; font-weight: 620; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qn-auto-rule-v { font-size: 11.5px; margin-top: 2px; color: var(--text-dim); }
.qn-auto-rule-v.ok { color: var(--sm-up); }
.qn-auto-rule-v.bad { color: var(--text-faint); }
.qn-auto-rule-sw { flex: none; appearance: none; font: inherit; font-size: 12px; font-weight: 700; padding: 7px 14px; border-radius: 999px; border: 1px solid var(--border); background: none; color: var(--text-faint); cursor: pointer; transition: background .14s, color .14s, border-color .14s; }
.qn-auto-rule-sw.on { background: color-mix(in srgb, var(--sm-up) 15%, transparent); border-color: color-mix(in srgb, var(--sm-up) 42%, transparent); color: var(--sm-up); }
/* Asked for, not enforced — visibly parked, never dressed as a setting. */
.qn-auto-unenf { margin-top: 8px; border-radius: 12px; border: 1px dashed color-mix(in srgb, var(--warn, #b8860b) 45%, transparent); padding: 8px 12px; }
.qn-auto-unenf-note { font-size: 11.5px; color: var(--warn, #b8860b); padding-bottom: 4px; }
.qn-auto-urow { display: flex; align-items: baseline; gap: 8px; padding: 5px 0; }
.qn-auto-utext { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text-dim); font-style: italic; }
.qn-auto-udrop { flex: none; appearance: none; border: none; background: none; font: inherit; font-size: 12px; color: var(--text-faint); cursor: pointer; padding: 2px 4px; }
.qn-auto-udrop:hover { color: var(--text-dim); }
.qn-auto-opts { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.qn-auto-opt { text-align: left; font: inherit; font-size: 13px; padding: 11px 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; }
.qn-auto-opt.on { border-color: var(--accent); background: var(--accent-dim); font-weight: 650; }
.qn-auto-fixed { display: flex; gap: 6px; margin-top: 8px; }
.qn-auto-amt { flex: 1; min-width: 0; font: inherit; font-size: 13px; padding: 10px 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); color: var(--text); }
.qn-auto-set { flex: none; font: inherit; font-size: 12.5px; padding: 10px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; }
.qn-auto-rest { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.qn-auto-rrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 12px; border-radius: 12px; background: var(--surface); }
.qn-auto-rname { font-size: 12.5px; font-weight: 620; }
.qn-auto-rwhy { flex: 1; min-width: 120px; font-size: 11.5px; color: var(--text-dim); }
.qn-auto-again { flex: none; font: inherit; font-size: 11.5px; padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; }
/* The sheet lists no positions. A "Running now" block lived here briefly and the
   owner cut it (2026-07-30): the open plays are already the Quant home's Running
   section, and this sheet is for the SETUP. Same reason "Lately" went. */
.qn-auto-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 20px; }
.qn-auto-toggle { font: inherit; font-size: 14px; font-weight: 650; padding: 13px; border-radius: 999px; border: none; background: var(--accent); color: var(--on-accent); cursor: pointer; }
.qn-auto-toggle.off { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.qn-auto-closeall { font: inherit; font-size: 12.5px; padding: 11px; border-radius: 999px; border: 1px solid var(--border); background: transparent; color: var(--text-dim); cursor: pointer; }
.qn-hero-sec { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-faint); margin-top: 14px; }
/* Which way the open book leans. Sits between the RUNNING label and the rows,
   dim and lower-case — an exposure fact, not an alarm. */
/* "In the market · 3" / "Waiting for a price · 2". NOT .qn-hero-sec: the tabbed
   layout hides that one (the tab names the panel), and these have to survive
   there — telling the two groups apart is the point. */
/* Title left, liveness stamp right (owner 2026-08-04) — one header row. */
.qn-run-grp { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); margin: 20px 0 8px; font-variant-numeric: tabular-nums; }
/* The composed pick, cut to the decision this screen actually asks for: which
   strategy, and how much of the Stack. Price, win rate and the trade breakdown
   moved to the strategy's own screen (one tap through the card).
   (owner 2026-07-31: "information overflow here — only info vital for user to
   make decision on that moment") */
.st-note { display: block; font-size: 10.5px; line-height: 1.5; color: var(--text-faint); margin-top: 3px; font-variant-numeric: tabular-nums; }
/* Deliberately NOT green. A coloured backtest reads as money made; grey reads as
   the spec it is. */
.st-note.out { color: var(--warn, #f3c969); }
.st-drop { flex: none; align-self: flex-start; width: 28px; height: 28px; display: grid; place-items: center; font-size: 13px; line-height: 1; color: var(--text-faint); background: none; border: none; border-radius: 50%; cursor: pointer; transition: background .14s, color .14s; }
.st-drop:hover { background: var(--bg-elev-2); color: var(--text); }
/* inline-block, not align-self: `.st-card` is a block, so a flex property here
   silently does nothing and the pill stretches the full width. */
.st-own { display: inline-block; font-size: 10.5px; font-weight: 560; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); border-radius: 999px; padding: 3px 9px; margin-top: 9px; font-variant-numeric: tabular-nums; }

/* Following a Stack and holding none of it. Tonal cards, no borders — this is a
   status block, not a list of things to tap, so nothing here reads as a control.
   (owner 2026-07-31: "why there's no open position pending or orders made") */
/* No record is not a result. Neutral, unbold, never green — a Stack that has
   never traded must not look like one that traded to a flat finish. */
.qn-tpret.none { color: var(--text-faint); font-weight: 500; font-size: 11px; }
/* A backtest number wears the word and the muted ink — never the earned green/red. */
.qn-tpret.tested { color: var(--text-dim); font-weight: 500; font-size: 11px; }
/* The composer's "Your rules" — the user's own passing rules, pickable like tiles. */
.st-myrules { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 16px; }
.st-myrules-cap { margin-bottom: 8px; }
.st-myrule { display: flex; align-items: center; justify-content: space-between; gap: 10px; background: var(--sm-card); border-radius: 14px; padding: 10px 12px; }
/* In a rule row the add/check disc is a flex item, not a corner overlay — the
   tile rule's absolute positioning anchored it to the SCRIM (no positioned
   ancestor here), so it floated at the screen's top-right corner. */
.st-myrule .qn-stile-check { position: relative; top: auto; right: auto; flex: 0 0 auto; }
/* The Custom tab's rules list — same rows, plus a verdict chip. */
.qn-myrules { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.qn-rule-chip { font-size: 10px; font-weight: 650; letter-spacing: 0.02em; color: var(--text-faint); background: var(--bg-elev-2); border-radius: 999px; padding: 2px 8px; margin-left: 8px; white-space: nowrap; }
.qn-rule-chip.pass { color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 12%, transparent); }
.qn-bsh-ret.none { color: var(--text-faint); font-weight: 560; font-size: 15px; letter-spacing: 0; }
/* Where the number came from. Quiet, but present — a percentage nobody can
   source is the kind this branch has spent all day deleting. */
.qn-bsh-note { font-size: 10.5px; color: var(--text-faint); text-align: center; margin-top: 6px; }

/* ── YOUR PORTFOLIOS (owner 2026-07-31) ──────────────────────────────────────
   The home's second block: the Stacks you copy, and the one you made. Cards, not
   rows — each carries four numbers, a status line and two controls, which is
   more than a row can hold without becoming a table nobody reads on a phone.
   The pieces (.qn-w*) are the ones the positions sheet used to render, kept
   under their old names because the markup didn't change, only its address. */
.qn-ptabs { padding: 0 0 10px; }
.qn-plist { display: flex; flex-direction: column; gap: 10px; }
.qn-pcard { display: flex; flex-direction: column; gap: 8px; width: 100%; background: var(--bg-elev-1); border-radius: 18px; padding: 13px 14px; }
.qn-wtop { display: flex; align-items: center; gap: 8px; }
.qn-wglyph { font-size: 15px; line-height: 1; flex: none; }
.qn-wname { font-size: 13.5px; font-weight: 660; letter-spacing: -0.01em; color: var(--text); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE DESCRIPTION. One line, always: it says what the Stack trades, and the
   status feed it replaced ("Running 3 of 4 · copying 11m · Next: SKHY · Sell the
   breakdown, in 7 days") wrapped to two and moved every couple of minutes. */
.qn-wline { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* No return yet is a STATE, not a fault — same tonal chip language as the rest
   of the screen, and the row keeps its shape either way. */
.qn-wna { flex: none; font-size: 10px; font-weight: 660; letter-spacing: .04em; color: var(--text-faint); background: var(--bg-elev-2); border-radius: 999px; padding: 3px 8px; }
/* The name is the tap target; the row around it is not, so the stepper beside
   it can never be hit by aiming at the card. */
.qn-wname-btn { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; background: none; border: none; padding: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; border-radius: 8px; }
.qn-wname-btn:focus-visible { outline: 2px solid color-mix(in srgb, var(--sm-up) 55%, transparent); outline-offset: 2px; }

/* YOU vs THE STACK, side by side with a hairline between — the comparison IS the
   card, so the two halves get equal width and the divider does the separating
   that a border around each would over-state. `minmax(0, 1fr)` twice, because a
   long figure in one half must not steal width from the other. */
.qn-pstats { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; padding-top: 2px; }
.qn-pgrp { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.qn-pgrp + .qn-pgrp { border-left: 1px solid var(--border, color-mix(in srgb, var(--text) 9%, transparent)); padding-left: 12px; }
.qn-pgt { font-size: 10px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-faint); }
.qn-pcells { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
.qn-pstat { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.qn-pv { font-size: 15px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qn-pv.up { color: var(--sm-up); } .qn-pv.down { color: var(--sm-down); }
.qn-pl { font-size: 10px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* The card's action row — just Edit, on a Stack you made. Allocation and the
   hard exit live in the Stack sheet (owner 2026-07-31: "no show allocator, dont
   show close all here"): a portfolio card is for reading. */
.qn-wctl { display: flex; align-items: center; gap: 6px; margin-top: 2px; }
.qn-pctl-end { justify-content: flex-end; }
/* The neutral sibling of .qn-wstop — Edit, Find one, Create one. Same pill, no
   colour: none of them destroy anything. */
/* The way out of an empty tab, at the width of the card that says it's empty. */
.qn-pbtn { align-self: stretch; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 13.5px; font-weight: 650; color: var(--text); background: var(--bg-elev-2); border: none; border-radius: 999px; padding: 13px 16px; cursor: pointer; transition: background .14s; }
.qn-pbtn svg { width: 19px; height: 19px; flex: none; }
.qn-pbtn:hover { background: var(--bg-elev-3); }
/* Empty is a state with an action, never a shrug. */
.qn-pempty { display: flex; flex-direction: column; align-items: stretch; gap: 12px; text-align: center; background: var(--bg-elev-1); border-radius: 18px; padding: 16px 14px; }
.qn-pempty-t { font-size: 12.5px; color: var(--text-dim); }
/* Liveness under the rows: a breathing dot and the last mark. Answers "is this
   number stale or is the market flat" without opening the bell. */
.qn-run-live { display: flex; align-items: center; gap: 6px; padding: 0; font-weight: 500; letter-spacing: 0; text-transform: none; }
.qn-live-t { font-size: 10.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.qn-run-live.late .qn-live-t { color: var(--warn, #f3c969); }
.qn-live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--sm-up); flex: none; animation: qn-breathe 2.4s ease-in-out infinite; }
.qn-live-dot.late { background: var(--warn, #f3c969); animation-duration: 1.1s; }
@keyframes qn-breathe { 0%, 100% { opacity: 0.35; transform: scale(0.82); } 50% { opacity: 1; transform: scale(1); } }
/* The loop itself breathes while it's hunting — the icon says ON, this says
   RUNNING. Kept slow and low-contrast: it's ambient, not an alert. */
.qn-autobtn.running svg { animation: qn-breathe 2.8s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .qn-live-dot, .qn-autobtn.running svg { animation: none; }
  .qn-live-dot { opacity: 1; }
}
/* Separate cards rather than a hairline-divided table: at six positions the
   ruled list read as a spreadsheet, and M3 puts air between items instead. */
.qn-run { display: flex; flex-direction: column; gap: 10px; width: 100%; }
.qn-run-row { appearance: none; border: none; background: var(--sm-card); border-radius: 18px; cursor: pointer; font: inherit; color: var(--text); display: flex; align-items: center; gap: 12px; text-align: left; padding: 14px 16px; }
/* Waiting-to-enter rows: the amber of their WAITING chip, as a whisper. */
.qn-run-row.pend { background: color-mix(in srgb, var(--sm-warn, #d2a13a) 6%, var(--sm-card)); }

.qn-run-row:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
.qn-run-l { flex: 1; min-width: 0; }
.qn-run-n { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 14px; }
/* Genie-picked provenance: the AUTO chip in quiet gray (owner 2026-08-07:
   "subtle and not distracting"), same shape as the GRID/WAITING chips. */
.qn-run-wait.auto { color: var(--text-faint); background: color-mix(in srgb, var(--text-faint) 12%, transparent); }
.qn-run-wait { font-size: 9px; font-weight: 800; letter-spacing: 0.06em; color: var(--sm-warn, #d2a13a); background: color-mix(in srgb, var(--sm-warn, #d2a13a) 14%, transparent); border-radius: 5px; padding: 2px 6px; }
/* Long/Short beside the ticker (owner spec rev 2 2026-08-06: title =
   symbol + side on every position card; money/leverage on the line below). */
.qn-run-dirw { font-size: 12px; font-weight: 650; }
.qn-run-dirw.long { color: var(--sm-up); }
.qn-run-dirw.short { color: var(--sm-down); }
/* Which Stack a mirrored position came from — quiet, but never absent: a
   position the user didn't tap has to say who opened it. */
.qn-run-from { font-size: 9.5px; font-weight: 620; color: var(--text-faint); background: var(--bg-elev-2); border-radius: 5px; padding: 2px 6px; max-width: 108px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Where the trade ends — the pair a copier most needs and could not see. */
.qn-run-exit { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; margin-top: 4px; }
/* Wraps to two lines, never truncates (owner 2026-08-05: the ellipsis was
   swallowing the pace — the one number that turns "3.6% away" into "about
   90 minutes"). Vital info doesn't get cut for tidiness. */
.qn-run-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 5px; white-space: normal; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-height: 1.45; font-variant-numeric: tabular-nums; }
/* The trigger-distance line under a watch row — quieter than the row's own sub. */
.qn-run-gap { font-size: 11px; color: var(--text-faint); margin-top: 2px; }
.qn-run-r { text-align: right; flex-shrink: 0; }
.qn-run-big { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
.qn-run-big.up { color: var(--sm-up); } .qn-run-big.down { color: var(--sm-down); }
.qn-run-big.dim { color: var(--text-faint); font-weight: 600; }
.qn-run-pct { font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.qn-run-pct.up { color: color-mix(in srgb, var(--sm-up) 70%, var(--text-dim)); }
.qn-run-pct.down { color: color-mix(in srgb, var(--sm-down) 70%, var(--text-dim)); }
.qn-run-pct.dim { color: var(--text-faint); font-weight: 500; }
.qn-run-x { flex-shrink: 0; color: var(--text-faint); font-size: 12px; padding: 4px 2px 4px 6px; cursor: pointer; border-radius: 6px; }
.qn-run-x:hover { color: var(--sm-down); }
/* It's keyboard-reachable now (role=button + tabindex), so it needs a focus ring —
   the row it sits in is itself a button, and an invisible focus stop inside another
   control is worse than no focus stop at all. */
.qn-run-x:focus-visible { outline: 2px solid var(--sm-down); outline-offset: 1px; color: var(--sm-down); }
.qn-feed { display: flex; flex-direction: column; width: 100%; background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 4px 0; }
.qn-feed-row { display: flex; gap: 10px; align-items: flex-start; padding: 8px 14px; }
.qn-feed-row + .qn-feed-row { border-top: 1px solid color-mix(in srgb, var(--text) 6%, transparent); }
.qn-dot { flex: 0 0 7px; height: 7px; border-radius: 50%; margin-top: 5px; background: var(--text-faint); }
.qn-dot.ok { background: var(--sm-up); } .qn-dot.bad { background: var(--sm-down); }
.qn-dot.warn { background: var(--sm-warn, #d2a13a); } .qn-dot.dim { background: var(--text-faint); }
.qn-feed-b { min-width: 0; }
.qn-feed-t { font-size: 12.5px; line-height: 1.4; }
.qn-feed-t.up { color: var(--sm-up); } .qn-feed-t.down { color: var(--sm-down); }
.qn-feed-ago { font-size: 10.5px; color: var(--text-faint); margin-top: 1px; }
.qn-band { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 14px 16px; }
.qn-band-t { font-weight: 700; font-size: 13px; }
.qn-band-s { font-size: 12px; color: var(--text-dim); margin-top: 3px; line-height: 1.45; }
.qn-plays { display: flex; flex-direction: column; gap: 8px; }
.qn-play { background: var(--sm-card); border: 1px solid var(--border); border-radius: 14px; padding: 10px 14px; }
.qn-play-hrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.qn-play-h { font-weight: 700; font-size: 13px; }
.qn-play-big { margin-left: auto; font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.qn-play-big.up { color: var(--sm-up); } .qn-play-big.down { color: var(--sm-down); }
.qn-play-big.dim { color: var(--text-dim); font-size: 14px; font-weight: 600; }
.qn-play-x { appearance: none; cursor: pointer; font: inherit; font-size: 11.5px; font-weight: 600; color: var(--text-dim); background: color-mix(in srgb, var(--text-dim) 12%, transparent); border: none; border-radius: 999px; padding: 4px 12px; }
.qn-play-x:hover { color: var(--sm-down); }
.qn-play-x:disabled { opacity: 0.6; cursor: default; }
.qn-play-d { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; margin-top: 2px; }
.qn-play-st { font-size: 11px; color: var(--text-faint); margin-top: 2px; }

/* TA legend controls (owner 2026-07-27): chips are toggles; RANGE pill retired. */
.sm-tatoggle.off { opacity: 0.38; }
.sm-tamode { font-weight: 700; color: var(--sm-up); background: color-mix(in srgb, var(--sm-up) 14%, transparent); border-radius: 999px; }

/* Chart mode: M3 segmented control + foldable overlay chips (owner 2026-07-27). */
.sm-taseg { display: inline-flex; padding: 2px; border: 1px solid var(--border); border-radius: 999px; background: color-mix(in srgb, var(--sm-card) 70%, transparent); }
.sm-tafold { display: flex; flex-wrap: wrap; gap: 6px; overflow: hidden; max-height: 90px; opacity: 1; transition: max-height 0.28s cubic-bezier(0.2, 0, 0, 1), opacity 0.2s; }
.sm-tafold.closed { max-height: 0; opacity: 0; }
@media (prefers-reduced-motion: reduce) { .sm-tafold { transition: none; } }


/* Go-live gate (§10.4 A0) — M3 form, mockup parity. */
.sm-gate { background: var(--sm-card); border: 1px solid var(--border); border-radius: 16px; padding: 14px 16px; margin-top: 10px; }
.sm-gate-h { font-size: 14px; font-weight: 700; margin-bottom: 10px; }
.sm-gate-f { margin-bottom: 10px; }
.sm-gate-l { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin-bottom: 5px; }
.sm-gate-i { appearance: none; background: none; border: none; cursor: pointer; font: inherit; font-size: 11.5px; color: var(--accent, #5b7cfa); padding: 0; }
.sm-gate-in { width: 100%; box-sizing: border-box; background: var(--bg, #131314); border: 1px solid var(--border); border-radius: 12px; color: var(--text); font: inherit; font-size: 13px; padding: 10px 12px; }
.sm-gate-help { background: var(--bg, #131314); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px; }
.sm-gate-step { display: flex; gap: 10px; align-items: flex-start; font-size: 12.5px; line-height: 1.45; padding: 4px 0; }
.sm-gate-n { flex: 0 0 20px; height: 20px; border-radius: 50%; background: color-mix(in srgb, var(--accent, #5b7cfa) 22%, transparent); color: var(--accent, #5b7cfa); font-size: 11px; font-weight: 700; font-style: normal; display: flex; align-items: center; justify-content: center; }
.sm-gate-note { font-size: 11px; color: var(--text-faint); line-height: 1.5; margin-top: 8px; }
.sm-gate-note.bad { color: var(--sm-down); }

/* TA chips: M3 outlined filter chips (owner 2026-07-27). */
/* Quant tiles: fewer + larger, so the small ones stay readable. */
.qn-tile-s { font-size: var(--sym, 15px); font-weight: 700; letter-spacing: -0.01em; white-space: nowrap; line-height: 1.15; }


/* Chart mode control — icons, hero top-right (owner 2026-07-27). */
.sm-chart-hero { position: relative; }
.sm-chart-mode { position: absolute; top: -4px; right: 0; z-index: 2; }
.sm-chart-mode .sm-taseg-b { padding: 5px 10px; }
.sm-chart-mode .material-symbols-rounded { font-size: 17px; line-height: 1; display: block; }
/* Summary as an M3 tonal card, consistent with the rest of the UI. */
/* The outer accent BOX this rule used to draw is gone (owner 2026-08-02: "we
   don't need the rectangle that wraps around the bubble area") — it was a
   leftover from the pre-chat-line design, silently winning the cascade over
   the chat-line rule above (later rule, equal specificity — the .qn-side-tabs
   lesson again). The bubble carries its own surface; the cols gap alone
   separates it from GENIE'S READ. */
.sm-asset-cols > .sm-verd { flex: 1 0 100%; margin-bottom: 0; }
/* The warn-state container tint that used to live here is gone (owner
   2026-08-02: "shouldn't have the brown rectangle around the speech bubble")
   — the second stale sibling of the rule removed at the same address earlier
   today. The bubble is the only surface; the words carry the warning. */
.sm-verd-title { font-size: 15px; font-weight: 700; }
/* One type scale for every pill in the TA row. */
.sm-strat-warn { font-size: 11.5px; color: var(--sm-warn, #d2a13a); margin: 2px 0 6px; line-height: 1.45; }

/* Strategy headline stats — the "why use this" row (owner 2026-07-27). */
/* ONE tonal container, three columns, hairline dividers — three separate
   rounded boxes read as three cards competing for attention. */
.sm-strat-hi { display: flex; margin: 12px 0 0; background: color-mix(in srgb, var(--text) 5%, transparent); border-radius: 12px; overflow: hidden; }
.sm-strat-stat { flex: 1; padding: 10px 6px; text-align: center; }
.sm-strat-stat + .sm-strat-stat { border-left: 1px solid color-mix(in srgb, var(--text) 8%, transparent); }
.sm-strat-statv { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.15; letter-spacing: -0.01em; }
.sm-strat-statv.up { color: var(--sm-up); } .sm-strat-statv.down { color: var(--sm-down); }
.sm-strat-statk { font-size: 10.5px; font-weight: 500; color: var(--text-faint); margin-top: 3px; }
/* The next trade, drawn (owner 2026-08-05): recent tape + the would-be entry,
   target and stop — the thesis as one picture, labels in the right gutter. */
.sm-nt-svg { width: 100%; height: auto; display: block; margin-top: 4px; }
.sm-nt-price { fill: none; stroke: var(--text); stroke-opacity: 0.8; stroke-width: 1.6; stroke-linejoin: round; }
.sm-nt-dot { fill: var(--text); }
.sm-nt-line { stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.8; }
/* The entry wears the trade's direction (owner 2026-08-05): green long, red
   short — same colors as the triangle mark riding the line. */
.sm-nt-line.in.long { stroke: var(--sm-up); }
.sm-nt-line.in.short { stroke: var(--sm-down); }
.sm-nt-line.tp { stroke: var(--sm-up); }
.sm-nt-line.sl { stroke: var(--sm-down); }
.sm-nt-lbl { font-size: 9.5px; font-weight: 600; }
.sm-nt-lbl.in.long { fill: var(--sm-up); }
.sm-nt-lbl.in.short { fill: var(--sm-down); }
.sm-nt-lbl.tp { fill: var(--sm-up); }
.sm-nt-lbl.sl { fill: var(--sm-down); }
.sm-nt-tri.long { fill: var(--sm-up); }
.sm-nt-tri.short { fill: var(--sm-down); }
/* Mode control sits ABOVE the chart, right-aligned — never over the candles. */
.sm-chart-mode { position: static; display: flex; justify-content: flex-end; margin: 0 0 6px; }

/* ---- TA controls: Material 3 filter chips (single source of truth) ----
   M3 spec: 32dp target, 8dp corner (chips are NOT pills), 1dp outline,
   labelSmall type, tonal secondary-container fill when selected. */
.sm-tatool {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; box-sizing: border-box; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: none; color: var(--text-dim);
  font: inherit; font-size: 10.5px; font-weight: 500; letter-spacing: 0.02em; line-height: 1;
  appearance: none; cursor: pointer;
  transition: background 0.16s, color 0.16s, border-color 0.16s, opacity 0.16s;
}
.sm-tatoggle:not(.off) { background: color-mix(in srgb, var(--text) 9%, transparent); color: var(--text); border-color: transparent; }
.sm-tatoggle.off { opacity: 0.55; }
.sm-tatool:hover { border-color: color-mix(in srgb, var(--text) 26%, var(--border)); }
.sm-taswatch { width: 10px; height: 2px; border-radius: 1px; background: var(--sw, var(--text-dim)); border: 0; margin: 0; flex: none; }
/* Segmented chart-mode control — same family, icon-sized. */
.sm-taseg { display: inline-flex; padding: 2px; gap: 2px; border: 1px solid var(--border); border-radius: 10px; background: none; }
.sm-taseg-b {
  appearance: none; cursor: pointer; font: inherit;
  display: flex; align-items: center; justify-content: center;
  height: 24px; padding: 0 9px; border: none; border-radius: 8px;
  color: var(--text-faint); background: none;
  transition: background 0.16s, color 0.16s;
}
.sm-taseg-b.on { background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text); }
.sm-tafold { gap: 6px; }

/* A candidate that failed the bar is EVIDENCE, not an offer. */
.sm-strat-card.rejected { opacity: 0.72; border-style: dashed; }
.sm-strat-rej { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); border: 1px solid var(--border); border-radius: 6px; padding: 2px 7px; margin-left: auto; }
.sm-strat-rejwhy { font-size: 11.5px; color: var(--sm-warn, #d2a13a); line-height: 1.45; margin: 6px 0 2px; }

/* Exchange-connect bottom sheet (owner 2026-07-27): slide-up modal, scrim,
   escape/backdrop/✕ to dismiss. */
.sm-gsheet-scrim { position: fixed; inset: 0; z-index: 90; display: flex; align-items: flex-end; justify-content: center; background: rgba(0,0,0,0); transition: background 0.22s ease; }
.sm-gsheet-scrim.in { background: rgba(0,0,0,0.55); }
.sm-gsheet { position: relative; width: min(520px, 100%); max-height: 88vh; overflow-y: auto; background: var(--bg-elev, #1e1f20); color: var(--text); border: 1px solid var(--border); border-bottom: none; border-radius: 26px 26px 0 0; padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px)); transform: translateY(100%); transition: transform 0.24s cubic-bezier(0.2, 0, 0, 1); box-shadow: 0 -18px 48px rgba(0,0,0,0.45); }
.sm-gsheet-scrim.in .sm-gsheet { transform: translateY(0); }
.sm-gsheet-grip { width: 36px; height: 4px; border-radius: 2px; background: var(--border); margin: 4px auto 10px; }
.sm-gsheet-x { position: absolute; top: 10px; right: 12px; appearance: none; background: none; border: none; cursor: pointer; color: var(--text-faint); font-size: 15px; line-height: 1; padding: 6px; }
.sm-gsheet .sm-gate { background: none; border: none; padding: 0 0 4px; margin: 0; }
@media (prefers-reduced-motion: reduce) {
  .sm-gsheet, .sm-gsheet-scrim { transition: none; }
  .sm-gsheet { transform: none; }
}
/* The watch-section header doubles as its collapse toggle — same label type
   as every other group heading (font: inherit was resetting the button to
   body size, which read as a foreign typeface). */
button.qn-run-grp-toggle {
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left; width: 100%; display: block;
  font-family: inherit; font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-faint); font-variant-numeric: tabular-nums;
}
