/* OptionsKing — high-end UI. Every value references tokens.css. */

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  /* No `fixed` background-attachment: broken/janky in iOS WKWebView (scrolls
     oddly, can flicker). A plain gradient renders identically on both platforms. */
  background: linear-gradient(180deg, var(--bg-grad-top), var(--bg-grad-bottom));
  color: var(--on-bg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
}

#app {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--s-4) calc(var(--tap) + var(--s-7));
  /* 100dvh (dynamic viewport) so content isn't clipped behind iOS WKWebView's
     dynamic toolbar; 100vh fallback for engines without dvh. Safe on Android. */
  min-height: 100vh;
  min-height: 100dvh;
}

/* ---- App bar ---- */
.appbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: calc(env(safe-area-inset-top, 0px) + var(--s-4)) 0 var(--s-3);
  background: linear-gradient(180deg, var(--bg-grad-top) 70%, transparent);
  backdrop-filter: saturate(1.2) blur(6px);
}
.brand-mark {
  width: 34px; height: 34px; border-radius: 10px; flex: none;
  background: linear-gradient(145deg, var(--green-400), var(--green-700));
  display: grid; place-items: center; color: #fff; font-weight: var(--fw-black);
  box-shadow: var(--e-2); font-size: 1.1rem;
}
/* The title claims all slack (flex:1) so the account/mode cluster stays pinned at the
   right padding edge — identical position on every screen. min-width:0 + ellipsis is
   the LAST-RESORT safety so an extreme case never overflows the bar (no horizontal
   page scroll); on real devices the narrow-screen rule below shrinks the font so
   "OptionsKing" always fits whole. */
.appbar h1 { flex: 1 1 auto; font-size: var(--fs-h1); font-weight: var(--fw-black); letter-spacing: -0.02em; margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- App-bar account + mode cluster ----
   ONE pill on the right of every app bar carrying two always-relevant facts:
   the active book (Paper/Live, color-coded dot) and the signed-in user (initials,
   or a person glyph while signed out). Kept compact so the title keeps its room. */
.acct-cluster {
  flex: none; display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 3px 3px 3px 10px; min-height: 0; border-radius: var(--r-pill);
  border: 1px solid var(--border-strong); background: var(--surface-2);
  color: var(--muted); box-shadow: none; cursor: pointer;
}
.acct-cluster .acct-mode {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: 0.02em;
}
.acct-cluster .acct-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--muted); }
/* Live book = real money → green accent so the pill reads as "hot"; a soft ring
   around the dot gives it a subtle presence without animation (WKWebView-safe). */
.acct-cluster.mode-live { border-color: transparent; background: var(--primary-container); color: var(--on-primary-container); }
.acct-cluster.mode-live .acct-dot { background: var(--green-500); box-shadow: 0 0 0 3px color-mix(in srgb, var(--green-500) 28%, transparent); }
/* The avatar segment: person glyph when signed out (always-visible account
   affordance), initials once a real provider session fills it. */
.acct-cluster .acct-avatar {
  width: 28px; height: 28px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--muted);
  font-size: var(--fs-xs); font-weight: var(--fw-black); letter-spacing: 0.02em;
}
.acct-cluster .acct-avatar svg { width: 16px; height: 16px; }
.acct-cluster .acct-avatar.is-user { background: var(--primary-container); color: var(--on-primary-container); }
/* Google profile photo fills the whole circle (initials remain the fallback when
   no photo / the image fails to load — see fillAvatar). */
.acct-cluster .acct-avatar img {
  width: 100%; height: 100%; border-radius: 50%;
  object-fit: cover; display: block;
}

/* Narrow phones (and split-screen columns / large accessibility fonts): shrink the
   title a touch and tighten the bar so "OptionsKing" always renders WHOLE — the
   Fold cover (~369px) and every wider screen keep the full-size title untouched. */
@media (max-width: 360px) {
  .appbar { gap: var(--s-2); }
  .appbar h1 { font-size: clamp(1.15rem, 5.4vw, 1.5rem); }
  .acct-cluster { padding-left: 8px; gap: 6px; }
}

h1, h2 { letter-spacing: -0.02em; }
h1 { font-size: var(--fs-h1); font-weight: var(--fw-black); margin: var(--s-3) 0 var(--s-4); }
h2 { font-size: var(--fs-h2); font-weight: var(--fw-bold); margin: 0 0 var(--s-2); }
p { color: var(--on-surface); margin: 0 0 var(--s-3); }
.muted { color: var(--muted); font-size: var(--fs-sm); }
.faint { color: var(--faint); font-size: var(--fs-xs); }
.center { text-align: center; }

/* ---- Cards ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s-5);
  margin-bottom: var(--s-4);
  box-shadow: var(--e-1);
  animation: rise var(--dur-3) var(--ease) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---- Buttons ---- */
button, .btn {
  font: inherit;
  font-weight: var(--fw-bold);
  min-height: var(--tap);
  padding: 0 var(--s-5);
  border-radius: var(--r-pill);
  border: none;
  cursor: pointer;
  background: linear-gradient(180deg, var(--primary), var(--primary-strong));
  color: var(--on-primary);
  box-shadow: var(--e-1);
  transition: transform var(--dur-1) var(--ease), box-shadow var(--dur-2) var(--ease), filter var(--dur-1);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  /* Labels never wrap to a second line — they ellipsize if space is truly tight.
     min-width:0 lets a flex button shrink instead of forcing a wrap. */
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
button > svg, .btn > svg { flex: none; }
button:active, .btn:active { transform: scale(0.97); }
/* Hover states only where a real pointer exists. On touch, :hover sticks after a tap
   and leaves buttons looking pressed until you tap elsewhere. */
@media (hover: hover) {
  button:hover:not(:disabled) { box-shadow: var(--e-2); }
}
button:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-secondary { background: var(--surface-2); color: var(--on-surface); border: 1px solid var(--border-strong); box-shadow: none; }
.btn-ghost { background: transparent; color: var(--primary); box-shadow: none; }
.btn-danger { background: transparent; color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--border)); box-shadow: none; }
/* Every button caps at 330px wide. A full-bleed CTA on a large phone, a tablet or a
   desktop reads as a banner rather than a control, and the cap keeps the pointer
   travel between stacked CTAs short. Block buttons still FILL a narrow column and
   only centre once that column is wider than the cap, so phones are unchanged.
   Nav items, chips, icon buttons and segmented controls are all narrower than 330px
   already, so the cap is inert for them. */
button, .btn { max-width: 330px; }
/* display:flex (not the base inline-flex) is what makes the auto margins actually
   centre the button once the column is wider than the 330px cap — auto margins do
   nothing on an inline-level box outside a flex parent. */
.btn-block { display: flex; width: 100%; margin-left: auto; margin-right: auto; }
.btn-lg { min-height: 56px; font-size: 1.05rem; }
.btn-row { display: flex; gap: var(--s-2); flex-wrap: wrap; }
/* Icon-only actions (Edit, Options chain). The glyph carries the meaning, so the
   accessible name MUST come from aria-label/title on the button. Square so the tap
   target stays a full --tap in both axes even without a label to widen it. */
.btn-icon { padding: 0; width: var(--tap); min-width: var(--tap); flex: none; }
.btn-icon > svg { width: 20px; height: 20px; }

/* ---- Provider (SSO) buttons ----
   Google + Apple sign-in follow each vendor's branding for trust. The mark sits
   left of a centered label; the label centers within the button while the mark is
   pinned so the row reads as one balanced unit. */
