/* ============================================================
   EduMarkets — Component Styles
   ============================================================ */

/* ── Unified Header (logo + tabs + settings) ── */
.app-header {
  display: flex;
  align-items: stretch;
  padding: 0;
  /* viewport-fit=cover (set in app.html) lets content flow under the iOS
     status bar / dynamic island. Pad the top of the sticky header so its
     content sits below the system clock + battery, not behind them.
     env() resolves to 0 on devices without a safe area, so this is a
     no-op on desktop. */
  padding-top: env(safe-area-inset-top);
  /* Locked height (not min-height) — non-negotiable, the in-module
     lockup must wrap to 2 lines inside this height without growing it.
     Module-name font is tuned in .header-logo-mount below so 2-line
     wrap fits within the fixed 64px content area. Overflow clipping
     lives on .header-logo, NOT here — dropdowns (settings hamburger,
     theme toggle popovers, etc.) open below the header and would be
     clipped if we hid overflow on .app-header. */
  height: calc(64px + env(safe-area-inset-top));
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  background: var(--bg-header, var(--bg-surface));
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  flex-shrink: 0;
}
[data-theme="light"] .app-header {
  border-bottom: none;
  box-shadow: 0 1px 3px rgba(27, 58, 92, 0.08);
}

/* The header is a sticky stacking context at var(--z-sticky), so the settings
   dropdown nested inside it can't out-stack a body-level floating chart
   (var(--z-max)) or the formula panel by its own z-index. While the dropdown is
   open, lift the whole header above them so the menu is never hidden behind an
   open chart or formula dialog. Scoped to the open state, so normal stacking is
   unchanged. (:has() is already used this way elsewhere in this file.) */
.app-header:has(.settings-dropdown:not([hidden])) {
  z-index: calc(var(--z-max) + 2);
}

.header-logo {
  display: flex;
  align-items: center;
  width: calc(var(--workspace-gutter) + var(--sidebar-width));
  flex-shrink: 0;
  /* Aligns the lockup bar's CENTRE with the workspace-content divider
     line (border-left on .workspace-content in base.css), which sits at
     `var(--workspace-gutter)` from the page-left edge — the same X as
     the sidebar's left edge when one is visible.
     Math: line center = gutter + 0.5px (1px border). Bar centre wanted
     at the same X. Bar width at --led-wm-size:16 = max(2, 16*0.25) = 4px,
     so bar half-width = 2px. Required button-left X = gutter + 0.5 - 2
     = gutter - 1.5px. (Mobile/tablet media queries below override this
     padding-left, where the gutter collapses to 0 anyway.) */
  padding-left: calc(var(--workspace-gutter) - 1.5px);
  /* Clips the lockup if its content somehow exceeds the locked 64px
     header height. This is the column where the lockup lives — clipping
     here (not on .app-header) keeps settings dropdowns / popovers that
     open below the header visible. */
  overflow: hidden;
}

/* ── Tab Navigation (inside header) ── */
/* Wrap holds the scrollable nav plus the chevron scroll buttons + edge fades. */
.header-tab-nav-wrap {
  flex: 1;
  min-width: 0;
  position: relative;
  display: flex;
  align-items: stretch;
}
/* Edge-fade tied to distance-from-edge (set by JS as --tab-fade-left/right).
   The chevron buttons read the same scroll position to ramp their own opacity,
   so the fade and the chevron disappear together as the user nears the edge. */
.header-tab-nav-wrap::before,
.header-tab-nav-wrap::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 36px;
  pointer-events: none;
  z-index: 1;
}
.header-tab-nav-wrap::before {
  left: 0;
  background: linear-gradient(to right, var(--bg-header, var(--bg-surface)), transparent);
  opacity: var(--tab-fade-left, 0);
}
.header-tab-nav-wrap::after {
  right: 0;
  background: linear-gradient(to left, var(--bg-header, var(--bg-surface)), transparent);
  opacity: var(--tab-fade-right, 0);
}

/* Chevron scroll buttons — distinct from header-icon cluster: subtle pill
   with a teal accent border. Bottom-anchored so it sits on the same
   vertical baseline as the tab text rather than the header midline. */
.tab-scroll-btn {
  position: absolute;
  top: auto;
  bottom: 15px;     /* tab-text center ≈ 27 px from header bottom; pill is 24 tall → bottom 15 + 12 = 27 */
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-header, var(--bg-surface));
  border: 1px solid var(--color-accent-light, var(--color-accent));
  border-radius: 999px;
  color: var(--color-accent-light, var(--color-accent));
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  padding: 0;
  transition: background var(--transition), color var(--transition);
}
.tab-scroll-btn:hover {
  background: var(--color-accent-light, var(--color-accent));
  color: #fff;
}
.tab-scroll-btn[hidden] { display: none; }
.tab-scroll-btn--left  { left: 6px; }
.tab-scroll-btn--right { right: 16px; }   /* visible gap before right-cluster */

.header-tab-nav {
  flex: 1;
  display: flex;
  align-items: flex-end;
  gap: 0;
  overflow-x: auto;
  min-width: 0;
  scroll-behavior: smooth;
  /* Hide native scrollbar — discoverability comes from fade + chevrons. */
  scrollbar-width: none;
}
.header-tab-nav::-webkit-scrollbar { display: none; }
/* No reserved padding for the chevrons: they sit inside the 36px edge-fade
   gradient as opaque pills, so any tab text behind them is already faded.
   Skipping the padding keeps the tab strip from reflowing as the chevron
   ramps in/out near the edges. */

/* Legacy .tab-nav fallback (modules that haven't migrated yet) */
.tab-nav {
  display: flex;
  align-items: flex-end;
  gap: 0;
  padding: 0 24px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
  overflow-x: auto;
  flex-shrink: 0;
  z-index: var(--z-dropdown);
}

