/* ============ Trade-in-public dashboard ============ */
:root {
  --bg:        oklch(0.16 0.014 250);
  --bg-2:      oklch(0.19 0.015 250);
  --surface:   oklch(0.22 0.016 250);
  --surface-2: oklch(0.25 0.018 250);
  --line:      oklch(0.30 0.014 250);
  --line-2:    oklch(0.36 0.014 250);
  --text:      oklch(0.94 0.008 250);
  --text-2:    oklch(0.78 0.012 250);
  --muted:     oklch(0.63 0.012 250);
  --dim:       oklch(0.46 0.012 250);

  --pos:       oklch(0.80 0.18 145);
  --pos-bg:    oklch(0.32 0.10 145 / 0.18);
  --neg:       oklch(0.70 0.21 25);
  --neg-bg:    oklch(0.34 0.12 25 / 0.18);
  --warn:      oklch(0.84 0.16 75);
  --info:      oklch(0.78 0.12 235);
  --accent:    oklch(0.82 0.16 75);

  /* Role bylines (PRD: the Options Log is written by three voices) — chosen
     to sit clear of the SAFE/WATCH/BREACH band hues (145/75/25) and of
     --info (235) so a role chip never reads as a risk state. */
  --role-analyst: oklch(0.80 0.15 300);
  --role-trader:  oklch(0.78 0.12 210);
  --role-user:    oklch(0.78 0.12 165);

  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;

  --r-sm: 4px;
  --r:    6px;
  --r-lg: 10px;

  --pad: 14px;
  --pad-sm: 10px;
}

[data-theme="light"] {
  --bg:        oklch(0.985 0.004 100);
  --bg-2:      oklch(0.97 0.004 100);
  --surface:   oklch(1 0 0);
  --surface-2: oklch(0.96 0.004 100);
  --line:      oklch(0.90 0.006 100);
  --line-2:    oklch(0.84 0.008 100);
  --text:      oklch(0.20 0.012 250);
  --text-2:    oklch(0.34 0.012 250);
  --muted:     oklch(0.50 0.012 250);
  --dim:       oklch(0.66 0.010 250);
  --pos:       oklch(0.55 0.16 145);
  --pos-bg:    oklch(0.92 0.10 145 / 0.55);
  --neg:       oklch(0.55 0.22 25);
  --neg-bg:    oklch(0.94 0.10 25 / 0.55);

  /* Darker/more saturated than the dark-theme values so each role chip stays
     legible (and passes contrast) on a white --surface instead of inverting
     straight through. */
  --role-analyst: oklch(0.48 0.17 300);
  --role-trader:  oklch(0.45 0.13 210);
  --role-user:    oklch(0.44 0.12 165);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: var(--sans); font-size: 14px; line-height: 1.45; -webkit-font-smoothing: antialiased; }
body { min-height: 100vh; }

/* density modifier */
[data-density="compact"] { --pad: 10px; --pad-sm: 7px; font-size: 13px; }
[data-density="comfortable"] { --pad: 18px; --pad-sm: 13px; font-size: 14.5px; }

/* type */
.mono { font-family: var(--mono); font-feature-settings: "tnum","ss01"; letter-spacing: -0.005em; }
.tnum { font-variant-numeric: tabular-nums; }
.up { color: var(--pos); }
.down { color: var(--neg); }
.muted { color: var(--muted); }
.dim { color: var(--dim); }
.warn { color: var(--warn); }

/* ============ Top status bar ============ */
.statusbar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  padding: 8px 16px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-2);
}
.statusbar .ticker-strip {
  display: flex;
  gap: 18px;
  overflow: hidden;
  white-space: nowrap;
}
.statusbar .ticker-strip span { display: inline-flex; gap: 6px; }
.statusbar .live { display: inline-flex; align-items: center; gap: 6px; color: var(--pos); }
.statusbar .live::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--pos); box-shadow: 0 0 0 0 var(--pos); animation: pulse 2s infinite; }
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 oklch(0.80 0.18 145 / 0.6); }
  70% { box-shadow: 0 0 0 6px oklch(0.80 0.18 145 / 0); }
  100% { box-shadow: 0 0 0 0 oklch(0.80 0.18 145 / 0); }
}

/* ============ Header ============ */
.header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 24px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark {
  width: 28px; height: 28px; border-radius: 6px;
  background: linear-gradient(135deg, var(--pos), var(--info));
  display: grid; place-items: center;
  font-family: var(--mono); font-weight: 700; color: #0c0c10; font-size: 13px;
}
.brand-name { font-family: var(--mono); font-weight: 600; font-size: 14px; letter-spacing: -0.01em; }
.brand-name span { color: var(--muted); }
.nav { display: flex; gap: 4px; font-family: var(--mono); font-size: 12px; }
.nav a {
  color: var(--text-2); text-decoration: none;
  padding: 6px 10px; border-radius: var(--r-sm);
}
.nav a.active { background: var(--surface); color: var(--text); }
.nav a:hover { color: var(--text); background: var(--surface-2); }

/* Hamburger (mobile nav) — CSS-only via :checked sibling selector.
   Hidden on desktop; the desktop .nav above is hidden on mobile. */
.nav-toggle-input { position: absolute; opacity: 0; pointer-events: none; }
.nav-toggle-btn {
  display: none;
  width: 44px; height: 44px;
  align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text);
  font-size: 20px; line-height: 1; cursor: pointer;
  user-select: none;
}
.nav-mobile { display: none; }
.nav-mobile a {
  display: block;
  padding: 12px 16px;
  font-family: var(--mono); font-size: 13px;
  color: var(--text-2); text-decoration: none;
  border-bottom: 1px solid var(--line);
  min-height: 44px;
  box-sizing: border-box;
}
.nav-mobile a.active { color: var(--text); background: var(--surface-2); }
.nav-mobile a:last-child { border-bottom: none; }

.bmc-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 12px; border-radius: var(--r);
  background: var(--accent); color: #0c0c10;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  text-decoration: none; border: 1px solid transparent;
  transition: transform 0.1s ease;
}
.bmc-btn:hover { transform: translateY(-1px); }
.bmc-btn .coffee { font-size: 14px; }

/* ============ Layout ============ */
.shell { padding: 16px; max-width: 1680px; margin: 0 auto; }
.grid { display: grid; gap: 12px; }
.grid-12 { grid-template-columns: repeat(12, 1fr); }

/* ============ Card (panel) ============ */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px var(--pad);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.card-head h3 {
  margin: 0;
  font-family: var(--mono); font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--muted); font-weight: 600;
}
.card-head .actions { display: flex; gap: 4px; }
.card-body { padding: var(--pad); }
.card-body.tight { padding: 0; }

/* ============ Hero P&L ============ */
.hero {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
}
.hero > div {
  padding: 18px 22px;
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 4px;
  position: relative;
}
.hero > div:last-child { border-right: none; }
.hero .label {
  font-family: var(--mono); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted);
}
.hero .value {
  font-family: var(--mono); font-size: 28px;
  font-weight: 600; letter-spacing: -0.02em;
  line-height: 1.05;
}
.hero .sub { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
.hero .pnl-hero .value { font-size: 44px; }
.hero .spark { position: absolute; right: 14px; top: 14px; opacity: 0.85; }

/* ============ Hero — Alpaca vs SPY (PRD 73) ============ */
/* The Alpaca account vs SPY is the single above-the-fold KPI. The lead column
   gets the big number; vs-SPY sits side-by-side; the sparkline overlays the
   equity curve and SPY curve in two distinct colors. */
.hero.hero-asv { grid-template-columns: 1.6fr 1fr 1.4fr; }
.hero.hero-asv > div { padding: 22px 26px; }
.hero.hero-asv .asv-lead .value { font-size: 44px; }
.hero.hero-asv .asv-vs .value { font-size: 32px; }
.hero.hero-asv .asv-spark .dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 4px; vertical-align: middle;
}
.hero.hero-asv .asv-spark svg { margin: 4px 0; }

/* ============ Per-strategy attribution (collapsible) ============ */
.per-strategy-attr details { width: 100%; }
.per-strategy-attr-summary {
  display: flex; align-items: center; gap: 12px;
  padding: 0 var(--pad);
  min-height: 44px; /* mobile touch target */
  cursor: pointer;
  list-style: none;
  font-family: var(--mono); font-size: 12px;
  border-bottom: 1px solid transparent;
}
.per-strategy-attr details[open] .per-strategy-attr-summary {
  border-bottom-color: var(--line);
}
.per-strategy-attr-summary::-webkit-details-marker { display: none; }
.per-strategy-attr-summary::before {
  content: "▸"; color: var(--muted); font-size: 10px;
  transition: transform 0.15s ease;
  display: inline-block; width: 12px;
}
.per-strategy-attr details[open] .per-strategy-attr-summary::before {
  transform: rotate(90deg);
}
.per-strategy-attr-summary .psa-title {
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--text);
}
.per-strategy-attr-summary .psa-meta { margin-left: auto; font-size: 10.5px; }

/* ============ Stat row ============ */
.statgrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0; }
.statgrid .stat {
  padding: 12px 14px;
  border-right: 1px solid var(--line);
}
.statgrid .stat:last-child { border-right: none; }
.statgrid .label {
  font-family: var(--mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}
.statgrid .value {
  font-family: var(--mono); font-size: 18px; font-weight: 600;
  letter-spacing: -0.01em; margin-top: 2px;
}

/* ============ Trade table ============ */
table.trades { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px; }
table.trades th {
  text-align: left; padding: 8px 10px;
  background: var(--surface-2);
  color: var(--muted); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.06em; font-size: 10px;
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0;
}
table.trades td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.trades tr:hover td { background: var(--surface-2); }
table.trades td.num { text-align: right; }
table.trades .ticker { font-weight: 600; letter-spacing: -0.005em; }
table.trades .sub-cell { font-size: 10.5px; color: var(--muted); }

/* status pills */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 7px;
  border-radius: 999px;
  font-family: var(--mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--surface-2); color: var(--text-2);
  border: 1px solid var(--line);
}
.pill.open { color: var(--info); border-color: oklch(0.50 0.10 235 / 0.6); }
.pill.closed { color: var(--muted); }
.pill.long { color: var(--pos); border-color: oklch(0.50 0.10 145 / 0.6); }
.pill.short { color: var(--neg); border-color: oklch(0.50 0.14 25 / 0.5); }
.pill.watching { color: var(--warn); border-color: oklch(0.50 0.10 75 / 0.6); }
.pill.passed { color: var(--muted); }
.pill.holding { color: var(--pos); border-color: oklch(0.50 0.10 145 / 0.6); }
.pill.stopped { color: var(--neg); border-color: oklch(0.50 0.14 25 / 0.5); }

/* sentiment bar */
.sent {
  position: relative;
  display: inline-block; vertical-align: middle;
  width: 60px; height: 6px; border-radius: 3px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.sent > i {
  display: block; height: 100%; border-radius: 3px;
}

/* ============ DEMO badge (unwired widgets) ============ */
.demo-badge {
  display: inline-block;
  padding: 2px 6px;
  margin-left: 8px;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  border-radius: 3px;
  background: var(--neg-bg);
  color: var(--neg);
  border: 1px solid oklch(0.55 0.20 25 / 0.4);
  vertical-align: middle;
  cursor: help;
}

/* ============ Heatmap (P&L calendar) ============ */
.calendar { --cal-cell: 22px; display: flex; flex-direction: column; gap: 4px; }
.cal-months {
  display: grid;
  gap: 3px;
  margin-left: calc(var(--cal-cell) + 6px); /* room for weekday labels */
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  height: 12px;
}
.cal-month-label { grid-row: 1; }
.cal-body { display: flex; gap: 6px; }
.cal-weekdays {
  display: grid;
  grid-template-rows: repeat(7, var(--cal-cell));
  gap: 3px;
  font-family: var(--mono);
  font-size: 9px;
  color: var(--muted);
  align-items: center;
  width: var(--cal-cell);
  text-align: center;
}
.cal-weekday { line-height: var(--cal-cell); }
.heatmap {
  display: grid;
  grid-template-rows: repeat(7, var(--cal-cell));
  grid-auto-flow: column;
  gap: 3px;
}
.heatmap .cell {
  width: var(--cal-cell);
  height: var(--cal-cell);
  border-radius: 3px;
  background: var(--surface-2);
  border: 1px solid oklch(0.30 0.014 250 / 0.4);
  cursor: default;
  transition: outline-color 80ms ease;
  outline: 1px solid transparent;
}
.heatmap .cell.has-trades { cursor: help; }
.heatmap .cell.has-trades:hover { outline-color: var(--text); }
.heatmap .cell.weekend { opacity: 0.35; }
.heatmap .cell.future { visibility: hidden; }
.heatmap .cell[data-v="0"] { background: var(--surface-2); }

.realized-tip {
  position: absolute;
  min-width: 240px; max-width: 320px;
  background: var(--bg-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  padding: 10px 12px;
  font-family: var(--mono); font-size: 11px;
  box-shadow: 0 4px 16px oklch(0 0 0 / 0.35);
  pointer-events: none;
  z-index: 5;
}
.realized-tip-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; padding-bottom: 6px; margin-bottom: 6px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
}
.realized-tip-empty { color: var(--muted); font-style: italic; padding: 2px 0; }
.realized-tip-table { width: 100%; border-collapse: collapse; }
.realized-tip-table td { padding: 3px 4px; vertical-align: baseline; }
.realized-tip-table td.t { color: var(--text); font-weight: 600; }
.realized-tip-table td.q { color: var(--muted); text-align: right; }
.realized-tip-table td.px { color: var(--text-2); }
.realized-tip-table td.p { text-align: right; }
.realized-tip-more { color: var(--muted); text-align: center; padding-top: 4px !important; font-style: italic; }

/* ============ Equity chart ============ */
.chart-wrap { padding: 6px 14px 12px; }
.chart-legend {
  display: flex; gap: 14px;
  font-family: var(--mono); font-size: 10.5px;
  color: var(--muted); padding-bottom: 6px;
}
.chart-legend .dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 2px;
  margin-right: 5px; vertical-align: middle;
}