.sso-row { display: flex; flex-direction: column; gap: var(--s-2); }
.btn-sso {
  position: relative; gap: var(--s-3);
  background: #fff; color: #1f1f1f; border: 1px solid #747775; box-shadow: none;
  font-weight: 500; letter-spacing: 0.01em;
}
@media (hover: hover) {
  .btn-sso:hover:not(:disabled) { box-shadow: var(--e-1); }
}
.btn-sso .sso-mark { position: absolute; left: var(--s-4); display: inline-flex; }
.btn-sso .sso-label { padding: 0 var(--s-6); }
.btn-sso svg { display: block; }
/* Google: neutral/white surface (light) or Google's dark surface (dark theme). */
.btn-google { background: #fff; color: #1f1f1f; border-color: #747775; }
/* Apple: black button, white mark+label per Apple's HIG (same in both themes). */
.btn-apple { background: #000; color: #fff; border-color: #000; }
/* Email/password: Cognito's own user directory, which belongs to no brand and has no
   guideline to follow — so it uses the app's own neutral surface and stroke icon
   rather than imitating a provider button. Themed, unlike the two branded ones. */
.btn-email { background: var(--surface-2); color: var(--on-surface); border-color: var(--border-strong); }
@media (prefers-color-scheme: dark) {
  .btn-google { background: #131314; color: #e3e3e3; border-color: #8e918f; }
}
:root[data-theme='dark'] .btn-google { background: #131314; color: #e3e3e3; border-color: #8e918f; }
:root[data-theme='light'] .btn-google { background: #fff; color: #1f1f1f; border-color: #747775; }
button:focus-visible, input:focus-visible, select:focus-visible { outline: none; box-shadow: var(--ring); }

/* ---- Inputs ---- */
.field { margin-bottom: var(--s-4); }
label { display: block; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--muted); margin-bottom: var(--s-2); }
input, select {
  font: inherit;
  width: 100%;
  min-height: var(--tap);
  padding: 0 var(--s-4);
  border-radius: var(--r-sm);
  border: 1.5px solid var(--border-strong);
  background: var(--surface);
  color: var(--on-surface);
  transition: border-color var(--dur-1), box-shadow var(--dur-1);
}
input::placeholder { color: var(--faint); }
input:focus, select:focus { border-color: var(--primary); }
.input-lg { min-height: 56px; font-size: 1.35rem; font-weight: var(--fw-bold); letter-spacing: 0.02em; }
/* Range sliders track the brand green, not the OS default blue. */
input[type='range'] { accent-color: var(--primary); }
.input-prefix { position: relative; }
.input-prefix > span { position: absolute; left: var(--s-4); top: 50%; transform: translateY(-50%); color: var(--faint); font-weight: var(--fw-bold); pointer-events: none; display: inline-flex; align-items: center; }
/* Constrain an icon prefix to a fixed box so it never overlaps the input text
   (the shares/search SVGs are 24px — wider than the "$" glyph). */
.input-prefix > span svg { width: 20px; height: 20px; }
.input-prefix input { padding-left: calc(var(--s-4) + 1.7em); }

/* segmented control */
.segment { display: flex; background: var(--surface-2); border-radius: var(--r-pill); padding: 3px; border: 1px solid var(--border); gap: 2px; }
.segment button {
  flex: 1; min-height: 40px; padding: 0 var(--s-2); border-radius: var(--r-pill);
  background: transparent; color: var(--muted); box-shadow: none; font-size: var(--fs-sm);
}
.segment button[aria-pressed='true'] { background: var(--surface); color: var(--on-surface); box-shadow: var(--e-1); }

/* Strategy picker: labeled groups (single-leg vs defined-risk spreads), each its own
 * segmented control — so it's clear "Auto" only spans its own group. A small subhead
 * sits above each group. */
.seg-subhead {
  font-size: var(--fs-xs); color: var(--faint); font-weight: var(--fw-medium);
  margin: var(--s-2) 0 4px;
}

/* Horizontally-scrollable segmented control. When a group has more pills than fit,
 * DON'T wrap or shrink them into an unreadable jumble — keep each pill its natural
 * size and let the row scroll sideways (momentum scroll on touch). The selected pill
 * still gets the raised look. Used by the strategy rows. */
.segment.scroll {
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* Firefox: hide the bar */
}
.segment.scroll::-webkit-scrollbar { display: none; } /* WebKit: hide the bar */
.segment.scroll button {
  flex: 0 0 auto; /* natural width, never shrink — overflow scrolls instead */
  white-space: nowrap; scroll-snap-align: start;
}

/* chips */
.chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.chip {
  min-height: 40px; padding: 0 var(--s-4); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--on-surface); border: 1px solid var(--border);
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
}
.chip[aria-pressed='true'] { background: var(--primary-container); color: var(--on-primary-container); border-color: transparent; }
/* Count badge inside a filter chip (Placed status filters). */
.chip-count {
  margin-left: 6px; font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-weight: var(--fw-black); font-size: var(--fs-xs); opacity: 0.75;
}
/* Placed status-filter row scrolls horizontally on narrow screens rather than wrap. */
.placed-filters { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px; }
.placed-filter { flex: none; padding: 0 var(--s-3); min-height: 36px; }

/* ---- Disclaimer + badges ---- */
.disclaimer {
  display: flex; gap: var(--s-2); align-items: flex-start;
  background: var(--primary-container);
  color: var(--on-primary-container);
  border-radius: var(--r-sm);
  padding: var(--s-3);
  font-size: var(--fs-xs);
  line-height: 1.4;
  border: 1px solid transparent;
}
.disclaimer svg { flex: none; margin-top: 1px; }
/* Compact, unobtrusive footnote variant — used on recurring surfaces (per-rec,
   performance) where the full T&C already ran at onboarding. Present for
   compliance (G1.5/G1.9) but visually quiet. */
.disclaimer-fine {
  display: flex; gap: var(--s-2); align-items: flex-start;
  color: var(--faint); font-size: var(--fs-xs); line-height: 1.35;
  padding: var(--s-2) var(--s-1) 0; background: none; border: none;
}
.disclaimer-fine svg { flex: none; width: 14px; height: 14px; margin-top: 1px; opacity: 0.7; }
.badge {
  display: inline-flex; align-items: center; gap: var(--pill-gap);
  padding: var(--pill-pad); border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: 0.02em;
  background: var(--surface-2); color: var(--muted);
}
.badge-delayed { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
.badge-delayed .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--warning); }

/* ---- Proposal card ---- */
.proposal {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--surface-raised);
  box-shadow: var(--e-1);
  animation: rise var(--dur-3) var(--ease) both;
}
.proposal.rank-1 { border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); box-shadow: var(--e-2); }
.proposal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-2); }
.proposal-head > div:first-child { min-width: 0; flex: 1; } /* allow the title column to shrink so the tag never overflows */
.proposal-title { font-weight: var(--fw-black); font-size: 1.1rem; letter-spacing: -0.01em; overflow-wrap: anywhere; word-break: break-word; }
.proposal-sub { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }
.rank-tag { flex: none; white-space: nowrap; font-size: var(--fs-xs); font-weight: var(--fw-bold); color: var(--primary); background: var(--primary-container); padding: var(--pill-pad); border-radius: var(--r-pill); }
.rank-tag.below { color: var(--warning); background: color-mix(in srgb, var(--warning) 16%, transparent); }
.proposal.belowgate { border-style: dashed; border-color: color-mix(in srgb, var(--warning) 45%, var(--border)); opacity: 0.94; }
.proposal.belowgate .conf-bar > i { background: linear-gradient(90deg, var(--warning), var(--gold-500)); }
.belowgate-head { display: flex; align-items: center; gap: var(--s-2); margin: var(--s-4) var(--s-1) var(--s-2);
  font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--warning); }

/* ---- Compact proposal card (dense, ~5 lines) ---- */
/* Tightened vertical rhythm so more cards fit on screen: smaller inner padding,
   snug gaps between the title / sub / metric / foot / action rows. */