.tab-btn {
  padding: 20px 22px 14px;
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-muted);
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--transition), border-color var(--transition), background var(--transition), font-weight var(--transition);
  font-family: var(--font);
}

.tab-btn:hover { color: var(--text); background: rgba(255,255,255,0.04); }
[data-theme="light"] .tab-btn:hover { background: rgba(27,58,92,0.04); }
.tab-btn.active {
  color: var(--text);
  font-weight: 600;
  letter-spacing: 0;
  border-bottom-color: var(--color-accent-light);
}
[data-theme="light"] .tab-btn.active {
  border-bottom-color: var(--color-accent);
}

/* In-module header lockup. The JS renderer deliberately omits a size
   preset (.xl/.lg/.md/.sm) for this surface so these custom sizes win
   the cascade — the preset rules in edumarkets-lockups.css load after
   components.css and would otherwise override variable-based sizing.
   Header is locked at 64px (see .app-header above); the 12 + 2 + 16*2*1.1
   = 49.2px lockup content fits inside the 52px logo-trigger area, so
   long module names wrap to 2 balanced lines without growing the bar. */
.header-logo-mount .lockup-led {
  --led-wm-size: 16px;
  --led-name-size: 22px;
}
.header-logo-mount .lockup-led .name {
  /* line-height 1.15 gives descenders (g / y / p) room inside the
     line-box. No text-box trim on the name: its line clamp needs
     overflow:hidden, which clips the dots of the i's and the ascenders
     a cap trim leaves outside the box (see edumarkets-lockups.css).
     With single-line names in the in-module header, the extra 3-4px
     never threatens the 64px locked header height.
     At 1.15 the line box ends about 0.21em under the baseline, and the
     overflow:hidden of the clamp cut the foot of the g in "Brokerage"
     (owner, 2026-11-08): the bottom padding widens the clip box by the
     descender's last 0.06em without moving the line. */
  line-height: 1.15;
  padding-bottom: 0.06em;
  font-weight: 700;
  text-transform: none;
}

/* ── Settings dropdown ──
   Institutional editorial register: 6px radius, single soft shadow,
   no per-item stagger, no uppercase eyebrow labels, no per-row icons.
   Pattern matches Stripe / Linear / Vercel / GitHub / AWS dropdowns. */
.settings-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 260px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  z-index: var(--z-sidebar);
  overflow-x: hidden;
  overflow-y: auto;
  max-height: calc(100vh - 80px);
  padding: 6px 0;
  animation: dropdown-appear 120ms ease;
}
[data-theme="light"] .settings-dropdown {
  box-shadow: 0 4px 14px rgba(27, 58, 92, 0.10);
}
/* Opacity-only entrance. A translateY here promotes the panel to a GPU
   layer that rasterizes the 1px teal accent rule under the name at sub-pixel
   positions while it slides, so the line renders as a soft ~2px band and then
   snaps crisp when the animation ends — a visible "thicker line" flash. Fading
   in place keeps every hairline painted at its final, crisp position. */
@keyframes dropdown-appear {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.settings-dropdown[hidden] { display: none; }

/* (Per-item stagger animation removed — the panel is one object,
   not a parade of children. Stripe / Linear / Vercel all fade the
   panel as a whole.) */

/* ── Tab Panels ── */
.tab-panel { display: none; }
.tab-panel.active {
  display: block;
  flex-shrink: 0;
  /* Fallback fade-in for browsers without the View Transitions API
     (Firefox < 136, Safari < 18). Modern browsers use document.startViewTransition()
     in js/main.js, which provides a true cross-fade between the outgoing and
     incoming panel; the @supports block below disables this keyframe there to
     avoid double-animating. */
  animation: tabPanelFadeIn 0.2s cubic-bezier(0.05, 0.7, 0.1, 1.0);
}

@keyframes tabPanelFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@supports (view-transition-name: none) {
  .tab-panel.active { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  .tab-panel.active { animation: none; }
}

/* ── Params Sidebar ── */
.params-sidebar {
  width: 0;
  min-width: 0;
  overflow: hidden;
  background: var(--bg-surface);
  flex-shrink: 0;
  align-self: stretch;
  /* no transition — instant collapse avoids layout reflow jank */
  display: flex;
  flex-direction: column;
  /* Divider line lives on .workspace-content (base.css) — single source of truth. */
}

.params-sidebar.visible {
  width: var(--sidebar-width);
  min-width: var(--sidebar-width);
  overflow-y: auto;
}

/* Collapsed by user while still on trade/sensitivity tab */
.workspace.sidebar-collapsed .params-sidebar.visible {
  width: 0;
  min-width: 0;
  overflow: hidden;
}

#market-making-params-content {
  padding: 20px 16px;
  min-width: var(--sidebar-width);  /* prevents reflow during animation */
}

.sidebar-disclaimer {
  padding: 20px 16px 16px;
  margin-top: auto;
  font-size: 0.65rem;
  line-height: 1.5;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  min-width: var(--sidebar-width);
}

/* Sidebar toggle button — floats on workspace border */
.sidebar-toggle-btn {
  position: absolute;
  top: 20px;
  left: calc(var(--workspace-gutter) + var(--sidebar-width) - 12px);
  z-index: var(--z-dropdown);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
  font-family: var(--font);
  line-height: 1;
}
.sidebar-toggle-btn:hover {
  background: var(--bg-elevated);
  border-color: var(--color-accent);
  color: var(--color-accent-light);
}
/* The base rule above sets display: flex, which overrides the UA stylesheet's
   default [hidden] { display: none }. Re-assert it so JS can hide the toggle
   on tabs without a sidebar (Handbook, Exercises, etc.). */
.sidebar-toggle-btn[hidden] { display: none; }
.workspace.sidebar-collapsed .sidebar-toggle-btn,
.workspace:not(:has(.params-sidebar.visible)) .sidebar-toggle-btn {
  left: calc(var(--workspace-gutter) - 12px);
}
.workspace { position: relative; }

/* ── Cards ── */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 20px;
  transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}