/* timeframe toggle */
.tf {
  display: inline-flex; gap: 0;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 2px;
}
.tf button {
  background: transparent; border: none;
  color: var(--muted); font-family: var(--mono); font-size: 10.5px;
  padding: 3px 8px; cursor: pointer; border-radius: 3px;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.tf button.active { background: var(--surface); color: var(--text); }
.tf button:hover { color: var(--text); }

/* small icon button */
.icon-btn {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--muted);
  cursor: pointer;
  font-family: var(--mono); font-size: 11px;
}
.icon-btn:hover { color: var(--text); border-color: var(--line-2); }

/* footer / inline coffee */
.footer-cta {
  margin-top: 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 22px 26px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 20px; align-items: center;
}
.footer-cta h2 {
  margin: 0 0 4px;
  font-family: var(--sans); font-size: 18px; font-weight: 600;
  letter-spacing: -0.01em;
}
.footer-cta p {
  margin: 0; color: var(--text-2); font-size: 13px; max-width: 56ch;
}
.footer-meta {
  display: flex; gap: 20px;
  padding: 18px 4px 4px;
  font-family: var(--mono); font-size: 10.5px; color: var(--muted);
}

/* inline coffee on profitable trades */
.tip-inline {
  font-family: var(--mono); font-size: 10px;
  color: var(--accent);
  margin-left: 8px;
  text-decoration: none;
  border-bottom: 1px dotted oklch(0.82 0.16 75 / 0.5);
  cursor: pointer;
}
.tip-inline:hover { color: var(--accent); }

/* sticky bottom-right BMC */
.bmc-floating {
  position: fixed; right: 18px; bottom: 18px;
  z-index: 50;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 14px; border-radius: 999px;
  background: var(--accent); color: #0c0c10;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  text-decoration: none;
  box-shadow: 0 8px 24px oklch(0 0 0 / 0.4), 0 1px 0 oklch(1 0 0 / 0.1) inset;
  transition: transform 0.12s ease;
}
.bmc-floating:hover { transform: translateY(-2px); }

/* tab bar inside reddit panel */
.tabs {
  display: flex; gap: 2px;
  font-family: var(--mono); font-size: 11px;
}
.tabs button {
  background: transparent; border: none;
  color: var(--muted); padding: 4px 9px; cursor: pointer;
  border-radius: 3px;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.tabs button.active { background: var(--surface-2); color: var(--text); }

/* utility flex */
.row { display: flex; align-items: center; gap: 10px; }
.between { justify-content: space-between; }
.wrap { flex-wrap: wrap; }
.right { margin-left: auto; }

/* col spans */
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-12 { grid-column: span 12; }

/* Breakpoints (documented; CSS media queries can't reference custom props):
     --bp-desktop-large: 1680px
     --bp-desktop:       1280px
     --bp-tablet:        1024px
     --bp-mobile:         820px
     --bp-mobile-sm:      480px
*/

@media (max-width: 1280px) {
  .col-md-12 { grid-column: span 12; }
  .col-md-6 { grid-column: span 6; }
  .hero { grid-template-columns: 1.4fr 1fr 1fr; }
  .hero > div:nth-child(4), .hero > div:nth-child(5) { display: none; }
  /* The Alpaca-vs-SPY hero only has 3 columns; keep them all visible. */
  .hero.hero-asv { grid-template-columns: 1.6fr 1fr 1.4fr; }
}

/* Tablet: nav visible but wraps; hero 3-col (reuses 1280px rule above);
   statgrid 4-col instead of 6. */
@media (max-width: 1024px) and (min-width: 821px) {
  .statgrid { grid-template-columns: repeat(4, 1fr); }
  .statgrid .stat:nth-child(4n) { border-right: none; }
  .nav { flex-wrap: wrap; row-gap: 2px; }
  .header { gap: 12px; }
}

@media (max-width: 820px) {
  .shell { padding: 12px; }
  .statgrid { grid-template-columns: repeat(2, 1fr); }
  .statgrid .stat { border-bottom: 1px solid var(--line); }
  .statgrid .stat:nth-child(2n) { border-right: none; }
  .statgrid .stat:nth-last-child(-n+2) { border-bottom: none; }
  .hero { grid-template-columns: minmax(40%, 1fr) 1fr; }
  .hero > div {
    border-right: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    padding: 12px 14px;
  }
  .hero > div:nth-child(2n) { border-right: none; }
  .hero > div:nth-last-child(-n+2) { border-bottom: none; }
  .hero .value { font-size: 22px; }
  .hero .pnl-hero .value { font-size: 32px; }
  .hero > div:nth-child(4), .hero > div:nth-child(5) { display: flex; }
  /* Alpaca-vs-SPY hero collapses to single column on mobile. */
  .hero.hero-asv { grid-template-columns: 1fr; }
  .hero.hero-asv > div:nth-child(2n) { border-right: none; }
  .hero.hero-asv .asv-lead .value { font-size: 32px; }
  .hero.hero-asv .asv-vs .value { font-size: 24px; }

  .header {
    grid-template-columns: auto 1fr auto auto;
    gap: 8px; padding: 10px 12px;
  }
  /* Desktop nav hidden; hamburger button visible */
  .nav { display: none; }
  .nav-toggle-btn { display: inline-flex; }
  /* Hamburger closed by default; expanded when checkbox checked */
  .nav-mobile {
    grid-column: 1 / -1;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    margin-top: 8px;
    overflow: hidden;
  }
  .nav-toggle-input:checked ~ .nav-mobile { display: block; }

  /* Tables: horizontal scroll wrappers, but also let any direct .card-body
     containing a table scroll without overflow leaking from the card. */
  .card-body { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .table-scroll-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  table.trades, table.theses { min-width: 560px; }

  /* Calendar: shrink cell + 3-letter weekday labels */
  .calendar { --cal-cell: 14px; }
  .cal-weekday { font-size: 9px; }

  /* Modals → bottom sheets */
  .realized-tip {
    position: fixed !important;
    left: 0 !important; right: 0 !important; bottom: 0 !important;
    top: auto !important; transform: none !important;
    width: 100% !important; max-width: 100% !important;
    border-radius: 12px 12px 0 0;
    max-height: 80dvh; overflow-y: auto;
  }

  /* Touch targets: any actionable element bumped to 44px */
  .bmc-btn { padding: 10px 14px; min-height: 44px; }
  table.trades th, table.trades td { padding: 12px 10px; }
  .icon-btn { width: 44px; height: 44px; font-size: 14px; }
  .tf button { padding: 12px 14px; font-size: 12px; min-height: 44px; }
  .tabs button { padding: 12px 14px; min-height: 44px; }

  /* Use dynamic viewport height where height was hardcoded */
  .shell { min-height: auto; }
}

/* Small mobile: tighter padding, single-column hero, hide tertiary stats */
@media (max-width: 480px) {
  .shell { padding: 8px; }
  .card-body { padding: 12px; }
  .card-head h3 { font-size: 10.5px; }
  table.trades, table.theses { font-size: 10px; }
  .hero { grid-template-columns: 1fr; }
  .hero > div {
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .hero > div:last-child { border-bottom: none; }
  .hero > div:nth-child(n+4) { display: none; }
  .hero .value { font-size: 20px; }
  .hero .pnl-hero .value { font-size: 28px; }
  .statgrid { grid-template-columns: 1fr; }
  .statgrid .stat {
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .statgrid .stat:last-child { border-bottom: none; }
}

/* Prevent horizontal page scroll on mobile viewports only.
   Avoided on desktop because overflow-x: hidden on <html> can break
   position: sticky descendants in some browsers (iOS Safari especially). */
@media (max-width: 820px) {
  html, body { overflow-x: hidden; }
}

/* ============ PRD 76 — Upcoming Catalysts widget ============ */
/* Compact, scrollable list grouped by date. Sits under the Hero. Aims to
   give the user one glance at "what's coming up this week" + "what we're
   doing about it". Hover/tap each row for the full position + thesis +
   plan-today popover. */
.upcoming-catalysts { position: relative; }
.uc-head-right {
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
}
.upcoming-catalysts .uc-summary {
  font-size: 11px; letter-spacing: 0.04em;
}
.uc-knowledge {
  font-size: 10px; letter-spacing: 0.03em;
}
.upcoming-catalysts .uc-body {
  position: relative;
  max-height: 360px; overflow-y: auto;
}
.upcoming-catalysts .uc-empty {
  padding: 24px; color: var(--muted); font-size: 13px; text-align: center;
}
.uc-date-group { border-bottom: 1px solid var(--line); }
.uc-date-group:last-child { border-bottom: none; }
.uc-date-header {
  position: sticky; top: 0; z-index: 2;
  padding: 6px 14px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--muted); font-weight: 600;
}
.uc-row-wrap { border-bottom: 1px solid var(--line); }
.uc-row-wrap:last-child { border-bottom: none; }
.uc-row {
  display: grid;
  /* Tier column is fixed (not `auto`) so the trade-log matrix in the next
     column starts at the same x on every row → numbers stack into a ledger. */
  grid-template-columns: 12px 18px 64px 110px 104px 1fr 50px;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  cursor: pointer;
  transition: background 120ms ease;
}
.uc-row:hover, .uc-row:focus-visible {
  background: var(--surface-2);
  outline: none;
}
.uc-row-open { background: var(--surface-2); }
.uc-ticker { font-size: 13px; font-weight: 600; color: var(--text); }
.uc-catalyst {
  font-family: var(--mono); font-size: 10px;
  font-weight: 600; letter-spacing: 0.06em;
  color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.uc-subject {
  font-size: 12px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.uc-days-away { font-size: 11px; text-align: right; }
.uc-position-dot {
  display: inline-block; width: 14px; text-align: center;
  font-size: 14px; line-height: 1;
}
.uc-dot-empty { color: var(--muted); }
.uc-dot-long  { color: var(--pos); }
.uc-dot-short { color: var(--neg); }
.uc-tier-badge {
  display: inline-block;
  padding: 1px 6px; border-radius: 3px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.4px; border: 1px solid transparent;
  white-space: nowrap;
}

/* PRD 82: regulatory tier (EDGAR insider + 8-K driven). Deep teal sits
   between the green/amber/grey of social/options_tech/naive so the trades
   table reads cleanly when all four show up in the same view. */
.tier-badge.regulatory {
  background: #1e6b7a;
  color: #e6f7fb;
}

/* Caret on each row signals the accordion-expand affordance. */
.uc-caret { font-size: 10px; text-align: center; line-height: 1; }

/* Inline detail panel — rendered beneath an expanded row (accordion), inset
   so it reads as belonging to the row above it. Replaces the old hover popover. */
.uc-detail {
  background: var(--surface-2);
  border-top: 1px solid var(--line);
  padding: 12px 14px 12px 40px;
  animation: uc-detail-in 120ms ease;
}
@keyframes uc-detail-in {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: none; }
}

/* Trade-log line on a held collapsed row — a fixed-column matrix so successive
   rows align into a blotter ("hedge-fund RSS"). Numeric columns right-align. */
.uc-row-trade {
  display: grid;
  grid-template-columns: 40px 96px 1fr 56px;
  gap: 8px;
  align-items: baseline;
  font-size: 11.5px;
  white-space: nowrap;
}
.uc-trade-type {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.06em;
  text-align: center; padding: 1px 0; border-radius: 3px;
}
.uc-trade-type.sell { color: var(--neg); background: color-mix(in srgb, var(--neg) 14%, transparent); }
.uc-trade-type.buy  { color: var(--pos); background: color-mix(in srgb, var(--pos) 14%, transparent); }
.uc-trade-size { text-align: left; overflow: hidden; text-overflow: ellipsis; }
.uc-trade-pnl  { text-align: right; font-weight: 600; }
.uc-trade-pct  { text-align: right; }

/* Exit plan — deterministic policy chips + LLM narrative. */
.uc-exit-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 7px; }
.uc-exit-chip {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  font-size: 10px; letter-spacing: 0.03em;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--text-2); white-space: nowrap;
}
.uc-exit-narrative {
  font-size: 12.5px; color: var(--text); line-height: 1.45;
  margin-bottom: 7px;
}
.uc-exit-today { margin-top: 6px; }
.uc-exit-today .uc-pop-v { color: var(--accent); font-weight: 600; }

.uc-pop-subject {
  font-size: 12px; color: var(--text-2);
  margin-bottom: 12px;
}
.uc-pop-block {
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.uc-pop-block:last-child {
  border-bottom: none; margin-bottom: 0; padding-bottom: 0;
}
.uc-pop-block-title {
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--muted); margin-bottom: 6px;
}
.uc-pop-grid {
  display: grid;
  grid-template-columns: 80px 1fr;
  row-gap: 4px; column-gap: 10px;
  font-size: 12px;
}
.uc-pop-k { color: var(--muted); }
.uc-pop-v { color: var(--text); }
.uc-pop-empty { font-size: 12px; color: var(--text-2); font-style: italic; }
.uc-pop-plan {
  font-family: var(--mono); font-size: 13px; font-weight: 600;
  color: var(--accent);
}

/* Mobile: tighten the row grid and stack the sentiment columns; the inline
   detail panel flows naturally so there's no bottom-sheet to pin. */
@media (max-width: 820px) {
  .uc-row {
    grid-template-columns: 12px 18px 56px 1fr auto 40px;
  }
  /* Hide the collapsed-row subject/trade-log on mobile — the inline detail
     carries the full position, and dropping it keeps the 6-column grid from
     wrapping. */
  .uc-row .uc-subject,
  .uc-row .uc-row-trade { display: none; }
  .uc-detail { padding-left: 14px; }
}

/* ============== Options / credit-spread surfaces ==============
   Scoped under .opt-* so nothing here can leak into the equity panels. */
.opt-log { padding: 14px; }
.opt-log-title { margin: 0; font-size: 16px; }
.opt-log-empty, .opt-log-foot { font-size: 11px; color: var(--muted); margin: 10px 0 0; max-width: 70ch; }
.opt-log-empty--halted { color: var(--warn); font-weight: 600; font-size: 12px; }
.opt-reconcile-warn, .opt-log-capped {
  color: var(--warn); font-weight: 600; padding: 6px 10px; margin: 8px 0 0;
  background: color-mix(in oklch, var(--warn) 12%, transparent);
  border: 1px solid color-mix(in oklch, var(--warn) 40%, transparent);
  border-radius: 6px;
}

.opt-ticker { font-size: 13px; letter-spacing: .02em; }
.opt-chev { color: var(--muted); margin-right: 6px; }

.opt-badge {
  font-size: 9px; letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line);
}
.opt-badge--open  { background: color-mix(in oklch, var(--pos) 18%, transparent); color: var(--pos); }
.opt-badge--close { background: color-mix(in oklch, var(--accent) 18%, transparent); color: var(--accent); }
.opt-badge--fill-unfilled { background: var(--surface); color: var(--muted); }
.opt-badge--fill-partial  { background: color-mix(in oklch, var(--warn) 18%, transparent); color: var(--warn); }
.opt-badge--fill-filled   { background: color-mix(in oklch, var(--pos) 14%, transparent); color: var(--pos); }
/* Structure chips (PRD 3987): `IC` on a folded iron condor, `from condor`
   on the remaining wing after the other wing closed on its own, and
   `WING CLOSE` on a wing_closed trade-log event. Same pill as every other
   opt-badge so a condor never reads as a different kind of row. */
.opt-badge--structure { background: color-mix(in oklch, var(--accent) 12%, transparent); color: var(--accent); margin-left: 6px; }
.opt-badge--lineage { background: var(--surface-2); color: var(--muted); margin-left: 6px; }
.opt-badge--wing-close { background: color-mix(in oklch, var(--warn) 18%, transparent); color: var(--warn); }
/* A wing_closed event nests under its parent condor's row in the Trade Log:
   indented, quieter, with a left rule so the parent/child relationship is
   visible without a colSpan (see test_no_inline_detail_row_or_colspan). */
.opt-row--nested td:first-child { padding-left: 26px; }
.opt-row--nested td { color: var(--text-2); font-size: 11px; }
.opt-row--nested .opt-chev { color: var(--line-2); }
.opt-nested-rule { display: inline-block; width: 10px; border-top: 1px solid var(--line-2); vertical-align: middle; margin-right: 6px; }
/* Condor wing table on #trade/<key> (option-trade-detail.jsx). */
.optd-wings-table { width: 100%; font-size: 12px; }
.optd-wings-table td, .optd-wings-table th { vertical-align: top; white-space: normal; }
.optd-wing-name { font-weight: 600; }
.optd-wing-status--open { color: var(--pos); }
.optd-wing-status--closed { color: var(--muted); }

.opt-trade-body { padding: 10px 12px 14px; border-top: 1px solid var(--line); background: var(--surface); }

.opt-structure-gloss { font-size: 11px; color: var(--text-2); margin: 0 0 10px; }
.opt-leg-lines { list-style: none; margin: 0; padding: 0; font-size: 12px; display: grid; gap: 3px; }
.opt-leg-dir { font-weight: 600; letter-spacing: .02em; }

.opt-kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px 14px; margin-bottom: 12px; }
.opt-kv-item { display: grid; gap: 1px; min-width: 0; }
.opt-kv-k { font-size: 9px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.opt-kv-sub { font-size: 9px; color: var(--muted); }
.opt-cell-sublabel { font-size: 9px; text-transform: uppercase; letter-spacing: .04em; }
.opt-kv-v { font-family: var(--mono); font-size: 12px; overflow: hidden; text-overflow: ellipsis; }

.opt-legblock { margin-top: 10px; }
.opt-legblock-title { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 4px; }
.opt-legs-empty { font-size: 11px; color: var(--muted); font-style: italic; }

/* Greeks table: numeric columns right-aligned, contract column left. */
.opt-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 11px; }
.opt-table th { color: var(--muted); font-weight: 500; text-align: right; padding: 3px 6px; white-space: nowrap; }
.opt-table td { text-align: right; padding: 4px 6px; white-space: nowrap; }
.opt-table th.al, .opt-table td.al { text-align: left; }
.opt-table tbody tr { border-top: 1px solid var(--line); }
.opt-table--legs { display: block; overflow-x: auto; }
.mono-dim { color: var(--text-2); }
.neg-tint { color: var(--neg); }