.proposal.compact { padding: var(--s-3); margin-bottom: var(--s-2); }
.proposal.compact .proposal-head { align-items: center; }
.proposal.compact .proposal-title { font-size: 1.02rem; }
.proposal.compact .proposal-sub { margin-top: 1px; }
.proposal.compact .metric-strip { margin: 6px 0 2px; }
.proposal.compact .proposal-foot { margin-top: 1px; }
/* The action row (Place / outcome buttons) sits closer to the content above. */
.proposal.compact .btn-row { margin-top: 8px !important; }
/* Slightly shorter action buttons inside a compact card to reclaim height. */
.proposal.compact .btn-row .btn-secondary,
.proposal.compact .btn-row .btn-ghost { min-height: 40px; }
.proposal.compact .btn-row .btn-icon { width: 40px; min-width: 40px; }
/* Confidence chip on the title row — replaces the whole 4th metric box. */
.conf-chip {
  flex: none; display: inline-flex; align-items: center; gap: var(--pill-gap); white-space: nowrap;
  font-family: var(--font-num); font-weight: var(--fw-black); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); padding: var(--pill-pad); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--on-surface);
}
.conf-chip svg { width: 13px; height: 13px; }
.conf-chip.confidence-High { background: color-mix(in srgb, var(--positive) 16%, transparent); color: var(--positive); }
.conf-chip.confidence-Medium { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
.conf-chip.confidence-Low { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
.conf-chip.below { background: color-mix(in srgb, var(--warning) 16%, transparent); color: var(--warning); }
/* The weighted (quality) chip on Placed cards sits beside the confidence chip as a
   quieter companion — outlined rather than filled so confidence stays the anchor. */
.conf-chip.weighted { background: transparent; box-shadow: inset 0 0 0 1px currentColor; opacity: 0.85; }
/* The score-chip cluster on the title row — one wrapper for BOTH tabs (Top Options
   ships a single confidence chip, Placed adds its quality companion) so the chips
   sit in the same place on every card. */
.card-chips { flex: none; display: inline-flex; align-items: center; gap: var(--pill-gap); }
/* One-line metric strip: keep / assign / fill / income, on a SINGLE row (no wrap
   — the income used to drop to its own line on narrow/foldable screens, wasting
   vertical space). Tokens never break mid-word; the gap tightens to fit. */
.metric-strip {
  display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: nowrap;
  margin: var(--s-2) 0 var(--s-1); font-size: var(--fs-sm); color: var(--muted);
}
.metric-strip > span { white-space: nowrap; }
.metric-strip b { font-family: var(--font-num); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; color: var(--on-surface); }
.metric-strip b.keep { color: var(--positive); }
.metric-strip b.risk { color: var(--danger); }
.metric-strip .income { margin-left: auto; flex: none; font-family: var(--font-num); font-weight: var(--fw-black); color: var(--on-surface); }
/* Placed puts its ledger net in the same trailing slot, signed by color. */
.metric-strip .income.positive { color: var(--positive); }
.metric-strip .income.danger { color: var(--danger); }
/* Multi-leg DEFINED-RISK structure card: a subtle left accent marks the capped-risk family. */
.proposal.structure { border-left: 3px solid color-mix(in srgb, var(--primary) 45%, transparent); }
.proposal-foot { font-size: var(--fs-xs); color: var(--faint); margin-top: 2px; }

/* ---- "Why this pick" expandable explainer (deterministic, engine-sourced) ---- */
.proposal .why { margin-top: var(--s-2); border-top: 1px solid var(--border); }
.proposal .why > summary {
  list-style: none; cursor: pointer; padding: var(--s-2) 0 0;
  font-size: var(--fs-xs); font-weight: var(--fw-bold); color: var(--muted);
  display: flex; align-items: center; gap: 6px;
}
.proposal .why > summary::-webkit-details-marker { display: none; }
.proposal .why > summary::after {
  content: '▸'; margin-left: auto; color: var(--faint); transition: transform 0.15s ease;
}
.proposal .why[open] > summary::after { transform: rotate(90deg); }
.why-body { padding: var(--s-2) 0 var(--s-1); display: grid; gap: var(--s-3); }
.why-head { font-size: var(--fs-xs); color: var(--faint); margin-bottom: 4px; }
.why-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; }
.why-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2);
  font-size: var(--fs-sm); color: var(--muted);
}
.why-list li b { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--on-surface); flex: none; }
.why-list b.why-cost { color: var(--warning); }
.why-list .why-none { color: var(--positive); }
.why-metrics .term { border-bottom: 1px dotted color-mix(in srgb, var(--muted) 45%, transparent); }

/* ---- Advisory "Quality score" (parallel weighted composite) ---- */
.why-section.quality .why-head { color: var(--muted); }
.qual-list li.qual-row { display: grid; grid-template-columns: 1fr 72px auto; align-items: center; gap: var(--s-2); }
.qual-bar { height: 6px; border-radius: 3px; background: color-mix(in srgb, var(--muted) 22%, transparent); overflow: hidden; }
.qual-bar > i { display: block; height: 100%; border-radius: 3px; background: var(--muted); }
.why-section.quality.qual-High .qual-bar > i { background: var(--positive); }
.why-section.quality.qual-Medium .qual-bar > i { background: var(--warning); }
.why-section.quality.qual-Low .qual-bar > i { background: var(--danger); }

/* ---- Placed "scoreboard" — sells the track record ---- */
.scoreboard {
  background: radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--primary) 16%, transparent), transparent 60%), var(--surface-raised);
  border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
  border-radius: var(--r-md); padding: var(--s-4); margin: var(--s-1) 0 var(--s-3); box-shadow: var(--e-1);
}
.scoreboard-big { font-family: var(--font-num); font-weight: var(--fw-black); font-size: 1.4rem; color: var(--positive); letter-spacing: -0.01em; }
.scoreboard-sub { font-size: var(--fs-sm); color: var(--muted); margin-top: 2px; }

/* ---- Collapsible cards (native <details>): About + Book risk ---- */
details.card > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: var(--s-2);
}
details.card > summary::-webkit-details-marker { display: none; }
details.card > summary::after {
  content: '▸'; margin-left: auto; color: var(--faint); transition: transform 0.15s ease; flex: none;
}
details.card[open] > summary::after { transform: rotate(90deg); }
.about-card > summary h2 { margin: 0; }

/* A card that acts as a tappable disclosure (opens a sheet) — same chevron
 * affordance as the collapsible <details> cards, so "Learn the terms" matches the
 * About row's look instead of a standalone button. */
.disclosure-card {
  display: flex; align-items: center; gap: var(--s-2); width: 100%; text-align: left;
  cursor: pointer; background: var(--surface); color: inherit;
  /* The base button rule sets white-space:nowrap + justify-content:center +
     overflow:hidden. On a narrow screen the description can't fit on one line, so a
     centered nowrap line overflows and is clipped at BOTH card edges. Let the text
     wrap and start at the left so it reads correctly at any width. */
  justify-content: flex-start; white-space: normal;
  /* This is a card row, not a CTA — opt out of the 330px button cap so it spans the
     column like the sibling About <details> card. On a wide screen (tablet, foldable
     inner display) the cap otherwise leaves it visibly short of the cards around it. */
  max-width: none;
}
/* Flex items default to min-width:auto and won't shrink below their content's
   intrinsic width — the other half of the overflow. Let the label/description column
   shrink (so it wraps inside the card) and take the row's free space so the chevron
   still sits flush right. */
.disclosure-card > div { flex: 1; min-width: 0; }
.disclosure-card::after {
  content: '▸'; margin-left: auto; color: var(--faint); flex: none;
}

/* ---- Book risk overlay (#1): concentration + directional tilt ---- */
.book-risk-summary b { font-size: var(--fs-md); }
.book-risk-body { margin-top: var(--s-3); }
.book-risk-card .book-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); margin: var(--s-3) 0; }
.book-k { font-size: var(--fs-xs); color: var(--faint); }
.book-v { font-family: var(--font-num); font-weight: var(--fw-black); color: var(--on-surface); margin-top: 2px; }
.book-v.tilt { font-family: var(--font-body); font-weight: var(--fw-bold); font-size: var(--fs-sm); }
.book-warn { background: color-mix(in srgb, var(--warning) 12%, transparent); border-radius: var(--r-sm); padding: var(--s-2) var(--s-3); margin: var(--s-2) 0; font-size: var(--fs-sm); color: var(--warning); display: grid; gap: 2px; }
.conc-group { margin-top: var(--s-3); }
.conc-head { font-size: var(--fs-xs); color: var(--faint); margin-bottom: var(--s-2); }
.conc-row { display: flex; align-items: center; gap: var(--s-2); margin: 3px 0; font-size: var(--fs-sm); }
.conc-label { flex: 0 0 26%; color: var(--on-surface); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conc-track { flex: 1; height: 8px; border-radius: 4px; background: color-mix(in srgb, var(--muted) 22%, transparent); overflow: hidden; }
.conc-fill { display: block; height: 100%; border-radius: 4px; background: var(--primary); }
.conc-row.high .conc-fill { background: var(--warning); }
.conc-pct { flex: 0 0 auto; font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--muted); min-width: 42px; text-align: right; }
.conc-row.high .conc-pct { color: var(--warning); font-weight: var(--fw-bold); }

