/*
 * Opportunity Board tab (Phase 2, spec §5/§6/§14; PM-dashboard redesign per
 * documentations/Opportunity_Board_UI_Enhancement_Specification.md). Loaded after app.css
 * and css/surface.css; scoped entirely under .ob-* / reused .tq-* so it cannot affect any
 * existing view.
 * Every colour is an app.css token. The file used to carry its own light hexes
 * plus a `:root[data-theme="dark"]` fork per rule; that pattern painted the LIGHT
 * hex whenever no data-theme attribute was set at all (JS disabled, pre-paint
 * fault) while app.css painted its DARK palette, and it knew nothing about
 * data-palette, so it showed slate's colours on warm and teal. The tokens carry
 * their own per-palette, per-mode value, so the forks are gone. Band chips reuse
 * .tq-* classes.
 *
 * SHAPE comes from css/surface.css. The board is a stack of DISCRETE CARDS floating
 * on the tinted ground, not a continuous hairline-divided sheet: ONE grouped rule
 * below carries the card chrome for every top-level block, `.view`'s own flex gap
 * carries the space between them (which is why the per-block margins this file used
 * to set are gone — a block that does not render must not leave its margin behind),
 * every label is uppercase mono at .16em tracking in --colhead, every figure is mono
 * and tabular, and a table's FRAME rounds while its CELLS stay square.
 *
 * Colour hierarchy (#8): Green (--bull-*) = Accepted · Blue (--info-*) = Watch ·
 * Grey (--text-muted / --surface-sunken) = Frozen · Red (--bear-*) = Rejected ·
 * Orange (--risk-*) = Risk warning (over-cap / concentration / crash). The three
 * alert rungs stay distinct: amber --warn-* (reduce) < orange --risk-* (over-cap)
 * < red --bear-* (halt). Do not collapse orange into amber.
 */

/* Base text tracks the site's body-prose token (soft off-white --text-2), not the
   brighter primary --text the section would otherwise inherit — so the board reads
   like the other tabs instead of near-white. Theme-correct (the token flips in
   light mode too). The board's one serif headline keeps the primary token, matching
   site headings; the semantic accent colours below still override per element.
   .ob-panel-title left this rule when it became a tracked-mono section label — a
   label lives in --colhead like every other label on the page. */
#view-opportunity-board { color: var(--text-2); }
#view-opportunity-board .ob-title { color: var(--text); }

/* ── The board's cards ─────────────────────────────────────────────────────── */
/* Every top-level block on the board is one card, and this is the ONLY place the
   surface / hairline / radius / shadow quartet is written on this page. A block
   that needs to differ — the risk banner's tint rung, the Watch section's info
   rail, the focus banner's accent edge — overrides exactly the one declaration it
   changes, further down the file, where its own rule already lives. */
.ob-cmdbar, .ob-risk, .ob-exec, .ob-themes, .ob-expect-flag, .ob-focus, .ob-tray,
.ob-cmp-panel, .ob-sec, .ob-xray, .ob-changed, .ob-frozen, .ob-empty {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  padding: clamp(13px, 1.2vw, 18px);
}

/* ── Labels ────────────────────────────────────────────────────────────────── */
/* The design's signature: uppercase mono at wide tracking in the quietest tier, so
   a section can be introduced without a heading big enough to compete with the data
   under it. Everything that NAMES something rather than saying it — section heads,
   column heads, KPI keys, the exec-summary and CIO cell keys, the collapsible
   panels' titles, the <details> summaries — is this one type at this one rank. */
.ob-h,
.ob-pf-sub,
.ob-themes-lab,
.ob-ex-k,
.ob-cio-q,
.ob-sim-sub,
.ob-kpi-k,
.ob-tray-lab,
.ob-focus-tag,
.ob-panel-title,
.ob-disclosure-h,
.ob-exec > summary,
.ob-changed > summary,
.ob-frozen > summary {
  font: 500 10.5px/1.4 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--colhead);
}

/* .ob-group-h is DATA, not a section name, so it is deliberately NOT in the
   label group above. Its text is a driver family that familyLabel() has already
   Title-Cased, and when a driver has no classifiable family it is up to 28
   characters of the driver's own prose plus an ellipsis. Uppercasing it
   overrode the authored casing and rendered a truncated sentence in full caps
   -- and the same value appears un-uppercased two panels away, in the theme
   chips and the comparison table's "strongest driver" cell. One datum, two
   casings, one screen. */
.ob-group-h {
  font: 500 10.5px/1.4 var(--font-sans);
  letter-spacing: .16em;
  color: var(--colhead);
}

/* ── Numbers ───────────────────────────────────────────────────────────────── */
/* Mono and tabular everywhere a figure sits in a column or is replaced between
   renders, so digits keep their place instead of jittering as a value updates. */