/* Open Orders / Trade Log — full-width cards stacked, Trade Log first.
   Previously a 1fr 1fr grid. Two dense numeric tables in half a column each
   forced a horizontal scrollbar on both at any normal window size, so neither
   could be read without dragging. One column gives each table the whole page. */
/* The stacked wrapper holds cards, which bring their own padding — the outer
   inset would double it and push the tables out of line with every other
   panel on the page. The empty state IS a card, so .opt-log keeps its own. */
.opt-log--stacked { padding: 0; }
.opt-log-stack { display: grid; grid-template-columns: 1fr; gap: 14px; align-items: start; }

.opt-panel { padding: 16px 18px; }
/* Divider under the head so the title block reads as its own row rather than
   floating against the first table row. */
.opt-panel-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  padding-bottom: 10px; border-bottom: 1px solid var(--line);
}
.opt-panel-title { margin: 0; font-size: 14px; letter-spacing: .01em; }
.opt-panel-stats { display: flex; gap: 16px; font-size: 11px; color: var(--muted); font-family: var(--mono); }
.opt-panel-stats em { font-style: normal; color: var(--text); }
.opt-panel-recon { white-space: nowrap; }

/* Trade Log filter + group-by row — sits between the panel head and the
   table so both controls read as "views onto the same table", not a
   secondary toolbar. */
.opt-filter-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 10px 0 12px;
}
.opt-filter-group { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.opt-filter-btn {
  font-family: var(--mono); font-size: 11px; letter-spacing: .01em;
  padding: 4px 9px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text-2); cursor: pointer;
}
.opt-filter-btn:hover { background: var(--surface); color: var(--text); }
.opt-filter-btn--active {
  background: color-mix(in oklch, var(--accent) 18%, transparent);
  color: var(--accent); border-color: var(--accent);
}
.opt-filter-count { color: var(--muted); }
.opt-filter-btn--active .opt-filter-count { color: inherit; opacity: .8; }
.opt-group-select { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); }
.opt-group-select select {
  font-family: var(--mono); font-size: 11px; padding: 3px 6px;
  border-radius: 6px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text);
}

/* Group header row inside the Trade Log table — one real <td> per column
   (no colSpan, see options-trade-log.jsx), so only the label column carries
   visible content and the rest sit blank in the same row. */
.opt-group-row td { background: var(--surface-2); border-top: 1px solid var(--line); padding: 6px; }
.opt-group-label { font-family: var(--mono); font-size: 11px; color: var(--text-2); }
.opt-group-count { color: var(--muted); }
/* A group whose rows straddle a page boundary repeats its header at the top
   of the next page — dimmed vs. a first-occurrence header so it reads as a
   continuation, not a second distinct group. */
.opt-group-row--cont td { opacity: .75; }

/* Trade Log pagination bar — sits below .opt-table-scroll, same visual
   register as .opt-filter-row above the table. */
.opt-pagination {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 12px 0 0;
}
.opt-pagination-controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.opt-pagination-btn {
  font-family: var(--mono); font-size: 11px; letter-spacing: .01em;
  padding: 4px 9px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text-2); cursor: pointer;
}
.opt-pagination-btn:hover:not(:disabled) { background: var(--surface); color: var(--text); }
.opt-pagination-btn:disabled { opacity: .45; cursor: not-allowed; }
.opt-pagination-readout { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.opt-pagination-size { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); }
.opt-pagination-size select {
  font-family: var(--mono); font-size: 11px; padding: 3px 6px;
  border-radius: 6px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text);
}

.opt-table-scroll { overflow-x: auto; margin-top: 0; }
.opt-table--orders th, .opt-table--orders td { white-space: nowrap; }

/* Alignment. Tabular figures make every digit the same width, so decimal
   points line up down a column instead of drifting with the glyphs. Applied
   to the header too, otherwise the label sits a fraction off its own column. */
.opt-table--orders { table-layout: auto; }
.opt-table--orders th, .opt-table--orders td { font-variant-numeric: tabular-nums; }
/* First column carries the badge; give it room so the badge never wraps under
   its own row and knocks the row height out of step with its neighbours. */
.opt-table--orders th:first-child, .opt-table--orders td:first-child { padding-left: 0; }
.opt-table--orders th:last-child, .opt-table--orders td:last-child { padding-right: 0; }
/* Sticky header: the Trade Log is the long table, and a full-width one is
   longer still — the column labels have to survive the scroll. */
.opt-table--orders thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--bg); padding-top: 0; padding-bottom: 8px;
}
.opt-table--orders tbody td { padding: 6px; height: 28px; }
.opt-table--orders tbody tr:first-child td { border-top: none; }
.opt-row { cursor: pointer; }
.opt-row:hover { background: color-mix(in oklch, var(--accent) 7%, transparent); }
.opt-row-detail td { padding: 0; }
.opt-row-detail .opt-trade-body { border-top: 1px solid var(--line); background: var(--surface); }
.opt-badge--terminal { background: color-mix(in oklch, var(--neg) 12%, transparent); color: var(--neg); }

@media (max-width: 900px) {
  /* Already one column at every width — kept so the panels tighten up on
     narrow screens where the outer page padding is doing less work. */
  .opt-panel { padding: 12px 12px; }
}