/* Pro-forma concentration note in the Place sheet (advisory) */
.proforma { border-radius: var(--r-sm); padding: var(--s-2) var(--s-3); margin: 0 0 var(--s-3); font-size: var(--fs-sm); display: grid; gap: 2px; }
.proforma.info { background: color-mix(in srgb, var(--primary) 10%, transparent); color: var(--on-surface); }
.proforma.warn { background: color-mix(in srgb, var(--warning) 14%, transparent); color: var(--warning); }
.proforma-head { font-weight: var(--fw-bold); }

/* Capital-at-stake line in the Place sheet (#56). */
.capital-line {
  display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap;
  font-size: var(--fs-sm); color: var(--muted); margin: var(--s-2) 0 0;
}
.capital-line b { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--on-surface); }
.capital-line.warn b { color: var(--warning); }
/* Fund-the-delta control: a full-width button whose label wraps onto two lines
   (amount headline + sub-line). Overrides the base button nowrap/ellipsis so the
   text is fully readable rather than clipped/spilling. */
.fund-delta-btn {
  width: 100%; margin-top: var(--s-2);
  min-height: 52px; padding: var(--s-2) var(--s-3);
  white-space: normal; overflow: visible; text-overflow: clip;
  justify-content: flex-start; text-align: left; gap: var(--s-3);
}
.fund-delta-label { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.fund-delta-label b { font-variant-numeric: tabular-nums; }
.fund-delta-sub { font-size: var(--fs-xs); color: var(--muted); font-weight: var(--fw-medium); }

/* Net Worth card: total on the summary line, then the named parts — cash available,
   stocks owned at today's price, and each lot's cost basis + profit underneath. */
.balance-list { margin-top: var(--s-3); display: grid; gap: var(--s-2); }
.balance-row { display: grid; grid-template-columns: 1fr auto auto; align-items: baseline; gap: var(--s-2); font-size: var(--fs-sm); color: var(--muted); }
.balance-row .balance-tk { color: var(--on-surface); font-weight: var(--fw-medium); }
.balance-row b { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--on-surface); }
/* A holding sits indented under "Stocks owned" so the two reading levels are obvious. */
.balance-hold { display: grid; gap: 2px; padding-left: var(--s-3); }
.balance-detail {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: var(--s-2);
  font-size: var(--fs-xs);
}
.balance-detail .gain { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-weight: var(--fw-medium); }
.balance-gain-row { padding-top: var(--s-2); border-top: 1px solid var(--border); }
.gain { color: var(--muted); }
.gain.up { color: var(--positive); }
.gain.down { color: var(--danger); }

/* ---- Market-posture banner (#2): stricter-only regime notice ---- */
.posture-banner { border-radius: var(--r-md); padding: var(--s-3); margin: 0 0 var(--s-3); border: 1px solid transparent; }
.posture-banner.elevated { background: color-mix(in srgb, var(--warning) 12%, transparent); border-color: color-mix(in srgb, var(--warning) 35%, transparent); }
.posture-banner.hostile { background: color-mix(in srgb, var(--danger) 12%, transparent); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
/* Neutral tone — used by the "market closed, these are last-close prices" note.
   Informational context, so it must NOT read as a warning. */
.posture-banner.calm { background: color-mix(in srgb, var(--muted) 10%, transparent); border-color: color-mix(in srgb, var(--muted) 28%, transparent); }
.posture-head { display: flex; align-items: center; gap: 6px; font-weight: var(--fw-bold); font-size: var(--fs-sm); }
.posture-banner.elevated .posture-head { color: var(--warning); }
.posture-banner.hostile .posture-head { color: var(--danger); }
.posture-banner.calm .posture-head { color: var(--muted); }
.posture-head svg { width: 15px; height: 15px; }
.posture-msg { font-size: var(--fs-sm); color: var(--on-surface); margin-top: 4px; line-height: 1.4; }

/* ---- Live status line on a placed order (quote-inferred) ---- */
.live-status { display: flex; align-items: flex-start; gap: var(--s-2); font-size: var(--fs-sm); color: var(--muted); line-height: 1.35; }
.live-status b { color: var(--on-surface); }
.live-status .live-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); margin-top: 5px; }
.live-status.ok .live-dot { background: var(--positive); }
.live-status.ok b { color: var(--positive); }
.live-status.warn .live-dot { background: var(--warning); }
.live-status.warn b { color: var(--warning); }

.plain-summary { margin: var(--s-3) 0 0; font-size: var(--fs-sm); color: var(--on-surface); line-height: 1.4; }
.metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-2); margin: var(--s-3) 0 var(--s-3); }
.metric { text-align: center; background: var(--surface-2); border-radius: var(--r-sm); padding: var(--s-3) var(--s-1); }
.metric .val { font-family: var(--font-num); font-weight: var(--fw-black); font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.metric .lbl { font-size: 0.68rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 2px; }
.metric.keep .val { color: var(--positive); }
.metric.assign .val { color: var(--on-surface); }

.confidence-pill { padding: var(--pill-pad); border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: var(--fw-black); display: inline-flex; align-items: center; gap: var(--pill-gap); }
.confidence-High { background: color-mix(in srgb, var(--positive) 16%, transparent); color: var(--positive); }
.confidence-Medium { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
.confidence-Low { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }

.conf-bar { height: 6px; border-radius: var(--r-pill); background: var(--surface-2); overflow: hidden; margin-top: var(--s-1); }
.conf-bar > i { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--green-400), var(--green-600)); transition: width var(--dur-3) var(--ease); }

.assignment-note {
  display: flex; gap: var(--s-2); align-items: center;
  font-size: var(--fs-xs); color: var(--muted);
  margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px dashed var(--border);
}
.limit-row { display: flex; justify-content: space-between; align-items: baseline; margin-top: var(--s-3); }
.limit-row .limit { font-family: var(--font-num); font-weight: var(--fw-black); font-size: 1.1rem; font-variant-numeric: tabular-nums; }

/* ---- Skeleton ---- */
.skeleton { position: relative; overflow: hidden; background: var(--skeleton); border-radius: var(--r-md); height: 132px; margin-bottom: var(--s-3); }
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--skeleton-shine) 60%, transparent), transparent);
  transform: translateX(-100%); animation: shimmer 1.3s infinite;
}
@keyframes shimmer { 100% { transform: translateX(100%); } }