.ob-sub, .ob-count-pill, .ob-posture-pill, .ob-risk-metrics, .ob-group-net,
.ob-dcard-hd, .ob-meter-num, .ob-ev-badge, .ob-chg-ts, .ob-card-hd,
.ob-kpi-v, .ob-exp-k, .ob-exp-v, .ob-leg-v, .ob-leg-p, .ob-mini-v,
.ob-ex-v b, .ob-cio-a b, .ob-cio-tq, .ob-focus-meta, .ob-tray-chip,
.ob-sim-tbl td, .ob-sim-chip, .ob-rp-date, .ob-rp-win, .ob-rp-loss {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Headlines are the serif and only headlines — surface.css already gives every
   h1/h2 the family, the weight and the optical tracking, so only size is set. */
.ob-title { margin: 0; font-size: 1.2rem; }
.ob-sub { font-size: 0.78rem; opacity: 0.7; }
.ob-h { margin: 0 0 0.6rem; }
.ob-pf-sub { margin: 0.6rem 0 0.4rem; }

/* ── §1 Command Bar (sticky) ───────────────────────────────────────────────── */
.ob-cmdbar {
  position: sticky; top: 0; z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.5rem 1rem; padding: 10px 16px;
}
.ob-cmdbar-left { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.ob-cmdbar-right { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }

.ob-count-pill {
  display: inline-block; font-size: 0.72rem; font-weight: 500; padding: 0.24em 0.7em;
  border-radius: var(--r-pill); border: 1px solid transparent; white-space: nowrap;
}
.ob-count-trade { background: var(--bull-tint); color: var(--bull-text); border-color: var(--bull-edge); }
.ob-count-watch { background: var(--info-tint); color: var(--info-text); border-color: var(--info-edge); }
.ob-count-rej { background: var(--surface-sunken); color: var(--text-muted); border-color: var(--line); }

.ob-narrative { font-size: 0.82rem; opacity: 0.85; max-width: 54ch; }

/* posture pill — distinct classes so it doesn't inherit the banner's border-left */
.ob-posture-pill { font-weight: 500; font-size: 0.74rem; padding: 0.26em 0.75em; border-radius: var(--r-pill); white-space: nowrap; border: 1px solid transparent; }
.ob-pp-normal { background: var(--bull-tint); color: var(--bull-text); border-color: var(--bull-edge); }
.ob-pp-reduce { background: var(--warn-tint); color: var(--warn-text); border-color: var(--warn-edge); }
.ob-pp-halt { background: var(--bear-tint); color: var(--bear-text); border-color: var(--bear-edge); }

.ob-groupby { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-pill); overflow: hidden; }
.ob-groupby[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }
.ob-groupby-btn {
  font: 500 11px/1 var(--font-sans); letter-spacing: 0.06em;
  padding: 0.5em 0.85em; background: transparent; border: 0; cursor: pointer;
  opacity: 0.7; color: inherit;
  transition: background-color var(--dur-micro) var(--ease-out);
}
.ob-groupby-btn[aria-pressed="true"] { background: var(--surface-row-active); opacity: 1; }
.ob-groupby-btn[disabled] { cursor: default; }

/* ── Phase-5B risk banner (reduce/halt only) ───────────────────────────────── */
/* The card chrome comes from the group above; only the rung's own tint and left
   rail are stated here. The three rungs stay separate families — amber "reduce"
   is not a paler orange, and orange "over cap" is not a paler red. */
.ob-risk-top { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.ob-risk-tag { font-weight: 600; font-size: 0.82rem; }
.ob-risk-metrics { font-size: 0.76rem; opacity: 0.8; }
.ob-risk-breaches { margin: 0.4rem 0 0; padding-left: 1.05rem; }
.ob-risk-breaches li { font-size: 0.76rem; margin: 0.1rem 0; }
.ob-risk-reduce { border-left: 3px solid var(--warn-dot); background: var(--warn-tint); }
.ob-risk-reduce .ob-risk-tag { color: var(--warn-text); }
.ob-risk-halt { border-left: 3px solid var(--bear-dot); background: var(--bear-tint); }
.ob-risk-halt .ob-risk-tag { color: var(--bear-text); }

/* ── §2 Top Themes ribbon ──────────────────────────────────────────────────── */
.ob-themes { display: flex; align-items: center; gap: 0.4rem; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.ob-themes-lab { flex: 0 0 auto; margin-right: 0.2rem; }
.ob-theme-chip { flex: 0 0 auto; font-size: 0.76rem; padding: 0.3em 0.7em; border-radius: var(--r-sm); border: 1px solid var(--line); border-left-width: 3px; cursor: pointer; white-space: nowrap; background: transparent; color: inherit; }
.ob-theme-chip.ob-th-pos { border-left-color: var(--bull-dot); }
.ob-theme-chip.ob-th-neg { border-left-color: var(--bear-dot); }
.ob-theme-chip.ob-theme-crash { border-left-color: var(--risk-dot); color: var(--risk-text); cursor: default; }
/* selected theme — a blue ring + tint (matches .ob-exp-on / .ob-cmp-on); leaves
   the pos/neg left accent intact. Click again to toggle it off. */
.ob-theme-chip.ob-th-on { background: var(--diff-hi); box-shadow: inset 0 0 0 1px var(--accent-edge); font-weight: 600; opacity: 1; }
.ob-theme-ct { font-size: 0.62rem; vertical-align: super; opacity: 0.6; margin-left: 0.15em; }
/* Expectation-concentration strip (Fable S1-4) — non-interactive info chips.
   It used to pull itself up under the themes ribbon with a negative margin; it is
   now its own card, separated by the view's gap like every other block. */
.ob-expect .ob-expect-chip { cursor: help; border-left-color: var(--info-edge); }
.ob-expect-flag { font-size: 0.76rem; color: var(--risk-text); }

/* ── §3 Today's Book — decision cards ──────────────────────────────────────── */
.ob-book { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 0.7rem; }
.ob-book-empty { padding: 1rem; border: 1px dashed var(--line); border-radius: var(--r-md); opacity: 0.75; font-size: 0.88rem; }
.ob-group { margin-bottom: 1rem; }
.ob-group-h { margin: 0.2rem 0 0.5rem; }
.ob-group-net { font-size: 0.74rem; opacity: 0.6; margin-left: 0.5rem; letter-spacing: normal; text-transform: none; }

/* A decision card is a NESTED surface: it steps down one radius rung, drops the
   shadow the card around it already carries, and sits on the sunken tier. The 4px
   left rail is the accept/watch identity, so that edge stays square. */
.ob-dcard {
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  padding: 0.7rem 0.8rem;
}
.ob-dcard.ob-acc { border-left-color: var(--bull-dot); }
.ob-dcard.ob-wch { border-left-color: var(--info-dot); }
.ob-dcard-hd { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; margin-bottom: 0.35rem; }
.ob-dpair { font-weight: 600; }
.ob-ddir { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.14em; opacity: 0.7; }
.ob-dir-long { color: var(--bull-text); }
.ob-dir-short { color: var(--bear-text); }
.ob-dir-range { opacity: 0.6; }

/* #3 visual score meter */
.ob-meter { display: flex; align-items: center; gap: 0.5rem; margin: 0.3rem 0 0.5rem; }
.ob-meter-track { position: relative; flex: 1; height: 14px; border-radius: var(--r-xs); background: var(--seg-empty); overflow: hidden; }
.ob-meter-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--r-xs); }
.ob-meter-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line); }
.ob-meter-num { font-weight: 600; font-size: 1.05rem; letter-spacing: -0.02em; }
.ob-meter-band { font: 500 0.62rem/1 var(--font-mono); text-transform: uppercase; letter-spacing: 0.16em; opacity: 0.65; }
.ob-meter--slim .ob-meter-track { height: 9px; }
.ob-meter--slim .ob-meter-num { font-size: 0.85rem; }
.ob-meter-fill--excellent { background: var(--bull-dot); }
.ob-meter-fill--good { background: var(--new-dot); }
.ob-meter-fill--watch { background: var(--warn-dot); }
.ob-meter-fill--weak { background: var(--risk-dot); }
.ob-meter-fill--reject { background: var(--bear-dot); }