@media (max-width: 640px) {
  .opt-trade-ts { margin-left: 0; width: 100%; }
  .opt-kv { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
}

/* ============== Options Summary panel (PRD 1010) ==============
   opts-* : the headline is the single most prominent element on the page —
   everything else is a stacked .opt-panel card, same primitives as the
   trade log above so dark/light/density/accent all keep working for free. */
.opts-summary { display: grid; gap: 14px; }

/* ---- Options Log page layout (#options) ----
   The page is a vertical rhythm of full-width rows and half-width pairs.
   auto-fit + minmax means a pair collapses to one column on narrow screens,
   and a row left with a single card fills the width rather than stranding
   half the page. */
/* minmax(0, 1fr) rather than the default `auto` track: the Positions table's
   max-content width is far wider than the viewport, and an auto track sizes to
   it — which pushed the whole page (header, headline, every card) out to
   ~1476px on a 760px screen. The tables already scroll inside
   .opt-table-scroll; the page itself must not. */
.opts-page { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
.opts-page > * { min-width: 0; }
.opts-page-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.opts-page-sub { font-size: 12px; color: var(--text-2); margin: 0; max-width: 70ch; }
/* Own line under the h2/sub-line pair — flex-basis: 100% forces the wrap
   inside .opts-page-head's flex-wrap row instead of crowding the title. */
.opts-page-loop { flex-basis: 100%; font-size: 12px; color: var(--muted); margin: 2px 0 0; max-width: 78ch; line-height: 1.5; }
/* Execution state — whether anything on this page can reach the broker. Sits
   directly under the three-voice line, full width, loud enough not to be read
   as a footnote: a halted book that looks live is the single most misleading
   thing this page could do. */
.opts-exec-state { flex-basis: 100%; font-size: 12px; margin: 8px 0 0; max-width: 78ch; line-height: 1.5; padding: 7px 10px; border-radius: 6px; border: 1px solid var(--line); }
.opts-exec-state--halted { color: var(--text-2); background: color-mix(in srgb, var(--warn, #d99b28) 12%, transparent); border-color: color-mix(in srgb, var(--warn, #d99b28) 45%, var(--line)); }
.opts-exec-state--live { color: var(--text-2); background: color-mix(in srgb, var(--pos) 10%, transparent); border-color: color-mix(in srgb, var(--pos) 40%, var(--line)); }
.opts-row2 {
  display: grid; gap: 14px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
}
/* Half-width cards hold the same tables as the full-width ones — let them
   scroll rather than force the page wider than the viewport. */
.opts-row2 > * { min-width: 0; }
.opts-section-title {
  margin: 0 0 6px; font-size: 13px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted);
}
.opts-worksheet { border-top: 1px solid var(--line); padding-top: 16px; }
/* Reference material, not a live reading — one compact block at the bottom. */
.opts-strategy { border-top: 1px solid var(--line); padding-top: 16px; display: grid; gap: 12px; }
.opts-strategy .opts-howto { margin: 0; }
.opts-strategy-card { padding: 12px 14px; }
.opts-strategy-leg-triggers {
  list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 4px;
  font-size: 11px; line-height: 1.45; color: var(--text-2); max-width: 760px;
}
.opts-strategy-leg-triggers li::before { content: "\2013"; color: var(--muted); margin-right: 6px; }

@media (max-width: 820px) {
  .opts-row2 { grid-template-columns: 1fr; }
}
.opts-howto {
  margin: 0 0 10px; padding: 10px 14px; border-radius: 8px;
  border: 1px dashed var(--line); background: transparent;
  font-size: 12px; line-height: 1.5;
}
.opts-headline {
  padding: 18px 20px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface-2);
}
.opts-headline-text { font-size: 18px; font-weight: 600; line-height: 1.45; letter-spacing: -0.005em; }
.opts-stale-banner {
  margin-top: 8px; display: inline-block; font-size: 11px; font-weight: 600;
  color: var(--warn); background: color-mix(in oklch, var(--warn) 16%, transparent);
  border: 1px solid color-mix(in oklch, var(--warn) 40%, transparent);
  border-radius: 6px; padding: 4px 10px;
}

/* Mandate pace — realized vs required monthly run-rate against the
   500%/yr objective (docs/MANDATE.md). Amber when the trailing-30d window
   is behind pace; plain text otherwise (on-pace is the default state, not
   an achievement to highlight). */
.opts-mandate-pace {
  margin-top: 8px; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--muted);
}
.opts-mandate-pace-label {
  display: inline-flex; align-items: center; font-weight: 600; color: var(--text);
}
.opts-mandate-pace-value { font-family: var(--mono); }
.opts-mandate-pace--behind .opts-mandate-pace-value {
  color: var(--warn); font-weight: 700;
}

/* Role byline — which of the three voices (Analyst/Trader/User) wrote this
   card. Wrapped together with its SectionStamp (when the card has one) in
   .opts-provenance so the pair reads as one provenance statement
   ("Analyst · Last updated ...") instead of two competing head items; on an
   Analyst-less/live card .opts-provenance just holds the bare chip. */
.opts-provenance {
  display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; max-width: 100%;
}
.opts-provenance:empty { display: none; }
.opts-provenance-sep { color: var(--muted); }
.opts-role-byline {
  display: inline-flex; align-items: center;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; line-height: 1.6;
  padding: 2px 7px; border-radius: 999px; white-space: nowrap;
}
.opts-role-byline--analyst {
  color: var(--role-analyst);
  background: color-mix(in oklch, var(--role-analyst) 16%, transparent);
  border: 1px solid color-mix(in oklch, var(--role-analyst) 40%, transparent);
}
.opts-role-byline--trader {
  color: var(--role-trader);
  background: color-mix(in oklch, var(--role-trader) 16%, transparent);
  border: 1px solid color-mix(in oklch, var(--role-trader) 40%, transparent);
}
.opts-role-byline--user {
  color: var(--role-user);
  background: color-mix(in oklch, var(--role-user) 16%, transparent);
  border: 1px solid color-mix(in oklch, var(--role-user) 40%, transparent);
}

/* Cron-run stamp in a card head ("Last updated 1:00 PM PDT, Aug 8 · ~2h old
   · refreshes every 2h"). Sits opposite the title in .opt-panel-head's
   space-between row, and drops to its own line on narrow/half-width cards
   via that head's flex-wrap. */
.opts-section-stamp {
  display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; max-width: 100%;
  font-size: 11px; font-family: var(--mono); color: var(--muted); line-height: 1.6;
}
/* nowrap on the clauses, not the whole stamp — see the JSX comment. */
.opts-section-stamp > span { white-space: nowrap; }
.opts-section-stamp--stale { color: var(--warn); font-weight: 600; }

.opts-bullets { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; font-size: 12.5px; line-height: 1.5; }
.opts-bullets li::before { content: "\2013"; color: var(--muted); margin-right: 7px; }
.opts-line-warn { color: var(--warn); }

/* --- outcome-stats.jsx — "How our exits actually end" ------------------ */
.outcome-pos { color: var(--pos); }
.outcome-neg { color: var(--neg); }
.outcome-pending { color: var(--warn); }
.outcome-overall {
  display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 0 0 12px;
  font-size: 12.5px; font-family: var(--mono);
}
.outcome-bar-list { display: grid; gap: 6px; margin-bottom: 14px; }
.outcome-bar-row {
  display: grid; grid-template-columns: 130px 1fr 32px 64px; align-items: center;
  gap: 8px; font-size: 11.5px;
}
.outcome-bar-row--thin { opacity: 0.55; }
.outcome-bar-label { color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.outcome-bar-track {
  height: 6px; border-radius: 999px; background: var(--surface-2); overflow: hidden;
}
.outcome-bar-fill { display: block; height: 100%; background: var(--accent); }
.outcome-bar-n { text-align: right; font-family: var(--mono); color: var(--muted); }
.outcome-bar-pnl { text-align: right; font-family: var(--mono); }
.outcome-subtable { margin-top: 12px; }
.outcome-subtable-title {
  margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted);
}
.outcome-table { width: 100%; border-collapse: collapse; font-size: 11.5px; }
.outcome-table th, .outcome-table td { padding: 4px 6px; text-align: right; }
.outcome-table th:first-child, .outcome-table td:first-child { text-align: left; }
.outcome-table th { color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--line); }
.outcome-table td { border-bottom: 1px solid var(--line); font-family: var(--mono); }
.outcome-row--thin { opacity: 0.55; }
@media (max-width: 820px) {
  .outcome-bar-row { grid-template-columns: 96px 1fr 26px 56px; }
}

.opts-what-we-think li { padding: 8px 10px; background: var(--surface-2); border-radius: 6px; }
.opts-what-we-think li::before { content: none; }

.opts-action-queue { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; font-size: 12.5px; }
.opts-action { padding: 6px 10px; border-radius: 4px; border-left: 3px solid var(--line); }
.opts-action--breached { border-left-color: var(--neg); background: var(--neg-bg); }
.opts-action--danger { border-left-color: var(--neg); background: color-mix(in oklch, var(--neg) 10%, transparent); }
.opts-action--watch { border-left-color: var(--warn); background: color-mix(in oklch, var(--warn) 10%, transparent); }
.opts-action--safe { border-left-color: var(--pos); background: var(--pos-bg); }
.opts-action--neutral { border-left-color: var(--line); }
/* Suppressed (🚫 exit blocked by a mark-suspect clamp) must never compete
   visually with an armed row — desaturated and struck-through, no red alarm
   colour, so it reads as blocked rather than actionable. */
.opts-action--suppressed {
  border-left-color: var(--line-2); background: var(--surface-2); color: var(--muted);
  opacity: .72; text-decoration: line-through; text-decoration-color: var(--line-2);
}
.opts-action--info { border-left-color: var(--line-2); }
.opts-action--clickable { cursor: pointer; }
.opts-action--clickable:hover { background: color-mix(in oklch, var(--text) 6%, transparent); }
.opts-action--clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Armed / Watching / Can't-trust groups (PRD: dollar-ranked, grouped action
   queue) — one labelled box per non-empty group, in emitted order. */
.opts-action-group + .opts-action-group { margin-top: 12px; }
.opts-action-group-label {
  margin: 0 0 6px; font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 600; color: var(--muted); display: flex; align-items: center; gap: 4px;
}

/* Analyst recommendation row's confidence/flags — inline, muted, secondary
   to the recommendation sentence itself. */
.opts-analyst-meta {
  display: inline-flex; align-items: center; gap: 2px; margin-left: 8px;
  font-size: 11px; font-family: var(--mono); color: var(--muted);
}

/* Counts chip in the card head, next to the SectionStamp. */
.opts-action-counts-chip {
  display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap;
  font-size: 11px; font-family: var(--mono); color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 10px;
}

/* Deep-link highlight for the target What-we-think row — transient pulse,
   no router, no persistent state. */
.opts-wwt-row--highlight { animation: opts-wwt-highlight-pulse 2s ease-out; outline: 2px solid var(--warn); }
@keyframes opts-wwt-highlight-pulse {
  0% { background: color-mix(in oklch, var(--warn) 55%, transparent); }
  100% { background: transparent; }
}

/* Cushion bands — same colour language (BREACHED/DANGER/WATCH/SAFE) used for
   severity everywhere else in the dashboard: neg for breached/danger, warn
   for watch, pos for safe. */
.opts-band {
  font-size: 9px; letter-spacing: .08em; text-transform: uppercase; font-weight: 600;
  padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line);
}
.opts-band--safe { background: var(--pos-bg); color: var(--pos); }
.opts-band--watch { background: color-mix(in oklch, var(--warn) 16%, transparent); color: var(--warn); }
.opts-band--danger { background: color-mix(in oklch, var(--neg) 14%, transparent); color: var(--neg); }
.opts-band--breached { background: var(--neg-bg); color: var(--neg); font-weight: 700; }

/* Frozen entry columns vs live now columns — a visible seam, not just a
   header label, so "what we paid" and "what it's worth now" never blur
   together while scanning. */
.opts-col-frozen { background: color-mix(in oklch, var(--surface-2) 60%, transparent); }
.opts-col-live-first { border-left: 2px solid var(--line-2); }

/* Trailing per-row feedback cell — visually a narrow icon column, so it
   never competes for width with the data columns it sits beside. */
.opts-col-feedback { width: 1%; white-space: nowrap; text-align: center; }

/* Positions table only: the Positions table is wide enough that this
   trailing cell scrolls off the right edge of .opt-table-scroll at any
   normal viewport width, so the per-position Feedback button reads as
   reachable only from the Trade Log below it. Pinning it to the scroll
   container's right edge (both the header and every body cell) makes it
   read the same way as the Trade Log's own row-end icon. Scoped to
   `.opts-positions-table` so the Trade Log's identical `.opts-col-feedback`
   cells — already visible without scrolling — are untouched. `var(--surface)`
   matches `.card`'s own background so the pinned column doesn't look like a
   different surface than the table it belongs to; without an opaque
   background here, cells scrolled underneath would show through. */