/* ---- States ---- */
.state { text-align: center; padding: var(--s-6) var(--s-4); }
.state .glyph { width: 64px; height: 64px; margin: 0 auto var(--s-4); border-radius: var(--r-lg); display: grid; place-items: center; background: var(--surface-2); }
.state.error .glyph { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.state.no-trade .glyph { background: color-mix(in srgb, var(--warning) 16%, transparent); color: var(--warning); }
.state.empty .glyph { background: var(--primary-container); color: var(--primary); }

/* ---- Goal ring (progress) ---- */
.ring-wrap { display: flex; align-items: center; gap: var(--s-5); }
/* The donut hole is an absolutely-positioned ::before, so .ring MUST be positioned —
   otherwise the hole's containing block is whatever ancestor happens to be positioned
   (today: none, so the initial containing block) and it only lands centered because the
   auto-offset static position falls back to the grid area. `inset` off a positioned
   .ring makes the geometry explicit instead: hole = ring inset by the stroke width.
   Same construction as .snapshot-mini-ring below. */
.ring { --p: 0; --ring-stroke: 13px; width: 104px; height: 104px; flex: none; border-radius: 50%; position: relative;
  background: conic-gradient(var(--primary) calc(var(--p) * 1%), var(--surface-2) 0);
  display: grid; place-items: center; transition: --p var(--dur-3) var(--ease); }
.ring::before { content: ''; position: absolute; inset: var(--ring-stroke); border-radius: 50%; background: var(--surface); }
.ring .ring-label { position: relative; font-family: var(--font-num); font-weight: var(--fw-black); font-size: 1.3rem; }
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); margin-top: var(--s-4); }
.stat { background: var(--surface-2); border-radius: var(--r-sm); padding: var(--s-3); }
.stat .n { font-family: var(--font-num); font-weight: var(--fw-black); font-size: 1.3rem; font-variant-numeric: tabular-nums; }
.stat .k { font-size: var(--fs-xs); color: var(--muted); }

/* ---- Trailing-12-months income bar chart ---- */
.bar-chart { display: flex; align-items: flex-end; gap: var(--s-1); height: 168px; }
.bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; height: 100%; min-width: 0; }
.bar-amt { font-size: 0.6rem; font-weight: var(--fw-bold); color: var(--muted); height: 1.1em; margin-bottom: 6px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bar-track { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.bar-fill {
  width: 100%; border-radius: var(--r-sm) var(--r-sm) 0 0; min-height: 3px;
  background: linear-gradient(180deg, var(--green-400), var(--green-600));
  transition: height var(--dur-3) var(--ease);
}
.bar-col.current .bar-fill { background: linear-gradient(180deg, var(--gold-400), var(--gold-500)); }
.bar-col.current .bar-lbl { color: var(--on-surface); font-weight: var(--fw-black); }
.bar-lbl { font-size: 0.6rem; color: var(--faint); margin-top: var(--s-1); }

/* ROI line on the resolved-trades card (return on committed capital). */
.roi-line {
  display: flex; align-items: baseline; gap: var(--s-2); margin-top: var(--s-3);
  padding-top: var(--s-3); border-top: 1px solid var(--border);
}
.roi-line .roi-val {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-weight: var(--fw-black); color: var(--positive); margin-left: auto;
}

/* ---- Bottom nav ----
   left/right:0 + margin:auto (rather than a bare full-bleed bar) so the tab row
   tracks the content column instead of stretching across a wide window — on a
   desktop screen the un-constrained version scattered three tabs along the whole
   bottom edge, miles from the content they navigate. */
.nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  max-width: var(--maxw); margin: 0 auto;
  display: flex; justify-content: space-around;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(14px);
  border-top: 1px solid var(--border);
  padding: var(--s-2) var(--s-2) calc(env(safe-area-inset-bottom, 0px) + var(--s-2));
}
.nav button {
  background: none; box-shadow: none; color: var(--muted);
  min-height: auto; padding: var(--s-1) var(--s-3); border-radius: var(--r-sm);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 0.66rem; font-weight: var(--fw-bold);
}
.nav button svg { width: 22px; height: 22px; }
.nav button[aria-current='true'] { color: var(--primary); }
.nav button[aria-current='true'] svg { transform: translateY(-1px); }

/* ---- Bottom sheet / wizard ---- */
.scrim { position: fixed; inset: 0; background: var(--scrim); z-index: 40; opacity: 0; animation: fade var(--dur-2) forwards; }
@keyframes fade { to { opacity: 1; } }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  background: var(--surface); border-radius: var(--r-lg) var(--r-lg) 0 0;
  padding: var(--s-4) var(--s-5) calc(env(safe-area-inset-bottom, 0px) + var(--s-5));
  max-width: var(--maxw); margin: 0 auto; box-shadow: var(--e-3);
  animation: sheetUp var(--dur-3) var(--ease) both;
  max-height: 90vh; overflow-y: auto;
}
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: none; } }
.sheet .grabber { width: 40px; height: 4px; border-radius: var(--r-pill); background: var(--border-strong); margin: 0 auto var(--s-4); }

/* ---- Full-screen sheet ----
   Pinning `top:0` against the base sheet's `bottom:0` fills the viewport with no
   vh/dvh unit at all (so no iOS toolbar clipping). The sheet itself stops
   scrolling; the ONE designated child (.chain-scroll) scrolls instead, which is
   what keeps a wide table's overflow inside its own box and off the page body. */
.sheet.sheet-full {
  top: 0; border-radius: 0; max-height: none; overflow: hidden;
  display: flex; flex-direction: column;
  padding: calc(env(safe-area-inset-top, 0px) + var(--s-4)) var(--s-4) calc(env(safe-area-inset-bottom, 0px) + var(--s-3));
}

/* ---- Options-chain snapshot (±10 strikes around spot) ---- */
.chain-head { flex: none; display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3); }
.chain-head-t { min-width: 0; }
.chain-title { font-size: var(--fs-h2); font-weight: var(--fw-black); letter-spacing: -0.01em; }
.chain-sub { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }
.chain-close { flex: 0 0 auto; }
/* Calls/Puts segmented switch (only when the pick's expiry has both sides). */
.chain-sides {
  flex: none; display: flex; gap: 2px; padding: 3px; margin: var(--s-3) 0 var(--s-2);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-pill);
}
.chain-sides button {
  flex: 1; min-height: 36px; padding: 0 var(--s-3); border: none; background: transparent;
  color: var(--muted); font-size: var(--fs-sm); font-weight: var(--fw-bold);
  border-radius: var(--r-pill); cursor: pointer; box-shadow: none;
}
.chain-sides button[aria-pressed='true'] { background: var(--surface); color: var(--on-surface); box-shadow: var(--e-1); }
.chain-scroll {
  flex: 1 1 auto; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch;
  margin-top: var(--s-2); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface);
}
/* Six columns have to fit a 390pt phone, so cells run tight (6px side padding, a
   notch below body size) with a little more breathing room only at the two outer
   edges. .chain-scroll still scrolls horizontally as the safety net for an extreme
   case (a six-figure OI at large Dynamic Type) — the page body never does. */
.chain-table {
  width: 100%; border-collapse: collapse;
  font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: 0.8rem;
}
.chain-table th {
  position: sticky; top: 0; z-index: 1; background: var(--surface-2); color: var(--muted);
  font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: var(--fw-bold);
  text-align: right; padding: var(--s-2) 6px; white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.chain-table th.term { border-bottom: 1px dotted color-mix(in srgb, var(--muted) 45%, transparent); }
.chain-table td {
  padding: var(--s-2) 6px; text-align: right; white-space: nowrap;
  border-top: 1px solid var(--border); color: var(--on-surface);
}
.chain-table th:first-child, .chain-table td:first-child { text-align: left; padding-left: var(--s-3); }
.chain-table th:last-child, .chain-table td:last-child { padding-right: var(--s-3); }
.chain-table tbody tr:first-child td { border-top: none; }
/* Order matters: a pick row must win over the thin/ATM tints below it. */
.chain-row.is-thin td { color: var(--faint); }
.chain-row.is-atm { background: var(--surface-2); }
.chain-row.is-pick { background: var(--primary-container); }
.chain-row.is-pick td { color: var(--on-primary-container); font-weight: var(--fw-bold); }
.chain-tag {
  display: inline-block; margin-left: 5px; padding: 1px 5px; border-radius: var(--r-pill);
  font-family: var(--font-sans); font-size: 0.6rem; font-weight: var(--fw-bold); letter-spacing: 0.02em;
  background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--muted);
}
.chain-row.is-pick .chain-tag { background: color-mix(in srgb, var(--primary) 22%, transparent); color: var(--on-primary-container); }
.chain-note { flex: none; color: var(--faint); font-size: var(--fs-xs); line-height: 1.4; margin-top: var(--s-2); }