.card:hover {
  border-color: var(--border);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  background: color-mix(in srgb, var(--sidebar-accent) 2%, var(--bg-surface));
}
[data-theme="light"] .card:hover {
  background: color-mix(in srgb, var(--sidebar-accent) 1%, var(--bg-surface));
  box-shadow: 0 2px 8px rgba(27, 58, 92, 0.08);
}

.card-sm { padding: 16px 18px; }

.card-title {
  font-size: 0.82rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.card-value {
  font-size: 1.45rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: lining-nums tabular-nums;  /* A1: lining figures; A2: tabular alignment */
  font-family: var(--font-mono);                   /* A2: monospaced for aligned numerals */
}

.card-value.positive { color: var(--positive); }
.card-value.negative { color: var(--negative); }

/* ── Buttons ── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  font-size: 0.9rem;
  font-weight: 500;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition), opacity var(--transition);
  font-family: var(--font);
  white-space: nowrap;
  text-align: center;
}

.btn-primary {
  background: var(--color-accent);
  color: #fff;
  border-color: var(--color-accent);
}
.btn-primary:hover { background: var(--color-accent-light); box-shadow: 0 2px 8px rgba(46, 109, 164, 0.35); }

.btn-secondary {
  background: color-mix(in srgb, var(--color-accent) 22%, var(--bg-elevated));
  color: var(--text);
  border-color: var(--color-accent);
  font-weight: 600;
}
.btn-secondary:hover {
  background: color-mix(in srgb, var(--color-accent) 38%, var(--bg-elevated));
  border-color: var(--color-accent-light);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--color-accent) 30%, transparent);
}

.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--text-muted);
  font-weight: 600;
}
.btn-ghost:hover {
  background: var(--bg-elevated);
  color: var(--text);
  border-color: var(--text-secondary);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}

.btn-sm { padding: 6px 12px; font-size: 0.82rem; }
/* Modifier for buttons sitting inline next to .sidebar-input / .sidebar-select.
   Native form inputs and CSS-styled buttons resolve `line-height: normal`
   slightly differently (browser-internal font metrics vs CSS box layout),
   so theoretical padding-matching always leaves a 1-2px gap. We pin an
   explicit height instead — `box-sizing: border-box` is set globally, so
   this is the final rendered height. Adjust the px value here if the
   inputs' rendered height changes platform-wide. */
.btn-match-input {
  padding: 0 14px;
  font-size: 0.88rem;
  line-height: 1;
  height: 34px;
}
/* "Advanced" disclosure for hiding power-user controls (e.g., random seed)
   beneath a primary action. Pairs with the "Simulate new path" button
   pattern across the platform. */
.sb-advanced > summary {
  cursor: pointer;
  color: var(--text-muted);
  font-size: 0.78rem;
  padding: 4px 0;
  user-select: none;
  list-style: none;
}
.sb-advanced > summary::before {
  content: "\25B8 ";
  display: inline-block;
  transition: transform 0.15s;
}
.sb-advanced[open] > summary::before { transform: rotate(90deg); }
.sb-advanced > summary:hover { color: var(--text-secondary); }
.sb-advanced[open] > summary { margin-bottom: 4px; }
.btn:disabled, .btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* Same look as :disabled but hoverable, so a data-tip can explain why the
   action is unavailable. The click must be guarded in JS. */
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:active:not(:disabled):not([aria-disabled="true"]) {
  transform: scale(0.97);
  opacity: 0.85;
}

/* ── Generic subtab nav (used across all modules) ── */
/*
   The hedge-fund module historically owned an .hf-scenario-btn class with
   identical primary-tier styling. The selectors below cover both so there is
   one canonical rule for the pill button look. The hedge-fund module retains
   .hf-scenario-btn--sub and .hf-scenario-btn--leaf modifiers for nested
   subtab tiers (those live in hedge-fund.css).
*/
/* Underline-on-active text-tab pattern. Replaces the prior pill design.
   Rank in multi-tier callers (.tier-row + .tier-row--sub / --leaf, see
   hedge-fund.css) is communicated by size + weight + indent, never by
   hue. Same primitive across all modules. */
.subtab-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0 1.5rem;
  border-bottom: 1px solid var(--border);
  margin: 0 0 1.25rem;
  padding: 0;
}

.subtab-btn {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  border-radius: 0;
  margin: 0;
  padding: 0.5rem 0 0.625rem;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.005em;
  line-height: 1.3;
  cursor: pointer;
  transition: color 0.12s ease;
}
.subtab-btn:hover {
  color: var(--text);
}
.subtab-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.subtab-btn.selected {
  color: var(--text);
  font-weight: 600;
}
.subtab-btn.selected::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  bottom: -1px;                  /* overlap the shared rule */
  height: 2px;
  background: var(--color-accent);
  border-radius: 1px;
}

/* ── Form controls ── */
.form-group {
  margin-bottom: 14px;
}

.form-control {
  width: 100%;
  padding: 9px 12px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
  font-size: 0.925rem;
  font-family: var(--font);
  transition: border-color var(--transition);
  -webkit-appearance: none;
  appearance: none;
}
input[type="number"].form-control { font-family: var(--font-mono); }

.form-control:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 1.5px rgba(46, 109, 164, 0.15);
}

.form-control::placeholder { color: var(--text-muted); }

/* Field hint — formatted display below a number input */
.field-hint {
  display: block;
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}

/* Range slider */
input[type="range"] {
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  height: 5px;
  background: var(--border);
  border-radius: 3px;
  outline: none;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  background: var(--color-accent);
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid var(--bg-surface);
}
input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  background: var(--color-accent);
  border-radius: 50%;
  border: 2px solid var(--bg-surface);
  cursor: pointer;
}

/* Remove number input spinners globally */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] { -moz-appearance: textfield; }