.opts-positions-table .opts-col-feedback {
  position: sticky; right: 0; z-index: 2;
  background: var(--surface);
  box-shadow: -4px 0 6px -4px rgba(0, 0, 0, 0.35);
}
.opts-positions-table thead .opts-col-feedback { z-index: 3; }

/* Visually hidden but NOT position:absolute, unlike .sr-only below — that
   rule's `position: absolute !important` fights `position: sticky` on the
   pinned header cell above. This keeps the "Feedback" label in normal flow
   (as an effectively zero-size span) so it stays reachable to screen readers
   without breaking the sticky positioning of its parent `<th>`. */
.opts-visually-hidden {
  width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Visually hidden but still reachable by screen readers — used for table
   header labels that would otherwise be redundant icon-only columns.
   !important because table header cells (.opt-table--orders thead th) set
   their own `position: sticky` at higher selector specificity than a bare
   utility class can otherwise win against. */
.sr-only {
  position: absolute !important; width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important; overflow: hidden !important;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ============== Trade detail page — narrative sections (PRD 1014) ==============
   optd-* : page-only classes layered on the opt-* primitives above, so dark
   mode / light mode / the accent tweak / density all keep working for free. */
.optd-section { margin-top: 14px; }
.optd-h2 { margin: 0 0 8px; font-size: 15px; letter-spacing: .01em; }
.optd-explainer { font-size: 12px; color: var(--text-2); margin: 0 0 10px; max-width: 70ch; }
.optd-note { font-size: 11px; color: var(--muted); margin: 4px 0 10px; }

/* Current price, next to the ticker (PRD 1033) — visually secondary to the
   ticker itself; wraps under it on narrow widths since it's inline content
   inside the same h3, no separate flex item needed. */
.optd-price { margin-left: 10px; font-family: var(--mono); font-size: 13px; color: var(--text-2); }
.optd-price-asof { margin-left: 6px; font-size: 10px; }
.optd-price-asof.opts-stale-banner { margin-top: 0; padding: 1px 6px; }

.optd-headline { font-size: 16px; line-height: 1.5; margin: 10px 0; max-width: 70ch; }
.optd-banner {
  display: inline-block; font-size: 12px; font-weight: 600; padding: 5px 12px;
  border-radius: 6px; border: 1px solid var(--line);
}
.optd-banner--pos { color: var(--pos); background: var(--pos-bg); border-color: color-mix(in oklch, var(--pos) 40%, transparent); }
.optd-banner--neg { color: var(--neg); background: var(--neg-bg); border-color: color-mix(in oklch, var(--neg) 40%, transparent); }
.optd-banner--neutral { color: var(--text-2); background: var(--surface-2); }

.optd-leg-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.optd-leg-sentence { margin: 0 0 2px; font-size: 13px; }
.optd-occ { font-size: 10px; }

.optd-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px; margin-bottom: 16px; }
.optd-stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.optd-stat-value { font-family: var(--mono); font-size: 18px; margin: 2px 0; }
.optd-stat-sub { font-size: 10px; color: var(--muted); margin-bottom: 2px; }
.optd-stat-caption { font-size: 11px; color: var(--muted); }

.optd-payoff { margin-top: 6px; }
.optd-payoff-track { position: relative; height: 40px; border-radius: 6px; background: var(--surface-2); overflow: hidden; }
.optd-payoff-zone { position: absolute; top: 0; bottom: 0; }
.optd-payoff-zone--profit { background: color-mix(in oklch, var(--pos) 22%, transparent); }
.optd-payoff-zone--loss { background: color-mix(in oklch, var(--neg) 22%, transparent); }
.optd-payoff-marker { position: absolute; top: 0; bottom: 0; display: flex; flex-direction: column; align-items: center; transform: translateX(-50%); }
.optd-payoff-dot { width: 2px; height: 100%; background: var(--text); opacity: .6; }
.optd-payoff-marker--be .optd-payoff-dot { background: var(--accent); opacity: 1; }
.optd-payoff-marker--spot .optd-payoff-dot { background: var(--warn); opacity: 1; }
.optd-payoff-marker--now .optd-payoff-dot { background: var(--pos); opacity: 1; }
.optd-payoff-tag { position: absolute; top: 100%; margin-top: 4px; font-size: 10px; font-family: var(--mono); color: var(--muted); white-space: nowrap; }
.optd-payoff-legend { display: flex; gap: 14px; font-size: 11px; color: var(--muted); margin-top: 28px; }
.optd-payoff-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px; vertical-align: middle; }
.optd-payoff-swatch--profit { background: color-mix(in oklch, var(--pos) 40%, transparent); }
.optd-payoff-swatch--loss { background: color-mix(in oklch, var(--neg) 40%, transparent); }
.optd-payoff-empty { font-size: 11px; color: var(--muted); font-style: italic; }
.optd-payoff-caption { font-size: 11px; color: var(--muted); margin: 8px 0 0; max-width: 60ch; }

.optd-timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.optd-timeline li { display: flex; gap: 10px; align-items: flex-start; }
.optd-timeline-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-top: 4px; flex: none; }
.optd-timeline-label { font-size: 12px; }
.optd-timeline-ts { font-size: 10px; }
.optd-reason { font-size: 12px; color: var(--text-2); margin-top: 10px; }

.optd-kv-group { margin-bottom: 10px; }
.optd-kv-group-title { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-2); margin-bottom: 6px; border-bottom: 1px solid var(--line); padding-bottom: 4px; }

/* Dense scorecard for "The numbers" (PRD 1031) — one bordered tile per
   metric on a CSS grid. `auto-fill` (not `auto-fit`) keeps unfilled tracks
   empty rather than stretching a short trailing row's few tiles across the
   whole card width, which is what produced the sparse-row look this
   replaces. Scoped to `.optd-tiles` so it never touches the plain `.opt-kv`
   rows still used by Options Summary / the trade log. */
.optd-tiles { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 6px; margin-bottom: 4px; }
.optd-tile {
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
  min-height: 42px; min-width: 0;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r);
  padding: 5px 8px;
}
/* Long-value fields (order id, ISO timestamps) span two tracks and wrap
   instead of ellipsis-truncating, rather than widening every tile to match. */
.optd-tile--wide { grid-column: span 2; }
.optd-tile--wide .opt-kv-v { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }

/* Greeks LEGS AT ENTRY/EXIT table (PRD 1031) — options-trade-log.jsx's shared
   internals.LegDetail sets `display:block` on the <table> itself for its own
   horizontal-scroll needs, which also breaks the table's own `width:100%`
   (the anonymous table box it generates shrinks to content instead of
   filling the block box). Scoped to the detail page's own wrapper: restore
   real table layout so `width:100%` distributes the freed space across
   columns, and move the scroll affordance to the wrapper div instead. */
.optd-legtable-wrap { overflow-x: auto; }
.optd-legtable-wrap .opt-table--legs { display: table; width: 100%; overflow: visible; }
.optd-explainer--wide { max-width: none; }

@media (max-width: 640px) {
  .optd-stat-grid { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
  .optd-tiles { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
}

/* ============== Glossary <Help/> tooltip (PRD 1018) ==============
   Shared everywhere a term needs a plain-English definition. Keyboard/touch
   reachable button, not a hover-only title= attribute. */
.help { position: relative; display: inline-flex; vertical-align: middle; margin-left: 4px; }
.help-btn {
  width: 15px; height: 15px; border-radius: 50%; padding: 0;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  font-size: 10px; line-height: 1; font-family: var(--sans);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.help-btn:hover, .help-btn:focus-visible { color: var(--text); border-color: var(--line-2); }
.help-tip {
  /* Rendered into a portal on <body> and positioned in VIEWPORT coordinates by
     glossary.js (inline top/left, measured from the `?` button and flipped at
     the edges). Fixed, not absolute: inside `.opt-table-scroll` an absolute
     popover counted toward the table's scroll width, so opening the last
     column's tip spawned a horizontal scrollbar and hid the card off to the
     right — the user had to scroll to read it. On top of the page, never
     inside the box (PRD 1034).
     Anchored to the trigger's LEFT edge (not centered) so it grows rightward,
     which is also what keeps a first-column button's popover from being cut
     off (PRD 1028: the Open-queue "Deployed" bullet). */
  position: fixed; z-index: 200; top: 0; left: 0;
  width: max-content; max-width: min(260px, calc(100vw - 24px));
  /* A tall calc popover scrolls itself rather than running off the viewport —
     the JS flips it above the button first, this is the last resort. */
  max-height: calc(100vh - 24px); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 8px 10px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  display: flex; flex-direction: column; gap: 4px;
  font-size: 12px; line-height: 1.4; white-space: normal; text-align: left;
  /* Reset — a `?` badge can sit inside an ancestor styled with
     `text-transform: uppercase` (e.g. `.opt-kv-k`'s field-label styling);
     without this the whole prose popover inherits it and reads as shouted
     caps instead of a normal sentence. */
  text-transform: none;
}
.help-tip-label { font-weight: 600; color: var(--text); }
.help-tip-short { color: var(--text); }
.help-tip-long { color: var(--text-2); }
.help-tip-example { color: var(--text-2); font-style: italic; }
.help-tip-note { color: var(--warn); font-weight: 600; }

/* Worked-calculation block (PRD 1024) — appended after the long definition
   when a term carries a `calc` and the call site supplied live `inputs`. */
.help-tip-calc {
  display: flex; flex-direction: column; gap: 3px;
  margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--line-2);
}
.help-tip-calc-title { font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-2); }
.help-tip-calc-expr { color: var(--text-2); font-family: var(--mono); font-size: 11px; }
.help-tip-calc-input { color: var(--text-2); font-family: var(--mono); font-size: 11px; }
.help-tip-calc-sub { color: var(--text); font-family: var(--mono); font-size: 11px; }
.help-tip-calc-result { color: var(--text); font-family: var(--mono); font-size: 12px; }
.help-tip-calc-names { color: var(--text-2); font-family: var(--mono); font-size: 11px; }
.help-tip-calc-unavailable { color: var(--text-2); font-style: italic; font-size: 11px; }
.help-tip-calc-asof { color: var(--text-2); font-size: 10px; }
.help-tip-calc-asof--stale { color: var(--text); font-weight: 600; }

/* No `.card:has(.help-tip) { overflow: visible }` escape hatch any more (it was
   PRD 1024's answer to a short card clipping a tall calc popover): the popover
   is portalled onto <body>, so no card, table scroller, or other
   `overflow: hidden` ancestor is in its way to begin with.

   Narrow screens: only the width clamp is left here. Edge handling is the
   positioner's job now — pinning `right` in CSS would fight the inline `left`
   the JS computes. */
@media (max-width: 480px) {
  .help-tip { max-width: calc(100vw - 32px); }
}

/* ============== Feedback button + modal (PRD 1040) ==============
   Sibling of the `?` Help button — same chip treatment, same reasoning for
   being a real keyboard-reachable <button>, not an icon font or emoji. */
.feedback { position: relative; display: inline-flex; vertical-align: middle; margin-left: 6px; }
/* A labelled pill, NOT a 15px ghost chip like `?`. The help button is an
   optional aside a reader reaches for; this is a call to action we actively
   want noticed, so it carries the accent colour and the word "Feedback".
   Deliberately louder than .help-btn — they are not peers. */
.feedback-btn {
  min-height: 22px; border-radius: 999px; padding: 2px 9px 2px 7px; gap: 4px;
  border: 1px solid var(--accent); background: transparent; color: var(--accent);
  font-size: 11px; font-weight: 600; line-height: 1; font-family: var(--sans);
  letter-spacing: 0.01em;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.feedback-btn:hover, .feedback-btn:focus-visible {
  background: var(--accent); color: var(--bg);
}
.feedback-btn-label { white-space: nowrap; }

/* A Reply is a continuation, not a new topic — the only control that STARTS a
   conversation keeps the loud accent pill; the in-thread Reply reads a step
   quieter so the two are told apart at a glance, not just by their word. */
.feedback-btn--reply {
  border-color: var(--line-2); color: var(--text-2);
}
.feedback-btn--reply:hover, .feedback-btn--reply:focus-visible {
  background: var(--text-2); color: var(--bg);
}

/* Inside a dense table cell there is no room for the word — the pill collapses
   to a circular icon chip, same accent treatment so it still reads as the same
   control. The row's own hover already tells the user the row is interactive. */
.opts-col-feedback .feedback-btn-label { display: none; }
.opts-col-feedback .feedback-btn {
  width: 24px; height: 24px; min-height: 24px; padding: 0; border-radius: 50%;
}

/* Same collapse for a feedback button that sits mid-sentence inside a policy
   bullet (the three buy-to-close triggers): a full pill mid-line breaks the
   paragraph's rhythm, so the exit rules get the icon chip while every policy
   SECTION HEADING keeps the labelled pill. */
.policy-feedback-compact .feedback-btn-label { display: none; }
.policy-feedback-compact .feedback-btn {
  width: 20px; height: 20px; min-height: 20px; padding: 0; border-radius: 50%;
}

/* Positions-row indicator: an existing feedback thread is findable without
   opening the modal. Quieter than .feedback-btn (it's a status, not a call to
   action) but still a real button when it links to a trade page. */
.opts-fb-indicator {
  display: inline-flex; align-items: center; margin-left: 4px;
  min-height: 22px; padding: 2px 7px; border-radius: 999px; gap: 3px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  font-size: 11px; font-weight: 600; line-height: 1; font-family: var(--sans);
  cursor: pointer;
}
.opts-fb-indicator--unlinked { cursor: default; }
.opts-fb-indicator:hover, .opts-fb-indicator:focus-visible { color: var(--text); }
.opts-fb-indicator--open { border-color: var(--warn); color: var(--warn); }
.opts-fb-indicator--open:hover, .opts-fb-indicator--open:focus-visible { color: var(--warn); }

/* Modal stacks above the glossary's .help-tip portal (z-index 200) so an
   open tooltip can never overlap the dialog. */
.fb-backdrop {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(0, 0, 0, 0.5);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.fb-modal {
  position: relative; z-index: 301;
  width: min(420px, 100%); max-height: calc(100vh - 32px); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  display: flex; flex-direction: column; gap: 10px;
  font-size: 13px; color: var(--text);
}
.fb-modal-head { font-weight: 600; color: var(--text); }
.fb-chips { display: flex; gap: 6px; }
.fb-chip {
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  border-radius: 999px; padding: 4px 10px; font-size: 12px; cursor: pointer;
}
.fb-chip--on { border-color: var(--accent); color: var(--text); background: var(--surface); }
.fb-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-2); }
.fb-field input, .fb-field textarea {
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r);
  color: var(--text); font-family: var(--sans); font-size: 13px; padding: 6px 8px; resize: vertical;
}
.fb-thumb { display: flex; align-items: center; gap: 8px; }
.fb-thumb img { max-width: 120px; max-height: 80px; border-radius: var(--r); border: 1px solid var(--line); }
.fb-msg { color: var(--text-2); font-size: 12px; }
.fb-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* ===== Feedback forum (panels/feedback-threads.jsx) =======================
   The read side of the feedback primitive above: one card per thread, replies
   nested under the message they answer. Design tokens only — no literal
   colours — so light/dark both work with the rest of the site. */