/* ---- Terms screen: scrolling body + pinned Accept footer ---- */
/* terms body: the disclaimer sits flush under the terms card (scaffold gap handles spacing). */
.prehome-body .disclaimer:last-child { margin-bottom: 0; }

/* ---- Glossary sheet ---- */
.glossary-search { margin: var(--s-3) 0 var(--s-2); }
.glossary-search input { min-height: 44px; }
.glossary-list { display: flex; flex-direction: column; gap: var(--s-2); }
.glossary-item {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: var(--s-3) var(--s-4);
}
.glossary-item > summary {
  cursor: pointer; list-style: none; display: flex; flex-direction: column; gap: 2px;
}
.glossary-item > summary::-webkit-details-marker { display: none; }
.glossary-item > summary b { font-size: var(--fs-body); }
.glossary-item .gl-short { color: var(--muted); font-size: var(--fs-sm); }
.glossary-item[open] { border-color: color-mix(in srgb, var(--primary) 35%, var(--border)); }
.glossary-item > p { margin: var(--s-3) 0 0; color: var(--on-surface); font-size: var(--fs-sm); line-height: 1.45; }

/* Tappable in-context term (metric labels / limit) — a subtle dotted underline
   signals "tap to learn" without shouting. */
.term { cursor: pointer; }
.term > b, .term { border-bottom: 1px dotted transparent; }
.metric-strip .term { border-bottom: 1px dotted color-mix(in srgb, var(--muted) 45%, transparent); }
.proposal-foot .term { border-bottom: 1px dotted color-mix(in srgb, var(--faint) 55%, transparent); }
.wizard-steps { display: flex; gap: var(--s-1); margin-bottom: var(--s-4); }
.wizard-steps i { flex: 1; height: 4px; border-radius: var(--r-pill); background: var(--border); transition: background var(--dur-2); }
.wizard-steps i.done { background: var(--primary); }
.wizard-steps i.active { background: var(--primary); opacity: 0.6; }

.resolve-hint { display: flex; align-items: center; gap: var(--s-2); margin-top: var(--s-2); font-size: var(--fs-sm); color: var(--muted); min-height: 1.4em; }
.resolve-hint.ok { color: var(--positive); }
.resolve-chip { background: var(--primary-container); color: var(--on-primary-container); padding: var(--pill-pad); border-radius: var(--r-pill); font-weight: var(--fw-bold); }
/* Loading indicator = the brand crown logo (breathing pulse), painted in the current
   text color via a mask so it adapts to light/dark and any context. Every `.spinner`
   in the app becomes the logo — one definition, used everywhere. Inline width/height
   overrides (e.g. 12px) still scale it via mask-size:contain. */
.spinner {
  display: inline-block; width: 16px; height: 16px; flex: none; vertical-align: -0.15em;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 7 7 12 12 5 17 12 21 7 21 18a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 7 7 12 12 5 17 12 21 7 21 18a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z'/%3E%3C/svg%3E") center / contain no-repeat;
  animation: crownPulse 1.1s ease-in-out infinite;
}
@keyframes crownPulse { 0%, 100% { opacity: 0.3; transform: scale(0.85); } 50% { opacity: 1; transform: scale(1); } }

/* ---- About rows (label + value, label bold) ---- */
.about-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3); padding: var(--s-2) 0; border-bottom: 1px solid var(--border); }
.about-row:last-child { border-bottom: none; }
.about-k { font-weight: var(--fw-bold); color: var(--on-surface); flex: none; }
.about-v { color: var(--muted); font-size: var(--fs-sm); text-align: right; overflow-wrap: anywhere; }
.about-note { color: var(--faint); font-size: var(--fs-xs); line-height: 1.4; margin-top: var(--s-3); }

.list-row { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); padding: var(--s-3) 0; border-bottom: 1px solid var(--border); }
.list-row:last-child { border-bottom: none; }
.toast { position: fixed; left: 50%; bottom: calc(var(--tap) + var(--s-5)); transform: translateX(-50%); z-index: 50;
  background: var(--ink-800); color: #fff; padding: var(--s-3) var(--s-4); border-radius: var(--r-pill); font-size: var(--fs-sm); box-shadow: var(--e-3); animation: rise var(--dur-2) var(--ease) both; }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

.row-between { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); }

/* Account card header: keep the fixed Sign-out button pinned right and truncate a
   long email/name to one line with an ellipsis instead of wrapping (flex children
   default to min-width:auto, so keep min-width:0 to let the text column shrink). */
.acct-head { align-items: flex-start; }
.acct-head .acct-who { min-width: 0; }
.acct-head .btn-secondary { flex: 0 0 auto; white-space: nowrap; }
/* Name on top of the account card, email/provider muted beneath it. Both lines can
   carry the email, so truncate each to a single line. */
.acct-who .acct-name { font-weight: var(--fw-bold); margin-top: 2px; }
.acct-head .acct-name,
.acct-head .muted { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Backup state, under the identity. Sits OUTSIDE .acct-head so it may wrap — it is a
   sentence, not a label, and truncating "we'll retry automatically" to one line would
   hide the reassuring half of it. */
.acct-sync { margin-top: var(--s-3); display: flex; align-items: center; gap: var(--s-1); }
.acct-sync svg { flex: 0 0 auto; }

/* ---- Device-only (no account) warning — web/local-only.ts ----
   One warning vocabulary shared by the Home/Income banner, the Settings account
   card and the sign-in sheet, so the three can never look like different messages.
   Amber, not red: this is a risk the user can still fix, not a failure. */
.acct-local { border: 1px solid var(--warning); }
.acct-local-head { display: flex; align-items: flex-start; gap: var(--s-3); }
.acct-local-mark { flex: none; display: inline-flex; color: var(--warning); }
.acct-local-mark svg { width: 22px; height: 22px; }

/* The slim banner under the app bar. Full-bleed (negative side margins cancel the
   page gutter) so it reads as a strip attached to the bar rather than a card. */
.local-warn {
  display: flex; align-items: center; gap: var(--s-3);
  margin: 0 calc(var(--s-4) * -1) var(--s-3); padding: var(--s-3) var(--s-4);
  background: var(--surface-2); border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--warning);
  font-size: var(--fs-sm);
}
.local-warn-mark { flex: none; display: inline-flex; color: var(--warning); }
.local-warn-mark svg { width: 20px; height: 20px; }
/* ONE line, always: the strip must never grow tall enough to push Top Options below
   the fold. The copy is five words (local-only.ts), so it fits a 360px phone next to
   the CTA; the ellipsis is the safety net for large Dynamic Type. */
.local-warn-text {
  flex: 1; min-width: 0; font-weight: var(--fw-medium);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.local-warn-cta {
  flex: none; min-height: 34px; padding: 0 var(--s-4); border-radius: var(--r-pill);
  background: var(--primary); color: var(--on-primary);
  font-weight: var(--fw-bold); font-size: var(--fs-sm);
}
.local-warn-x {
  flex: none; background: none; box-shadow: none; color: var(--muted);
  min-height: auto; padding: var(--s-1); border-radius: var(--r-sm);
}
.local-warn-x svg { width: 16px; height: 16px; }

/* The sign-in sheet (the banner's CTA + the once-per-session post-trade nudge). */
.local-sheet-head { display: flex; align-items: flex-start; gap: var(--s-3); }
.local-sheet-mark { flex: none; display: inline-flex; color: var(--warning); }
.local-sheet-mark svg { width: 24px; height: 24px; }
.local-sheet-head .muted { margin-top: var(--s-2); font-size: var(--fs-sm); }
.local-sheet-actions { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-4); }

/* ---- Intro slideshow (post-install interstitials) ---- */
/* ---- Pre-home scaffold ----
   intro / onboarding / terms all render inside ONE frame so the flow feels like a
   single product: a safe-area-aware header (crown + "OptionsKing", + an optional
   right action like Skip), a flexible body, and a bottom CTA zone. Only the body
   content differs between the three screens. (Bottom sheets/popups are NOT part of
   this — they keep their own sheet chrome.) */