/* Checkbox / toggle */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 0;
}
.toggle-label {
  font-size: 0.9rem;
  color: var(--text-secondary);
}

.toggle {
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--border);
  border-radius: 22px;
  transition: background var(--transition);
}
.toggle-slider::before {
  content: '';
  position: absolute;
  height: 16px; width: 16px;
  left: 3px; top: 3px;
  background: white;
  border-radius: 50%;
  transition: transform var(--transition);
}
.toggle input:checked + .toggle-slider { background: var(--color-accent); }
.toggle input:checked + .toggle-slider::before { transform: translateX(16px); }

/* ── Badge ──
   Radius 20px → 4px. The pill shape + outlined chip + tinted background
   was the v0/Lovable default (T10 + T18). At 4px it reads as a small
   labelled tag — finance-document register, not Notion-template. */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 0.75rem;
  font-weight: 600;
  border-radius: 4px;
  border: 1px solid;
}
.badge-warning  { color: var(--warning);  border-color: var(--warning);  background: rgba(245,158,11,0.08); }  /* E1: static data */

/* ── Data table ── */
.data-table-wrap {
  overflow-x: auto;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

/* The header carries no fill band: the 2px bottom rule plus the weight and
   colour do the separating. Re-adding a background here flattens that back
   into the boxed look the rule replaced. The mobile sticky-first-column rule
   (in the 768px block) paints its own opaque background and is unaffected. */
.data-table thead th {
  padding: 8px 10px;                               /* D5: compact header height (was 11px 14px) */
  text-align: right;
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-secondary);
  border-bottom: 2px solid var(--border);
  white-space: nowrap;
}
.data-table thead th:first-child { text-align: left; }

.data-table tbody td {
  padding: 6px 10px;                               /* D5: compact row height (was 9px 14px) */
  text-align: right;
  color: var(--text);
  border-bottom: 1px solid var(--border-subtle);
  font-variant-numeric: lining-nums tabular-nums;  /* A1: lining + tabular numerals */
  white-space: nowrap;
  transition: background var(--transition);
}
.data-table tbody td:first-child { text-align: left; }
/* Opt-in monospace for numeric VALUES only. Headers are text labels, so they
   stay in the proportional (Inter) font even over numeric columns — only the
   numbers themselves are mono. Non-numeric tables inherit the proportional font. */
.data-table td.num,
.data-table tbody tr td[class*="num"] {
  font-family: var(--font-mono);
}

.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td {
  background: rgba(255,255,255,0.04);
  box-shadow: inset 2px 0 0 var(--color-accent-light);
}
[data-theme="light"] .data-table tbody tr:hover td {
  background: rgba(27,58,92,0.03);
  box-shadow: inset 2px 0 0 var(--color-accent);
}
.data-table tbody tr:hover td:first-child {
  box-shadow: inset 2px 0 0 var(--color-accent-light);
}
[data-theme="light"] .data-table tbody tr:hover td:first-child {
  box-shadow: inset 2px 0 0 var(--color-accent);
}
.data-table tbody tr:hover td:not(:first-child) {
  box-shadow: none;
}

.data-table .positive { color: var(--positive); }
.data-table .negative { color: var(--negative); }
.data-table .neutral  { color: var(--neutral); }  /* B2: zero / unchanged amounts */

/* ── Loading skeleton ── */
.skeleton {
  background: linear-gradient(90deg, var(--bg-elevated) 25%, var(--border) 50%, var(--bg-elevated) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
  border-radius: var(--r-sm);
}
@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* D3 / E1: data source footer at bottom of params sidebar */
.data-source-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 12px;
  margin-top: 12px;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.75rem;
  color: var(--text-muted);
  flex-wrap: wrap;
}

.chart-fullscreen-btn {
  position: absolute;
  top: 8px;
  left: 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  cursor: pointer;
  font-size: 0.82rem;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  transition: color var(--transition), border-color var(--transition), opacity var(--transition);
  font-family: var(--font);
  opacity: 0.55;
}
.chart-fullscreen-btn:hover { color: var(--text); border-color: var(--color-accent); opacity: 1; }

/* ── Theme toggle (inside settings dropdown) ── */
#theme-toggle {
  font-size: 0.9rem;
  font-weight: 500;
}

/* ── Loading spinner ── */
.spinner {
  display: inline-block;
  width: 16px; height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Shared sidebar form controls (used by MM, FX, and other modules) ── */

.sidebar-input,
.sidebar-select {
  width: 100%;
  padding: 7px 10px;
  font-size: 0.88rem;
  font-family: var(--font);
  background-color: var(--bg-input);
  color: var(--text);
  color-scheme: dark;
  border: 1px solid var(--sidebar-accent-dim);
  border-radius: var(--r-md);
  outline: none;
  transition: border-color var(--transition);
}
/* Numerical sidebar inputs are right-aligned platform-wide. Targets
   type=number AND text inputs declared numeric via inputmode (text inputs
   are the common pattern for comma-formatted large numbers). Text/select
   fields stay left-aligned. */
.sidebar-input[type="number"],
.sidebar-input[inputmode="numeric"],
.sidebar-input[inputmode="decimal"] {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
[data-theme="light"] .sidebar-input,
[data-theme="light"] .sidebar-select { color-scheme: light; }
.sidebar-input:hover:not(:focus),
.sidebar-select:hover:not(:focus) {
  border-color: var(--sidebar-accent);
}
.sidebar-input:focus,
.sidebar-select:focus {
  border-color: var(--sidebar-accent);
}

/* Mono font only for numeric inputs — text inputs and selects keep Inter */
input[type="number"].sidebar-input,
input[type="number"].form-control { font-family: var(--font-mono); }

/* ── Standardised sidebar typography (.sb-* classes) ─────────────────────── */
/* NEW shared classes — modules migrate to these incrementally.
   Old classes (.param-label, etc.) remain untouched until all modules
   are migrated. */

/* Section heading — UPPERCASE, used sparingly (2-5 per sidebar) */
.sb-heading {
  display: block;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
  margin: 0 0 10px;
}
.sb-heading:not(:first-child) { margin-top: 18px; }

/* Input label — Sentence case, used for every input field */
.sb-label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-secondary);
  margin: 8px 0 4px;
}
.sb-label:first-child { margin-top: 0; }