/* chip row */
.ob-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.2rem 0 0.5rem; align-items: center; }
.ob-driver-chip { font-size: 0.7rem; padding: 0.2em 0.6em; border-radius: var(--r-sm); background: var(--surface); border: 1px solid var(--line); white-space: nowrap; cursor: help; }
/* M3a — per-trade Market Expectations engine chip (owner-only feed) */
.ob-mxe-chip { font-size: 0.7rem; padding: 0.2em 0.6em; border-radius: var(--r-sm); background: var(--info-tint); border: 1px solid var(--info-edge); white-space: nowrap; cursor: help; }
.ob-mxe-chip--risk { background: var(--warn-tint); border-color: var(--warn-edge); color: var(--warn-text); }
.ob-ev-badge { font-size: 0.72rem; padding: 0.2em 0.55em; border-radius: var(--r-sm); background: var(--surface); border: 1px solid var(--line); white-space: nowrap; }
.ob-ev-badge.ob-ev-faint { opacity: 0.55; }
.ob-fit-badge { font-size: 0.7rem; font-weight: 600; padding: 0.2em 0.6em; border-radius: var(--r-sm); border: 1px solid var(--line); white-space: nowrap; }
.ob-fit-accept { background: var(--bull-tint); color: var(--bull-text); border-color: var(--bull-edge); }
.ob-fit-reduced { background: var(--warn-tint); color: var(--warn-text); border-color: var(--warn-edge); }