.prehome { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }
.prehome-head {
  flex: none; display: flex; align-items: center; gap: var(--s-3);
  padding: calc(env(safe-area-inset-top, 0px) + var(--s-4)) 0 var(--s-4);
}
.prehome-head h1 { flex: 1 1 auto; min-width: 0; margin: 0; font-size: var(--fs-h1); font-weight: var(--fw-black); letter-spacing: -0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.prehome-skip { flex: none; margin-left: var(--s-2); color: var(--muted); }
.prehome-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; }
.prehome-foot {
  flex: none; display: flex; flex-direction: column; gap: var(--s-3);
  padding: var(--s-4) 0 calc(env(safe-area-inset-bottom, 0px) + var(--s-4));
  background: linear-gradient(0deg, var(--bg-grad-bottom) 78%, transparent);
}
@media (max-width: 360px) { .prehome-head h1 { font-size: clamp(1.15rem, 5.4vw, 1.5rem); } }

/* Public web landing page (screens/onboarding.ts renderWebLanding) — the signed-out
   home page of optionsking.app. Native shells never render these classes.

   .prehome-landing turns the shared pre-home shell back into an ordinary DOCUMENT.
   The three app pre-home screens (intro/onboarding/terms) are fixed-height panels:
   100dvh, an inner-scrolling body, a pinned CTA foot. This page is longer than the
   viewport by design, and in that shell the inner scroll produces a scrollbar inside a
   scrollbar plus dead space above the foot. So: no forced height, no inner scroll —
   the page scrolls as one, and the hero "Try OptionsKing free" button is what carries
   the reader down to the sign-in CTA at the end. */
.prehome-landing { min-height: 0; }
.prehome-landing .prehome-body { flex: 0 0 auto; overflow-y: visible; }
/* #app reserves 96px of bottom padding to clear the app's FIXED bottom nav. The
   landing page is signed-out, so no nav is rendered and that reservation becomes a
   band of dead space between the sign-in CTA and the legal footer. Browsers without
   :has() simply keep the extra whitespace — cosmetic, never broken. */
#app:has(.prehome-landing) { padding-bottom: 0; }
.lp-hero {
  font-size: var(--fs-display); font-weight: var(--fw-black); letter-spacing: -0.02em;
  margin: 0 0 var(--s-3); line-height: 1.1;
}
.lp-h {
  font-size: var(--fs-sm); font-weight: var(--fw-black); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted); margin: 0 0 var(--s-3);
}
/* Sign-in screen (screens/signin.ts) — the list of ways in. Each <li> is one button
   plus its explanatory note, so the vertical rhythm belongs to the list, not to the
   buttons (which are shared with onboarding and Settings and must not carry margins).
   Centred as a column because .btn-block caps at 330px. */
.signin-list { list-style: none; padding: 0; margin: var(--s-5) 0 0; }
.signin-method { margin-bottom: var(--s-4); }
.signin-note { margin: var(--s-2) 0 0; }
/* A text-weight action sitting inside a sentence ("Already have an account? Sign in").
   Overrides the global button chrome: no pill height, no padding, no width cap. */
.btn-inline {
  display: inline; min-height: 0; padding: 0; border-radius: 0;
  font: inherit; font-weight: var(--fw-bold); text-decoration: underline;
  vertical-align: baseline; max-width: none;
}
.lp-step-n {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; color: #fff; font-weight: var(--fw-black);
  font-size: var(--fs-sm); background: linear-gradient(145deg, var(--green-400), var(--green-700));
}

.intro-slide { flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: center; padding: var(--s-4) var(--s-2); animation: rise var(--dur-3) var(--ease) both; }
.intro-title { font-size: var(--fs-display); font-weight: var(--fw-black); letter-spacing: -0.02em; margin: 0 0 var(--s-3); }
.intro-glyph {
  width: 96px; height: 96px; margin: 0 auto var(--s-5); border-radius: var(--r-lg);
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(145deg, var(--green-400), var(--green-700));
  box-shadow: var(--e-2);
}
.intro-glyph svg { width: 44px; height: 44px; }
.intro-body { color: var(--on-surface); font-size: 1.05rem; line-height: 1.5; max-width: 32ch; margin: 0 auto; }
.intro-dots { display: flex; gap: var(--s-2); justify-content: center; margin-bottom: var(--s-2); }
.intro-dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); transition: width var(--dur-2), background var(--dur-2); }
.intro-dots i.active { width: 22px; border-radius: var(--r-pill); background: var(--primary); }

/* ---- Home goal snapshot (compact strip) ---- */
.snapshot-strip {
  width: 100%; display: flex; align-items: center; gap: var(--s-3);
  background: transparent; color: inherit; box-shadow: none;
  min-height: auto; padding: var(--s-2) var(--s-1); margin-bottom: var(--s-1);
  border-radius: var(--r-sm);
}
.snapshot-strip:active { background: var(--surface-2); }
.snapshot-text { flex: 1; text-align: left; font-size: var(--fs-sm); }
.snapshot-text b { font-family: var(--font-num); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.snapshot-chev { color: var(--faint); display: inline-flex; }
.snapshot-chev svg { width: 18px; height: 18px; }
.snapshot-mini-ring {
  flex: none; width: 32px; height: 32px; border-radius: 50%; position: relative; isolation: isolate;
  display: grid; place-items: center; font-size: 0.62rem; font-weight: var(--fw-black);
  color: var(--on-surface);
  background: conic-gradient(var(--primary) calc(var(--p) * 1%), var(--surface-2) 0);
}
.snapshot-mini-ring::before { content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--surface); z-index: -1; }

/* ---- Wizard: add-a-holding controls (budget step) ---- */
/* Ticker on its own full-width row; quantity + Add on the next row, so the qty
   box is comfortably wide (the old single-row layout squeezed it to a sliver). */
.hold-add { display: flex; flex-direction: column; gap: var(--s-2); }
.hold-add .hold-ticker { width: 100%; }
/* Quantity + price-paid + Add wrap onto a second line rather than squeezing: three
   controls never fit one phone-width row legibly. */
.hold-qty-row { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: stretch; }
.hold-qty-row .hold-qty { flex: 1 1 40%; min-width: 118px; }
.hold-qty-row .btn-secondary { flex: none; white-space: nowrap; }

/* ---- Wizard: multi-ticker holdings list (budget step) ---- */
.hold-list { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-3); }
.hold-row {
  display: flex; align-items: center; gap: var(--s-2);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3); font-size: var(--fs-sm);
}
.hold-row .hold-tk { font-weight: var(--fw-bold); color: var(--on-surface); flex: 1; }
/* Price-paid, editable in place. Narrow and quiet — it's an optional detail on a row
   whose subject is the holding, not a primary field. */
.hold-row .hold-cost { flex: 0 1 124px; min-width: 96px; }
.hold-row .hold-cost input { min-height: 34px; padding-top: 0; padding-bottom: 0; font-size: var(--fs-sm); }
.hold-row .hold-cost > span { left: var(--s-2); font-weight: var(--fw-medium); }
.hold-row .hold-cost input { padding-left: calc(var(--s-2) + 0.9em); }
.hold-row .hold-x {
  flex: none; min-height: 0; padding: 2px; background: transparent; box-shadow: none;
  color: var(--muted); border: none;
}
.hold-row .hold-x svg { width: 16px; height: 16px; }

/* ---- Home tabs (Recommendations / Placed) — equal-width segmented row ---- */
.stage-tabs {
  display: flex; gap: var(--s-2); padding: var(--s-1) 0 var(--s-3);
}
.stage-tab {
  flex: 1; min-height: 42px; padding: 0 var(--s-3); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
  box-shadow: none; font-size: var(--fs-sm); font-weight: var(--fw-bold);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2); white-space: nowrap;
}
.stage-tab[aria-selected='true'] { background: var(--primary-container); color: var(--on-primary-container); border-color: transparent; }
.stage-count {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--primary) 22%, transparent); color: var(--on-primary-container);
  font-size: 0.68rem; font-weight: var(--fw-black); display: inline-grid; place-items: center;
}
.stage-tab[aria-selected='true'] .stage-count { background: var(--primary); color: var(--on-primary); }