.fbt-panel { margin-top: 12px; }
.fbt-lede { margin-bottom: 10px; }
.fbt-count { font-size: 11px; color: var(--text-2); }
.fbt-count--open { color: var(--warn); font-weight: 600; }
.fbt-empty { font-size: 12px; color: var(--text-2); margin: 6px 0 0; max-width: 70ch; }

/* The submit -> triage -> route -> reconcile -> resolved pipeline state, so
   the loop is visible at a glance instead of reconstructed from a log tail —
   same numbers brief.py's "Feedback:" lines render, computed once in Python
   (feedback_threads.build_funnel()). */
.fbt-funnel {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 2px 0 10px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2); font-size: 11px; color: var(--text-2);
}
.fbt-funnel-stat b { color: var(--text); font-family: var(--mono); }
.fbt-funnel-stat--warn b { color: var(--warn); }
.fbt-funnel-sub { color: var(--muted); }
.fbt-list { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.fbt-thread {
  border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: var(--r); background: var(--surface-2); padding: 10px 12px;
}
.fbt-thread--open { border-left-color: var(--warn); }
.fbt-thread--answered { border-left-color: var(--accent); }
.fbt-thread--archived { border-left-color: var(--text-2); opacity: .85; }
.fbt-thread-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.fbt-title { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); }
.fbt-when { font-size: 11px; color: var(--text-2); }
.fbt-status { font-size: 11px; margin-left: auto; }
.fbt-status--open { color: var(--warn); }
.fbt-status--answered { color: var(--text-2); }
.fbt-status--archived { color: var(--text-2); }
.fbt-badge {
  font-size: 10px; letter-spacing: .04em; border-radius: 999px; padding: 2px 8px;
  border: 1px solid var(--line); color: var(--text-2);
}
.fbt-badge--bad { color: var(--neg); border-color: color-mix(in oklch, var(--neg) 40%, transparent); }
.fbt-badge--warn { color: var(--warn); border-color: color-mix(in oklch, var(--warn) 40%, transparent); }
/* Stage chip (PRD 1088) — which point in the spread's life this thread was
   asked at (proposed / open / trade log), so a merged contract conversation
   still reads as a timeline rather than one undifferentiated pile. */
.fbt-stage {
  font-size: 10px; letter-spacing: .02em; border-radius: 999px; padding: 2px 8px;
  border: 1px solid var(--line); color: var(--text-2); background: var(--surface);
}
.fbt-target { font-size: 11px; color: var(--text-2); margin: 4px 0 0; }
.fbt-body { font-size: 12px; color: var(--text-2); margin: 6px 0 0; max-width: 80ch; white-space: pre-wrap; }
.fbt-msg-count { font-size: 11px; color: var(--text-2); margin: 6px 0 0; }
/* Two-sided conversation: visitor messages align left with a neutral edge,
   agent replies get the warn-toned edge + a faint tint so a reader can tell
   who said what at a glance, in both themes. */
.fbt-messages { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.fbt-msg {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); padding: 6px 10px 8px;
}
.fbt-msg--user { border-left: 3px solid var(--text-2); }
.fbt-msg--agent {
  border-left: 3px solid var(--warn);
  background: color-mix(in oklch, var(--warn) 8%, var(--surface));
}
.fbt-msg-head { display: flex; align-items: baseline; gap: 8px; }
.fbt-author { font-size: 11px; font-weight: 600; color: var(--accent); }
/* A just-submitted comment, echoed locally before the pull+publish round
   trip has put the real row on the server — visible only to its author. */
.fbt-msg--pending { border-left-style: dashed; }
.fbt-pending-chip {
  font-size: 10px; letter-spacing: .02em; border-radius: 999px; padding: 2px 8px;
  border: 1px dashed var(--line); color: var(--text-2); background: var(--surface-2);
}
.fbt-thread-foot { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
.fbt-hint { font-size: 11px; color: var(--text-2); }

/* Long message bodies clamp to a few lines behind a "Show more" toggle, so
   one rambling post can't push a whole thread list past the fold. */
.fbt-body-wrap--clamped .fbt-body {
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical;
  overflow: hidden;
}
.fbt-linklike {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  color: var(--accent); font-size: 11px; font-family: var(--sans);
  text-decoration: underline; text-underline-offset: 2px;
}
.fbt-linklike:hover, .fbt-linklike:focus-visible { color: var(--text); }
.fbt-clamp-toggle { margin-top: 4px; display: inline-block; }

/* Filter chips — open / answered / archived / all. A zero-count chip stays
   visible (disabled) rather than reflowing the control set as data changes. */
.fbt-filters { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.fbt-chip {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  border-radius: 999px; padding: 3px 10px; font-size: 12px; font-family: var(--sans);
  cursor: pointer;
}
.fbt-chip:hover:not(:disabled), .fbt-chip:focus-visible { color: var(--text); }
.fbt-chip--on { border-color: var(--accent); color: var(--text); background: var(--surface); }
.fbt-chip:disabled { cursor: default; opacity: .5; }
.fbt-chip-count { font-size: 10px; color: var(--text-2); }
.fbt-chip--on .fbt-chip-count { color: var(--text); }
.fbt-empty--filtered { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* Per-thread collapse — the header row IS the toggle button, so the whole
   badge/title/time/status/count line stays visible collapsed or expanded. */
.fbt-thread-toggle {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0;
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font-family: var(--sans); text-align: left; color: inherit;
}
.fbt-thread-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fbt-caret { font-size: 10px; color: var(--text-2); width: 1em; flex: none; }
.fbt-thread-body { margin-top: 6px; }

/* Owner-mode moderation controls (PRD 1071) — Archive/Unarchive/Delete,
   rendered only when a valid owner token is present. Sits beside the thread
   toggle rather than inside it (a <button> cannot nest another <button>). */
.fbt-mod { flex: none; }
.fbt-mod-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.fbt-mod-btn {
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2);
  border-radius: var(--r); padding: 3px 8px; font-size: 11px; font-family: var(--sans);
  cursor: pointer;
}
.fbt-mod-btn:hover:not(:disabled), .fbt-mod-btn:focus-visible { color: var(--text); }
.fbt-mod-btn:disabled { cursor: default; opacity: .5; }
.fbt-mod-btn--danger { color: var(--neg); border-color: color-mix(in oklch, var(--neg) 40%, transparent); }
.fbt-mod-confirm { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text-2); }
.fbt-mod-note, .fbt-mod-error {
  font-size: 11px; color: var(--text-2); margin: 4px 0 0; max-width: 60ch;
}
.fbt-mod-error { color: var(--neg); }
.fbt-mod-note code { font-family: var(--mono, monospace); font-size: 10px; }

/* Archived drawer — a single disclosure below the active list, collapsed by
   default so retired topics never crowd the threads still in play. */
.fbt-archive-drawer { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 8px; }
.fbt-archive-summary {
  cursor: pointer; font-size: 12px; color: var(--text-2); font-weight: 600;
  list-style: revert;
}
.fbt-archive-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fbt-list--archived { margin-top: 8px; }

/* --- Proposed trades (panels/proposed-trades.jsx) -------------------------
   The human-in-the-loop card. Deliberately louder than a live table: a
   proposal is the one thing on the page a reader can still change, so it gets
   an accent left-edge and full-width cards rather than a compact row. */
.prop-panel { border-left: 3px solid color-mix(in oklch, var(--accent) 55%, transparent); }
.prop-lede { max-width: 90ch; margin: 6px 0 10px; }

/* Collapse control for the proposed-trades deck. The deck stopped being the
   human-in-the-loop gate when `require_proposal_approval` went false, so it
   renders collapsed by default on #options and this is how a reader opens it.
   Quiet, secondary treatment — same reasoning as .feedback-btn--reply: still
   available, no longer the headline call to action. */
.prop-collapse-toggle {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  padding: 1px 7px;
}
.prop-collapse-toggle:hover { color: var(--text); border-color: var(--muted); }
.prop-collapse-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The one-line stand-in shown in place of the deck while it's collapsed. */
.prop-collapsed-sub {
  color: var(--muted);
  display: block;
  font-size: 12px;
  margin: 4px 0 2px;
}
.prop-deployment {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 12px; color: var(--text-2); margin-bottom: 12px;
}
.prop-margin-breach { color: var(--neg); font-weight: 600; }
.prop-card {
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: var(--r); background: var(--surface-2);
  padding: 12px 14px; margin-top: 12px;
}
.prop-card-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.prop-card-title { margin: 0; font-size: 14px; font-weight: 700; color: var(--text); }
.prop-card-head-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.prop-badge {
  font-size: 10px; letter-spacing: .04em; border-radius: 999px; padding: 2px 8px;
  border: 1px solid color-mix(in oklch, var(--accent) 40%, transparent); color: var(--accent);
}
/* Approval chip — the gate's own verdict/STATE, never re-derived client-side.
   Six states (spread_trader._approval_state()): not_reviewed is neutral
   (nothing decided yet); awaiting_scan/submitted are the two "on track"
   states and share the pos tone; stranded is its own amber tone — approved
   but the scanner has stopped offering anything it matches, which is neither
   "fine" nor "over"; declined/expired share the neg tone. */
.prop-approval-chip {
  display: inline-flex; align-items: center; gap: 2px;
  font-size: 10px; font-weight: 600; letter-spacing: .03em;
  border-radius: 999px; padding: 2px 8px; white-space: nowrap;
}
.prop-approval-chip--not_reviewed {
  color: var(--muted); border: 1px solid color-mix(in oklch, var(--muted) 45%, transparent);
}
.prop-approval-chip--awaiting_scan,
.prop-approval-chip--submitted {
  color: var(--pos); border: 1px solid color-mix(in oklch, var(--pos) 45%, transparent);
  background: color-mix(in oklch, var(--pos) 12%, transparent);
}
.prop-approval-chip--stranded {
  color: var(--warn); border: 1px solid color-mix(in oklch, var(--warn) 50%, transparent);
  background: color-mix(in oklch, var(--warn) 14%, transparent);
}
.prop-approval-chip--declined,
.prop-approval-chip--expired {
  color: var(--neg); border: 1px solid color-mix(in oklch, var(--neg) 45%, transparent);
  background: color-mix(in oklch, var(--neg) 12%, transparent);
}
.prop-approval-summary { font-size: 12px; color: var(--text-2); display: inline-flex; align-items: center; gap: 2px; }
.prop-approval-stranded {
  font-size: 12px; color: var(--warn); font-weight: 600;
  display: block; margin-top: 4px;
}
.prop-headline { font-size: 14px; font-weight: 600; color: var(--text); margin: 10px 0 6px; max-width: 80ch; }
.prop-bullets { margin: 0; padding-left: 18px; }
.prop-bullets li { font-size: 13px; color: var(--text-2); margin: 4px 0; max-width: 85ch; line-height: 1.5; }
.prop-structure { font-size: 12px; color: var(--text-2); margin: 8px 0 0; max-width: 85ch; }
.prop-error { font-size: 12px; color: var(--neg); margin: 8px 0 0; }
/* Narrative left, scorecard right. The card was a full-width column of prose
   with the KPIs squeezed into a strip underneath and the entire right half
   empty — the numbers are what a reviewer decides on, so they get real estate
   of their own. Collapses to one column below 900px. */
.prop-card-body { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 20px; align-items: start; }
.prop-narrative { min-width: 0; }
@media (max-width: 900px) { .prop-card-body { grid-template-columns: 1fr; } }

.prop-scorecard {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); padding: 10px 12px 12px;
}
.prop-score-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding-bottom: 6px; margin-bottom: 4px; border-bottom: 1px solid var(--line);
}
.prop-score-title {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.prop-score-gates { font-size: 11px; font-weight: 600; font-family: var(--mono); }
.prop-score-gates--pass { color: var(--accent); }
.prop-score-gates--fail { color: var(--neg); }
.prop-score-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 5px 0; border-bottom: 1px solid var(--line);
}
.prop-score-row:last-child { border-bottom: 0; }
.prop-score-label { font-size: 11px; color: var(--text-2); }
.prop-score-value {
  font-size: 13px; font-weight: 600; font-family: var(--mono); color: var(--text);
  text-align: right; white-space: nowrap;
}
/* The two figures a reviewer weighs against each other get the larger type. */
.prop-score-row--big .prop-score-value { font-size: 18px; }
.prop-score-value--pos { color: var(--pos); }
.prop-score-value--neg { color: var(--neg); }
/* Per-contract decomposition under a package total — quieter than the row
   it explains, and never tinted pos/neg so a frozen figure can't read live.
   The parent row's own divider moves down to sit under this sub-line instead
   of between the two, so the pair reads as one group. */