/* Two-inputs-per-row sidebar grid. Introduced in counterparty-risk (the first
   module to pair sidebar fields on one row) and promoted here so every module's
   sidebar can use it. Cells reflow via grid auto-flow when some are hidden, so
   no gap is left. Each cell is a <div> holding an .sb-label + a .sidebar-input
   / .sidebar-select. */
.sb-field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 12px;
  align-items: end;
  /* The cell labels below drop .sb-label's own 8px top margin, so without this the
     row would butt straight up against the field above it. Re-apply the same 8px
     here to keep a paired row on the same rhythm as a full-width field. */
  margin-top: 8px;
}
.sb-field-grid:first-child { margin-top: 0; }
.sb-field-grid .sb-label { margin: 0 0 3px; }

/* Secondary action button (Draw Chart, Add Bond, etc.) */
.sb-btn-secondary {
  width: 100%;
  padding: 8px 14px;
  min-height: 38px;
  box-sizing: border-box;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: var(--font);
  text-align: left;
  text-transform: none;
  background: var(--bg-elevated);
  color: var(--text);
  border: 1px solid var(--sidebar-accent-dim);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.sb-btn-secondary:hover { border-color: var(--sidebar-accent); }

/* (legacy .sb-btn-reset removed — replaced by shared .btn .btn-ghost .btn-sm) */

/* Mono font for numeric text inputs (inputmode="numeric"/"decimal") */
.sidebar-input[inputmode="numeric"],
.sidebar-input[inputmode="decimal"] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ── Responsive layout ── */
/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE BREAKPOINT STANDARD (platform-wide)

   Three widths, everywhere — module stylesheets must use these values too:
     1024px  tablet step. Shell narrows the sidebar (340 → 280). Content that
             lives on a sidebar tool tab collapses its multi-column grids here,
             because the in-flow sidebar squeezes the content column through
             the whole tablet range.
     768px   the mobile shell. Tab strip → dropdown, sidebar → drawer + FAB,
             gutter collapses, tables become scrollers, 44px touch floor.
             Content on wide (no-sidebar) tabs collapses its grids here.
      480px  small-phone tier. Density/typography floors, 2-col switcher,
             desktop-only fallback banners replace the five gated tools.
   Plus `@media (pointer: coarse)` for touch-target sizing (input-device
   keyed, not width keyed) and the landscape-phone height combo below.

   Do not introduce new width values. The former 900px module tier was folded
   into 768/1024 in the July 2026 mobile remediation (audit:
   edumarkets/audit/mobile-responsiveness-audit-2026-07.md).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  .tab-content-area {
    padding: 20px 20px 40px;
  }
  .header-logo {
    width: auto;
    padding-left: 16px;
    padding-right: 16px;
  }
  /* Sidebar: reduce from 340px to 280px on tablets */
  :root { --sidebar-width: 280px; }
}