/* ---- Watchlist chips (wizard + settings) ---- */
.wl-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.wl-chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 6px 6px 6px var(--s-3); border-radius: var(--r-pill);
  background: var(--primary-container); color: var(--on-primary-container);
  font-weight: var(--fw-bold); font-size: var(--fs-sm);
}
.wl-x {
  display: inline-grid; place-items: center; width: 22px; height: 22px; min-height: 22px;
  padding: 0; border-radius: 50%; background: color-mix(in srgb, var(--on-primary-container) 12%, transparent);
  color: inherit; box-shadow: none;
}
.wl-x svg { width: 14px; height: 14px; }

/* ---- Ideas section header: title + icon-only refresh on ONE line ---- */
.ideas-head { flex-wrap: nowrap; }
.ideas-head-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ideas-refresh {
  flex: none; width: 40px; min-width: 40px; height: 40px; min-height: 40px; padding: 0;
  display: inline-grid; place-items: center; border-radius: var(--r-pill);
}
.ideas-refresh svg { width: 20px; height: 20px; }

/* ---- Ideas ad-hoc lookup (quiet single-line search, not a card) ---- */
.ideas-search { display: flex; gap: var(--s-2); align-items: center; margin: var(--s-1) 0; }
/* The ad-hoc resolve hint reserves no vertical space until it actually has text
   (avoids an empty gap between the search box and the watchlist header). */
#ideas-hint:empty { display: none; margin: 0 !important; }
.ideas-search .input-prefix input { min-height: 44px; background: var(--surface-2); border-color: var(--border); }
.ideas-search button { min-height: 44px; min-width: 44px; padding: 0 var(--s-3); }

/* ---- Idea grid compact lines (no-trade / error per watchlist ticker) ---- */
.idea-slot { }
.idea-line {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); margin-bottom: var(--s-2);
  border-radius: var(--r-sm); background: var(--surface-2); border: 1px solid var(--border);
}
.idea-line-t { font-weight: var(--fw-black); }
.idea-line.error { border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); }
.idea-line .muted { flex: 1; }

/* ---- Debug-only API-stats footer (DEBUG builds only; gated on bridge.isDebug) ---- */
.debug-footer {
  margin: var(--s-4) 0 var(--s-2); padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm); background: var(--surface-2);
  border: 1px dashed var(--border-strong); font-size: var(--fs-xs);
}
.debug-title {
  font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin-bottom: var(--s-2);
}
.debug-row { display: flex; justify-content: space-between; gap: var(--s-3); padding: 1px 0; }
.debug-row b { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--on-surface); }

/* ---- "Get the mobile app" promotion (WEB BUILD ONLY) ----
   Emitted only by app-promo.ts, which returns '' inside both native shells, so these
   rules are inert in the APK/IPA. Kept here rather than a web-only stylesheet because
   the bundle must stay byte-identical across platforms (verify_parity.mjs). */
.promo-banner {
  display: flex; align-items: center; gap: var(--s-3);
  margin: var(--s-2) calc(var(--s-4) * -1) 0; padding: var(--s-3) var(--s-4);
  background: var(--surface-2); border-bottom: 1px solid var(--border);
  font-size: var(--fs-sm);
}
.promo-banner .promo-mark { flex: none; display: inline-flex; color: var(--primary); }
.promo-banner .promo-mark svg { width: 22px; height: 22px; }
.promo-text { flex: 1; min-width: 0; line-height: 1.3; }
/* One-line only: the banner must never grow tall enough to push Top Options down. */
.promo-text .faint { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.promo-cta {
  flex: none; padding: var(--s-2) var(--s-4); border-radius: var(--r-pill);
  background: var(--primary); color: var(--on-primary); font-weight: var(--fw-bold);
  text-decoration: none; font-size: var(--fs-sm);
}
.promo-x {
  flex: none; background: none; box-shadow: none; color: var(--muted);
  min-height: auto; padding: var(--s-1); border-radius: var(--r-sm);
}
.promo-x svg { width: 16px; height: 16px; }
.promo-soon { color: var(--faint); font-size: var(--fs-sm); }

.promo-card { margin-top: var(--s-4); }
.promo-card-head { display: flex; align-items: flex-start; gap: var(--s-3); }
.promo-card-head .promo-mark { flex: none; color: var(--primary); display: inline-flex; }
.promo-card-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-3); }
.promo-store { text-decoration: none; display: inline-flex; align-items: center; justify-content: center; }

/* ===========================================================================
   RESPONSIVE TIERS
   Everything above this line is the phone layout (and the two max-width:360px
   refinements for foldable cover screens / large accessibility fonts). These two
   tiers widen it for tablets and desktops.

   These fire on iPad and large Android tablets too, not only on the web — that is
   intended (a tablet should not render a stretched phone), but it means any change
   here needs a tablet smoke test on BOTH shells, not just a browser resize.
   =========================================================================== */

/* ---- Tablet / small laptop ---- */
@media (min-width: 768px) {
  #app { max-width: var(--maxw-md); }
  .nav { max-width: var(--maxw-md); }

  /* Sheets stop being edge-to-edge drawers and become centred panels: rounded on
     all corners, lifted off the bottom edge, and capped so a tall desktop window
     doesn't produce a 1000px-tall sheet. sheet-full still fills the viewport. */
  .sheet {
    max-width: var(--maxw-md);
    border-radius: var(--r-lg);
    bottom: var(--s-5);
    max-height: min(90vh, 760px);
  }
  .sheet.sheet-full { bottom: 0; border-radius: 0; max-height: none; }

  /* Fixed 2-up grids get the room to go 4-up rather than growing very wide cells. */
  .stat-grid, .book-risk-card .book-metrics { grid-template-columns: repeat(4, 1fr); }

  /* The chain table's 6px side padding exists to fit six columns on a 390pt phone
     (see .chain-table above). With the wider column that constraint is gone. */
  .chain-table th, .chain-table td { padding-left: var(--s-3); padding-right: var(--s-3); }
  .chain-table { font-size: var(--fs-sm); }
}

/* ---- Desktop: bottom tab bar becomes a left rail ----
   A bar pinned to the bottom of a 900px-tall window is a phone idiom; at this width
   the tabs move into a persistent vertical rail. The rail lives in a body gutter of
   its own width, so it can never overlap the centred column no matter how narrow the
   remaining space gets.

   The gutter is MIRRORED on the right. Padding only the left centred the column in
   the space beside the rail, which reads as off-centre against the window: at 1512px
   the column sat 424px from the left edge and 208px from the right. Equal gutters
   put the column on the window's true centre line, and they double as the guard that
   keeps a narrow desktop window from sliding the column under the rail. */
@media (min-width: 1120px) {
  body { padding-left: var(--rail); padding-right: var(--rail); }
  #app {
    max-width: var(--maxw-lg);
    /* No bottom bar to clear any more — just normal breathing room. */
    padding-bottom: var(--s-7);
  }
  .nav {
    top: 0; bottom: 0; left: 0; right: auto; margin: 0;
    width: var(--rail); max-width: none;
    flex-direction: column; justify-content: flex-start; gap: var(--s-1);
    padding: var(--s-6) var(--s-3);
    border-top: none; border-right: 1px solid var(--border);
  }
  .nav button {
    flex-direction: row; justify-content: flex-start; gap: var(--s-3);
    width: 100%; padding: var(--s-3) var(--s-4);
    font-size: var(--fs-sm); border-radius: var(--r-sm);
  }
  .nav button[aria-current='true'] { background: var(--surface-2); }
  .nav button[aria-current='true'] svg { transform: none; }
  /* Toast and sheets are position:fixed, so the body gutters don't move them — but
     with the gutters mirrored the content column is centred on the viewport, so the
     plain viewport-centred values from the base rules already line up with it. Only
     the bottom offset changes (no bottom bar to clear). The scrim deliberately still
     covers everything, rail included — nothing behind a modal should be clickable. */
  .toast { bottom: var(--s-6); }
}