.prop-score-row:has(+ .prop-score-sub) { border-bottom: 0; }
.prop-score-sub {
  font-size: 10px; color: var(--muted); text-align: right;
  padding: 0 0 5px; margin-top: -3px; border-bottom: 1px solid var(--line);
}
.prop-score-sub:last-child { border-bottom: 0; }
.prop-meter {
  height: 4px; border-radius: 999px; background: var(--surface-2);
  overflow: hidden; margin: 2px 0 6px;
}
.prop-meter-fill { display: block; height: 100%; background: var(--accent); }
.prop-score-chip {
  margin-top: 8px; font-size: 10px; font-family: var(--mono); color: var(--accent);
  border: 1px solid color-mix(in oklch, var(--accent) 35%, transparent);
  border-radius: 999px; padding: 3px 8px; text-align: center;
}
/* Per-leg quote provenance — the bid/ask/last the scored credit was priced
   from, and when it was taken. Lives inside the scorecard aside, so it
   inherits its border/padding rather than opening a second card frame. */
.prop-quote-block { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.prop-quote-leg {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 3px 0; font-size: 11px;
}
.prop-quote-leg-label { color: var(--muted); flex-shrink: 0; }
.prop-quote-leg-figs { font-family: var(--mono); color: var(--text-2); text-align: right; }
.prop-quote-leg--unavailable .prop-quote-leg-error { color: var(--neg); text-align: right; }
.prop-quote-block .opts-section-stamp { display: block; margin-top: 6px; font-size: 10px; }
.prop-block { margin-top: 14px; }
.prop-block-title {
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 6px;
}
.prop-toggle {
  background: none; border: 1px solid var(--line); border-radius: 999px;
  color: var(--text-2); font-size: 10px; padding: 1px 8px; cursor: pointer;
}
.prop-checks { width: 100%; font-size: 12px; table-layout: fixed; }
/* "Why the rule exists" is prose, not a number: without a fixed layout it
   refuses to wrap and pushes the column off the right edge of the card. */
.prop-checks th:nth-child(1), .prop-checks td:nth-child(1) { width: 24%; }
.prop-checks th:nth-child(2), .prop-checks td:nth-child(2) { width: 13%; }
.prop-checks th:nth-child(3), .prop-checks td:nth-child(3) { width: 22%; }
.prop-checks th:nth-child(4), .prop-checks td:nth-child(4) { width: 41%; }
.prop-checks td { vertical-align: top; white-space: normal; word-break: break-word; }
.prop-checks .prop-mono { font-family: var(--mono); }
.prop-checks .prop-why { color: var(--text-2); }
.prop-check-fail { color: var(--neg); }
.prop-check-info { color: var(--muted); }
.prop-tick { display: inline-block; width: 14px; font-weight: 700; }
.prop-tick--pass { color: var(--accent); }
.prop-tick--fail { color: var(--neg); }
.prop-tick--info { color: var(--muted); }
.prop-rules { list-style: none; margin: 0; padding: 0; }
.prop-rules li {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  font-size: 12px; padding: 4px 0; border-bottom: 1px solid var(--line);
}
.prop-rule-plain { color: var(--text-2); max-width: 70ch; }
.prop-rule-field { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-left: auto; }
.prop-rules-note { font-size: 11px; color: var(--muted); margin: 6px 0 0; }
.prop-discussion { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--line); }

/* Compared to reviewed proposals — the structured precedent block. Reuses
   .prop-checks's table look (same fixed-ish layout, same mobile
   overflow-x: auto rule) rather than a third table style. */
.prop-comparison-table th, .prop-comparison-table td { white-space: normal; }
.prop-comparison-note { font-size: 12px; color: var(--text-2); margin: 0 0 8px; }
.prop-comparison-na { color: var(--muted); font-size: 11px; }
.prop-comparison-pos { font-size: 11px; }
.prop-comparison-pos--above { color: var(--pos); }
.prop-comparison-pos--below { color: var(--neg); }
.prop-comparison-pos--at { color: var(--text-2); }
.prop-comparison-nearest {
  font-size: 12px; color: var(--text-2); margin: 10px 0 0; padding-top: 8px;
  border-top: 1px solid var(--line); max-width: 85ch;
}

/* Precedent-similarity score line inside the scorecard (PRD 2058) —
   measurement only, never a gate. */
.prop-precedent-note { font-size: 11px; color: var(--text-2); margin: 8px 0 0; }
.prop-precedent-note--none { color: var(--muted); }
.prop-precedent-note--close { color: var(--pos); }

.prop-empty { margin-top: 4px; }
.prop-funnel { list-style: none; margin: 8px 0 0; padding: 0; max-width: 560px; }
.prop-funnel li {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 12px; color: var(--text-2); padding: 4px 0; border-bottom: 1px solid var(--line);
}
.prop-funnel li b { font-family: var(--mono); color: var(--text); }
.prop-funnel-note { font-size: 11px; color: var(--warn); margin: 8px 0 0; }
.prop-funnel-headline li b { font-size: 13px; }
/* Six columns since the select_spreads attribution (drop reason + shaded/mid
   ratio): wider than the 560px funnel list, and it scrolls inside the
   disclosure on a phone rather than pushing the page wider. */
.prop-funnel-byticker { margin: 12px 0 0; max-width: 760px; overflow-x: auto; }
.prop-funnel-byticker summary { font-size: 12px; color: var(--text-2); cursor: pointer; }
.prop-funnel-byticker-table {
  width: 100%; margin: 8px 0 0; border-collapse: collapse; font-size: 12px;
}
.prop-funnel-byticker-table th, .prop-funnel-byticker-table td {
  text-align: right; padding: 3px 8px; border-bottom: 1px solid var(--line); color: var(--text-2);
}
.prop-funnel-byticker-table th:first-child, .prop-funnel-byticker-table td:first-child {
  text-align: left; color: var(--text); font-family: var(--mono);
}
.prop-funnel-byticker-table .prop-funnel-byticker-reason { text-align: left; }
.prop-funnel-byticker-table .prop-funnel-byticker-ratio { font-family: var(--mono); white-space: nowrap; }
/* Mobile (<= --bp-mobile): six columns don't fit 390px, so the drop reason
   and shaded/mid ratio move to a sub-line under the ticker and their two
   columns are hidden — same strings, no second formatting pass. */
.prop-funnel-byticker-sub { display: none; }
@media (max-width: 820px) {
  .prop-funnel-byticker-table th, .prop-funnel-byticker-table td { padding: 3px 5px; font-size: 11px; }
  .prop-funnel-byticker-table .prop-funnel-byticker-reason,
  .prop-funnel-byticker-table .prop-funnel-byticker-ratio { display: none; }
  .prop-funnel-byticker-sub {
    display: block; font-family: var(--sans, inherit); font-size: 10.5px; color: var(--text-2);
    white-space: normal; line-height: 1.3;
  }
}

/* Pager for the one-at-a-time proposal deck. Rendered above AND below the
   card: at full height a proposal is taller than the viewport, so a pager
   only at the top would mean scrolling back up to move on. */
.prop-deck { outline: none; }
.prop-deck:focus-visible { outline: 1px solid var(--accent); outline-offset: 4px; border-radius: var(--r); }
.prop-pager { display: flex; align-items: center; gap: 10px; margin: 12px 0 0; }
.prop-pager-arrow {
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 6px;
  color: var(--text); font-size: 16px; line-height: 1; padding: 3px 10px; cursor: pointer;
}
.prop-pager-arrow:hover { border-color: var(--accent); color: var(--accent); }
.prop-dots { display: flex; align-items: center; gap: 7px; }
.prop-dot {
  width: 9px; height: 9px; padding: 0; border-radius: 50%; cursor: pointer;
  background: transparent; border: 1px solid var(--muted);
}
.prop-dot:hover { border-color: var(--accent); }
/* The open proposal reads as the big filled dot in ‹ o o O o o ›. */
.prop-dot--on { width: 13px; height: 13px; background: var(--accent); border-color: var(--accent); }
/* An unanswered comment on a proposal you are not looking at is still worth
   a glance, but demoted (thinner, lower-opacity ring) now that the open
   position — not the proposal — is the primary comment target (see
   CLAUDE.md's "propose -> act -> review loop"). */
.prop-dot--flagged { box-shadow: 0 0 0 1px color-mix(in oklch, var(--warn) 40%, transparent); }
/* Approval state is a distinct channel from the flagged ring above (border
   color vs box-shadow), so a dot can show both at once — declined-and-
   unanswered is not the same as declined-and-read. Declared after --on so
   the tone still shows through on the active (filled) dot. States match
   `spread_trader._approval_state()`; `not_reviewed` keeps the default
   neutral border (no rule needed). */
.prop-dot--awaiting_scan, .prop-dot--submitted { border-color: var(--pos); }
.prop-dot--stranded { border-color: var(--warn); }
.prop-dot--declined, .prop-dot--expired { border-color: var(--neg); }
/* A stranded card's own dot — approved-but-stuck is exactly the case this
   panel exists to surface without opening every card, so it gets a ring
   like an unanswered comment does, not just the border tone. */
.prop-dot--stranded { box-shadow: 0 0 0 2px color-mix(in oklch, var(--warn) 45%, transparent); }
.prop-card--stranded { border-left-color: var(--warn); }
/* A card whose contract has already expired — client-side freshness
   backstop (PRD 1163). Muted rather than removed: it stays visible so a
   reviewer isn't left wondering where a card went, but nothing on it should
   read as live or actionable. */
.prop-card--expired { border-left-color: var(--neg); opacity: 0.72; }
/* Fund hibernated — the deck is replaced by a single stopped-state card.
   Neutral and muted (not --neg): nothing is wrong, trading is deliberately off. */