@media (max-width: 768px) {
  :root { --sidebar-width: 300px; --workspace-gutter: 0px; }
  .tab-content-area {
    padding: 16px 16px 32px;
  }
  .tab-btn {
    padding: 12px 14px 11px;
    font-size: 0.875rem;
  }

  /* ── Nav bar: logo shrinks ── */
  .header-logo {
    width: auto;
    padding-left: 8px;
    padding-right: 4px;
    /* Let the lockup give way instead of crushing the tab selector.
       Long module names ("Interest Rate Options") used to hold their
       full one-line width here, squeezing .mobile-tab-select to a bare
       chevron. With the shrink chain open (min-width: 0 down to the
       lockup) the name wraps to its 2-line clamp and the tab selector
       keeps its label (min-width floor below). */
    flex-shrink: 1;
    min-width: 0;
  }
  .header-logo-mount { min-width: 0; }
  .header-logo-mount .lockup-led {
    max-width: 100%;
    /* Phone-size lockup: the desktop 22px module name filled the whole
       64px header once it wrapped to 2 lines (e.g. "Life Cycle of a
       Trade"). Same values as the .sm preset in edumarkets-lockups.css,
       set here because the header lockup carries no size preset (see
       the cascade note on .header-logo-mount .lockup-led above). */
    --led-wm-size: 10px;
    --led-name-size: 16px;
  }

  /* ── Tab bar: scroll hint gradient ── */
  .header-tab-nav {
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    mask-image: linear-gradient(to right, black 90%, transparent 100%);
    -webkit-mask-image: linear-gradient(to right, black 90%, transparent 100%);
  }
  .header-tab-nav::-webkit-scrollbar { display: none; }

  /* ── Sidebar → slide-in drawer ──
     On mobile the sidebar is CLOSED by default even when `.visible` (the
     class owned by the per-module tab state). The shared drawer toggle in
     js/main.js adds `.drawer-open` to reveal it and removes it to close. */
  .params-sidebar.visible {
    position: fixed;
    top: 64px;
    left: 0;
    bottom: 0;
    width: var(--sidebar-width);
    min-width: var(--sidebar-width);
    z-index: var(--z-sidebar);
    box-shadow: 4px 0 24px rgba(0,0,0,0.4);
    overflow-y: auto;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    background: var(--bg-surface);
  }
  .params-sidebar:not(.visible) {
    position: fixed;
    transform: translateX(-100%);
  }
  .sidebar-toggle-btn { display: none; }

  /* ── Tables: horizontal scroll with a sticky first column ──
     The wrap sets overflow-x: auto and an opaque background so the
     pinned first column has something to render against. Headers stay
     on one line (white-space: nowrap) so wide labels like "INDEXATION
     LAG" don't wrap to one letter per line. The first column is
     pinned to the left edge with position: sticky; left: 0 — same
     trick we use on the pricer grid — so the row label always stays
     visible while the user swipes the rest of the columns into view. */
  .data-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
    background: var(--bg-surface);
  }
  .data-table-wrap .data-table thead th { white-space: nowrap; }
  .data-table-wrap .data-table thead th:first-child,
  .data-table-wrap .data-table tbody td:first-child {
    position: sticky;
    left: 0;
    background: var(--bg-surface);
    z-index: 2;
    box-shadow: 1px 0 0 var(--border-subtle);
  }
  .data-table-wrap .data-table thead th:first-child { z-index: 3; }

  /* ── Generic data-table mobile fallback ──
     Tables that AREN'T inside a wrapper (.data-table-wrap, .table-
     container, ad-hoc overflow-x divs) and AREN'T inside a chapter
     card layout become horizontal scrollers themselves. Columns may
     not align perfectly across rows under display:block — that's a
     known trade-off of the responsive-tables pattern — but it beats
     having cells smashed to 20-30px wide with garbled headers (the
     Risk Portfolio / VaR & ES screenshots showed exactly that). */
  .data-table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
    /* Override per-module inline width:100% / table-layout:fixed so
       cells use their natural width and the table scrolls horizontally
       instead of squashing 8 columns into 30px each (Risk Portfolio
       was the smoking gun). !important is the only way to beat inline
       style here; we intentionally accept the heavy-handedness. */
    width: max-content !important;
    min-width: 100% !important;
    table-layout: auto !important;
  }
  /* When the table IS inside a known wrapper, keep it as a real
     table so column alignment is preserved — the wrapper handles
     the horizontal scroll. */
  .data-table-wrap .data-table {
    display: table;
    overflow-x: visible;
  }
  /* The chapter-body card layout (.learn-chapter-body .data-table)
     has its own block-level rules earlier in this file; it's already
     compatible with display:block. */

  /* Hide inline formula buttons inside tables on phones — at 20x20
     each they squeeze short table cells. Formulas are best read on
     desktop; the dedicated Formulas section in Reference still works. */
  .data-table .formula-btn { display: none; }

  /* ── Touch targets ── */
  .btn { min-height: 44px; }
  .btn-sm { min-height: 36px; }

  /* ── Formula panel: full-width on mobile ── */
  .formula-panel {
    width: calc(100vw - 16px);
    right: -100vw;
    top: 64px;
    max-height: calc(100vh - 80px);
  }
  .formula-panel--visible { right: 8px; }

  /* ── Touch targets: ensure 44px minimum ── */
  button, [role="button"], select {
    min-height: 44px;
  }
  input[type="text"], input[type="number"], input[type="email"],
  input[type="password"], input[type="search"], textarea, select {
    min-height: 44px;
    font-size: 16px; /* prevents iOS zoom on focus */
  }

  /* ── Tab bar replaced with a dropdown on mobile.
     The horizontal .tab-btn strip was unreadable on narrow phones; we
     swap it for a single "Current tab ▾" button that opens a full list
     of every tab in the active module. ── */
  .header-tab-nav { display: none; }
  .header-tab-nav-wrap { display: none; }
  .tab-scroll-btn { display: none !important; }

  /* ── Learn content: drop the nested scroll container on mobile.
     The outer .workspace-content is the one and only scroll container, so
     scrollIntoView lands the chapter under the sticky header and the rest
     of the content flows below. Also pins overflow-x so long words and
     wide tables can't push the content off the left edge. ── */
  .tab-content-area { overflow-x: hidden; }
}

@media (max-width: 480px) {
  :root { --sidebar-width: 88vw; }

  .tab-btn { padding: 10px 10px 9px; font-size: 0.82rem; }
  .card { padding: 14px; }
  .card-sm { padding: 10px 12px; }
  .form-control { padding: 10px 12px; }  /* touch-friendly inputs */
  .sidebar-input { padding: 10px 10px; }

  /* ── Header button cluster: tighten so theme / menu fit ── */
  .app-header { padding-right: 6px; }

  /* ── Font size floors ── */
  .data-table td, .data-table th { font-size: 0.78rem; }
}

/* ── Landscape phone: give more vertical space ── */
@media (max-height: 500px) and (max-width: 900px) {
  .app-header { min-height: 48px; }
  .tab-btn { padding: 8px 12px 7px; font-size: 0.82rem; }
  .params-sidebar.visible { top: 48px; }
}

/* ── Touch devices (input-keyed, not width-keyed) ──
   Ported from the Bank ALM sim touch pass. Sliders keep their thin
   visual track (redrawn on the track pseudo-elements, which WebKit
   centres vertically in the input box) but the input becomes a 40px
   hit strip with a 26px thumb; small controls get tap-target floors.
   Mouse/trackpad devices are untouched (pointer: fine). The equities
   sidebar slider variant (#eq-params-content, equities.css) is
   ID-scoped and keeps its own sizing by specificity, as documented
   where the shared .sidebar-slider is defined. */
@media (pointer: coarse) {
  /* The `html` prefix lifts these to (0,1,2): fi.css, credit.css and
     ir-options.css each carry an unscoped `input[type="range"]` rule
     (0,1,1) that loads after this file and would otherwise win the
     height back. The #eq-params-content variant (1,1,0) still wins,
     by design. */
  html input[type="range"] { height: 40px; background: transparent; }
  html input[type="range"]::-webkit-slider-runnable-track {
    height: 5px; background: var(--border); border-radius: 3px;
  }
  html input[type="range"]::-moz-range-track {
    height: 5px; background: var(--border); border-radius: 3px;
  }
  html input[type="range"]::-webkit-slider-thumb {
    width: 26px; height: 26px; margin-top: -10.5px; /* centre on the 5px track */
  }
  html input[type="range"]::-moz-range-thumb { width: 26px; height: 26px; }

  /* Tap-target floors for the controls the width-keyed 768 block does
     not reach on touch laptops/tablets at desktop widths. */
  .btn { min-height: 44px; }
  .btn-sm { min-height: 40px; }
  .sidebar-input, .sidebar-select, .form-control { min-height: 40px; }
  .formula-btn { width: 26px; height: 26px; }
}