/* #4 why bullets */
.ob-why { margin: 0.3rem 0 0.4rem; padding-left: 1.05rem; font-size: 0.8rem; }
.ob-why li { margin: 0.14rem 0; opacity: 0.9; }
.ob-why-derived { opacity: 0.72; font-style: italic; }

/* #11 stability chip */
.ob-stability { font-size: 0.68rem; padding: 0.16em 0.55em; border-radius: var(--r-sm); background: var(--surface); border: 1px solid var(--line); white-space: nowrap; }
.ob-stab-new { color: var(--new-text); }
.ob-stab-up { color: var(--bull-text); }
.ob-stab-down { color: var(--bear-text); }

/* #7 marginal-risk line */
.ob-marginal { font-size: 0.74rem; opacity: 0.7; margin-top: 0.35rem; }
.ob-marginal-warn { color: var(--risk-text); opacity: 1; }
.ob-watch-why { font-size: 0.78rem; opacity: 0.82; margin: 0.1rem 0 0.1rem; }
/* #3 Watch holding-state band — reads as "waiting", distinct from an Accept */
.ob-watch-status { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin: 0.1rem 0 0.4rem; }
.ob-watch-pill { font: 500 0.66rem/1 var(--font-mono); text-transform: uppercase; letter-spacing: 0.16em; padding: 0.4em 0.7em; border-radius: var(--r-pill); background: var(--info-tint); color: var(--info-text); border: 1px solid var(--info-edge); white-space: nowrap; }
.ob-watch-wait { font-size: 0.74rem; opacity: 0.82; }

/* ── §4 What Changed ───────────────────────────────────────────────────────── */
.ob-changed > summary { cursor: pointer; padding: 0.15rem 0; }
.ob-changed-row { display: flex; gap: 0.45rem; font-size: 0.78rem; padding: 0.16rem 0; align-items: baseline; }
.ob-chg-glyph { flex: 0 0 auto; width: 1.2em; text-align: center; }
.ob-chg-txt { flex: 1 1 auto; min-width: 0; }
.ob-chg-ts { flex: 0 0 auto; font-size: 0.7rem; opacity: 0.5; white-space: nowrap; }
.ob-chg-new { color: var(--new-text); }
.ob-chg-drop { color: var(--text-muted); }
.ob-chg-band { color: var(--warn-text); }
.ob-chg-watch { color: var(--info-text); }
.ob-chg-dir { color: var(--mixed-text); }
.ob-chg-mv { opacity: 0.7; }
.ob-showall {
  margin-top: 0.5rem; font: 500 11.5px/1 var(--font-sans); letter-spacing: 0.06em;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 0.55em 1em; cursor: pointer; color: var(--accent);
  transition: background-color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.ob-showall:hover { background: var(--surface-sunken); border-color: var(--accent-edge); }

/* ── §5 Watch section ──────────────────────────────────────────────────────── */
/* The info rail replaces the card's own hairline on that edge — Watch is blue in
   the colour hierarchy, and the rail is how the section says so without a fill. */
.ob-sec-watch { border-left: 3px solid var(--info-dot); }
.ob-considered { margin-top: 0.7rem; }

/* ── §6 Rejected cards ─────────────────────────────────────────────────────── */
/* The section itself needs nothing beyond the card — its old rule was empty. The
   `.ob-sec-rej` class is still load-bearing even with no rule against it: the
   staged reveal in js/opportunity-board.js excludes it from the generic `.ob-sec`
   step so Rejected arrives after the book and the X-ray, not with them. */
.ob-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0.6rem; }
.ob-card { background: var(--surface-sunken); border: 1px solid var(--line); border-radius: var(--r-md); padding: 0.55rem 0.7rem; font-size: 0.8rem; }
.ob-card-rej { border-left: 3px solid var(--bear-dot); }
.ob-card-hd { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 0.3rem; }
.ob-card-why { opacity: 0.85; }
.ob-card-fix { margin-top: 0.3rem; font-size: 0.74rem; opacity: 0.6; font-style: italic; }

/* action badge (rejected cards) */
.ob-act { display: inline-block; font-size: 0.7rem; font-weight: 500; padding: 0.2em 0.55em; border-radius: var(--r-sm); white-space: nowrap; border: 1px solid transparent; }
.ob-act-accept { background: var(--bull-tint); color: var(--bull-text); border-color: var(--bull-edge); }
.ob-act-reduced { background: var(--warn-tint); color: var(--warn-text); border-color: var(--warn-edge); }
.ob-act-reject { background: var(--bear-tint); color: var(--bear-text); border-color: var(--bear-edge); }
.ob-act-neutral { background: var(--surface-sunken); border-color: var(--line); opacity: 0.8; }