.prop-card--halted { border-left-color: var(--line-2); background: var(--surface-2); color: var(--muted); }
.prop-card-meta { margin-top: 4px; }
.prop-expired-note {
  font-size: 12px; color: var(--neg); font-weight: 600; margin: 8px 0 0; max-width: 85ch;
}
.prop-scorecard--expired { border-color: color-mix(in oklch, var(--neg) 40%, var(--line)); }
.prop-score-expired-flag {
  font-size: 11px; font-weight: 700; color: var(--neg); margin-bottom: 6px;
}
.prop-all-expired-note { font-size: 12px; color: var(--neg); font-weight: 600; margin-top: 8px; }
/* A card RETAINED past its own expiry because its feedback obligation isn't
   discharged yet (PRD 1167 — "if there has been any feedback then we act on
   it, don't just sweep them"). Distinct amber tone from the plain --expired
   red: this one is still awaiting a human action, not just stale. */
.prop-card--carried { border-left-color: var(--warn); opacity: 0.85; }
.prop-carried-note {
  font-size: 12px; color: var(--warn); font-weight: 600; margin: 8px 0 0; max-width: 85ch;
}
.prop-deck-cap-note { font-size: 11px; color: var(--muted); margin: 4px 0 0; }
/* Then-vs-now on a re-priced carried card — context for the scorecard's real
   numbers, not a competing figure, so it follows the same quiet-annotation
   treatment as .prop-deck-cap-note rather than a headline weight. */
.prop-reprice-drift { font-size: 11px; color: var(--muted); margin: 4px 0 0; max-width: 85ch; }
.prop-reprice-drift-metric { color: var(--muted); }
/* "We cannot compute then-vs-now" must not read like a real measurement —
   de-emphasised further than a known drift value via italics + reduced opacity. */
.prop-reprice-drift--unknown { font-style: italic; opacity: 0.75; }
/* Modifier layered on .prop-expired-note (DisqualifiedBanner renders both
   classes together) — a delta, not a standalone block, so it must not fight
   the base rule's warning weight. */
.prop-disqualified-note { }
.prop-reprice-failed-note {
  font-size: 12px; color: var(--neg); font-weight: 600; margin: 8px 0 0; max-width: 85ch;
}
.prop-pager-count {
  font-size: 11px; font-family: var(--mono); color: var(--muted); margin-left: auto;
}

.prop-deployment-note { font-size: 11px; color: var(--muted); max-width: 100ch; }

/* Banner: prose left, account balance pinned right. The day's P&L is a
   percentage of something; that something belongs on the same line. */
.opts-headline { display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap; }
.opts-headline-main { flex: 1 1 420px; min-width: 0; }
.opts-headline-balance { margin-left: auto; text-align: right; }
.opts-balance-main { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.opts-balance-label {
  font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
}
.opts-balance-value { font-size: 22px; font-weight: 700; font-family: var(--mono); color: var(--text); }
.opts-balance-sub { font-size: 11px; color: var(--muted); margin-top: 3px; font-family: var(--mono); }
@media (max-width: 720px) {
  .opts-headline-balance { margin-left: 0; text-align: left; }
  .opts-balance-main { align-items: flex-start; }
}

/* Unpaired option legs — real broker positions that are not spreads. */
.opts-unpaired { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.opts-unpaired-title { font-size: 12px; color: var(--warn); margin: 0 0 6px; }
.opts-unpaired-table { width: 100%; font-size: 12px; }
.opts-unpaired-note { font-size: 11px; color: var(--muted); margin: 6px 0 0; max-width: 90ch; }

/* Open Orders: an order that cannot fill, and an exit that never did. */
.opt-panel-sub { font-size: 11px; font-weight: 400; color: var(--muted); margin-left: 8px; }
.opt-unfillable { font-size: 10px; color: var(--neg); font-weight: 600; }
.opt-money-note { font-size: 10px; color: var(--muted); }
.opt-dead-exit { font-size: 12px; color: var(--warn); margin: 10px 0 0; max-width: 95ch; }

.prop-funnel-verdict { font-size: 12px; color: var(--text-2); margin: 10px 0 0; max-width: 95ch; }
.prop-funnel-verdict--short { color: var(--warn); }

/* ================= MOBILE APP LAYOUT (Options Log on a phone) =============
   Paired with dashboard/lib/viewport.js — MOBILE_QUERY there is this file's
   --bp-mobile (820px). Two definitions of "mobile" that drift is the bug this
   pairing exists to prevent: if you change one, change the other.

   The phone surface serves exactly two use-cases:
     1. review the open book + the Analyst's read, and give a human opinion
     2. review a proposed trade, and give a human opinion
   Everything below is in service of those two and nothing else. Desktop is
   untouched — every rule here is inside a max-width query.
   ========================================================================= */

/* ---- Bottom tab bar (pages/ticker-details.jsx MobileTabBar) ------------- */
/* Hidden above the breakpoint: the desktop page is one scroll, no tabs. */
.mob-tabbar { display: none; }

@media (max-width: 820px) {
  .mob-tabbar {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 250;
    background: color-mix(in oklch, var(--surface) 92%, transparent);
    backdrop-filter: blur(10px);
    border-top: 1px solid var(--line);
    /* env() keeps the tabs above the iOS home indicator rather than under it. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .mob-tab {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; min-height: 54px; padding: 6px 4px;
    background: none; border: none; border-top: 2px solid transparent;
    color: var(--muted); font-family: var(--sans); font-size: 11px; font-weight: 600;
    letter-spacing: .02em; cursor: pointer;
  }
  .mob-tab--on { color: var(--accent); border-top-color: var(--accent); }
  .mob-tab-icon { font-size: 15px; line-height: 1; }
  .mob-tab-badge {
    position: absolute; top: 5px; left: 50%; margin-left: 8px;
    min-width: 16px; height: 16px; padding: 0 4px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 999px; background: var(--accent); color: var(--bg);
    font-size: 10px; font-weight: 700; line-height: 1;
  }
  /* The bar is fixed, so the page needs a floor of its own height + inset or
     the last card sits permanently under it and can never be read. */
  .opts-page--mobile { padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px)); }

  .opts-page-head--mobile { margin-bottom: 10px; }
  .opts-mob-title { margin: 0; font-size: 18px; }
  .opts-mob-loop {
    margin: 4px 0 0; font-size: 12px; line-height: 1.45; color: var(--muted); max-width: none;
  }
  /* The desktop page's five-line "three voices" preamble is desk reading; the
     mobile head states the same contract in two lines (.opts-mob-loop). */
  .opts-page--mobile .opts-page-loop { display: none; }

  /* ---- Positions as stacked cards (panels/options-summary.jsx) ---------- */
  .opts-pos-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
  .opts-pos-card {
    border: 1px solid var(--line); border-radius: var(--r);
    background: var(--surface-2); overflow: hidden;
  }
  .opts-pos-card--error { padding: 12px; }
  .opts-pos-card-tap { display: block; padding: 12px 12px 10px; cursor: pointer; }
  .opts-pos-card-tap:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  .opts-pos-card-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    margin-bottom: 10px;
  }
  .opts-pos-card-title {
    font-family: var(--mono); font-size: 13px; font-weight: 600; color: var(--text);
  }
  .opts-pos-card-label {
    display: block; font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--muted);
  }
  /* The two numbers a reader opened the page for, big enough to read at a
     glance without zooming — everything else on the card is support. */
  .opts-pos-card-lead {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    padding-bottom: 10px; border-bottom: 1px solid var(--line);
  }
  .opts-pos-card-big { font-family: var(--mono); font-size: 20px; font-weight: 600; }
  .opts-pos-card-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px;
    margin: 10px 0 0; padding: 0;
  }
  .opts-pos-card-cell > dt { margin: 0; }
  .opts-pos-card-value {
    margin: 0; font-family: var(--mono); font-size: 13px; color: var(--text);
  }
  .opts-pos-card-frozen {
    margin: 10px 0 0; padding-top: 8px; border-top: 1px solid var(--line);
    font-family: var(--mono); font-size: 11px; color: var(--text-2);
  }
  .opts-pos-card-suspect { margin: 8px 0 0; font-size: 11px; }
  /* Stacked, not side-by-side: commenting is the job, "Full detail" is the
     escape hatch. Two equal-weight buttons in a row made the reader choose
     between them; this makes the primary action unmissable and puts the
     secondary one underneath at link weight. */
  .opts-pos-card-foot {
    display: block;
    padding: 10px 12px; border-top: 1px solid var(--line); background: var(--surface);
  }
  .opts-pos-card-act { display: flex; align-items: center; gap: 8px; }
  /* The button, and the <span class="feedback"> wrapper it sits in, both go
     full width — otherwise the wrapper shrink-wraps and the 100% below
     resolves against the wrong box. */
  .opts-pos-card-act .feedback { flex: 1; display: block; }
  /* Commenting is the point of this page on a phone — a real full-width 44px
     target, filled rather than outlined, so it reads as THE action. */
  .opts-pos-card-foot .feedback-btn {
    width: 100%; justify-content: center;
    min-height: 44px; padding: 10px 16px; font-size: 14px; font-weight: 600;
    background: var(--accent); border-color: var(--accent); color: var(--bg);
  }
  .opts-pos-card-foot .feedback-btn:hover { filter: brightness(1.08); }
  /* Still a 40px target even at link weight — it is a tappable control on a
     phone, and the quieter styling must not shrink the box a thumb aims at. */
  .opts-pos-card-more {
    display: block; width: 100%; margin: 4px 0 0; min-height: 40px; padding: 4px;
    background: none; border: none; color: var(--muted);
    font-family: var(--sans); font-size: 12px; cursor: pointer; text-align: center;
  }

  /* An unpaired leg's table is 5 columns of prose; let it scroll in its own
     box rather than clip inside the card. */
  .opts-unpaired { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .opts-unpaired-table { min-width: 520px; }
  .opts-unpaired-note { min-width: 0; }

  /* ---- Proposal deck --------------------------------------------------- */
  .prop-pager-arrow { min-width: 44px; min-height: 44px; font-size: 20px; }
  .prop-dot { min-width: 14px; min-height: 14px; }
  .prop-swipe-hint {
    margin: 10px 0 0; text-align: center; font-size: 11px; color: var(--muted);
  }
  .prop-deck { touch-action: pan-y; }
  /* Stacked, the scorecard must come FIRST. On desktop the two columns sit
     side by side so credit / max loss / win probability are visible while
     you read the narrative; collapsed into one column they land seven
     bullets down, and a reviewer would be scrolling past the prose to reach
     the numbers they are actually deciding on. */
  .prop-card-body { display: flex; flex-direction: column; }
  .prop-scorecard { order: -1; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
  /* The gate table is the one wide thing left on a proposal card; let it
     scroll inside its own box rather than widening the page. */
  .prop-checks { display: block; overflow-x: auto; white-space: nowrap; }

  /* ---- Feedback modal → bottom sheet ----------------------------------- */
  /* A centered 420px dialog under an on-screen keyboard leaves the textarea
     off-screen. Docked to the bottom, the compose field sits directly above
     the keyboard, which is where a thumb already is. */
  .fb-backdrop { align-items: flex-end; padding: 0; }
  .fb-modal {
    width: 100%; max-width: 100%;
    border-radius: 14px 14px 0 0; border-bottom: none;
    /* dvh, not vh: the visible viewport shrinks when the keyboard opens. */
    max-height: 88dvh;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    font-size: 14px;
  }
  /* 16px is the threshold below which iOS Safari zooms the page on focus —
     never go under it on an input a phone user is expected to type into. */
  .fb-field input, .fb-field textarea { font-size: 16px; padding: 10px; }
  .fb-field textarea { min-height: 120px; }
  .fb-chip { min-height: 36px; padding: 8px 14px; font-size: 13px; }
  .fb-chips { flex-wrap: wrap; }
  /* Cancel left, Send right — the source order, which is also the platform
     convention; both stretched so neither is a small target. */
  .fb-actions { justify-content: stretch; gap: 10px; }
  .fb-actions button { flex: 1; min-height: 46px; font-size: 14px; }

  /* ---- Ticker worksheet form ------------------------------------------- */
  .opts-worksheet-form { flex-wrap: wrap; }
  .opts-worksheet-input { flex: 1 1 140px; width: auto; font-size: 16px; min-height: 44px; }
  .opts-worksheet-go { min-height: 44px; }
}

/* Worksheet form — shared shape, desktop default (was inline styles). */
.opts-worksheet-form {
  display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap;
}
.opts-worksheet-input {
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 6px;
  padding: 7px 12px; color: var(--text); font-family: var(--mono); font-size: 13px; width: 160px;
}
.opts-worksheet-go {
  padding: 7px 16px; background: var(--accent); color: var(--bg);
  border: 1px solid var(--line); border-radius: 6px; font-size: 12px; font-weight: 600;
}