/* ── Formula Panel (floating) ──
   Spec 20-formula-spec.md F1-F6 visual sweep applied. Equations
   de-carded (F1), section labels mixed-case (F2), intuition prose
   in Newsreader (F3), positioning via --header-clearance token (F4),
   numeric ink via --formula-numeric token (F5), variables table
   textbook-grade (F6). */
.formula-panel {
  position: fixed;
  top: var(--header-clearance);
  right: -620px;
  width: 560px;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - var(--header-clearance) - 24px);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 28px rgba(0,0,0,0.3), 0 1px 4px rgba(0,0,0,0.15);
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  transition: right 0.25s ease;
  overflow: hidden;
}
[data-theme="light"] .formula-panel {
  box-shadow: 0 8px 28px rgba(27, 58, 92, 0.12), 0 1px 4px rgba(27, 58, 92, 0.06);
}
.formula-panel--visible { right: 16px; }

.formula-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  cursor: grab;
  user-select: none;
  flex-shrink: 0;
}
.formula-panel-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}
.formula-panel-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 1.3rem;
  cursor: pointer;
  line-height: 1;
  padding: 0 4px;
}
.formula-panel-close:hover { color: var(--text); }

.formula-panel-body {
  padding: 18px 20px;
  overflow-y: auto;
  flex: 1;
}
.formula-section {
  margin-bottom: var(--space-block);
}
.formula-section:last-child { margin-bottom: 0; }

/* F2 — mixed-case quiet semibold (was uppercase + tracked T09 tell) */
.formula-section-label {
  font-family: var(--font);
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
  margin-bottom: 6px;
  font-weight: 600;
}

/* F1 — de-card. Equations sit in flowing white space, flex-centred
   like .learn-formula-box for coherence with handbook R2. */
.formula-symbolic,
.formula-numeric,
.formula-extra {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  margin: 4px 0;
  overflow-x: auto;
  font-size: 1.15rem;
}
.formula-symbolic .katex-display,
.formula-numeric .katex-display,
.formula-extra .katex-display { margin: 0; }
/* The symbolic/numeric sections render KaTeX into the full-width flex
   container itself, but the extra ("Where"/approximation) section renders
   into this child wrapper. Under align-items:center the child shrink-wraps
   to its content, so _wrapIfOverflowing (formula-panel.js) measures a
   too-narrow box and needlessly fragments a formula that fits at 518px.
   Stretch it to full width so the overflow check matches the other sections. */
.formula-extra-content { align-self: stretch; }
.formula-numeric {
  font-size: 1.05rem;
  color: var(--formula-numeric);
}

/* F6 — variables table: textbook-grade header (weight, no caps),
   no per-row separator on the body, subtle top hairline only on
   row transitions (denser than handbook tables — this is a reference
   card, not flowing prose). */
.formula-var-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  font-family: var(--font);
}
.formula-var-table th {
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.85rem;
  color: var(--text);
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
}
.formula-var-table td {
  padding: 6px 10px;
  border-bottom: 0;
  color: var(--text);
  vertical-align: top;
}
.formula-var-table tbody tr + tr td {
  border-top: 1px solid var(--border-subtle);
}
.formula-var-sym {
  color: var(--text);
  font-weight: 500;
  min-width: 40px;
  font-family: 'KaTeX_Main', 'STIX Two Text', serif;
  font-style: italic;
}
.formula-var-val {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--formula-numeric);
  text-align: right;
}
.formula-var-unit {
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* F3 — intuition prose as panel chrome (Inter sans, --text-secondary,
   no italic). Treats the intuition block as an annotation/tooltip
   note rather than a textbook commentary. Quieter than the earlier
   Newsreader-italic variant; the tinted background keeps the visual
   separation from the variables table above. */
.formula-intuition {
  font-family: var(--font);
  font-size: 0.88rem;
  font-weight: 400;
  font-style: normal;
  line-height: 1.55;
  color: var(--text-secondary);
  padding: 12px 14px;
  margin-top: var(--space-subsection);
  background: color-mix(in srgb, var(--color-accent) 6%, transparent);
  border-radius: 4px;
}

/* F10 — Distill hover-on-symbol-to-highlight. Hovering any rendered
   variable symbol in the formula highlights both that symbol (in
   every formula it appears) and the matching row in the variables
   table. Reverse: hover a row → highlight matching symbols. */
.is-formula-symbol {
  cursor: help;
  transition: background 0.1s, color 0.1s;
  border-radius: 2px;
}
[data-formula-sym].is-highlighted {
  background: color-mix(in srgb, var(--formula-numeric) 28%, transparent);
  color: var(--formula-numeric);
}
.formula-var-table tbody tr.is-highlighted td {
  background: color-mix(in srgb, var(--formula-numeric) 10%, transparent);
}

/* F12 — Copy LaTeX button. Sits next to the section label, hidden
   visual weight at rest, picks up on hover. Briefly turns positive
   colour after a successful copy. */
.formula-section-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.formula-copy-btn {
  background: transparent;
  border: 0;
  color: var(--text-muted);
  padding: 3px 5px;
  cursor: pointer;
  border-radius: 3px;
  display: inline-flex;
  align-items: center;
  transition: color 0.15s, background 0.15s;
}
.formula-copy-btn:hover {
  color: var(--text);
  background: var(--bg-elevated);
}
.formula-copy-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}
.formula-copy-btn.is-copied {
  color: var(--positive);
}

/* F11 — See-in-handbook cross-reference link. Footer slot under the
   intuition prose; quiet underlined link with a trailing arrow. */