/* ── §7 Frozen (collapsed) ─────────────────────────────────────────────────── */
.ob-frozen > summary { cursor: pointer; padding: 0.15rem 0; }
.ob-frz-row { display: flex; gap: 0.5rem; align-items: center; font-size: 0.78rem; padding: 0.16rem 0 0.16rem 0.5rem; opacity: 0.85; border-left: 3px solid var(--line); margin-top: 0.2rem; }
.ob-frz-why { opacity: 0.8; }

/* ── §8 Portfolio Exposure X-ray ───────────────────────────────────────────── */
.ob-xray-flags { list-style: none; margin: 0 0 0.8rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ob-xray-flags li { font-size: 0.76rem; padding: 0.2em 0.65em; border-radius: var(--r-sm); background: var(--risk-tint); color: var(--risk-text); border: 1px solid var(--risk-edge); }
.ob-xray-kpis { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-bottom: 0.9rem; }
/* The design's stat tile: its own raised tier, a large mono figure, a tracked
   label under it. One radius rung down from the card it sits in. */
.ob-kpi-tile { flex: 1 1 120px; background: var(--surface-kpi); border: 1px solid var(--line); border-radius: var(--r-md); padding: 0.65rem 0.75rem; }
.ob-kpi-v { font-size: 1.35rem; font-weight: 500; line-height: 1.05; letter-spacing: -0.02em; color: var(--text); }
.ob-kpi-k { margin-top: 0.3rem; }
.ob-xray-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; }
.ob-xray-col { min-width: 0; }
.ob-exp-row2 { display: grid; grid-template-columns: 6.5rem 1fr 3.2rem auto; align-items: center; gap: 0.4rem; font-size: 0.78rem; padding: 0.12rem 0; }
.ob-exp-k { font-size: 0.72rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: default; }
.ob-exp-v { text-align: right; }
.ob-exp-meta { font-size: 0.68rem; opacity: 0.6; text-align: right; white-space: nowrap; }

/* diverging exposure bar (centre 0, positive right / negative left) with a limit tick */
.ob-bar { position: relative; height: 10px; border-radius: var(--r-xs); background: var(--seg-empty); overflow: hidden; }
.ob-barf { position: absolute; top: 0; bottom: 0; display: block; border-radius: var(--r-xs); }
.ob-bar-pos { left: 50%; background: var(--bull-dot); }
.ob-bar-neg { right: 50%; background: var(--bear-dot); }
.ob-bar-over { background: var(--risk-dot); box-shadow: 0 0 0 1px var(--risk-edge) inset; }
.ob-bar-limit { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--line2); }
.ob-headroom { font-size: 0.68rem; opacity: 0.55; }
.ob-headroom.ob-over { color: var(--risk-text); opacity: 1; }

/* ══ Future-Vision (Phase 2) additions ══════════════════════════════════════ */

/* #11 Executive Summary — the 60-second read */
.ob-exec > summary { cursor: pointer; padding: 0.15rem 0; }
.ob-ex-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 0.55rem 1rem; margin-top: 0.75rem; }
.ob-ex-cell { min-width: 0; }
.ob-ex-wide { grid-column: 1 / -1; }
.ob-ex-k { margin-bottom: 0.25rem; }
.ob-ex-v { font-size: 0.86rem; line-height: 1.35; }
.ob-ex-risk { color: var(--risk-text); }
.ob-ex-link { font: inherit; color: var(--accent); background: transparent; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }

/* #2/#5 exposure/theme focus — a card wearing the accent edge and tint, so the
   active filter is visibly a different state of the same object. */
.ob-focus { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; font-size: 0.82rem; border-color: var(--accent-edge); background: var(--cell-hi); }
.ob-focus-meta { font-size: 0.74rem; opacity: 0.75; }
.ob-focus-clear {
  margin-left: auto; font: 500 11px/1 var(--font-sans); letter-spacing: 0.06em;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 0.5em 0.9em; cursor: pointer; color: var(--accent);
  transition: background-color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.ob-focus-clear:hover { background: var(--surface-sunken); border-color: var(--accent-edge); }
.ob-dim { opacity: 0.3; filter: grayscale(0.45); transition: opacity var(--dur-fast) var(--ease-out); }

/* #3 allocation wheel */
.ob-alloc { margin-bottom: 1rem; }
.ob-alloc-hd { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; flex-wrap: wrap; }
.ob-alloc-body { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; margin-top: 0.3rem; }
.ob-wheel-sel { flex: 0 0 auto; }
.ob-wheel-svg { flex: 0 0 auto; }
/* The donut's unfilled remainder ring. Its colour lives here rather than in the
   `stroke=` attribute the renderer writes, because a presentation attribute is not
   a reliable place for var() — a stylesheet rule is. --seg-empty is the token for
   an unfilled track. The FILLED arcs keep their JS-side categorical palette (see
   WHEEL_COLORS in js/opportunity-board.js): series identity is not a semantic role,
   so no token in the vocabulary carries it. */
.ob-wheel-track { stroke: var(--seg-empty); }
.ob-wheel-c1 { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; letter-spacing: -0.02em; fill: currentColor; }
.ob-wheel-c2 { font-family: var(--font-mono); font-size: 8px; text-transform: uppercase; letter-spacing: 0.16em; fill: currentColor; opacity: 0.55; }
.ob-wseg { transition: opacity var(--dur-fast) var(--ease-out); }
.ob-wseg-x { cursor: pointer; }
.ob-wseg-x:hover { opacity: 0.82; }
.ob-legend { display: grid; grid-template-columns: 1fr 1fr; gap: 0.15rem 0.9rem; flex: 1 1 220px; min-width: 200px; }
.ob-leg {
  display: flex; align-items: center; gap: 0.4rem; font-size: 0.74rem;
  background: transparent; border: 0; padding: 0.2rem 0.3rem; border-radius: var(--r-sm);
  cursor: pointer; color: inherit; text-align: left;
  transition: background-color var(--dur-micro) var(--ease-out);
}
.ob-leg:hover { background: var(--surface-row-active); }
.ob-leg-static { cursor: default; }
.ob-leg-static:hover { background: transparent; }
.ob-leg-dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: var(--r-xs); }
.ob-leg-k { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ob-leg-v { opacity: 0.75; }
.ob-leg-p { opacity: 0.5; font-size: 0.68rem; width: 2.6em; text-align: right; }

/* #2 clickable exposure rows */
.ob-exp-click { cursor: pointer; border-radius: 0; transition: background-color var(--dur-micro) var(--ease-out); }
.ob-exp-click:hover { background: var(--surface-row-active); }
.ob-exp-on { background: var(--diff-hi); box-shadow: inset 2px 0 0 var(--accent); }

/* #4 trade comparison */
.ob-cmp-btn { font-size: 0.68rem; padding: 0.2em 0.55em; border-radius: var(--r-sm); border: 1px solid var(--line); background: transparent; color: inherit; cursor: pointer; opacity: 0.7; white-space: nowrap; }
.ob-cmp-btn:hover { opacity: 1; }
.ob-cmp-on { background: var(--diff-hi); border-color: var(--accent-edge); color: var(--accent); opacity: 1; }
.ob-tray { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; }
.ob-tray-chip { display: inline-flex; align-items: center; gap: 0.25rem; font-size: 0.74rem; padding: 0.2em 0.25em 0.2em 0.6em; border-radius: var(--r-sm); background: var(--surface-sunken); border: 1px solid var(--line); }
.ob-tray-x { background: transparent; border: 0; cursor: pointer; color: inherit; opacity: 0.6; font-size: 0.72rem; padding: 0 0.15em; }
.ob-tray-x:hover { opacity: 1; }
.ob-tray-go {
  margin-left: auto; font: 500 11.5px/1 var(--font-sans); letter-spacing: 0.06em;
  padding: 0.6em 1.1em; border-radius: var(--r-md); border: 1px solid var(--accent-edge);
  background: var(--cell-hi); color: var(--accent); cursor: pointer;
}
.ob-tray-go[disabled] { opacity: 0.4; cursor: default; }
.ob-tray-clear {
  font: 500 11px/1 var(--font-sans); letter-spacing: 0.06em;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 0.5em 0.9em; cursor: pointer; color: var(--accent);
  transition: background-color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.ob-tray-clear:hover { background: var(--surface-sunken); border-color: var(--accent-edge); }
.ob-cmp-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.6rem; }
.ob-cmp-close {
  font: 500 11px/1 var(--font-sans); letter-spacing: 0.06em;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 0.5em 0.9em; cursor: pointer; color: var(--accent);
  transition: background-color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.ob-cmp-close:hover { background: var(--surface-sunken); border-color: var(--accent-edge); }

/* ── Tables ────────────────────────────────────────────────────────────────── */
/* The FRAME rounds and clips, the CELLS stay square, and the header row sits on
   the sunken tier in tracked mono capitals so it reads as chrome rather than as
   data. `.ob-cmp-scroll` is both the comparison table's horizontal scroller and
   its frame — a non-visible overflow on one axis makes the other a clipping
   context, which is what lets the header fill follow the radius. `.ob-tblcard` is
   that same frame around the simulator's table, which has no scroller of its own. */
.ob-cmp-scroll, .ob-tblcard {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.ob-tblcard { max-width: 520px; margin: 0.6rem 0; }

.ob-cmp-tbl, .ob-sim-tbl { border-collapse: collapse; width: 100%; }
.ob-cmp-tbl { font-size: 0.78rem; }
.ob-sim-tbl { font-size: 0.8rem; }
.ob-cmp-tbl th, .ob-cmp-tbl td { text-align: left; padding: 0.4rem 0.7rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.ob-sim-tbl th, .ob-sim-tbl td { text-align: left; padding: 0.4rem 0.7rem; border-bottom: 1px solid var(--line); }
.ob-cmp-tbl thead th, .ob-sim-tbl thead th {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--colhead);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  padding: 11px 14px;
}
.ob-cmp-tbl tbody th { font-weight: 500; opacity: 0.75; white-space: nowrap; }
.ob-sim-tbl tbody th { font-weight: 500; opacity: 0.78; }
/* The frame draws the last edge, so the last row must not draw one too. */
.ob-cmp-tbl tbody tr:last-child > *, .ob-sim-tbl tbody tr:last-child > * { border-bottom: 0; }
.ob-cmp-sub th { padding-left: 1.4rem; opacity: 0.55; font-size: 0.72rem; }
.ob-cmp-tbl .ob-meter { margin: 0; }
.ob-mini { display: inline-block; vertical-align: middle; width: 46px; height: 7px; border-radius: var(--r-xs); background: var(--seg-empty); overflow: hidden; }
.ob-mini-f { display: block; height: 100%; background: var(--info-dot); border-radius: var(--r-xs); }
.ob-mini-v { font-size: 0.72rem; opacity: 0.7; margin-left: 0.35rem; }

/* ══ Item-2 (deferred backend): CIO Workspace / Simulator / Replay ═══════════ */
/* Shared collapsible-panel chrome (a header button + a body revealed when open).
   These are `.ob-sec` sections, so the card quartet above already reached them and
   nothing is restated here. The head is the design's label-plus-gloss pairing — a
   tracked mono title beside the plain-English line that keeps the tracked caps
   from reading as jargon. */
.ob-panel-hd { display: flex; align-items: baseline; gap: 0.55rem; width: 100%; text-align: left; background: transparent; border: 0; color: inherit; cursor: pointer; padding: 0.15rem 0 0.5rem; flex-wrap: wrap; }
.ob-panel-caret { flex: 0 0 auto; opacity: 0.6; font-size: 0.8rem; color: var(--colhead); }
.ob-panel-sub { font: 400 12px/1.5 var(--font-sans); color: var(--text-muted); }
/* A label tier is quiet by design, and a quiet heading on a BUTTON can stop
   looking clickable — so the head lifts its own title on hover. Colour only:
   nothing moves, per the motion rules in reveal.css. */
.ob-panel-hd .ob-panel-title,
.ob-panel-hd .ob-panel-caret { transition: color var(--dur-micro) var(--ease-out); }
.ob-panel-hd:hover .ob-panel-title,
.ob-panel-hd:hover .ob-panel-caret { color: var(--text-2); }

/* #12 CIO Workspace briefing */
.ob-cio { border-left: 4px solid var(--mixed-dot); }
.ob-cio-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 0.7rem 1.1rem; margin: 0.3rem 0 0.4rem; }
.ob-cio-cell { min-width: 0; }
.ob-cio-q { margin-bottom: 0.3rem; }
.ob-cio-a { font-size: 0.84rem; line-height: 1.4; }
.ob-cio-link { font: inherit; color: var(--accent); background: transparent; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.ob-cio-tq { font-size: 0.72rem; opacity: 0.6; }
.ob-cio-note { font-size: 0.72rem; opacity: 0.62; }

/* #8 Portfolio Decision Simulator */
.ob-sim { border-left: 4px solid var(--info-dot); }
.ob-sim-body { margin-top: 0.2rem; }
.ob-sim-sub { margin: 0.9rem 0 0.45rem; }
.ob-sim-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.ob-sim-chip {
  font-size: 0.74rem; padding: 0.28em 0.65em; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: transparent; color: inherit;
  cursor: pointer; white-space: nowrap;
  transition: border-color var(--dur-micro) var(--ease-out);
}
.ob-sim-chip:hover { border-color: var(--accent-edge); }
.ob-sim-add-on { background: var(--bull-tint); border-color: var(--bull-edge); color: var(--bull-text); }
.ob-sim-remove-on { background: var(--bear-tint); border-color: var(--bear-edge); color: var(--bear-text); text-decoration: line-through; }
.ob-sim-actions { display: flex; gap: 0.5rem; align-items: center; margin: 0.8rem 0 0.4rem; }
.ob-sim-run {
  font: 500 11.5px/1 var(--font-sans); letter-spacing: 0.06em;
  padding: 0.7em 1.3em; border-radius: var(--r-md); border: 1px solid var(--accent-edge);
  background: var(--cell-hi); color: var(--accent); cursor: pointer;
}
.ob-sim-run[disabled] { opacity: 0.5; cursor: default; }
.ob-sim-clear {
  font: 500 11px/1 var(--font-sans); letter-spacing: 0.06em;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 0.55em 1em; cursor: pointer; color: var(--accent);
  transition: background-color var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.ob-sim-clear:hover { background: var(--surface-sunken); border-color: var(--accent-edge); }
/* #7 interactive simulator — live-status indicator replaces the "Run" button */
.ob-sim-live { font-size: 0.74rem; font-weight: 600; color: var(--bull-text); opacity: 0.9; }
.ob-sim-live-busy { color: var(--warn-text); }
.ob-sim-empty { padding: 0.9rem 0; opacity: 0.72; font-size: 0.85rem; }
.ob-sim-note { font-size: 0.75rem; color: var(--risk-text); margin: 0.4rem 0; }
.ob-sim-d { font-size: 0.72rem; margin-left: 0.25rem; }
.ob-sim-worse { color: var(--bear-text); }
.ob-sim-better { color: var(--bull-text); }
.ob-sim-flat { opacity: 0.5; }
.ob-sim-posture { font-weight: 600; font-size: 0.72rem; padding: 0.2em 0.55em; border-radius: var(--r-sm); }
.ob-sim-verdicts { list-style: none; margin: 0.3rem 0 0.4rem; padding: 0; }
.ob-sim-verdict { font-size: 0.8rem; padding: 0.18rem 0; display: flex; gap: 0.4rem; align-items: baseline; flex-wrap: wrap; }
.ob-sim-vtag { font-size: 0.68rem; font-weight: 600; padding: 0.2em 0.55em; border-radius: var(--r-sm); white-space: nowrap; }
.ob-sim-vok { background: var(--bull-tint); color: var(--bull-text); }
.ob-sim-vno { background: var(--warn-tint); color: var(--warn-text); }
.ob-sim-vwhy { opacity: 0.72; font-size: 0.76rem; }

/* #10 Historical Portfolio Replay */
.ob-replay { border-left: 4px solid var(--new-dot); }
.ob-rp-body { margin-top: 0.2rem; }
.ob-rp-dates { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.7rem; }
.ob-rp-date {
  font-size: 0.74rem; padding: 0.28em 0.65em; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: transparent; color: inherit;
  cursor: pointer; white-space: nowrap;
  transition: border-color var(--dur-micro) var(--ease-out);
}
.ob-rp-date:hover { border-color: var(--accent-edge); }
.ob-rp-date-on { background: var(--new-tint); border-color: var(--new-edge); color: var(--new-text); }
.ob-rp-asof { font-size: 0.72rem; opacity: 0.55; margin-bottom: 0.5rem; }
.ob-rp-block { margin: 0.5rem 0; }
.ob-rp-row { display: flex; align-items: center; gap: 0.45rem; font-size: 0.8rem; padding: 0.14rem 0; flex-wrap: wrap; }
.ob-rp-row .ob-meter { flex: 0 0 130px; margin: 0; }
.ob-rp-muted { opacity: 0.55; font-size: 0.76rem; }
.ob-rp-win { color: var(--bull-text); }
.ob-rp-loss { color: var(--bear-text); }

/* ── performance disclosures (the full hypothetical-results legend) ─────────── */
/* The last card on the board, under everything it qualifies. Only the container
   chrome lives here — the injected `.g10-disc-*` markup is sized and coloured by
   disclaimer.css, so the legend, the prose and the version stamp read identically
   here and in the persistent footer bar. Deliberately not collapsed and not dimmed
   further: the expectancy badge above is simulated, and a legend a reader cannot
   read is no legend. The hairline that used to separate it from the board above is
   now the card's own edge, so the border-top and its margin are gone. */
.ob-disclosure-h { margin-bottom: 0.6rem; }
.ob-disclosure .g10-disc { max-width: 100ch; }

/* ── empty / loading ───────────────────────────────────────────────────────── */
.ob-empty, .ob-empty-sec { padding: 1.2rem; opacity: 0.7; font-size: 0.9rem; }
.ob-empty-sec { padding: 0.6rem 0; }

/* ── narrow viewports (the app.css mobile layer also handles overflow) ──────── */
@media (max-width: 760px) {
  .ob-xray-cols { grid-template-columns: 1fr; }
  .ob-cmdbar { position: static; }
  .ob-cmdbar-right { width: 100%; }
  .ob-narrative { max-width: none; }
  .ob-legend { grid-template-columns: 1fr; }
  .ob-alloc-body { justify-content: center; }
  .ob-cio-grid { grid-template-columns: 1fr; }
  .ob-tblcard { max-width: none; }
}

/* Touch targets: raise the small toggle/chip/expander controls to the WCAG 2.5.8
   24px minimum on coarse pointers, without changing desktop density. */
@media (pointer: coarse) {
  .ob-groupby-btn, .ob-theme-chip, .ob-showall,
  .ob-cmp-btn, .ob-leg, .ob-focus-clear, .ob-tray-go, .ob-tray-clear, .ob-cmp-close,
  .ob-sim-chip, .ob-sim-run, .ob-sim-clear, .ob-rp-date {
    min-height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .ob-leg { justify-content: flex-start; }
}