.formula-cross-ref {
  margin-top: var(--space-block);
  padding-top: var(--space-block);
  border-top: 1px solid var(--border-subtle);
  font-size: 0.85rem;
}
.formula-cross-ref-link {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--text-muted);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.formula-cross-ref-link:hover {
  text-decoration-color: var(--text);
}
.formula-cross-ref-link::after { content: ' \2192'; }

/* F13 — Refresh nudge animation. Brief background flash on a numeric
   cell whose value changed during a refreshFormula() pass. */
.formula-var-val.num-flash {
  animation: formula-num-flash 700ms ease-out;
}
@keyframes formula-num-flash {
  0%   { background: color-mix(in srgb, var(--formula-numeric) 28%, transparent); }
  100% { background: transparent; }
}

/* F9 — visible focus ring inside the panel for keyboard users */
.formula-panel button:focus-visible,
.formula-panel a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ── Formula icon button (next to result labels) ── */

/* The formula button sits inline in the label cell. It is centred via
   vertical-align (below), NOT by making the cell display:flex — a flex cell is
   pulled out of the table's row-height model, so a label cell holding a button
   stops matching the height of its (taller, Tier-1 font) value cell and the
   row's bottom border drifts out of line between the two columns. Keeping the
   cell as a normal table-cell lets both columns share one row height. */

.formula-btn {
  display: inline-flex;
  vertical-align: middle;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-elevated);
  color: var(--text-muted);
  cursor: pointer;
  margin-left: 4px;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
  line-height: 1;
  padding: 0;
  /* Hide the markup's text content (most modules emit a literal 'f'
     character, not the italic ƒ glyph) — the actual glyph is drawn
     via ::before so every button is visually consistent regardless
     of what the per-module markup says. */
  font-size: 0;
}
.formula-btn::before {
  /* Matches the original pre-change Georgia italic ƒ exactly: 0.72rem
     italic, normal weight (no bold). The ::before approach is needed
     because most modules emit a literal 'f' in markup, not the U+0192
     glyph — drawing via ::before guarantees the right character.
     The translateY compensates for the italic ƒ's hook descender:
     align-items: center centers the line box, but the visible glyph
     (including the hook below baseline) sits low. -1.5px nudges it
     to optical centre inside the 20×20 button. */
  content: 'ƒ';
  font-family: Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-weight: normal;
  font-size: 0.72rem;
  display: inline-block;
  line-height: 1;
  transform: translateY(-1.5px);
}
.formula-btn:hover {
  color: var(--color-accent-text);
  border-color: var(--color-accent);
  background: rgba(46,109,164,0.1);
}
.formula-btn--active {
  color: var(--color-accent-text);
  border-color: var(--color-accent);
  background: rgba(46,109,164,0.15);
}

/* ── Custom Tooltip ──
   Quiet popover: 1px border + soft shadow + offset from trigger is enough
   to read as a tooltip without an accent-coloured left stripe. Above
   every pop-up, the expanded chart dialog included (z-max + 1), because
   the tooltip answers inside those too (owner, 2026-10-03). */
.edu-tooltip {
  position: fixed;
  z-index: calc(var(--z-max) + 2);
  max-width: 280px;
  padding: 8px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: 0 4px 12px rgba(0,0,0,0.25), 0 1px 3px rgba(0,0,0,0.1);
  color: var(--text);
  font-size: 0.78rem;
  line-height: 1.45;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
[data-theme="light"] .edu-tooltip {
  box-shadow: 0 4px 12px rgba(27, 58, 92, 0.1), 0 1px 3px rgba(27, 58, 92, 0.06);
}
.edu-tooltip--visible {
  opacity: 1;
  transform: translateY(0);
}

/* ── Content tabs (Overview / Handbook / Exercises) — global rules ──
   On these three "content" tabs, every module should look the same:
   no sidebar chevron, and a single vertical divider at the left edge of
   the content area.

   The body[data-active-tab="..."] attribute is set by main.js on every
   tab change, so these rules work regardless of per-module HTML
   structure (.workspace, .options-module, .macro-workspace, etc.) and
   regardless of how each module's activateTab manages chevron state. */

/* 1. Hide both chevron classes used across the platform. */
body[data-active-tab="reference"] .sidebar-toggle-btn,
body[data-active-tab="exercises"] .sidebar-toggle-btn {
  display: none !important;
}

/* (Divider line for no-sidebar modules now handled by the global
   .workspace-content rule in base.css — single source of truth.) */

/* ═══════════════════════════════════════════════════════════════════════════
   CROSS-MODULE PRICER PRIMITIVES (promoted 2026-07-10)
   .link-toggle and .sidebar-select-sm originated inside options.css and
   ir-options.css but are emitted by the shared grid_kit builders and used by
   several modules (options, ir-options, structured-products). Promoted here
   verbatim so new modules get them without carrying copies. The original
   module-CSS definitions are identical and harmless; they can be retired in a
   later cleanup.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Small sidebar select (sweep / metric pickers in pricer sidebars). */
.sidebar-select-sm {
  width: 100%;
  padding: 4px 6px;
  font-size: 0.8rem;
  font-family: var(--font);
  background-color: var(--bg-input);
  color: var(--text);
  border: 1px solid var(--sidebar-accent-dim);
  border-radius: var(--r-md);
  outline: none;
  cursor: pointer;
  margin-top: 2px;
  transition: border-color var(--transition);
}
.sidebar-select-sm:focus {
  border-color: var(--sidebar-accent);
}
.sidebar-select-sm option {
  background-color: var(--bg-input);
  color: var(--text);
}
[data-theme="light"] .sidebar-select-sm {
  background-color: #f5f5f5;
  border-color: var(--sidebar-accent-dim);
  color: #222;
  color-scheme: light;
}
[data-theme="light"] .sidebar-select-sm option {
  background-color: #f5f5f5;
  color: #222;
}
