/*
 * Lodestone — style.css (PostCSS entry).
 *
 * Phase A3 of the rebrand migration. PostCSS resolves these @imports
 * at build time (postcss-import) and emits the concatenated artefact
 * to /static/style.css, which is what templates/base.html loads.
 *
 * Import order matters — later rules override earlier ones for
 * conflicting selectors / tokens:
 *
 *   1. tokens.css      Marcus's :root token block (rebrand 2026)
 *   2. fonts.css       Plex @font-face (lands in A4; empty in A3)
 *   3. base.css        element resets + global typography (lands later)
 *   4. layout.css      global layout primitives (lands later)
 *   5. style-legacy.css  the pre-rebrand hand-rolled stylesheet
 *
 * style-legacy.css is imported last so its existing :root block can
 * still shadow tokens.css for any keys it overrides — this preserves
 * today's render byte-for-byte while Phase B sweeps tokens through.
 *
 * Component / layout @imports are added by Phase C as each lift lands.
 */

/*
 * Lodestone — TOKENS.css
 * Marcus Holm, rebrand 2026.
 *
 * Single :root block. Self-contained. PostCSS-ready (postcss-import +
 * postcss-nesting only — no Sass, no Tailwind). @import this at the top
 * of static/style.css and the existing render keeps working; renamed /
 * removed tokens are flagged in section comments for MIGRATION.md.
 *
 * Contrast: every text-on-surface pair checked against WCAG 2.2 AA at
 * 14px / regular weight. Worst-case ratio documented inline. Indigo
 * --ink is a brand colour, not body text — its lower contrast on dark
 * canvas is intentional and matches the Discord-blurple / Spotify-green
 * usage pattern (mark, accent, never small text).
 *
 * SPEC.md §4 + §22.2 — CVD-tuned status palette. Live / warn / bad
 * values refreshed within the existing CVD-safe constraints. Documented
 * inline in the status block.
 */

:root {
  /* ==========================================================================
     SURFACE RAMP — warm-dark, leather notebook not VS Code.
     Pushed marginally further warm than v5 (#0F0E0D → #100D0A). Discord
     runs ~#2B2D31, Spotify ~#191414; Lodestone sits warmer-and-deeper.
     ========================================================================== */

  --canvas:    #100D0A;     /* Base canvas — warm tungsten-dark. */
  --surface:   #181410;     /* First lift — header, status strip, modal. */
  --surface-2: #221C16;     /* Second lift — popovers, raised tiles, journal cards. */
  --border:    #2A2520;     /* Standard border — warm, matches surface temperature. */
  --hairline:  #1F1A15;     /* Sub-border separator — between dim and border. */

  /* Phase F (2026-05-26) — alias the Phase E token names that auth.css /
     rec-card.css / state-chip.css reference but were never defined in
     this file. Marcus's R1 token-duality warning bit on the auth card.
     Aliasing rather than re-numbering keeps the existing semantic
     surface-X / border-X / radius-X usage stable. */
  --surface-1:     var(--surface);   /* alias — first-lift name used by Phase E */
  --border-strong: #3A332C;          /* stronger than --border — for card edges + input borders */
  --border-card:   var(--border);    /* alias for card border references */
  --radius-lg:     8px;              /* one step above --radius-md (4px) — auth cards */

  /* Back-compat alias — keep old --bg references rendering through migration.
     MIGRATION: deprecated, grep static/style.css for var(--bg) and rewrite. */
  --bg: var(--canvas);

  /* ==========================================================================
     INK / FOREGROUND RAMP — paper-warm off-white, four levels of dim.
     ========================================================================== */

  --fg:    #ECE6D8;         /* Body text — warm off-white. Contrast 13.4:1 on canvas. */
  --dim:   #A19C92;         /* Secondary text. Contrast 6.1:1 on canvas — AAA at 14px. */
  --off:   #645F56;         /* Tertiary / EXPIRED terminal state. Contrast 3.1:1 — AA large only. */
  --off-2: #423D36;         /* Quaternary / placeholder / disabled outline. Decorative — not for text. */

  /* ==========================================================================
     BRAND ACCENT — indigo, pushed louder.
     Discord-blurple / Spotify-green confidence: this is the brand statement,
     not a hairline hint. Today's #6366F1 (Tailwind indigo-500) was right hue,
     wrong commitment. Stepping up to #7C7CFF — slightly punchier, slightly
     lighter, sits as a clear brand presence on the warm-dark canvas without
     losing the indigo identity.
     Contrast on --canvas: 6.9:1 — AAA for large text, AA for small. Brand
     surfaces (wordmark, primary CTA, focus ring, .bk--ink bracket fill)
     all clear AA.
     ========================================================================== */

  --ink:        #7C7CFF;            /* Brand indigo — the statement. */
  --ink-rgb:    124 124 255;        /* RGB triplet for rgba() compositions. */
  --ink-low:    rgb(var(--ink-rgb) / 0.12);  /* 12% — surface tints, button rests. */
  --ink-hover:  #9A9AFF;            /* Hover step — lighter, +7% L*. */
  --ink-strong: #5F5FFF;            /* "Really lean in" — pressed states, focus ring core. */

  /* ==========================================================================
     SECONDARY ACCENT — warm amber.
     Used ONLY for moments of positive signal: positive-skew chips,
     pull-quote dashes on the marketing page, the honest-silence pull-quote
     punctuation, journal "trade closed" affirmations.
     DO NOT use as a success-state colour — we have --live for that
     (state machinery). Amber is editorial / brand celebration, not status.
     Contrast on --canvas: 9.2:1 — AAA at all sizes.
     ========================================================================== */

  --amber:       #E6B450;           /* Warm pale gold. */
  --amber-rgb:   230 180 80;
  --amber-low:   rgb(var(--amber-rgb) / 0.14);  /* Soft tint behind pull-quotes. */

  /* ==========================================================================
     STATUS SEMANTICS — CVD-tuned per SPEC §4 + §22.2.
     Live / warn / bad form a luminance ramp that survives both deuteranopia
     and protanopia. Refreshed values stay inside the CVD-safe envelope:
     live drops a hair to read less "fluorescent" against the warmer canvas;
     warn nudges toward honey rather than highway-sign yellow; bad stays
     brick-red. Glyph-fill use only (inside .bk brackets) — never body text.
     ========================================================================== */

  --live:   #9FE53A;        /* Lime, CVD-safe. Contrast on canvas: 11.6:1. */
  --warn:   #E5A82A;        /* Honey-amber, distinct from --amber. Contrast: 8.7:1. */
  --bad:    #DC5526;        /* Brick. Contrast: 4.6:1 — AA small text safe. */

  --live-rgb: 159 229 58;
  --warn-rgb: 229 168 42;
  --bad-rgb:  220 85 38;

  /* Live halo — static, no breathe (SPEC §22.7 prefers-reduced-motion safety). */
  --live-halo-rest-inset: 0 0 0 1px rgb(var(--live-rgb) / 0.18) inset;
  --live-halo-rest-glow:  0 0 12px rgb(var(--live-rgb) / 0.04);

  /* ==========================================================================
     INTERACTION SURFACES.
     ========================================================================== */

  --row-hover-tint:    rgb(255 255 255 / 0.025);    /* Marginally warmer than v5 (0.02 → 0.025). */
  --row-active-tint:   rgb(var(--ink-rgb) / 0.06);  /* Selected row / current rec. */

  --focus-ring-width:  2px;
  --focus-ring-offset: 2px;
  --focus-ring-color:  var(--ink-strong);  /* Brand-tied focus ring — Discord pattern. */

  /* ==========================================================================
     TYPOGRAPHY — Plex Sans + Plex Mono. No third font.
     The wordmark uses outlined SVG paths (see WORDMARK.svg) so no display
     face is loaded. Body stays cohesive.
     ========================================================================== */

  --font-mono: "IBM Plex Mono", ui-monospace, "JetBrains Mono", "SF Mono", "Cascadia Mono", Menlo, monospace;
  --font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  /* OpenType features. Mono: tabular nums + stylistic alts for slashed-0
     and straight-1 (Plex's defaults read too humanist on prices). Sans:
     ligatures + small stylistic alts only. */
  --font-feat-mono: "tnum" 1, "zero" 1, "ss01" 1, "ss02" 1;
  --font-feat-sans: "kern" 1, "liga" 1, "ss03" 1;

  /* ==========================================================================
     TYPE SCALE — 6 steps now (was 5).
     Inserted --text-md at 1.0625rem (17px) to host the rec-page rationale,
     which the creative review pushed to sentence-primacy.
     Bumped --text-lg from 18 → 20px for page titles (was sitting too close
     to rationale). --text-rationale (22px / 1.375rem) is the dedicated
     rec-page headline weight — Stratechery sentence-primacy.

     MIGRATION: NO breaking renames. Existing xs/sm/base/lg/hero all keep
     their values; --text-md and --text-rationale are additive. --text-lg
     bumped 18 → 20px; review page titles and confirm none look balloon.
     ========================================================================== */

  --text-2xs:        0.625rem;    /* 10px — micro-labels (uppercase tracking, pill metadata). */
  --text-xs:         0.75rem;     /* 12px — labels, footers, badges. */
  --text-sm:         0.875rem;    /* 14px — body default, table cells, dim metadata. */
  --text-base:       0.9375rem;   /* 15px — L1 row content, primary CTA, cockpit tile body. */
  --text-md:         1.0625rem;   /* 17px — NEW. Editorial body for context paragraphs. */
  --text-lg:         1.25rem;     /* 20px — page titles. (Was 18px.) */
  --text-rationale:  1.375rem;    /* 22px — NEW. Rec-page rationale headline only. */
  --text-hero:       2.5rem;      /* 40px — marketing H1 + wordmark display. (Was 36px.) */

  /* Line-height ramps. Tight on display, comfortable on body. */
  --leading-tight:    1.15;        /* Display, wordmark, page titles. */
  --leading-snug:     1.35;        /* Rationale, pull-quotes. */
  --leading-base:     1.5;         /* Body, context paragraphs. */
  --leading-mono:     1.45;        /* Prices, timestamps. */

  /* Weights — Plex Sans 400/600, Plex Mono 400/500/700.
     No 500 or 700 sans available. */
  --weight-regular:   400;
  --weight-medium:    500;         /* Mono only — prices, "fresh as of" timestamps. */
  --weight-semibold:  600;         /* Sans titles + emphasis. */
  --weight-bold:      700;         /* Mono only — wordmark fallback if SVG fails. */

  /* ==========================================================================
     SPACING SCALE — 7 steps (was 6).
     Phase G (2026-05-27) — add --space-7 for the marketing layout top/bottom
     padding. Continues the existing rhythm: each step adds ~32px over the
     previous. Scale: 4 / 8 / 16 / 32 / 64 / 96 / 128px.
     ========================================================================== */

  --space-1: 0.25rem;     /* 4px */
  --space-2: 0.5rem;      /* 8px */
  --space-3: 1rem;        /* 16px */
  --space-4: 2rem;        /* 32px */
  --space-5: 4rem;        /* 64px */
  --space-6: 6rem;        /* 96px — hero-page padding only; auth cards use --space-4 */
  --space-7: 8rem;        /* 128px — marketing page main column top/bottom padding */

  /* Phase F (2026-05-26) — text-2xl was referenced by auth.css for the
     sign-in card title and was undefined. Sits between --text-lg (20px)
     and --text-hero (40px). */
  --text-2xl: 1.5rem;     /* 24px — card titles, second-tier display headings */

  /* Back-compat. DEPRECATED — MIGRATION: grep & rewrite to --space-N. */
  --gap:    var(--space-3);
  --gap-sm: var(--space-2);
  --gap-xs: var(--space-1);
  --gap-lg: var(--space-4);
  --gap-xl: var(--space-5);

  /* ==========================================================================
     RADIUS — two steps. Lodestone is barely-rounded.
     2px on small chrome (buttons, pills, inputs), 4px on cards / panels.
     The brand reads as flat-and-precise — radius is punctuation, not
     decoration. No rounded-pill anywhere.
     ========================================================================== */

  --radius-sm: 2px;       /* Buttons, inputs, .bk brackets, pills. */
  --radius-md: 4px;       /* Cards, panels, modals, rec-page sections. */

  /* ==========================================================================
     SHADOW — M6a (2026-05-27): define tokens; cockpit.css and auth.css both
     reference var(--shadow-card), var(--shadow-pop), and var(--shadow-elevated).
     Values are tuned for the dark-canvas v5 register: very subtle (dark
     backgrounds absorb shadow contrast), two-layer for soft diffusion.

     --shadow-card     : default card lift — cockpit placed-cards, rec tiles.
     --shadow-pop      : interactive / hovered lift — modals, tooltips, dropdowns.
     --shadow-elevated : auth-page card elevation (slightly stronger than card).

     Design rationale: Lodestone stays flat-first; these are the minimum
     shadows needed to separate overlapping surfaces and floating cards from
     the canvas. No decorative drop-shadows elsewhere.
     ========================================================================== */

  --shadow-card:     0 1px 2px rgb(0 0 0 / 0.18), 0 2px 8px rgb(0 0 0 / 0.10);
  --shadow-pop:      0 4px 16px rgb(0 0 0 / 0.30);
  --shadow-elevated: 0 2px 4px rgb(0 0 0 / 0.22), 0 4px 16px rgb(0 0 0 / 0.14);

  /* ==========================================================================
     MOTION VOCABULARY — HANDOFF Decision 5: 150–250ms band, considered
     transitions only. Refreshed values tighten everything into that band:
     - tick (state pip swap, counter): 150ms
     - row (HTMX OOB row, tile state change): 200ms
     - terminal (INVALIDATED / EXPIRED fade): 250ms
     - pulse (live-rec status pip, fresh-data arrival): 1.4s — the one
       carved exception, allowed by HANDOFF Decision 5.

     prefers-reduced-motion override at the bottom zeros everything out
     including the pulse.
     ========================================================================== */

  --dur-tick:     150ms;
  --dur-row:      200ms;
  --dur-terminal: 250ms;
  --dur-pulse:    1.4s;

  --ease-arrival:  cubic-bezier(0.0, 0.0, 0.2, 1);   /* Decelerate-in — row arrives. */
  --ease-terminal: cubic-bezier(0.4, 0.0, 0.6, 1);   /* Symmetric — fade-out. */
  --ease-glow:     cubic-bezier(0.4, 0.0, 0.2, 1);   /* Standard — hover, focus. */

  --stagger-row: 60ms;     /* Was 80ms — tighter cascade on /intel poll. */

  /* ==========================================================================
     CONTRAST AUDIT — worst-case pair documented for the migration record.
     All checked against WCAG 2.2 AA (4.5:1 small / 3:1 large).

     PAIR                              RATIO     PASS
     --fg     on --canvas              13.4:1    AAA
     --fg     on --surface             12.1:1    AAA
     --dim    on --canvas               6.1:1    AAA
     --dim    on --surface              5.5:1    AA  small / AAA large
     --off    on --canvas               3.1:1    AA  large only — by design (terminal state)
     --ink    on --canvas               6.9:1    AAA large / AA small
     --ink    on --surface              6.3:1    AAA large / AA small
     --amber  on --canvas               9.2:1    AAA
     --warn   on --canvas               8.7:1    AAA
     --bad    on --canvas               4.6:1    AA  small (borderline; do not shrink below 14px)
     --live   on --canvas              11.6:1    AAA

     WORST CASE: --bad on --canvas at 4.6:1. Acceptable but no margin —
     do not use --bad for text below 14px regular weight.
     ========================================================================== */
}

/* ==========================================================================
   SPEC §22.7 — prefers-reduced-motion: zero motion vocabulary including
   the carved pulse exception. Brand-mark stays lit, no animation.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-tick:     0s;
    --dur-row:      0s;
    --dur-terminal: 0s;
    --dur-pulse:    0s;
    --stagger-row:  0s;
  }
  *, *::before, *::after {
    animation-duration: 0s !important;
    transition-duration: 0s !important;
  }
}

/*
 * Lodestone — fonts.css
 *
 * Phase A4 of the rebrand migration. The Plex @font-face block was
 * lifted from static/src/styles.css (the Tailwind v4 source) verbatim
 * so the hand-rolled CSS tree owns its own font loading.
 *
 * During the A4 → D2 overlap, both this file AND static/src/styles.css
 * declare the same five @font-face rules. Identical declarations with
 * identical src URLs deduplicate at the browser's font-loading layer
 * — same file fetched once. D2 deletes the Tailwind source and this
 * becomes the only declaration.
 *
 * Self-hosted IBM Plex (Latin subset, ~91KB total). CSP default-src
 * 'self' — no Google Fonts CDN.
 */

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-latin-700-normal.woff2") format("woff2");
}

/*
 * Lodestone — base.css
 *
 * Phase C1 of the rebrand migration. Lifted from style-legacy.css §"Reset +
 * base" plus the generic typographic + text utility classes that used to
 * live at §"Section headings + utility". No selector renames; no value
 * changes; structural move only.
 *
 * Scope: element reset, html/body defaults, link defaults, :focus-visible
 * ring, .page-title family, .section-title, .text-mono/--dim/--warn/--bad/
 * --ok utility classes, generic spacing helpers, and the .empty-state
 * placeholder. The skip-link rules moved alongside into skip-link.css.
 */

/* ==========================================================================
   Reset + base.
   ========================================================================== */

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  font-size: 16px;
  background-color: var(--bg);
  color: var(--fg);
}

body {
  font-family: var(--font-sans);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  line-height: 1.6;
}

a {
  color: var(--ink);
  text-decoration: none;
}

/* Mouse hover on links — editorial underline only, NO boxed outline.
   The outline-on-hover pattern is a 2008 Bootstrap tell; modern craft
   reserves outlines for keyboard focus (:focus-visible) below. */

a:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

/* Visible focus styles — KEYBOARD focus only (§22). The outline appears
   when the user reaches the element via Tab, NOT when the mouse passes
   over it. Mouse hover gets the subtle treatments defined per-component. */

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* §12.5 step 1c — .page-title split (v4).
   Three variants:
   - .page-title             — default mono 18px --fg (status-detail, generic pages)
   - .page-title--cockpit    — small uppercase --dim (dashboard, intel, journal)
   - .page-title--hero       — --text-hero Inter 600 (marketing/auth hero)
   Untagged .page-title now ships the default; cockpit templates migrate to
   --cockpit modifier in the same commit. */

.page-title {
  font-family: var(--font-mono);
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--fg);
  letter-spacing: 0;
  text-transform: none;
  margin: 0 0 var(--space-3) 0;
}

.page-title--cockpit {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 var(--space-2) 0;
  font-weight: 500;
}

.page-title--hero {
  font-family: var(--font-sans);
  font-size: var(--text-hero);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0 0 var(--space-3) 0;
  line-height: 1.1;
}

button, input, select, textarea {
  font-family: inherit;
  font-size: inherit;
}

/* ==========================================================================
   Section headings + text utility classes.
   ========================================================================== */

.section-title {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
  margin-bottom: var(--gap-sm);
}

/* §12.5 step 1c — duplicate .page-title block deleted (was at line 1059).
   Canonical definition now at the top of the file. Three variants:
   .page-title / .page-title--cockpit / .page-title--hero. */

/* Status semantics — .text-ok paints --live (lime), NOT --accent (cyan).
   Brand accent is for brand/CTA/focus only; the "ok" state earns its own
   colour. Earlier version painted .text-ok with --accent and broke the
   §3.6 brand-vs-state non-collision on every status-bearing surface. */

.text-ok   { color: var(--live); }

.text-warn { color: var(--warn); }

.text-bad  { color: var(--bad); }

.text-dim  { color: var(--dim); }

.text-mono {
  font-family: var(--font-mono);
  /* Rev 20 (Opus R1 #2) — tabular-nums on every mono surface. The cockpit
     pivots on five-decimal prices and tick timers; non-tnum digits jitter
     as values advance ("1.08543 → 1.08621" shifts columns), betraying the
     register. Same rule on .data-table cells + .rec-pair below. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.mt-gap  { margin-top: var(--gap); }

.mb-gap  { margin-bottom: var(--gap); }

/* Empty state */

.empty-state {
  text-align: center;
  padding: 2rem;
  color: var(--dim);
  font-size: 0.85rem;
}

/*
 * Lodestone — layout.css
 *
 * Phase C2 of the rebrand migration. Lifted from style-legacy.css §"Layout
 * — header / main / footer (a11y landmarks §22)" plus the page-wide mobile
 * breakpoint at ≤640 px (Codex R1 #4). No selector renames; no value
 * changes; structural move only.
 *
 * Scope: a11y landmarks ([role="banner"], [role="main"], [role="contentinfo"]),
 * the .header-brand / .header-controls / .header-clock wrapper rules, and
 * the page-wide responsive collapse that condenses the header chrome on
 * iPhone-SE-class widths.
 *
 * Note: the .header-brand .brand-mark nested rules stay here because they
 * are layout-context rules (positioning the brand mark inside the header),
 * not brand-mark intrinsic styling. Phase E may reconcile.
 */

/* ==========================================================================
   Layout — header / main / footer (a11y landmarks §22).
   ========================================================================== */

[role="banner"] {
  background-color: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: var(--gap-sm) var(--gap);
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex-wrap: wrap;
}

/* §10 — header brand wrapper. .brand-mark carries all type/colour;
   wrapper only enforces the 44×44 touch target (§22.10). */

.header-brand {
  display: inline-flex;
  align-items: center;
}

.header-brand .brand-mark {
  font-size: var(--text-base);
  min-height: 44px;
  padding: 0 8px;
}

.header-brand .brand-mark:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: var(--focus-ring-offset);
}

.header-controls {
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex: 1;
  justify-content: flex-end;
  flex-wrap: wrap;
}

.header-clock {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--dim);
  min-width: 8ch;
  white-space: nowrap;
}

/* Local primary — same colour as the parent .header-clock */

.header-clock__local {
  font-family: var(--font-mono);
}

/* UTC secondary — smaller and dimmer, separated by a dot in the JS string */

.header-clock__utc {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  opacity: 0.6;
  margin-left: 0.35em;
}

[role="main"] {
  flex: 1;
  width: 100%;
  /* Removed `padding: var(--gap)` + `max-width: 1200px` + `margin: 0 auto`
     2026-05-26 per team review R1 (Marcus): the legacy pre-wrapper
     constraint fought every Phase E page that owns its own shell
     max-width. Phase E surfaces all wrap their content in their own
     layout container (.rec-page, .cockpit, .marketing-wrap, .form-card)
     which sets max-width + padding. The bare [role="main"] now just
     stretches to fill, lets the inner shell decide its own bounds. */
}

[role="contentinfo"] {
  background-color: var(--surface);
  border-top: 1px solid var(--border);
  padding: var(--gap-sm) var(--gap);
  font-size: 0.75rem;
  color: var(--dim);
  text-align: center;
}

/* ==========================================================================
   Codex R1 #4 — minimum-viable mobile at ≤640 px.
   The header was wrapping into a 5–7 row pile on iPhone-SE-class widths,
   pushing the disclaimer gate below the fold on first visit. One media
   query: collapse the nav to a horizontally-scrollable strip, condense
   the controls row, keep the 44 px touch-target height.
   ========================================================================== */

@media (max-width: 640px) {
  header[role="banner"] {
    flex-wrap: wrap;
    padding: 0.4rem 0.75rem;
  }

  .header-brand {
    flex: 0 0 auto;
  }

  .header-nav {
    flex: 1 1 100%;
    order: 3;
    overflow-x: auto;
    overflow-y: hidden;
    white-space: nowrap;
    gap: 0.5rem;
    padding: 0.15rem 0;
    /* Hide the horizontal scrollbar; the strip is short enough to drag. */
    scrollbar-width: none;
  }

  .header-nav::-webkit-scrollbar {
    display: none;
  }

  .header-nav a {
    font-size: 0.78rem;
    padding: 0 8px;
  }

  .header-controls {
    flex: 1 1 auto;
    gap: 0.4rem;
    flex-wrap: wrap;
  }

  .btn-mode,
  .sound-toggle {
    font-size: 0.7rem;
    padding: 0 8px;
  }

  /* Status strip — drop the inline padding so it fits one row. */
  section[role="status"] > div {
    padding: 0.25rem 0.75rem !important;
    font-size: 0.72rem !important;
  }

  /* Page-title h1 stays mono but tighter. */
  .page-title {
    font-size: 0.72rem;
  }

  /* Dashboard pre-amble strips — tighten vertical rhythm. */
  .since-last-open,
  .exposure-line,
  .calendar-peek {
    padding: 0.5rem 0.75rem;
    font-size: 0.78rem;
  }
}

/*
 * Lodestone — components/skip-link.css
 *
 * Phase C1 of the rebrand migration. Lifted from style-legacy.css §"Reset +
 * base" (the .skip-link rule sat between :focus-visible and .page-title).
 * Structural move only — no selector renames, no value changes.
 */

/* Rev 20 (Codex R1 #3) — skip-to-content. WCAG 2.4.1 Bypass Blocks.
   Visually hidden until focused; on Tab from the URL bar it springs into the
   top-left so keyboard + screen-reader users skip the header chrome. */

.skip-link {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
  background-color: var(--surface);
  color: var(--fg);
  padding: 0.5rem 1rem;
  border: 2px solid var(--ink);
  z-index: 1000;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  text-decoration: none;
}

.skip-link:focus {
  left: 1rem;
  top: 1rem;
  width: auto;
  height: auto;
  overflow: visible;
}

/*
 * Lodestone — components/bk.css
 *
 * Phase E rebrand — bracket-atom retirement per Sir's resolved decision:
 * "No brackets - we have a logo now we don't even need them on the
 * wordmark." (PHASE-E-REC-PAGE-RESPEC-2026-05-26.md §Sir's resolved
 * decisions, item 2.)
 *
 * WHAT CHANGED:
 *   - ::before "[ " and ::after " ]" pseudo-elements REMOVED.
 *   - .bk is retained as a typography/layout helper for the new
 *     unbracketed pill treatment. It now sets font-family, numeric
 *     features, and whitespace — the structural role — without adding
 *     any bracket chrome.
 *   - .bk--ink retains its role as the accent-colour modifier; the
 *     colour now applies to the element itself (not the bracket
 *     pseudo-elements that no longer exist).
 *   - State variants and glyph colour rules survive untouched — they
 *     colour the .bk__glyph indicator, which is still used inside
 *     freshness / state chips.
 *   - Action atoms (button.bk, a.bk) survive; bracket-hover is gone,
 *     plain colour-shift on hover replaces it.
 *   - @keyframes bk-pulse survives (chunk-1 state-chip.css may reference
 *     it for the pill pulse). If chunk-1 does not reference it, it is
 *     inert dead code — harmless. Remove in Phase F cleanup.
 *
 * CROSS-CHUNK NOTE (chunk-1):
 *   chunk-1 owns state-chip.css. The new unbracketed pill treatment
 *   (LONG / SHORT / ACTIVE / DECAYING / EXPIRED / INVALIDATED / placed)
 *   should define its own .state-chip selectors in state-chip.css.
 *   This file deliberately does NOT define pill shapes — that avoids a
 *   naming collision. .bk is the raw type-and-colour primitive; pills
 *   are a layer on top. If chunk-1 uses .bk as a base class on chips,
 *   the typography rules here flow through correctly.
 */

/* ==========================================================================
   .bk — typography and numeric-feature primitive.
   No bracket pseudo-elements. Just the layout and font contract.
   ========================================================================== */

.bk {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--fg);
  white-space: nowrap;
}

/* Brand accent variant — element text paints brand indigo.
   Previously coloured the bracket pseudo-elements; now colours the
   element text directly. Used by sound-toggle button and any remaining
   inline accent text that doesn't live in a full state-chip. */

.bk--ink {
  color: var(--ink);
}

/* ==========================================================================
   .bk__glyph — the geometric state indicator inside a chip/atom.
   Colours the inline glyph (●, ○, ▲, etc.) only. The outer text
   stays --fg so the bracket reads as "measurement" and the glyph reads
   as "state". Unchanged from v5 — state semantics survive the
   bracket retirement.
   ========================================================================== */

.bk__glyph {
  display: inline-block;
  margin-right: 0.25em;
}

.bk--state-live    .bk__glyph { color: var(--live); }

.bk--state-pending .bk__glyph { color: var(--warn); }

.bk--state-decay   .bk__glyph { color: var(--warn); }

.bk--state-invalid .bk__glyph { color: var(--bad); }

.bk--state-expired .bk__glyph { color: var(--off); }

/* ==========================================================================
   Action atoms — button.bk, a.bk.
   Bracket hover replaced by direct colour transition.
   ========================================================================== */

button.bk,
a.bk {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-size: inherit;
  text-decoration: none;
  transition: color var(--dur-tick, 150ms) ease;
}

button.bk:hover,
a.bk:hover {
  color: var(--ink);
  text-decoration: none;
}

/* ==========================================================================
   @keyframes bk-pulse — retained for potential chunk-1 state-chip use.
   If unused after Phase F audit, delete this block.
   ========================================================================== */

@keyframes bk-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.65; }
}

.bk--pulsing {
  animation: bk-pulse var(--dur-pulse, 1.4s) var(--ease-glow, cubic-bezier(0.4, 0.0, 0.2, 1)) 1;
}

/* ==========================================================================
   DEPRECATED — bracket pseudo-elements removed.
   Commented out so Phase F can audit and delete cleanly.
   ========================================================================== */

/*
.bk::before { content: "[ "; }
.bk::after  { content: " ]"; }

.bk--ink::before,
.bk--ink::after {
  color: var(--ink);
}

button.bk:hover::before,
button.bk:hover::after,
a.bk:hover::before,
a.bk:hover::after {
  color: var(--ink);
}
*/

/*
 * Lodestone — components/brand-mark.css
 *
 * Phase E rebrand — wordmark rewrite.
 *
 * The [lodestone] bracket mark is RETIRED. The new brand mark is the
 * Marcus Holm compass-rose glyph + custom-serif "lodestone" wordmark,
 * rendered as an inline SVG lockup inside .brand-mark.
 *
 * Decisions carried forward from WORDMARK.md + PHASE-E-REC-PAGE-RESPEC:
 *
 *   - Brackets OUT everywhere — including the wordmark. Sir verbatim:
 *     "No brackets - we have a logo now we don't even need them on
 *     the wordmark."
 *   - Wordmark height: ~32px desktop; scales to 26px on mobile.
 *   - Glyph + word side-by-side, vertically centred at x-height.
 *   - Gap between glyph and word: equals wordmark x-height (~22px in
 *     the 320×80 SVG source, which renders as ~8px at 32px height).
 *
 * Phosphor pulse decision (see §22.7):
 *   The old pulse fired on .brand-mark__bracket--left.is-pulsing.
 *   web.js adds .is-pulsing to that span on /intel polls. That class is
 *   now REPOINTED to .brand-mark__glyph.is-pulsing (the SVG wrapper).
 *   The pulse is RETAINED — it signals that the engine is active and
 *   Sir likes the alive feel. The glyph's opacity pulses rather than
 *   colour, which reads as "engine thinking" without the old bracket
 *   syntax framing it. If web.js is not updated to target
 *   .brand-mark__glyph, the pulse simply does not fire (graceful
 *   degradation — the mark stays lit at full opacity).
 */

/* ==========================================================================
   Brand mark container — link wrapper.
   Flex row, vertically centred. No text baseline alignment because the
   lockup is SVG-driven, not text-driven. gap matches the ~22px gap-unit
   from WORDMARK.md scaled to 32px display height.
   ========================================================================== */

.brand-mark {
  display: inline-flex;
  align-items: center;
  gap: 0;          /* gap is baked into the SVG viewBox geometry */
  text-decoration: none;
  color: inherit;
  line-height: 1;
  flex-shrink: 0;
}

/* The SVG lockup itself. height drives the scale; width auto-scales
   per the 320×80 viewBox (4:1 ratio → 128px wide at 32px height). */

.brand-mark__lockup {
  display: block;
  height: 32px;
  width: auto;
}

/* Glyph-only wrapper — used when we need to hang the .is-pulsing class
   for the /intel poll phosphor effect. Contains the <svg> or <img>. */

.brand-mark__glyph {
  display: inline-flex;
  align-items: center;
}

/* ==========================================================================
   Phosphor pulse — repointed from the bracket to the glyph.
   Opacity pulse on the whole lockup, 1.4s, one iteration per /intel poll.
   The CSS animation-fill-mode is "forwards" so the glyph returns to full
   opacity when the class is removed (web.js removes .is-pulsing after
   the animation ends via an animationend listener — same as before).
   ========================================================================== */

@keyframes glyph-pulse {
  0%   { opacity: 1.0; }
  40%  { opacity: 0.55; }
  100% { opacity: 1.0; }
}

.brand-mark__glyph.is-pulsing {
  animation: glyph-pulse var(--dur-pulse, 1.4s) var(--ease-glow, cubic-bezier(0.4, 0.0, 0.2, 1)) 1;
}

/* ==========================================================================
   Mobile — scale the lockup down on narrow viewports.
   The SVG stays crisp at any size; we just reduce height.
   ========================================================================== */

@media (max-width: 480px) {
  .brand-mark__lockup {
    height: 26px;   /* ~83px wide at 4:1 ratio */
  }
}

/* ==========================================================================
   §22.7 — prefers-reduced-motion: kill the pulse, keep the glyph lit.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .brand-mark__glyph.is-pulsing {
    animation: none;
    opacity: 1;
  }
}

/* ==========================================================================
   DEPRECATED — bracket-era selectors.
   Kept as stubs to avoid breaking any template or test that references
   these classes before Phase E completes its full sweep. They produce
   no visible output. Remove in Phase F cleanup.
   ========================================================================== */

/* .brand-mark__bracket     { display: none; } */

/* .brand-mark__bracket--left  { display: none; } */

/* .brand-mark__bracket--right { display: none; } */

/* .brand-mark__word           { display: none; } */

/*
 * Lodestone — components/buttons.css
 *
 * Phase C4 of the rebrand migration. Lifted from style-legacy.css §"Mode
 * toggle" plus the loose .btn family + .btn-run-cycle rules that sat
 * inside the Forms section. Structural move only — no selector renames,
 * no value changes.
 *
 * Scope: .mode-toggle wrapper + .btn-mode (legacy — most call-sites
 * removed but the rule survives until Phase E reviews); .btn family
 * (.btn, .btn--secondary, .btn--danger, .btn--sm); .btn-run-cycle and
 * its single-flight disabled state.
 *
 * Note: .btn uses var(--radius) which was deleted in an earlier sweep;
 * the rule renders border-radius: 0 today. Phase E rewrites to
 * var(--radius-sm) / var(--radius-md). Out of C4 scope (structural only).
 */

/* ==========================================================================
   Mode toggle.
   ========================================================================== */

.mode-toggle {
  display: flex;
  gap: var(--gap-xs);
  align-items: center;
}

.mode-toggle form {
  display: inline;
}

.btn-mode {
  background: none;
  border: 1px solid var(--border);
  color: var(--dim);
  /* Rev-17 UX-stage — §22.10 WCAG 2.5.5 floor: 44 × 44 CSS px on every
     interactive control. The prior 2px × 8px padding rendered at 127 × 20
     and failed touch-target sizing on phones. */
  min-height: 44px;
  min-width: 44px;
  padding: 10px 14px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.8rem;
  transition: border-color var(--dur-tick), color var(--dur-tick);
}

/* Mouse hover — border + colour shift only. No outline ring. */

.btn-mode:hover {
  border-color: var(--ink);
  color: var(--fg);
}

/* Keyboard focus — inherits the global :focus-visible outline above.
   We don't redeclare it here; the inheritance handles it. */

.btn-mode--active {
  border-color: var(--ink);
  color: var(--ink);
}

/* ==========================================================================
   .btn family — primary CTA + secondary + danger + small.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  background-color: var(--ink);
  color: #000; /* one-off: max-contrast text on indigo button; no --black token by design */
  border: none;
  border-radius: var(--radius);
  padding: 8px 16px;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: opacity var(--dur-tick);
  text-decoration: none;
}

/* Mouse hover — opacity dip only. The global :focus-visible rule paints
   the keyboard outline (lines 215-219). Removed the boxed outline that
   was firing on every mouse hover. */

.btn:hover {
  opacity: 0.85;
  text-decoration: none;
  color: #000; /* one-off: max-contrast text on indigo button (matches .btn rest state) */
}

.btn--secondary {
  background-color: transparent;
  color: var(--dim);
  border: 1px solid var(--border);
}

.btn--secondary:hover {
  /* v5 §3 — secondary action hover lifts to --fg, not --ink. Klein blue
     is reserved for the primary CTA fill. */
  border-color: var(--fg);
  color: var(--fg);
}

.btn--danger {
  background-color: transparent;
  color: var(--bad);
  border: 1px solid rgb(var(--bad-rgb) / 0.5);
}

.btn--danger:hover {
  background-color: rgb(var(--bad-rgb) / 0.1);
  color: var(--bad);
}

/* .btn--danger keyboard focus inherits the global :focus-visible outline
   in --accent. If a future review wants a red-tinted focus ring on the
   danger variant specifically, declare it here as :focus-visible. */

.btn--sm {
  padding: 4px 10px;
  font-size: 0.78rem;
}

/* ==========================================================================
   .btn-run-cycle — manual "Run cycle now" header button.
   ========================================================================== */

.btn-run-cycle {
  background: none;
  border: 1px solid var(--border);
  color: var(--dim);
  /* Rev-17 UX-stage — §22.10 touch-target floor. */
  min-height: 44px;
  min-width: 44px;
  padding: 10px 14px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.8rem;
  font-family: var(--font-mono);
  transition: border-color var(--dur-tick), color var(--dur-tick);
}

.btn-run-cycle:hover,
.btn-run-cycle:focus-visible {
  border-color: var(--ink);
  color: var(--ink);
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

/* Single-flight state on the run-cycle button. */

.btn-run-cycle[disabled],
.btn-run-cycle[aria-busy="true"] {
  opacity: 0.55;
  cursor: wait;
}

/* ==========================================================================
   .login-submit — login page submit button.
   Phase D Tailwind rip-out (2026-05-26). Sweeping login.html off Tailwind
   left a one-rule gap: the brand-coloured primary button on the login form
   needs a hover state. Visual rules live inline on the button element; only
   the :hover step lives here.
   ========================================================================== */

.login-submit:hover,
.login-submit:focus-visible {
  background: var(--ink-hover) !important;
}

/*
 * Lodestone — components/forms.css
 *
 * Phase C5 of the rebrand migration. Lifted from style-legacy.css §"Forms
 * — login / signup / journal / invites" plus the form-adjacent rules that
 * lived further down (logout-form, radio-group, suggested-action, the
 * preferences timezone note). Structural move only — no selector renames,
 * no value changes.
 *
 * Scope: .form-card (+ .form-card--anchored auth variant + .form-card__brand /
 * __subtitle / __glyph children), .form-group (labels + inputs + selects +
 * textareas + focus states), .form-error, .prefs-tz-note, .logout-form,
 * .radio-group, .suggested-action.
 *
 * Note: several rules reference var(--radius) — broken-ref issue documented
 * in the legacy file's top docblock. Phase E rewrites. Out of C5 scope.
 */

/* ==========================================================================
   Forms — login / signup / journal / invites.
   ========================================================================== */

.form-card {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--gap);
  max-width: 420px;
  margin: var(--gap) auto;
}

.form-card h1 {
  font-size: 1.1rem;
  margin-bottom: var(--gap);
  font-family: var(--font-mono);
}

/* Rev 21 (Opus R1 #9) — brand anchor for unauthenticated pages.
   The login + signup cards were a bare 420px form-card on grey with a 1.1rem
   <h1>Lodestone</h1>, giving a visitor zero brand cue. Add a 3× hex glyph
   above the H1 and a one-line subtitle below so the page identifies itself
   before the form chrome. Centered for the marketing-card register. */

.form-card--anchored {
  text-align: center;
}

/* v4: legacy .form-card__glyph retained as no-op for back-compat (no live
   markup references it after the login/signup brand-mark migration). */

.form-card--anchored .form-card__glyph {
  display: none;
}

/* v4: the [lodestone] brand mark in the auth-card register. */

.form-card--anchored .form-card__brand {
  display: inline-flex;
  font-size: 1.5rem;
  margin-bottom: var(--space-3);
  padding: 0;
  min-height: auto;
}

.form-card--anchored h1 {
  text-align: center;
  margin-bottom: var(--gap-xs);
}

.form-card--anchored .form-card__subtitle {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--dim);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: var(--gap);
}

/* Form content inside an anchored card stays left-aligned — labels + inputs
   read best flush-left even when the brand header above is centered. */

.form-card--anchored form,
.form-card--anchored .form-error {
  text-align: left;
}

.form-group {
  margin-bottom: var(--gap-sm);
}

.form-group label {
  display: block;
  font-size: 0.8rem;
  color: var(--dim);
  margin-bottom: var(--gap-xs);
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  background-color: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--fg);
  padding: 6px 10px;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  transition: border-color var(--dur-tick);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--ink);
  outline: 2px solid var(--ink);
  outline-offset: 0;
}

.form-group textarea {
  resize: vertical;
  min-height: 80px;
}

.form-error {
  color: var(--bad);
  font-size: 0.8rem;
  margin-bottom: var(--gap-sm);
  padding: var(--gap-xs) var(--gap-sm);
  background-color: rgb(var(--bad-rgb) / 0.1);
  border-radius: var(--radius);
  border: 1px solid rgb(var(--bad-rgb) / 0.3);
}

/* Preferences — timezone auto-note. Replaces the removed display_timezone
   field (R4 ship-now #7, 2026-05-26). One line, dim, mono, no field chrome. */

.prefs-tz-note {
  font-size: 0.78rem;
  margin: 0.5rem 0 var(--gap-sm) 0;
}

/* ==========================================================================
   Form-adjacent: logout form, inline radio group, suggested-action note.
   ========================================================================== */

/* Logout form inline */

.logout-form {
  display: inline;
}

.logout-form button {
  background: none;
  border: none;
  color: var(--dim);
  cursor: pointer;
  font-size: 0.85rem;
  /* Rev-17 UX-stage — §22.10 touch-target floor (was 56×16). */
  min-height: 44px;
  min-width: 44px;
  padding: 10px 12px;
  text-decoration: underline;
}

.logout-form button:hover,
.logout-form button:focus-visible {
  color: var(--bad);
  outline: 2px solid var(--bad);
  outline-offset: 2px;
}

/* Inline radio group */

.radio-group {
  display: flex;
  gap: var(--gap);
  align-items: center;
}

.radio-group label {
  display: flex;
  align-items: center;
  gap: var(--gap-xs);
  font-size: 0.85rem;
  color: var(--fg);
  cursor: pointer;
}

/* Suggested action text (beginner mode §8.9) */

.suggested-action {
  font-size: 0.78rem;
  color: var(--dim);
  font-style: italic;
  margin-top: var(--gap-xs);
}

/*
 * Lodestone — components/pair-tile.css
 *
 * Phase C6 of the rebrand migration. Lifted from style-legacy.css §"Pair
 * grid — cockpit redesign (Tasks 3–5)" plus the orphan pair-tile children
 * (.pair-tile__tier-chip, .pair-tile__tier-line, .pair-tile__risk-flags)
 * that sat further up the file inside the recommendation-rows section.
 * Structural move only — no selector renames, no value changes.
 *
 * Scope: .pair-grid container + .pair-grid__break separator + the full
 * .pair-tile family — base tile, .pair-tile--idea + confidence variants
 * (high/medium/low), .pair-tile--watching, header (.pair-tile__pair /
 * __price / __status-glyph / __watching-label / __conf), .pair-tile__
 * subhead / __rationale / __levels / __terminal-hint / __llm-via /
 * __meta / __sep / __pct-change variants / __actions / __tier-chip
 * variants / __tier-line / __risk-flags.
 *
 * Note: .rec-page__rationale-headline lives inline next to
 * .pair-tile__rationale (the rec-page rule mirrors the cockpit-tile
 * promoted-rationale visual) so it stays with the pair-tile section.
 * Phase E may reconcile selector ownership.
 *
 * Out of scope (handled elsewhere in Phase C): .pair-grid-error and
 * .cockpit-empty-state move with the alerts family in C9.
 */

/* ==========================================================================
   .pair-tile orphan children — tier chip + tier line + risk-flag list.
   Previously sat in the recommendation-rows section above; coupled to the
   .pair-tile component by selector and by the cockpit redesign.
   ========================================================================== */

/* R7 signal-gate refactor (2026-05-25) — opportunity-tier chip.
   Renders on the cockpit tile header (and the pair-detail recent-ideas
   row) alongside confidence + trigger + asymmetry. Mono, bracket-atom,
   tier-specific colour weight:
     best_shots   — indigo bright (the lead recommendation)
     with_caveats — indigo dim
     watch_list   — very dim foreground
   recent_history is intentionally chip-less (terminal status-pill already
   carries that signal). */

.pair-tile__tier-chip {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  margin-left: 0.25rem;
  letter-spacing: 0.02em;
}

.pair-tile__tier-chip::before { content: "[ "; opacity: 0.55; }

.pair-tile__tier-chip::after  { content: " ]"; opacity: 0.55; }

.pair-tile__tier-chip--best_shots   { color: var(--ink); font-weight: 600; }

.pair-tile__tier-chip--with_caveats { color: rgb(var(--ink-rgb) / 0.6); }

.pair-tile__tier-chip--watch_list   { color: var(--dim); }

.pair-tile__tier-chip--recent_history { color: var(--off); }

/* Tier sub-line — subordinate positioning below the header, above the
   rationale. Reduced weight + opacity so the directional bracket remains
   the visual anchor. R4 ship-now #5 (2026-05-26). */

.pair-tile__tier-line {
  margin-top: -0.15rem;
  margin-bottom: 0.1rem;
}

.pair-tile__tier-line .pair-tile__tier-chip {
  font-size: 0.68rem;
  opacity: 0.75;
}

/* R7 signal-gate refactor (2026-05-25) — risk-flag pills on the cockpit
   tile + the pair-detail recent-ideas row. Mono, compact, two severity
   variants:
     --material      — --warn colour (the four named material flags
                       plus any unknown flag, conservative default).
     --informational — --dim colour (near_session_boundary,
                       daily_bars_stale).
   Hover tooltip provides the plain-English explanation; the chip itself
   stays compact so a rec with 2-3 flags doesn't bloat the tile. */

.pair-tile__risk-flags {
  list-style: none;
  margin: 0.4rem 0 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.4rem;
}

/* ==========================================================================
   Pair grid — cockpit redesign (Tasks 3–5).
   Replaces the old rec-list with one tile per tracked pair.
   Live-idea tiles: full opacity, confidence-coloured ring.
   Watching tiles: opacity 0.55, no ring.
   ========================================================================== */

/* Grid container */

.pair-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
  margin: 0;
  padding: 0;
}

/* Visual break between live tiles and watching tiles */

.pair-grid__break {
  height: 0;
  border-top: 1px solid var(--border);
  opacity: 0.45;
  margin: var(--gap-xs) 0;
}

/* Base tile */

.pair-tile {
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: var(--gap-sm) var(--gap);
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  transition: border-color 0.15s ease;
}

.pair-tile:hover,
.pair-tile:focus-within {
  border-color: var(--dim);
}

/* Live-idea tile — confidence ring colours */

.pair-tile--idea {
  opacity: 1;
}

/* Strong confidence: var(--live) ring */

.pair-tile--idea.pair-tile--conf-high {
  box-shadow: 0 0 0 1px rgb(163 230 53 / 0.35) inset,
              0 0 8px  rgb(163 230 53 / 0.06);
}

/* Regular confidence: indigo ring */

.pair-tile--idea.pair-tile--conf-medium {
  box-shadow: 0 0 0 1px rgb(var(--ink-rgb) / 0.35) inset,
              0 0 8px  rgb(var(--ink-rgb) / 0.06);
}

/* Weak confidence: dim ring */

.pair-tile--idea.pair-tile--conf-low {
  box-shadow: 0 0 0 1px rgb(155 148 142 / 0.30) inset;
}

/* Watching tile */

.pair-tile--watching {
  opacity: 0.55;
}

.pair-tile--watching:hover,
.pair-tile--watching:focus-within {
  opacity: 0.80;
}

/* Tile header row */

.pair-tile__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.pair-tile__pair {
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.04em;
}

.pair-tile__price {
  margin-left: auto;
  font-size: 0.88rem;
}

.pair-tile__status-glyph {
  font-size: 0.75rem;
  margin-left: 0.25rem;
}

.pair-tile__watching-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.pair-tile__conf {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Plain-English subhead — creative review Item 5 (2026-05-26). One line
   below the rationale that absorbs confidence / trigger / asymmetry /
   tier into prose so the cockpit reads at sentence-speed, not chip-stack
   speed. Smaller and dimmer than the rationale so it reads as the
   subhead it is. */

.pair-tile__subhead {
  font-size: 0.82rem;
  line-height: 1.4;
  margin: 0.1rem 0 0;
}

/* Rationale — promoted to the visual headline on cockpit tiles
   (creative review Item 3, 2026-05-26). Weightier + ink-coloured so the
   rationale sentence is the first thing a glance reads on each tile, not
   the chip stack. Margin pulled up to sit directly under the pair-name
   header row. */

.pair-tile__rationale {
  font-size: 1rem;
  line-height: 1.4;
  margin: 0.1rem 0 0;
  color: var(--fg);
  font-weight: 500;
}

/* Rec-page rationale headline (creative review Item 3, 2026-05-26).
   On the canonical /idea/<rec_id> surface the rationale becomes the
   visual centre of gravity — large, indigo, sentence-leading. Sits as
   the first content under the header, above the pre-flight warnings.
   No chips inline; the categorical signals collapse into the prose
   beneath. */

.rec-page__rationale-headline {
  font-size: 1.35rem;
  line-height: 1.35;
  color: var(--ink);
  margin: 0.5rem 0 1rem;
  font-weight: 500;
  letter-spacing: -0.005em;
}

/* Structural levels (entry / SL / TP) */

.pair-tile__levels {
  font-size: 0.77rem;
  color: var(--dim);
}

/* Terminal hint on watching tiles */

.pair-tile__terminal-hint {
  font-size: 0.78rem;
  margin: 0;
}

/* Feed/update meta line */

/* LLM provider label — "via groq · llama-3.3-70b" line on idea tiles.
   Sits below the rationale, mono, very small. Indigo = primary; amber =
   fallback leg fired (throttle-recovered cycle). */

.pair-tile__llm-via {
  font-size: 0.68rem;
  font-family: var(--font-mono);
  color: var(--ink);
  opacity: 0.55;
  margin: 0.15rem 0 0;
}

.pair-tile__llm-via--fallback {
  color: var(--warn);
  opacity: 0.75;
}

.pair-tile__meta {
  font-size: 0.72rem;
}

.pair-tile__sep {
  color: var(--off);
}

/* Daily % change chips on cockpit tiles */

.pair-tile__pct-change {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-feature-settings: var(--font-feat-mono);
}

.pair-tile__pct-change--up {
  color: var(--live);
}

.pair-tile__pct-change--down {
  color: var(--bad);
}

.pair-tile__pct-change--flat {
  color: var(--dim);
}

.pair-tile__pct-change--na {
  color: var(--dim);
}

/* Action row */

.pair-tile__actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: baseline;
  margin-top: 0.25rem;
}

/*
 * Lodestone — components/preamble.css
 *
 * Phase C7 of the rebrand migration. Lifted from style-legacy.css
 * §"Preamble status line" plus the §10 "Collapse the four preamble panels"
 * rule that targets the preamble panel IDs (display: none on market-closed
 * weekends). Structural move only — no selector renames, no value changes.
 *
 * Scope: .preamble-status-line (single-line status when system_state is
 * non-live) + .preamble-status-line__detail; the body[data-market-state]
 * collapse rule that hides #preamble-since / #preamble-calendar /
 * #preamble-exposure on closed-market days.
 */

/* ==========================================================================
   Preamble status line — collapsed single-line state when system_state is
   non-live (silent / refused / gate-killed / market-closed / degraded).
   Bracket-atom grammar, mono, dim. R4 ship-now #6 (2026-05-26).
   ========================================================================== */

.preamble-status-line {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  border: 1px solid var(--border);
  border-radius: 2px;
}

.preamble-status-line__detail {
  font-size: var(--text-xs);
}

.preamble-status-line__since {
  font-size: var(--text-xs);
  color: var(--dim);
  margin-left: auto;
}

/* Exposure summary line (§17) — sits inside the preamble strip family
   alongside since-last-open / calendar-peek. */

.exposure-line {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--dim);
  padding: var(--gap-xs) var(--gap);
  border-bottom: 1px solid var(--border);
}

/* Collapse the four preamble panels to minimal height when market is closed.
   The since-last-open, calendar-peek, and exposure-line panels have nothing
   actionable to say on a weekend. They remain in the DOM (JS hooks stay live)
   but collapse visually so the trader reaches the status line faster. */

body[data-market-state="closed"] #preamble-since,
body[data-market-state="closed"] #preamble-calendar,
body[data-market-state="closed"] #preamble-exposure {
  display: none;
}

/*
 * Lodestone — components/market-status.css
 *
 * Phase C7 of the rebrand migration. Lifted from style-legacy.css §"§10.1
 * — market-status banner" plus the §18.1.4 SINCE LAST OPEN delta strip
 * (.since-last-open) — both are market-state preamble surfaces that drive
 * off the same data-market-state attribute. Structural move only — no
 * selector renames, no value changes.
 *
 * Scope: .market-status-banner (+ open/closed data-attribute variants,
 * __state, __detail, __prices, __price-item, __price-age, __price-feed),
 * .market-countdown (+ --soon variant), .since-last-open.
 */

/* ==========================================================================
   §18.1.4 — SINCE LAST OPEN delta strip.
   ========================================================================== */

.since-last-open {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
  font-size: 0.8rem;
  font-family: var(--font-mono);
  color: var(--dim);
}

/* ==========================================================================
   §10.1 — market-status banner (R4 ship-now, 2026-05-25).

   Persistent strip above the activity canvas. Two states driven by the
   data-market-state attribute (set via OOB swap from _intel_rows.html):
     open   → indigo ink brackets, "synthesis active" copy, live presence
     closed → dim, authoritative calm - the system is honest, not alarmed

   No emojis. No animation on the closed state. Bracket-atom grammar.
   ========================================================================== */

.market-status-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem var(--gap);
  padding: var(--gap-sm) var(--gap);
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-left-width: 3px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  margin-bottom: var(--gap);
}

/* Open state — indigo left-edge accent. */

.market-status-banner[data-market-state="open"] {
  border-left-color: var(--ink);
}

/* Closed state — dim left-edge, no accent. Calm authority. */

.market-status-banner[data-market-state="closed"] {
  border-left-color: var(--off);
}

.market-status-banner__state {
  font-weight: 600;
}

/* Open: indigo bracket colour via bk--ink (already defined). No extra rule needed. */

/* Closed: bracket colour is --off (matches terminal state palette). */

.market-status-banner[data-market-state="closed"] .market-status-banner__state {
  color: var(--dim);
}

.market-status-banner__detail {
  color: var(--dim);
  font-size: var(--text-xs);
}

.market-status-banner__prices {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem var(--gap);
  width: 100%;
  margin-top: 0.25rem;
  padding-top: 0.25rem;
  border-top: 1px solid var(--border);
}

.market-status-banner__price-item {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: var(--text-xs);
  color: var(--fg);
}

.market-status-banner__price-age {
  color: var(--off);
  font-size: 0.7rem;
}

/* Per-pair feed source + cadence tag (2026-05-25 hybrid routing).
   IBM Plex Mono, --dim weight — visually subordinate to the price itself
   but readable at a glance. Middle-dot separator matches existing row style. */

.market-status-banner__price-feed {
  font-family: var(--font-mono);
  color: var(--dim);
  font-size: 0.7rem;
  letter-spacing: -0.01em;
}

/* Market-open countdown ticker.
   Sits inline inside .market-status-banner__detail.
   IBM Plex Mono, --dim weight, tight letter-spacing — matches the banner
   bracket grammar without competing for visual weight.
   No CSS animation — the live text update IS the motion. */

.market-countdown {
  font-family: var(--font-mono);
  color: var(--dim);
  letter-spacing: -0.01em;
}

/* Under 1h remaining — shift to brand indigo, signalling "soon". */

.market-countdown--soon {
  color: var(--ink);
}

/*
 * Lodestone — components/calendar-peek.css
 *
 * Phase C7 of the rebrand migration. Lifted from style-legacy.css §"§18.1.5
 * — next-4h calendar peek". Includes the .calendar-event detail rows that
 * render inside the peek list. Structural move only — no selector renames,
 * no value changes.
 *
 * Scope: .calendar-peek + __title + __item + __item--urgent + __sep + __in,
 * .calendar-event + __time + __title + __currency.
 */

/* ==========================================================================
   §18.1.5 — next-4h calendar peek.
   ========================================================================== */

.calendar-peek {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
  font-size: 0.85rem;
}

.calendar-peek__title {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
  margin-bottom: var(--gap-xs);
}

.calendar-peek__item {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 0.15rem 0;
  color: var(--fg);
}

.calendar-peek__item--urgent {
  color: var(--warn);
  font-weight: 700;
}

.calendar-peek__sep {
  color: var(--off);
}

.calendar-peek__in {
  color: var(--dim);
}

.calendar-event {
  display: flex;
  gap: var(--gap-sm);
  align-items: baseline;
  padding: var(--gap-xs) 0;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 0.8rem;
}

.calendar-event:last-child {
  border-bottom: none;
}

.calendar-event__time {
  color: var(--warn);
  min-width: 7ch;
  flex-shrink: 0;
}

.calendar-event__title {
  color: var(--fg);
}

.calendar-event__currency {
  color: var(--dim);
  font-size: 0.75rem;
}

/*
 * Lodestone — components/activity-canvas.css
 *
 * Phase C7 of the rebrand migration. Lifted from style-legacy.css §"§10 —
 * activity canvas (minimal placeholder, real canvas drawn by web.js)".
 * Structural move only — no selector renames, no value changes.
 *
 * Scope: .activity-canvas (+ open/closed data-market-state variants),
 * .activity-pair, plus the historical-only deletion comment that documents
 * why .activity-warming is a no-op.
 *
 * Note: the "Collapse the four preamble panels" rule moved alongside the
 * preamble component into preamble.css — it targets preamble IDs, not the
 * canvas itself.
 */

/* ==========================================================================
   §10 — activity canvas (minimal placeholder, real canvas drawn by web.js).
   ========================================================================== */

.activity-canvas {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  height: 80px;
  margin-bottom: var(--gap);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  /* Rev-17 UX-stage — was `var(--off)` which fails WCAG AA: per §4 palette
     comment, --off is the UI-only token (3:1 contrast against --surface is
     fine for borders, fails 4.5:1 for text). --dim is the text token
     (4.6:1 against --bg, Rev 9 fix). */
  color: var(--dim);
  font-family: var(--font-mono);
  overflow: hidden;
}

.activity-pair {
  margin: 0 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--dim);
  letter-spacing: 0.02em;
}

/* v5 §6 — activity-canvas breathe DELETED. "Pages don't breathe. Pips
   don't pulse. The chrome doesn't decorate." JS may still apply the
   .activity-warming class as a no-op; the class no longer carries any
   animation. The activity canvas is now a static row of pair labels. */

/* R4 ship-now #7 (2026-05-25) — market-state CSS rules.
   Two rules, no JS. The data-market-state attribute is set server-side on
   every /intel poll via hx-swap-oob from _intel_rows.html (R2 design pair).
   "open"   → standard canvas (existing styles apply unchanged)
   "closed" → inert register: dimmed, no animation, [ closed ] pseudo-label.
   The distinction is animation-present vs animation-absent — the trader's eye
   reads running animation as "something is happening"; stilled canvas as
   "deliberately quiet." (R2 design pair, Marcus / Yuki joint finding.) */

.activity-canvas[data-market-state="closed"] {
  opacity: 0.55;
  animation: none;
}

.activity-canvas[data-market-state="closed"]::after {
  content: "[ closed ]";
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  letter-spacing: 0.04em;
}

/* Body market-state is set via a separate OOB body attribute update — or
   via the CSS cascade from the activity-canvas sibling. Since we can only
   target children/siblings in CSS, the preamble panels use the adjacent
   sibling approach: when the activity-canvas[data-market-state="closed"]
   sibling precedes them, this doesn't work in CSS alone. The preamble IDs
   are therefore driven by a JS one-liner in web.js (setBodyMarketState)
   that mirrors the canvas attribute to the body. This is NOT a JS rewrite
   of the state machine — it's a one-time attribute mirror (10 tokens of JS).
   Until that web.js hook lands, preamble panels remain visible on market-closed
   days (graceful degradation; the empty-state text in the rec list handles
   the communication). */

/*
 * Lodestone — components/bracket-pills.css
 *
 * Phase C8 of the rebrand migration. Lifted from style-legacy.css §"§18.1
 * — recommendation rows": the recommendation status colour set,
 * .rec-dir-bracket direction-pill family, all .rec-meta-* metadata atoms
 * (sep / pips / conf / trigger / asymmetry / vol / risk-flag), and the
 * remaining chip-style countdowns (.rec-stale-counter, .rec-confidence,
 * .rec-event-countdown, .refresh-countdown). Structural move only — no
 * selector renames, no value changes.
 *
 * Note: the .rec-row / .rec-thesis / .rec-structural / .rec-status-glyph /
 * .rec-stale-pip / .rec-detail / row keyframes + .rec-row--* transition
 * classes stay in legacy. They are recommendation-row container styling
 * (cockpit moved to .pair-tile in Rev 21) and Phase E will triage whether
 * they live, fold, or die.
 */

/* ==========================================================================
   Status colour set — applied to status glyphs / pills / column cells.

   Status semantics — paint state colour, NEVER brand --accent.
   .status-ACTIVE was the third cyan-on-state collision missed by the
   4378a57 sweep (R3A ship-readiness gate caught it). When a ACTIVE rec
   row finally renders, the green pip would otherwise paint cyan
   under a cyan brand mark — exact spec §3.6 collision the v4 was
   designed to prevent.
   ========================================================================== */

.status-ACTIVE        { color: var(--live); }

.status-PENDING     { color: var(--warn); }

.status-DECAYING    { color: var(--warn); }

.status-EXPIRED     { color: var(--off); }

.status-INVALIDATED { color: var(--bad); }

/* ==========================================================================
   Direction badges — Rev 21 (Opus R1 #3) — chip→bracket migration complete.
   .rec-direction (legacy chip) is gone. Every direction surface — L1 row,
   journal open/closed tables, history list, trade-ticket instrument section —
   now uses the cockpit bracket treatment. No background, no border, brand
   text colour. The "SaaS tell" the Rev 19 refactor meant to kill is dead.
   ========================================================================== */

.rec-dir-bracket {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  background: none;
  border: 0;
  padding: 0;
}

/* Direction brackets — paint the brackets only, text stays --fg.
   v5 §3 — --live (lime) is glyph-fill inside brackets ONLY, never text.
   When .rec-dir-bracket is also .bk, the ::before / ::after carry the
   state colour; the text inside reads in --fg. */

.bk.rec-dir-bracket--LONG::before,
.bk.rec-dir-bracket--LONG::after  { color: var(--live); }

.bk.rec-dir-bracket--SHORT::before,
.bk.rec-dir-bracket--SHORT::after { color: var(--bad); }

.rec-dir-bracket { color: var(--fg); }

/* ==========================================================================
   L2 metadata atoms — Rev 19 UX-stage (Opus R2 terse format).
   Pipe separators are out; dot separators in dim are in.
   ========================================================================== */

.rec-meta-sep {
  color: var(--off);
  margin: 0 0.1rem;
}

.rec-meta-pips {
  color: var(--dim);
  font-family: var(--font-mono);
  margin-left: 0.2rem;
}

.rec-meta-conf {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
}

/* v5 §3 — confidence is data, not brand. HIGH gets --fg weight, not --ink.
   Klein blue stays at the wordmark / active row / primary CTA only. */

.rec-meta-conf--HIGH   { color: var(--fg); }

.rec-meta-conf--MEDIUM { color: var(--fg); }

.rec-meta-conf--LOW    { color: var(--dim); }

/* R5 multi-modal rewrite (2026-05-25) — trigger-type chip. Indigo at low
   opacity, mono, brackets, visible-but-subordinate next to the confidence
   tier. Surfaces which axis minted the idea (news/macro/positioning/
   structure/technical/intermarket/confluence). Hidden when the rec carries
   the default 'unspecified' value so historical/pre-migration rows stay
   visually clean. */

.rec-meta-trigger {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: rgb(var(--ink-rgb) / 0.65);
  margin-left: 0.25rem;
}

.rec-meta-trigger::before { content: "[ "; opacity: 0.55; }

.rec-meta-trigger::after  { content: " ]"; opacity: 0.55; }

/* R6 senior-analyst rewrite (2026-05-25) — asymmetry + vol-regime chips.
   Mono, bracket-atom, subordinate to confidence + trigger. Surfaces the
   R:R skew framing on the cockpit tile (positive_skew only, as "wide TP")
   and on the trade ticket [ why this idea ] mini-row (all non-default
   values). Default (symmetric / normal) is hidden — middle-of-distribution
   stays out of the eye unless it changes. */

.rec-meta-asymmetry {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: rgb(var(--ink-rgb) / 0.65);
  margin-left: 0.25rem;
}

.rec-meta-asymmetry::before { content: "[ "; opacity: 0.55; }

.rec-meta-asymmetry::after  { content: " ]"; opacity: 0.55; }

.rec-meta-asymmetry--positive_skew { color: var(--fg); }

.rec-meta-asymmetry--negative_skew { color: var(--dim); }

.rec-meta-vol {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--dim);
  margin-left: 0.25rem;
}

.rec-meta-vol::before { content: "[ "; opacity: 0.55; }

.rec-meta-vol::after  { content: " ]"; opacity: 0.55; }

.rec-meta-vol--elevated      { color: var(--warn); }

.rec-meta-vol--regime_break  { color: var(--bad); }

.rec-meta-risk-flag {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
  cursor: help;
  white-space: nowrap;
}

/* Bracket-atom grammar for risk flags (R4 ship-now #5, 2026-05-26).
   Replaces the border:1px solid currentColor pill that introduced a fourth
   rendering pattern. Now consistent with the .bk atom family. */

.rec-meta-risk-flag::before { content: "[ "; opacity: 0.65; }

.rec-meta-risk-flag::after  { content: " ]"; opacity: 0.65; }

.rec-meta-risk-flag--material      { color: var(--warn); }

.rec-meta-risk-flag--informational { color: var(--dim); }

/* ==========================================================================
   Chip-style countdowns / legacy confidence chip.
   ========================================================================== */

/* §18.1.1 staleness counter populated client-side by web.js */

.rec-stale-counter {
  font-family: var(--font-mono);
  color: var(--dim);
}

.rec-stale-counter--stale { color: var(--warn); }

.rec-stale-counter--bad   { color: var(--bad); }

/* Legacy class kept for history list; live recs no longer render this. */

.rec-confidence {
  font-size: 0.7rem;
  font-family: var(--font-mono);
  color: var(--dim);
}

/* §18.1.5 calendar countdown */

.rec-event-countdown {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--warn);
}

/* Refresh countdown (§18.1.1) */

.refresh-countdown {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--dim);
}

/*
 * Lodestone — components/alerts.css
 *
 * Phase C9 of the rebrand migration. Consolidates the alert / banner /
 * empty-state family from across style-legacy.css into one component
 * file. Structural move only — no selector renames, no value changes.
 *
 * Scope:
 *   - .disclaimer-gate (§15.1 first-run gate)
 *   - .warn-box + --warn + --bad (rec-page warning blocks)
 *   - .rec-history-zero-state (history list zero-state)
 *   - .htmx-error-banner (above #intel-container)
 *   - .panel-error (per-panel error helper)
 *   - .run-cycle-toast + --visible (bottom-right confirmation)
 *   - .pair-grid-error (cockpit grid error banner)
 *   - .cockpit-empty-state + __pullquote + __bracket + __detail
 *     (honest-silence empty state, Marcus's --amber accent per Rebrand B4)
 */

/* ==========================================================================
   Rec history list zero-state.
   ========================================================================== */

/* Zero-state for no history AND no live recs. */

.rec-history-zero-state {
  padding: var(--gap-sm) var(--gap);
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--off);
}

/* ==========================================================================
   §15.1 — first-run disclaimer flow.
   ========================================================================== */

.disclaimer-gate {
  background-color: var(--surface);
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  padding: var(--gap);
  margin-bottom: var(--gap);
  max-width: 640px;
}

.disclaimer-gate h2 {
  font-size: 1rem;
  margin-bottom: var(--gap-sm);
  color: var(--warn);
}

.disclaimer-gate p {
  font-size: 0.85rem;
  color: var(--dim);
  margin-bottom: var(--gap-sm);
}

/* ==========================================================================
   Rec-page warning blocks — .warn-box family.
   ========================================================================== */

.warn-box {
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
  border-left: 4px solid var(--warn);
  background: var(--surface);
  font-size: 0.85rem;
}

.warn-box--bad {
  border-left-color: var(--bad);
}

.warn-box--warn {
  border-left-color: var(--warn);
}

.warn-box strong {
  display: block;
  margin-bottom: 0.25rem;
}

.warn-box ul {
  margin: 0.4rem 0 0 1rem;
  padding: 0;
  font-size: 0.8rem;
}

/* ==========================================================================
   HTMX error banner + per-panel error helper.
   ========================================================================== */

/* Sonnet R1 #2 — error banner. Sits above #intel-container, dim, polite,
   not a row replacement. Auto-clears after 12s via web.js. */

.htmx-error-banner {
  display: block;
  min-height: 1.2em;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--warn);
  padding: 0.15rem 0;
}

.htmx-error-banner:empty {
  display: none;
}

/* Per-panel error helper — Sonnet R1 #13 (since-last-open / exposure /
   calendar-peek silent .catch). Appended after a panel by markPanelError(). */

.panel-error {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--warn);
}

.panel-error:empty {
  display: none;
}

/* ==========================================================================
   Run-cycle toast.
   ========================================================================== */

/* Sonnet R1 #4 — run-cycle toast. Bottom-right, dim --surface, slides in. */

.run-cycle-toast {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  background-color: var(--surface);
  border: 1px solid var(--ink);
  color: var(--fg);
  padding: 0.6rem 1rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  z-index: 100;
  max-width: 320px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s var(--ease-arrival),
              transform 0.2s var(--ease-arrival);
  pointer-events: none;
}

.run-cycle-toast--visible {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Cockpit grid error + honest-silence empty state.
   ========================================================================== */

/* Error banner above the grid */

.pair-grid-error {
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
  font-size: 0.85rem;
}

/* Cockpit empty state — honest-silence pull-quote shown when no live ideas
   exist across all tracked pairs. Bracket-atom grammar, centred, mono.
   R4 ship-now #3 (2026-05-26). */

.cockpit-empty-state {
  padding: var(--gap) 0;
  margin-bottom: var(--gap);
}

/* Pullquote body text uses Marcus's --amber accent (rebrand B4). Amber is
   the editorial / brand celebration token — reserved for positive-signal
   moments. Honest-silence is the canonical positive-signal moment per
   HANDOFF: "engine says nothing" is the design, not a bug. The brackets
   stay --ink (indigo, brand chrome) via __bracket below. Contrast on
   canvas: 9.2:1 AAA per tokens.css. */

.cockpit-empty-state__pullquote {
  font-size: clamp(0.9rem, 1.8vw, 1.1rem);
  line-height: 1.5;
  margin: 0 0 0.6rem 0;
  color: var(--amber);
}

.cockpit-empty-state__bracket {
  color: var(--ink);
}

.cockpit-empty-state__detail {
  font-size: 0.82rem;
  margin: 0;
}

/* Phase E rebrand 2026-05-26 — modern register, brackets out. */

/*
 * state-chip.css — unbracketed pill chips, Lodestone RESET 2026
 *
 * Sir's resolved decision #2: brackets are OUT everywhere. State chips
 * become unbracketed pills: weight + colour + thin border. No bracket
 * pseudo-elements here.
 *
 * These replace .bk usage on:
 *   - Direction chips: LONG / SHORT
 *   - Lifecycle state chips: ACTIVE / DECAYING / EXPIRED / INVALIDATED
 *   - Placed state: placed / closed
 *   - Tier chips: Best shot / With caveats / Watch list / Recent history
 *   - Engine-state chips: market open / market closed / silent / etc.
 *
 * Anatomy: pill shape (radius-sm), uppercase mono label, restrained
 * colour via background tint + coloured border + coloured text.
 * No brackets. No pseudo-elements. No .bk dependency.
 *
 * Usage:
 *   <span class="chip chip--long" aria-label="LONG">LONG</span>
 *   <span class="chip chip--active" aria-label="Active">Active</span>
 *   <span class="chip chip--tier chip--best-shot">Best shot</span>
 */

/* ── Base chip ──────────────────────────────────────────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);                       /* 4px — icon gap if needed */

  padding: 2px var(--space-2);               /* 2px 8px — compact pill */
  border-radius: var(--radius-sm);           /* 4px */
  border: 1px solid currentColor;           /* border inherits colour below */

  font-family: var(--font-mono);
  font-size: var(--text-xs);                 /* 12px */
  line-height: 1.4;
  font-weight: var(--weight-medium);         /* 500 */
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;

  /* Colour defaults — overridden by modifiers below. */
  color: var(--dim);
  background: rgb(var(--ink-rgb) / 0);      /* transparent base */
  border-color: var(--border-strong);

  /* No cursor — chips are decorative, not interactive. */
  user-select: none;
}

/* ── Direction chips ─────────────────────────────────────────────────────── */

/* LONG — indigo. Direction is not a value judgement: indigo = the brand. */

.chip--long {
  color: var(--ink);
  background: var(--ink-low);               /* 12% indigo tint */
  border-color: var(--ink);
}

/* SHORT — amber. RESET: SHORT moves from --bad (red) to --amber so red
   stays reserved for invalidation/warning states only. */

.chip--short {
  color: var(--amber);
  background: var(--amber-low);             /* 14% amber tint */
  border-color: var(--amber);
}

/* ── Lifecycle state chips ──────────────────────────────────────────────── */

/* ACTIVE — lime, CVD-safe. The actionable state. Halo matches the .bk--state-live glyph. */

.chip--active {
  color: var(--live);
  background: rgb(var(--live-rgb) / 0.10);
  border-color: rgb(var(--live-rgb) / 0.40);
}

/* DECAYING — amber warn. Between fresh and terminal. */

.chip--decaying {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.10);
  border-color: rgb(var(--warn-rgb) / 0.40);
}

/* EXPIRED — dim. Horizon ran out. Not a failure state, just elapsed. */

.chip--expired {
  color: var(--off);
  background: transparent;
  border-color: var(--border-strong);
}

/* INVALIDATED — brick red. Thesis falsified. */

.chip--invalidated {
  color: var(--bad);
  background: rgb(var(--bad-rgb) / 0.10);
  border-color: rgb(var(--bad-rgb) / 0.35);
}

/* PENDING — dim, mid-state. */

.chip--pending {
  color: var(--dim);
  background: transparent;
  border-color: var(--border-strong);
}

/* ── Placed-state chips ─────────────────────────────────────────────────── */

/* placed — indigo, lighter than LONG. */

.chip--placed {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

/* closed — dim. */

.chip--closed {
  color: var(--off);
  background: transparent;
  border-color: var(--border-strong);
}

/* Phase E consolidation (2026-05-26) — strengthened chip. Shows the
   evidence delta count when the LLM has reinforced an existing thesis.
   Subtle indigo treatment — meant to read as "this idea has been
   gathering supporting evidence" without screaming for attention. */

.chip--strengthened {
  color: var(--ink);
  background: transparent;
  border-color: var(--ink-low);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  letter-spacing: 0;
  padding: 1px 6px;
}

/* ── Opportunity tier chips ─────────────────────────────────────────────── */

/* Tier chip base — slightly larger padding, sans not mono. */

.chip--tier {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.01em;
  text-transform: none;                       /* tier labels are sentence case */
  font-weight: var(--weight-semibold);        /* 600 */
  padding: 2px 10px;
}

/* Best shot — indigo. The top tier. */

.chip--best-shot {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

/* With caveats — amber. Second tier. */

.chip--with-caveats {
  color: var(--amber);
  background: var(--amber-low);
  border-color: var(--amber);
}

/* Watch list — dim warm. Third tier. */

.chip--watch-list {
  color: var(--dim);
  background: transparent;
  border-color: var(--border-strong);
}

/* Recent history — very dim. Fourth tier, filtered by default. */

.chip--recent-history {
  color: var(--off);
  background: transparent;
  border-color: var(--border);
}

/* ── Confidence chips ───────────────────────────────────────────────────── */

.chip--conf-high {
  color: var(--ink);
  background: var(--ink-low);
  border-color: rgb(var(--ink-rgb) / 0.35);
}

.chip--conf-med,
.chip--conf-medium {
  color: var(--dim);
  background: transparent;
  border-color: var(--border-strong);
}

.chip--conf-low {
  color: var(--off);
  background: transparent;
  border-color: var(--border);
}

/* ── Market-status chips ────────────────────────────────────────────────── */

.chip--market-open {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

.chip--market-closed {
  color: var(--off);
  background: transparent;
  border-color: var(--border-strong);
}

/* ── Engine-state chips (silent / refused / gate-killed / degraded) ──────── */

.chip--engine-state {
  font-family: var(--font-mono);
  font-size: var(--text-base);
  color: var(--fg);
  background: transparent;
  border-color: var(--border);
}

/* ── Risk-flag chips ─────────────────────────────────────────────────────── */

.chip--risk-flag {
  color: var(--off);
  background: transparent;
  border-color: var(--border);
  font-family: var(--font-sans);
  text-transform: none;
  letter-spacing: 0;
}

.chip--risk-flag--warn {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.08);
  border-color: rgb(var(--warn-rgb) / 0.35);
}

/* ── Size modifiers ─────────────────────────────────────────────────────── */

/* Slightly larger — for hero cards or prominent display. */

.chip--lg {
  font-size: var(--text-sm);                /* 14px */
  padding: 3px 12px;
  border-radius: var(--radius-md);
}

/* ── Phase F (2026-05-26) — placed-position health pills ─────────────────
   Sits in the card footer of placed-trades cards. Shape mirrors .chip but
   stays distinct so the scanning eye doesn't conflate it with the
   freshness state chip in the header. */

.idea-card__health-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  border: 1px solid currentColor;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.4;
  font-weight: var(--weight-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--off);
  background: transparent;
  user-select: none;
}

/* ok — green, calm */

.idea-card__health-pill--ok {
  color: var(--live);
  background: rgb(var(--live-rgb) / 0.10);
  border-color: rgb(var(--live-rgb) / 0.40);
}

/* near_sl — amber warn, leading edge */

.idea-card__health-pill--near-sl {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.10);
  border-color: rgb(var(--warn-rgb) / 0.40);
}

/* near_tp — amber-leaning-green; user choice moment */

.idea-card__health-pill--near-tp {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.08);
  border-color: rgb(var(--warn-rgb) / 0.35);
}

/* sl_hit — brick red, terminal in spirit but not auto-closed */

.idea-card__health-pill--sl-hit {
  color: var(--bad);
  background: rgb(var(--bad-rgb) / 0.12);
  border-color: rgb(var(--bad-rgb) / 0.45);
}

/* tp_hit — indigo positive payoff signal */

.idea-card__health-pill--tp-hit {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

/* time_stop_approaching — dim, "time's up" signal */

.idea-card__health-pill--time-stop-approaching {
  color: var(--off);
  background: transparent;
  border-color: var(--border-strong);
}

/* 2026-05-26 expansion — additional states for actionable health guidance:
   running_favorable / running_strong / stalled / time_stop_due. */

/* running_favorable — green, "let it run" */

.idea-card__health-pill--running-favorable {
  color: var(--live);
  background: rgb(var(--live-rgb) / 0.10);
  border-color: rgb(var(--live-rgb) / 0.40);
}

/* running_strong — indigo, "this is working — consider amending up" */

.idea-card__health-pill--running-strong {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

/* stalled — amber dim, "thesis may not play out" */

.idea-card__health-pill--stalled {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.06);
  border-color: rgb(var(--warn-rgb) / 0.30);
}

/* time_stop_due — brick red, exit signal */

.idea-card__health-pill--time-stop-due {
  color: var(--bad);
  background: rgb(var(--bad-rgb) / 0.10);
  border-color: rgb(var(--bad-rgb) / 0.35);
}

/* renew_recommended — indigo, "thesis still in play, extend" */

.idea-card__health-pill--renew-recommended {
  color: var(--ink);
  background: var(--ink-low);
  border-color: var(--ink);
}

/* close_recommended — amber-red, "exit" but distinct from sl_hit which
   is a price event rather than a thesis-review verdict */

.idea-card__health-pill--close-recommended {
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.10);
  border-color: rgb(var(--warn-rgb) / 0.40);
}

/*
 * tooltip.css — shared tooltip pattern, Lodestone RESET 2026
 *
 * Shared by all chunks. Owns the hover-target, keyboard, touch, and
 * screen-reader patterns for inline jargon explainers.
 *
 * Pattern:
 *   <span class="tt" data-tip="The tooltip copy goes here.">term</span>
 *
 * On hover  (hover:hover): tooltip shows above the term.
 * On focus  (keyboard tab): same tooltip via :focus-visible.
 * On touch  (hover:none): tap toggles .tt--open via JS.
 * Screen readers: aria-describedby wired by web.js; the .tt__content
 *   element carries role="tooltip" id="tt-{n}".
 *
 * Tier 1 (cockpit chrome) and Tier 2 (state chips) terms are wired
 * inline in _intel_rows.html and dashboard.html. This file is the
 * single CSS source of truth — no overrides needed in surface CSS.
 */

/* ── Trigger element — the underlined jargon term ───────────────────────── */

.tt {
  display: inline;
  position: relative;

  /* Dotted underline signals "this term has an explanation". */
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: var(--off);
  text-underline-offset: 3px;

  cursor: help;
  outline: none;              /* focus ring comes from :focus-visible below */
}

/* Indigo underline on hover/focus — signals interactivity */

@media (hover: hover) {
  .tt:hover {
    text-decoration-color: var(--ink);
    color: inherit;           /* don't change text colour — just the underline */
  }
}

.tt:focus-visible {
  text-decoration-color: var(--ink);
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: 2px;
}

/* ── Tooltip bubble ──────────────────────────────────────────────────────── */

.tt__content {
  /* Positioning: floats above the term. */
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 900;

  /* Sizing: max-width keeps it readable; min-width avoids orphaned words. */
  width: max-content;
  max-width: min(320px, 90vw);
  min-width: 160px;

  /* Surface: surface-2 (raised) with elevated shadow. */
  background: var(--surface-2);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-elevated);
  padding: var(--space-3) var(--space-4);     /* 16px / 24px */

  /* Typography: sans body, not mono. */
  font-family: var(--font-sans);
  font-size: var(--text-sm);                  /* 14px */
  line-height: var(--leading-base);
  color: var(--fg);
  font-weight: var(--weight-regular);
  font-feature-settings: var(--font-feat-sans);
  /* 2026-05-26 — when the trigger lives inside an uppercase-styled
     parent (e.g. .status-strip__label), the tooltip body inherited
     text-transform: uppercase and rendered the whole bubble in caps.
     Reset every inheritable text-shape property so the bubble owns its
     own typography regardless of where the trigger sits. */
  text-transform: none;
  letter-spacing: normal;
  text-align: left;
  white-space: normal;

  /* Arrow pointer — pure CSS, no extra element. */
  /* Arrow drawn in ::after. */

  /* Hidden by default; revealed on hover/focus. */
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition:
    opacity var(--dur-tick) var(--ease-glow),
    visibility 0s var(--dur-tick);           /* delay visibility change */
}

/* Arrow pointing down toward the term */

.tt__content::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--surface-2);
  pointer-events: none;
}

/* ── Show state — hover (pointer devices) ──────────────────────────────── */

@media (hover: hover) {
  .tt:hover .tt__content {
    opacity: 1;
    pointer-events: auto;
    visibility: visible;
    transition:
      opacity var(--dur-tick) var(--ease-glow),
      visibility 0s 0s;
  }
}

/* ── Show state — keyboard focus ───────────────────────────────────────── */

.tt:focus-visible .tt__content {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
  transition:
    opacity var(--dur-tick) var(--ease-glow),
    visibility 0s 0s;
}

/* ── Show state — touch tap (JS adds .tt--open) ────────────────────────── */

.tt.tt--open .tt__content {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
  transition:
    opacity var(--dur-tick) var(--ease-glow),
    visibility 0s 0s;
}

/* ── Edge-case: tooltip near viewport top — flip below term ─────────────── */

/* JS can add .tt--flip-down when the term is near the top. */

.tt--flip-down .tt__content {
  bottom: auto;
  top: calc(100% + 8px);
}

.tt--flip-down .tt__content::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--surface-2);
}

/* ── Edge-case: tooltip overflowing viewport horizontally ───────────────
   The default centre-on-trigger (left: 50%; translateX(-50%)) bleeds off
   the viewport when the trigger sits near the left/right edge — Sir
   flagged this on mobile (Status tooltip clipped on the left) and on
   desktop (Next-4h tooltip clipped on the right). JS measures the bubble
   rect on display and applies one of these modifiers when needed. The
   arrow position is preserved via CSS variable so it still points at the
   trigger, not the (shifted) bubble centre. */

.tt__content {
  --tt-shift: 0px;
  transform: translateX(calc(-50% + var(--tt-shift)));
}

.tt__content::after {
  /* Cancel out the shift so the arrow still points at the trigger. */
  transform: translateX(calc(-50% - var(--tt-shift)));
}

/* ── Reduced motion — skip the opacity fade ─────────────────────────────── */

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

/* ── Screen-reader only node (for aria-describedby targets) ─────────────── */

/* web.js generates hidden <span id="tt-N" role="tooltip"> nodes and wires
   aria-describedby. Those nodes use .sr-only if they aren't the visible bubble. */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/*
 * components/cycle-dial.css — §11.9 cockpit cycle dial.
 *
 * Two concentric SVG arcs. Outer = heavy cycle (every HEAVY_INTERVAL_MIN);
 * inner = light cycle (every LIGHT_INTERVAL_MIN). Both fill clockwise from
 * 12 o'clock, showing fraction-elapsed since the last fire. When an arc
 * crosses 85%, the dial enters its imminent-fire state and the stroke
 * weight ticks up + the glyph weight-pulses — borrowed from the .bk
 * weight-pulse pattern so the dial speaks the same vocabulary as the
 * brand mark.
 *
 * The atom honours the three v5 principles:
 *   1. Brackets aren't chrome here — dial sits inside the bracket-free
 *      status strip and uses geometry alone to signal cycle phase.
 *   2. One ink. --ink (Klein blue) paints the outer-arc fill; --live (lime)
 *      paints inside the dial when imminent fire is < 85% elapsed (the
 *      "earned moment"). --dim is everything else.
 *   3. Delight only when the data earned it. The pulse ONLY fires when an
 *      actual cycle is < 15% from firing. No ambient breathe, no idle
 *      rotation. The arc fill IS the measurement.
 *
 * Mobile (≤ 480px): dial shrinks to 16px and sits inline with the run
 * button. No tooltip — long-press fallback handled by browser.
 *
 * prefers-reduced-motion: holds the imminent-fire pulse static at the
 * peak weight; arcs still fill (they're informational, not decoration).
 *
 * Graceful degrade: when next_heavy_at_iso is None (scheduler down or
 * not yet registered), JS leaves the arcs at zero fraction and the
 * tooltip reads "Cycle scheduler unavailable". CSS does nothing extra —
 * the empty dial is the honest signal.
 */

/* ==========================================================================
   Dial atom — sits inline in the status strip.
   ========================================================================== */

.cycle-dial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  cursor: pointer;
  text-decoration: none;
  color: var(--dim);
  transition: color var(--dur-tick, 150ms) ease;
}

.cycle-dial:hover,
.cycle-dial:focus-visible {
  color: var(--ink);
  outline: none;
}

.cycle-dial:focus-visible .cycle-dial__svg {
  /* Keyboard focus uses an additional outline-ish ring on the SVG itself
     rather than the link's outline (which would clip oddly). */
  filter: drop-shadow(0 0 1px var(--ink));
}

.cycle-dial__svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* The arcs are drawn rotated -90deg so they fill clockwise from the
     12 o'clock position. */
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}

/* ==========================================================================
   Track + progress arcs.
   Track is a faint full-circle; progress is the dash-offset arc. JS
   updates --frac-heavy and --frac-light (0.0 → 1.0) on 1Hz tick.
   stroke-dasharray = circumference; stroke-dashoffset = circumference
   × (1 - fraction).
   ========================================================================== */

.cycle-dial__track {
  fill: none;
  stroke: var(--off);
  stroke-opacity: 0.35;
  stroke-width: 1.5;
}

.cycle-dial__arc {
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: butt;
  transition: stroke-dashoffset var(--dur-tick, 150ms) linear,
              stroke-width var(--dur-tick, 150ms) ease;
}

.cycle-dial__arc--heavy {
  /* Outer arc, radius 9. Circumference = 2π·9 ≈ 56.549. */
  stroke: var(--dim);
  stroke-dasharray: 56.549;
  stroke-dashoffset: 56.549;
}

.cycle-dial__arc--light {
  /* Inner arc, radius 5. Circumference = 2π·5 ≈ 31.416. */
  stroke: var(--dim);
  stroke-dasharray: 31.416;
  stroke-dashoffset: 31.416;
}

/* ==========================================================================
   Imminent-fire state — arc is > 85% complete.
   Painted via .cycle-dial--heavy-imminent / .cycle-dial--light-imminent
   class toggled by JS. Honest-silence: only fires when the cycle truly is
   close, never ambient. --live (lime) paints the about-to-fire arc; the
   pulse is the weight-pulse vocabulary borrowed from .bk so the dial
   reads as part of the same brand language as the wordmark.
   ========================================================================== */

.cycle-dial--heavy-imminent .cycle-dial__arc--heavy {
  stroke: var(--live);
  stroke-width: 2.25;
  animation: cycle-dial-pulse var(--dur-pulse, 1.4s) ease-in-out infinite;
}

.cycle-dial--light-imminent .cycle-dial__arc--light {
  stroke: var(--live);
  stroke-width: 2.25;
  animation: cycle-dial-pulse var(--dur-pulse, 1.4s) ease-in-out infinite;
}

@keyframes cycle-dial-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* ==========================================================================
   Tooltip — same .tt vocabulary the rest of the cockpit uses, but the
   dial's tooltip content lives on a sibling .cycle-dial__tip that JS
   updates with the live "Next heavy mint: 3m 14s · Next light: 0m 42s"
   string. The outer .tt wrapper handles positioning.
   ========================================================================== */

.cycle-dial-wrap {
  /* Inline container holding the dial + a hover tooltip. Sits inside the
     status strip's right-aligned cluster. */
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-right: var(--space-2, 0.5rem);
}

.cycle-dial__tip {
  /* Reuses the .tt__content pattern: hidden until hover/focus, then
     positioned above the dial. */
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
  padding: 0.35rem 0.55rem;
  border: 1px solid var(--off);
  border-radius: 2px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-tick, 150ms) ease;
  z-index: 10;
}

.cycle-dial-wrap:hover .cycle-dial__tip,
.cycle-dial-wrap:focus-within .cycle-dial__tip {
  opacity: 1;
}

/* ==========================================================================
   Reduced-motion contract (§22 a11y). Arcs still fill — they're data, not
   decoration — but the imminent-fire pulse holds static at peak opacity.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .cycle-dial--heavy-imminent .cycle-dial__arc--heavy,
  .cycle-dial--light-imminent .cycle-dial__arc--light {
    animation: none;
    opacity: 1;
  }
  .cycle-dial__arc {
    transition: stroke-dashoffset 0s linear;
  }
}

/* ==========================================================================
   Mobile — shrink the dial at 375px so it doesn't dominate the status
   strip. Tooltip hides on small screens (touch UA has no hover; the
   countdown info lives one tap away at /status/detail).
   ========================================================================== */

@media (max-width: 480px) {
  .cycle-dial {
    width: 16px;
    height: 16px;
  }
  .cycle-dial__track,
  .cycle-dial__arc {
    stroke-width: 1.4;
  }
  .cycle-dial__tip {
    display: none;
  }
}

/*
 * Lodestone — components/watchlist.css
 *
 * Cross-pair watchlist (2026-05-26-evening). Completes the honest-silence
 * mental model: pairs the engine is watching but not currently acting on
 * are shown here, so absence reads as discipline instead of bug.
 *
 * Design constraints inherited from the Phase E creative direction:
 *   - Brackets retired; type rhythm + alignment carry hierarchy.
 *   - --ink (Klein blue) is the brand accent — never paint state with it.
 *   - --live (lime) reserved for ACTIVE state — not used here.
 *   - --dim / --off for non-active vocabulary; --fg for legible text.
 *
 * Aja R1 (2026-05-26-evening): quiet rows must carry the same type
 * weight as recent-history rows — absence as content. The only signal
 * differentiating them is the trailing state word.
 *
 * Craft sweep 2026-05-27: added margin-bottom so the watchlist tile has
 * breathing room above the disclaimer footer. Previously the grid edge
 * abutted the footer with no gap.
 */

.watchlist {
  margin-top: var(--space-6);
  margin-bottom: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.watchlist__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}

.watchlist__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
  letter-spacing: 0.01em;
}

.watchlist__count {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
  letter-spacing: 0.02em;
}

.watchlist__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 0;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
}

.watchlist__cell {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.watchlist__link {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "pair  price  pct"
    "state state  state";
  gap: 0.15rem 0.5rem;
  align-items: baseline;
  padding: 0.55rem 0.7rem;
  text-decoration: none;
  color: var(--fg);
  transition: background-color 120ms ease;
}

.watchlist__link:hover,
.watchlist__link:focus-visible {
  background: rgb(var(--ink-rgb) / 0.05);
  outline: none;
}

.watchlist__link:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: -1px;
}

.watchlist__pair {
  grid-area: pair;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.watchlist__price {
  grid-area: price;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.watchlist__price--missing {
  color: var(--off);
}

.watchlist__pct {
  grid-area: pct;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-regular);
  min-width: 4.5ch;
  text-align: right;
}

.watchlist__pct--up   { color: var(--live); }

.watchlist__pct--down { color: var(--bad); }

.watchlist__pct--flat { color: var(--dim); }

.watchlist__pct--missing { color: var(--off); }

.watchlist__state {
  grid-area: state;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
  text-transform: lowercase;
  letter-spacing: 0.04em;
}

/* Recent-terminal rows pick up a subtle severity by state. */

.watchlist__cell--recent .watchlist__state {
  color: var(--dim);
}

/* Quiet rows — the absence is the content. Same type weight as recent
   rows, only the state word differs. No greying-out — that would
   contradict Aja's R1 directive. */

.watchlist__cell--quiet .watchlist__state {
  color: var(--off);
}

/* Mobile — single column under 480px. The cell layout stays 3-col
   inside (pair | price | pct on row 1, state on row 2) so the row
   feels intentional even on a narrow viewport. */

@media (max-width: 480px) {
  .watchlist__grid {
    grid-template-columns: 1fr;
  }
}

/*
 * cockpit.css — idea-feed layout, Lodestone RESET 2026
 *
 * Replaces the pair-grid tile layout entirely. The cockpit is now a
 * chronological feed of idea CARDS sorted by opportunity_tier then recency.
 *
 * Reference aesthetic: Linear's issue list, Stripe Dashboard, Mercury.
 * Not Bloomberg, not a terminal, not pair-grid-with-bracket-atoms.
 *
 * Includes: Volume gauge drift fix (PHASE-D-REGRESSIONS regression #3
 * Option 1) — transition: none on .sessions-volume__svg.
 */

/* ==========================================================================
   COCKPIT SHELL
   ========================================================================== */

/* Outer wrapper constrains the page column width. */

.cockpit {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);                      /* 32px between major sections */
  max-width: 1080px;
  margin-inline: auto;
  padding-inline: var(--space-4);           /* 24px side padding */
  padding-top: var(--space-4);
  padding-bottom: var(--space-7);           /* 64px breathing room at the bottom */
}

/* ==========================================================================
   COCKPIT HEADER / EXPOSURE SUMMARY
   Replaces the EXPOSURE chip. Shows "41 ideas open · USD-short biased"
   as a calm header above the feed. No chip mismatch.
   ========================================================================== */

.cockpit-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);                      /* 16px */
}

.cockpit-exposure {
  font-family: var(--font-sans);
  font-size: var(--text-md);               /* 17px */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  line-height: var(--leading-snug);
}

.cockpit-exposure__count {
  /* The number itself — mono for numerals. */
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.cockpit-exposure__detail {
  font-family: var(--font-sans);
  font-size: var(--text-sm);               /* 14px */
  font-weight: var(--weight-regular);
  color: var(--dim);
  margin-left: var(--space-2);             /* 8px */
}

/* ==========================================================================
   CONTROL STRIP — sort + filter dropdowns above the feed
   ========================================================================== */

.idea-controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);                      /* 16px between controls */
  flex-wrap: wrap;
}

.idea-controls__group {
  display: flex;
  align-items: center;
  gap: var(--space-2);                      /* 8px label→select */
}

.idea-controls__label {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  white-space: nowrap;
}

/* Select element — minimal, on-brand. */

.idea-controls__select {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--fg);
  background: var(--surface-2);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3) var(--space-1) var(--space-2);
  cursor: pointer;
  appearance: none;                         /* custom arrow below */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236B665D' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
  transition: border-color var(--dur-tick) var(--ease-glow);
}

.idea-controls__select:hover {
  border-color: var(--border-strong);
}

.idea-controls__select:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-color: var(--ink);
}

/* ==========================================================================
   IDEA FEED — the card list
   ========================================================================== */

.idea-feed {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);                      /* 24px between cards */
}

/* Tier divider — a subtle label between tier groups when sorted by tier */

.idea-feed__tier-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-2);
}

.idea-feed__tier-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);               /* 12px */
  font-weight: var(--weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

.idea-feed__tier-rule {
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ==========================================================================
   IDEA CARD — the atom of the cockpit feed
   ========================================================================== */

.idea-card {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);         /* 12px */
  box-shadow: var(--shadow-card);
  padding: var(--space-4);                 /* 24px */
  overflow: hidden;                        /* contain the tier border */

  display: flex;
  flex-direction: column;
  gap: var(--space-3);                     /* 16px between card rows */

  transition:
    box-shadow var(--dur-card) var(--ease-card),
    background var(--dur-card) var(--ease-card);
}

.idea-card:hover {
  background: var(--card-hover-bg);
  box-shadow: var(--shadow-pop);
}

/* ── Tier border — left edge treatment ────────────────────────────────── */

/* Best shot: indigo 4px left border (Linear/Stripe convention) */

.idea-card--best-shot {
  border-left: 4px solid var(--ink);
  padding-left: calc(var(--space-4) - 3px); /* compensate 4px border */
}

/* With caveats: no accent border (standard card chrome) */

.idea-card--with-caveats {
  /* inherits base .idea-card */
}

/* Watch list: slightly dimmed card */

.idea-card--watch-list {
  opacity: 0.85;
}

/* Recent history: heavily dimmed — filtered out by default */

.idea-card--recent-history {
  opacity: 0.60;
}

/* Hidden by JS/CSS when filter excludes recent-history */

.idea-card[data-tier="recent_history"].idea-card--hidden,
.idea-feed[data-hide-history="true"] .idea-card--recent-history {
  display: none;
}

/* ── Card header row ──────────────────────────────────────────────────── */

.idea-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-2);                     /* 8px */
  flex-wrap: wrap;
}

/* Pair name — monospace, prominent */

.idea-card__pair {
  font-family: var(--font-mono);
  font-size: var(--text-base);             /* 15px */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feat-mono);
  flex-shrink: 0;
}

/* Direction + tier chips sit inline after the pair name */

.idea-card__chips {
  display: flex;
  align-items: center;
  gap: var(--space-1);                     /* 4px */
  flex-wrap: wrap;
  flex: 1;
}

/* Price — right-aligned in the header */

.idea-card__price {
  font-family: var(--font-mono);
  font-size: var(--text-sm);              /* 14px */
  color: var(--dim);
  margin-left: auto;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feat-mono);
}

.idea-card__pct {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.idea-card__pct--up   { color: var(--live); }

.idea-card__pct--down { color: var(--bad); }

.idea-card__pct--flat { color: var(--off); }

.idea-card__pct--na   { color: var(--off); }

/* ── Rationale headline ────────────────────────────────────────────────── */

/* The thesis, promoted to the visual headline of the card.
   Craft sweep 2026-05-27: only the LEAD thesis renders here — the
   strengthen_recommendation updates are split off into .idea-card__timeline. */

.idea-card__rationale {
  font-family: var(--font-sans);
  font-size: var(--text-md);              /* 17px */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  line-height: var(--leading-snug);       /* 1.35 */
  margin: 0;

  /* Best shot cards: slightly larger rationale */
}

.idea-card--best-shot .idea-card__rationale {
  font-size: var(--text-lg);             /* 20px — best shots read more prominent */
}

/* Watch list / recent history cards: collapse rationale by default */

.idea-card--watch-list .idea-card__rationale,
.idea-card--recent-history .idea-card__rationale {
  font-size: var(--text-base);           /* 15px — de-emphasised */
  font-weight: var(--weight-regular);
  color: var(--dim);
}

/* ── Strengthening timeline — craft sweep 2026-05-27 ──────────────────────
   Renders the list of strengthening_events beneath the lead rationale.
   Each event: a dim clock-time prefix + the delta_rationale body.
   Ordered list — order is the meaning (chronological). */

.idea-card__timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);                    /* 8px between events */
  padding-left: var(--space-3);          /* 16px indent — subordinate to the lead */
  border-left: 2px solid var(--border);  /* left rule marks the update chain */
}

.idea-card__timeline-event {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.idea-card__timeline-time {
  font-family: var(--font-mono);
  font-size: var(--text-xs);            /* 12px — dim timestamp prefix */
  color: var(--off);
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
}

.idea-card__timeline-body {
  font-family: var(--font-sans);
  font-size: var(--text-sm);            /* 14px — readable body weight */
  color: var(--dim);
  line-height: var(--leading-base);
  margin: 0;
}

/* ── +N chip tooltip event atoms — craft sweep 2026-05-27 ─────────────────
   Inside .tt__content on the .chip--strengthened trigger, each
   strengthening event renders as a stacked pair: clock-time above, body below.
   Overrides tooltip.css defaults only for layout — typography resets are
   already handled by .tt__content. */

.idea-card__tt-event {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Not the first event — add top separator so events are scannable. */

.idea-card__tt-event + .idea-card__tt-event {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}

.idea-card__tt-time {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
}

.idea-card__tt-body {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--fg);
  line-height: var(--leading-base);
}

/* ── Setup subhead ────────────────────────────────────────────────────── */

/* Plain-English summary: confidence / trigger / asymmetry / vol. */

.idea-card__subhead {
  font-family: var(--font-sans);
  font-size: var(--text-sm);             /* 14px */
  color: var(--dim);
  line-height: var(--leading-base);
  margin: 0;
}

/* Tier label inline-prefix on the subhead (creative review 2026-05-27).
   Editorial judgement in the editorial register — mono, dim, no chip chrome.
   Replaces the .chip--tier pill that used to compete with ACTIVE in the
   card header for visual primacy.
   Craft sweep 2026-05-27: text-transform: uppercase to match the LONG /
   DECAYING / ACTIVE chip register. Letter-spacing tightened to 0.06em to
   sit comfortably at the xs mono size. */

.idea-card__tier-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.idea-card__subhead-sep {
  color: var(--off);
}

/* ── Levels row — entry / SL / TP ─────────────────────────────────────── */

.idea-card__levels {
  display: flex;
  align-items: center;
  gap: var(--space-3);                   /* 16px */
  flex-wrap: wrap;
}

.idea-card__level-item {
  font-family: var(--font-mono);
  font-size: var(--text-xs);            /* 12px */
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feat-mono);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.idea-card__level-label {
  color: var(--off);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
}

.idea-card__level-value {
  color: var(--fg);
}

/* ── Risk flags ────────────────────────────────────────────────────────── */

.idea-card__risk-flags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* ── Card footer — timestamp + state chip + CTA ───────────────────────── */

.idea-card__footer {
  display: flex;
  align-items: center;
  gap: var(--space-3);                   /* 16px */
  flex-wrap: wrap;
  padding-top: var(--space-2);           /* 8px hairline gap */
  border-top: 1px solid var(--border);
  margin-top: auto;                      /* push footer to bottom of card */
}

.idea-card__meta {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  flex: 1;
  min-width: 0;
}

/* CTA button — "Open this idea →" */

.idea-card__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);

  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;

  padding: var(--space-1) var(--space-3); /* 4px 16px */
  border: 1px solid var(--ink);
  border-radius: var(--radius-md);
  background: var(--ink-low);

  transition:
    background var(--dur-tick) var(--ease-glow),
    border-color var(--dur-tick) var(--ease-glow),
    color var(--dur-tick) var(--ease-glow);
}

.idea-card__cta:hover {
  background: rgb(var(--ink-rgb) / 0.22);
  border-color: var(--ink-hover);
  color: var(--ink-hover);
}

.idea-card__cta:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* ── Cockpit inline Place affordance — Yuki R2 (2026-05-26) ───────────────
   "Mark as placed" details/summary that expands to the broker-fill form
   directly inside the card. Mirrors .rec-place-form chrome from the rec
   page but sized for the smaller card surface. Sits below the footer so
   the card's resting state stays compact. */

.idea-card__place-form-wrap {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px dashed var(--border);
}

.idea-card__place-form-wrap[open] {
  padding-bottom: var(--space-2);
}

.idea-card__place-summary {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--dim);
  cursor: pointer;
  list-style: none;
  padding: var(--space-1) 0;
  user-select: none;
}

.idea-card__place-summary::-webkit-details-marker { display: none; }

.idea-card__place-summary::before {
  content: "▸ ";
  display: inline-block;
  margin-right: var(--space-1);
  color: var(--off);
  transition: transform var(--dur-tick) var(--ease-glow);
}

.idea-card__place-form-wrap[open] .idea-card__place-summary::before {
  transform: rotate(90deg);
}

.idea-card__place-summary:hover {
  color: var(--ink);
}

.idea-card__place-summary:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

.idea-card__place-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.idea-card__place-hint {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
  line-height: 1.4;
}

.idea-card__place-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

@media (max-width: 480px) {
  .idea-card__place-grid {
    grid-template-columns: 1fr;
  }
}

.idea-card__place-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.idea-card__place-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.idea-card__place-field input {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  padding: var(--space-1) var(--space-2);
  background: var(--canvas);
  /* Marcus R1 (2026-05-26-evening) — ink economy: --ink is the brand
     accent for chrome (focus border, submit button) below. Form-input
     body text uses --fg so the page never paints --ink twice per
     viewport, which dilutes the brand signal. */
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.idea-card__place-field input:focus {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-color: var(--ink);
}

.idea-card__place-submit {
  align-self: flex-start;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--ink);
  background: var(--ink-low);
  border: 1px solid var(--ink);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
  cursor: pointer;
  transition:
    background var(--dur-tick) var(--ease-glow),
    border-color var(--dur-tick) var(--ease-glow),
    color var(--dur-tick) var(--ease-glow);
}

.idea-card__place-submit:hover {
  background: rgb(var(--ink-rgb) / 0.22);
  border-color: var(--ink-hover);
  color: var(--ink-hover);
}

.idea-card__place-submit:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* ==========================================================================
   EMPTY STATE — honest silence card
   No brackets. Just the sentence in a card.
   ========================================================================== */

.cockpit-empty {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: var(--space-7) var(--space-4); /* 64px / 24px */
}

.cockpit-empty__card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-6);               /* 48px — hero card padding */
  max-width: 480px;
  text-align: center;
}

.cockpit-empty__title {
  font-family: var(--font-mono);
  font-size: var(--text-lg);           /* 20px — instrument-register headline */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  letter-spacing: 0.01em;
  margin: 0 0 var(--space-3);
}

.cockpit-empty__sentence {
  font-family: var(--font-sans);
  font-size: var(--text-md);            /* 17px */
  font-weight: var(--weight-regular);
  color: var(--fg);
  line-height: var(--leading-base);
  margin: 0 0 var(--space-2);
}

.cockpit-empty__cadence {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: 0 0 var(--space-3);
}

.cockpit-empty__timestamp {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
}

/* ==========================================================================
   ERROR / FIRST-RUN CARD
   Replaces .pair-grid-error — same semantic, new card shape.
   ========================================================================== */

.cockpit-notice {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);              /* 24px */
}

.cockpit-notice p {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: var(--space-2) 0 0;
  line-height: var(--leading-base);
}

.cockpit-notice p:first-child {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin-top: 0;
}

.cockpit-notice a {
  color: var(--ink);
  text-decoration: underline;
}

/* ==========================================================================
   PREAMBLE STRIP — market context above the feed
   Calendar peek, market status, since-last-open.
   ========================================================================== */

/* Preamble panels use card chrome now, not bare strips. */

.preamble-card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-3) var(--space-4); /* 16px 24px */
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.preamble-card__label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--off);
  white-space: nowrap;
}

.preamble-card__content {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  flex: 1;
}

/* ── Collapsed preamble — single status line ───────────────────────────── */

.preamble-status-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.preamble-status-line__detail {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--off);
}

/* ==========================================================================
   TRACKED-TRADES WIDGET SHELL (Phase G placeholder)
   ========================================================================== */

.tracked-trades {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
}

.tracked-trades__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}

.tracked-trades__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
}

/* Yuki R1 (2026-05-26-evening) — orienting subhead under the section
   title so the monitoring paradigm is described where it lives, not
   inferred from the cards. */

.tracked-trades__subhead {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  line-height: var(--leading-base);
  margin: var(--space-1) 0 0 0;
}

.tracked-trades__empty {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--off);
  line-height: var(--leading-base);
  padding: var(--space-5) 0;            /* 32px vertical breathing room */
  text-align: center;
}

.tracked-trades__empty-hint {
  display: block;
  font-size: var(--text-xs);
  color: var(--off);
  max-width: 52ch;
  margin: var(--space-2) auto 0;
  line-height: var(--leading-base);
}

/* Phase F (2026-05-26) — list wrapper for placed cards. Stacks the
   .idea-card partial vertically with the same gap the idea feed uses. */

.tracked-trades__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ==========================================================================
   PHASE F (2026-05-26) — idea-card additions
   - .idea-card__health  static health line (Ask 4: visible at a scan)
   - .idea-card__fills   broker-fill row on placed cards (Ask 2)
   - .idea-card--placed  subtle indigo edge on placed-card chrome
   ========================================================================== */

/* Static health line — small, dim, sans. Always visible (no hover). Sits
   directly under the rationale headline so the eye lands on it during
   the scan pattern. */

.idea-card__health {
  font-family: var(--font-sans);
  font-size: var(--text-sm);             /* 14px */
  color: var(--dim);
  line-height: var(--leading-base);
  margin: 0;
  max-width: 60ch;                       /* wrap on wide viewports */
}

/* Placed cards take a slightly stronger indigo edge so the user clocks
   them as "in flight" even without reading the chips. Doesn't compete
   with the Best Shot border — best-shot wins on tier-class specificity. */

.idea-card--placed {
  border-left: 4px solid var(--ink);
  padding-left: calc(var(--space-4) - 3px);
}

/* Broker-fill row — sits below the levels row on placed cards. Same
   visual weight as the levels row but visually segregated by a hairline. */

.idea-card__fills {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-top: var(--space-2);
  border-top: 1px dashed var(--border);
}

.idea-card__fills-label {
  font-family: var(--font-sans);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--off);
}

.idea-card__fill-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}

.idea-card__fill-label {
  color: var(--off);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
}

.idea-card__fill-value {
  color: var(--fg);
}

/* ==========================================================================
   PHASE F (2026-05-26) — broker-fill capture form (trade ticket)
   ========================================================================== */

.rec-place-form-wrap {
  display: inline-block;
}

.rec-place-form-wrap > summary {
  list-style: none;                       /* remove default triangle */
  cursor: pointer;
}

.rec-place-form-wrap > summary::-webkit-details-marker {
  display: none;
}

.rec-place-form-wrap[open] > summary {
  /* Once the form is open, the summary becomes a static "section heading"
     visual — same chrome but no longer the primary CTA. */
  opacity: 0.7;
  cursor: default;
}

.rec-place-form {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-1);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.rec-place-form__hint {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: 0;
  line-height: var(--leading-base);
}

.rec-place-form__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}

.rec-place-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.rec-place-form__label {
  font-family: var(--font-sans);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--off);
}

.rec-place-form__field input {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  padding: var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-0, var(--surface-1));
  color: var(--fg);
}

.rec-place-form__field input:focus {
  outline: 2px solid var(--ink);
  outline-offset: -1px;
}

.rec-place-form__submit {
  align-self: flex-start;
}

@media (max-width: 480px) {
  .rec-place-form__grid {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================================
   MARKET SESSIONS WIDGET — volume gauge drift fix
   PHASE-D-REGRESSIONS regression #3 Option 1: drop the 60s transition
   so the SVG snaps each tick alongside the dot. No accumulated drift.
   ========================================================================== */

/* ==========================================================================
   STATUS STRIP — extracted from dashboard.html inline styles
   Marcus R1 (2026-05-26-evening) flagged 11 inline-style declarations on
   the status strip with no matching CSS class — craft inconsistency that
   blocks stylelint discipline. Each declaration is moved here verbatim.
   ========================================================================== */

.status-strip {
  display: flex;
  gap: 1rem;
  align-items: center;
  flex-wrap: wrap;
  padding: 0.25rem 1.5rem;
  font-size: 0.8rem;
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
}

.status-strip__label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--off);
}

.status-strip .status-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
}

.status-strip .status-age {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
}

.status-strip__trailing {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.refresh-countdown-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
}

.refresh-countdown {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
}

/* .sessions-volume__svg rules removed 2026-05-26-evening — the curve
   element is gone (Sir: "if we can't get accurate traffic numbers,
   lets just delete this part"). See _market_sessions.html for the
   note. The label-row CSS below the curve is unchanged. */

/* ==========================================================================
   PAGE TITLE — cockpit dialect
   ========================================================================== */

.page-title--cockpit {
  font-family: var(--font-sans);
  font-size: var(--text-lg);            /* 20px */
  font-weight: var(--weight-semibold);
  color: var(--dim);
  letter-spacing: 0.01em;
  margin: 0 0 var(--space-4);
}

/* ==========================================================================
   RESPONSIVE — narrow viewports
   ========================================================================== */

@media (max-width: 768px) {
  .cockpit {
    padding-inline: var(--space-3);    /* 16px on narrow */
    gap: var(--space-4);
  }

  .idea-card {
    padding: var(--space-3);           /* 16px on narrow */
    gap: var(--space-2);
  }

  .idea-card--best-shot {
    padding-left: calc(var(--space-3) - 3px);
  }

  .idea-card__levels {
    gap: var(--space-2);
  }

  .cockpit-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .idea-controls {
    flex-direction: column;
    align-items: flex-start;
  }
}

@media (max-width: 480px) {
  .idea-card__footer {
    flex-direction: column;
    align-items: flex-start;
  }
}

/*
 * Lodestone — layouts/auth.css
 *
 * Phase E — auth + forms layout vocabulary.
 *
 * Covers:
 *   .auth-page        — full-viewport centering shell for login / signup.
 *   .auth-card        — the single card container (480px max, hero-card padding).
 *   .auth-card__brand — wordmark anchor at the top of the card.
 *   .auth-card__title — page-level heading (Sign in / Create account).
 *   .auth-card__subtitle — tagline line below the title.
 *   .auth-input       — semantic class for all auth-form inputs. Fixes Phase D
 *                        regression #1: inline style= attributes dropped the
 *                        ::placeholder and :focus pseudo-class rules. These
 *                        MUST live in a real selector, not an inline style.
 *   .auth-submit      — primary submit button inside auth forms.
 *   .form-card        — shared card chrome (signup, invites mint, prefs sections).
 *   .form-card--anchored — centred brand-header variant.
 *   .form-card__brand — brand anchor inside form-card.
 *   .form-card__subtitle — subtitle inside form-card.
 *   .form-error       — error banner (shared by login, signup, preferences).
 *   .prefs-page       — preferences page wrapper.
 *   .prefs-card       — card wrapper for each preferences section.
 *   .prefs-card__title — section heading inside a prefs card.
 *   .prefs-save-row   — bottom row with the save button + feedback.
 *   .prefs-tz-note    — timezone informational note (device-auto).
 *
 * Register: Linear / Stripe / Mercury — generous radius, real shadow,
 * Plex Sans for body, indigo on CTAs only.
 *
 * Cross-chunk coordination: append
 *   @import "layouts/auth.css";
 * to static/css/style.css (the PostCSS source) after the existing
 * component @imports. The apply step owns this.
 */

/* ==========================================================================
   AUTH PAGE SHELL
   Full-viewport centring — login and signup both use this wrapper.
   Plex Sans body here overrides any mono leak from base.css.
   ========================================================================== */

.auth-page {
  /* 100vh minus the header band so the card sits visually centred in
     the remaining space, not pinned to the top with a sea of empty
     canvas below it. */
  min-height: calc(100vh - 6rem);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
  font-family: var(--font-sans);
}

/* ==========================================================================
   AUTH CARD
   Single card, 480px max. Hero-card padding (--space-6).
   Same card anatomy as RESET-2026-05-25 §Card anatomy:
     background --surface-2 (raised), border-card, radius-lg, shadow-elevated.
   Narrower than the desktop hero (480px) so the form reads intimate, not
   stretched — matches Mercury's account-create register.
   ========================================================================== */

.auth-card {
  width: 100%;
  max-width: 420px;                 /* tightened from 480 — feels intimate, not stretched */
  background: var(--surface-1);     /* stronger contrast vs canvas than surface-2 */
  border: 1px solid var(--border-strong);  /* stronger border so the card has real edges */
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
  padding: var(--space-4);                  /* 32px — sensible card pad, not hero-sized */
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Brand anchor at the top of the auth card — compass-rose + wordmark.
   Centred for the marketing-card register. */

.auth-card__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

/* Title + subtitle row */

.auth-card__header {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.auth-card__title {
  font-family: var(--font-sans);
  font-size: var(--text-2xl);        /* 24px — appropriate for a login card, not hero */
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0;
}

.auth-card__subtitle {
  font-family: var(--font-sans);
  font-size: var(--text-sm);         /* 14px */
  color: var(--dim);
  margin: 0;
  letter-spacing: 0.01em;
}

/* Honest-silence framing for friends arriving via DM. Same dim mono
   register as the cockpit's preamble status line so the apex-to-cockpit
   voice is continuous across the auth gate. Creative review 2026-05-27,
   Devi R3 (invite-copy gate per CONTRIBUTING.md). */

.auth-card__honesty {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  margin: var(--space-3) 0 0;
  line-height: var(--leading-base);
}

/* ==========================================================================
   AUTH FORM
   Flex-column with generous gaps between field rows.
   ========================================================================== */

.auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);               /* 16px between field rows */
}

/* Field row: label + input stacked */

.auth-field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.auth-field label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);         /* 12px */
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
}

/* Field hint — appears below an input */

.auth-field__hint {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
  line-height: var(--leading-snug);
}

/* ==========================================================================
   AUTH INPUT — Phase D regression #1 fix.
   Strips all inline style= attributes from login.html inputs.
   ::placeholder and :focus cannot live in inline style — they need real
   CSS selectors. This class provides them.
   ========================================================================== */

.auth-input {
  width: 100%;
  padding: 0.625rem 0.875rem;        /* Slightly more generous than Phase D's 0.5/0.75 */
  font-size: var(--text-sm);         /* 14px */
  font-family: var(--font-sans);     /* SANS — not mono. Body register. */
  /* Input sits darker than the card so it reads as recessed/inset, not
     flush with the card surface. Was --surface-1 which matched the new
     card bg → invisible inputs. */
  background: var(--canvas);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--fg);
  line-height: var(--leading-base);
  transition: border-color var(--dur-tick) var(--ease-glow),
              box-shadow var(--dur-tick) var(--ease-glow);
  /* Prevent iOS zoom on focus (14px equivalent on screen) */
  -webkit-text-size-adjust: none;
}

/* Placeholder: dim, not body colour. Regression #1 fix. */

.auth-input::placeholder {
  color: var(--off);
}

/* Mouse-click focus: indigo border. Regression #1 fix.
   :focus-visible carries the a11y ring globally from base.css — do not
   override it here. This rule is additive, not a replacement. */

.auth-input:focus {
  border-color: var(--ink);
  outline: none;
  box-shadow: 0 0 0 3px rgb(var(--ink-rgb) / 0.12);
}

/* Hover state: step toward the active border so the input feels responsive */

.auth-input:hover:not(:focus) {
  border-color: var(--border-strong);
}

/* ==========================================================================
   AUTH SUBMIT BUTTON
   Full-width, indigo, SANS. Matches the btn--primary pattern but scoped
   here so auth pages don't depend on buttons.css loading order.
   ========================================================================== */

.auth-submit {
  width: 100%;
  padding: 0.75rem 1rem;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  border-radius: var(--radius-md);
  color: var(--canvas);
  background: var(--ink);
  border: none;
  cursor: pointer;
  transition: background-color var(--dur-tick) var(--ease-glow),
              transform var(--dur-tick) var(--ease-card);
  letter-spacing: 0.01em;
  margin-top: var(--space-2);        /* Small gap above the button */
}

.auth-submit:hover {
  background: var(--ink-hover);
}

.auth-submit:active {
  background: var(--ink-strong);
  transform: translateY(1px);
}

.auth-submit:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* ==========================================================================
   FORM ERROR — shared by login, signup, preferences.
   Restores the regression-safe pattern from forms.css but with RESET tokens.
   ========================================================================== */

.form-error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--bad);
  background: rgb(var(--bad-rgb) / 0.08);
  border: 1px solid rgb(var(--bad-rgb) / 0.25);
  border-left: 2px solid var(--bad);
  border-radius: var(--radius-sm);
  line-height: var(--leading-snug);
}

/* ==========================================================================
   FORM CARD — shared chrome for signup, invites mint box, prefs sections.
   Legacy .form-card is in forms.css; these rules bring it into the RESET
   token set (radius-lg, shadow-card, surface-2, border-card).
   ========================================================================== */

.form-card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);           /* 24px — card min per RESET */
  max-width: 480px;
  margin: 0 auto;
}

/* Centred brand-header variant (signup) */

.form-card--anchored {
  text-align: center;
}

.form-card--anchored form,
.form-card--anchored .form-error {
  text-align: left;
}

.form-card__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  margin-bottom: var(--space-3);
}

.form-card__subtitle {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  letter-spacing: 0.04em;
  margin: 0 0 var(--space-3) 0;
}

/* ==========================================================================
   PREFERENCES PAGE
   Full-page wrapper + card-per-section pattern.
   ========================================================================== */

.prefs-page {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--space-5) var(--space-4);
  font-family: var(--font-sans);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);              /* 32px between sections */
}

.prefs-page__header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.prefs-page__title {
  font-family: var(--font-sans);
  font-size: var(--text-lg);        /* 20px — section-heading tier */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
}

.prefs-page__description {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  line-height: var(--leading-base);
  margin: 0;
}

/* Success / save confirmation */

.prefs-saved {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--live);
  background: rgb(var(--live-rgb) / 0.06);
  border: 1px solid rgb(var(--live-rgb) / 0.18);
  border-radius: var(--radius-sm);
}

/* Card for each prefs section */

.prefs-card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.prefs-card__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);       /* 15px */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0 0 var(--space-1) 0;
}

.prefs-card__note {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  line-height: var(--leading-base);
  margin: 0;
}

/* Field group inside prefs card — same as .auth-field but named separately
   so it can diverge if needed */

.prefs-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.prefs-field label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--dim);
}

.prefs-field input {
  padding: 0.625rem 0.875rem;
  font-size: var(--text-sm);
  font-family: var(--font-sans);
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--fg);
  transition: border-color var(--dur-tick) var(--ease-glow),
              box-shadow var(--dur-tick) var(--ease-glow);
  width: 100%;
}

.prefs-field input::placeholder {
  color: var(--off);
}

.prefs-field input:focus {
  border-color: var(--ink);
  outline: none;
  box-shadow: 0 0 0 3px rgb(var(--ink-rgb) / 0.12);
}

.prefs-field__hint {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
  line-height: var(--leading-snug);
}

/* Timezone auto-note */

.prefs-tz-note {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
}

/* Save row */

.prefs-save-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

/* ==========================================================================
   INVITES PAGE
   Page-level layout + DM-copy card chrome.
   ========================================================================== */

.invites-page {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--space-5) var(--space-4);
  font-family: var(--font-sans);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.invites-page__header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.invites-page__title {
  font-family: var(--font-sans);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
}

/* Quota row */

.invite-quota {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
}

.invite-quota__count {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

/* DM copy card */

.invite-dm-card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.invite-dm-card__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
}

.invite-dm-card__hint {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: 0;
  line-height: var(--leading-base);
}

/* The DM text block itself */

.invite-dm-text {
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--fg);
  line-height: var(--leading-base);
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  user-select: all;                  /* Easy drag-select on click */
}

/* Copy DM text button row */

.invite-dm-card__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Invite list card */

.invites-list-card {
  background: var(--surface-1);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;                  /* table border-radius clip */
}

.invites-list-card__header {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--fg);
}

.invites-empty {
  padding: var(--space-5) var(--space-4);
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
}

/* ==========================================================================
   RESPONSIVE — auth card fills at narrow viewport.
   ========================================================================== */

@media (max-width: 540px) {
  .auth-card {
    border-radius: var(--radius-md);
    padding: var(--space-4);
  }

  .auth-card__title {
    font-size: var(--text-rationale);  /* Drop from hero to 24px on small screens */
  }

  .auth-page {
    padding: 3rem 1rem 2rem;
    align-items: flex-start;
  }

  .prefs-page,
  .invites-page {
    padding: var(--space-4) var(--space-3);
  }
}

/*
 * analytics.css — performance dashboard for closed placed-trades.
 * 2026-05-26.
 *
 * No chart library — uses inline SVG for the equity curve.
 * Same card chrome grammar as the rest of the post-Phase-E surface.
 */

.analytics-page {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ── Header ─────────────────────────────────────────────────────────── */

.analytics-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.analytics-title {
  font-family: var(--font-sans);
  font-size: var(--text-hero);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
  line-height: var(--leading-tight);
  color: var(--fg);
  margin: 0;
}

.analytics-subtitle {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: 0;
}

.analytics-warning {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--warn);
  background: rgb(var(--warn-rgb) / 0.06);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin: 0;
}

/* ── Empty state ────────────────────────────────────────────────────── */

.analytics-empty {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.analytics-empty__title {
  font-family: var(--font-sans);
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
}

.analytics-empty__detail {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--dim);
  margin: 0;
  max-width: 56ch;
  align-self: center;
  line-height: 1.6;
}

.analytics-empty__link {
  color: var(--ink);
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.analytics-empty__link:hover { text-decoration: underline; }

/* ── Headline strip ─────────────────────────────────────────────────── */

.analytics-headline {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}

@media (max-width: 720px) {
  .analytics-headline { grid-template-columns: 1fr; }
}

.analytics-stat {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.analytics-stat__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 2rem;             /* 32px — second tier display */
  font-weight: var(--weight-semibold);
  color: var(--fg);
  letter-spacing: -0.02em;
}

.analytics-stat__value--good { color: var(--live); }

.analytics-stat__value--bad  { color: var(--bad); }

.analytics-stat__label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.analytics-stat__caveat {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
}

/* ── Equity curve ───────────────────────────────────────────────────── */

.analytics-equity {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.analytics-equity__svg {
  width: 100%;
  height: 120px;
  display: block;
}

.analytics-equity__caption {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
}

/* ── Breakdown tables ───────────────────────────────────────────────── */

.analytics-breakdowns {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}

@media (max-width: 900px) {
  .analytics-breakdowns { grid-template-columns: 1fr; }
}

.analytics-table-card {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.analytics-section-title {
  font-family: var(--font-sans);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0;
  letter-spacing: -0.01em;
}

.analytics-section-hint {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
  line-height: 1.5;
}

.analytics-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.analytics-table th {
  text-align: left;
  font-weight: var(--weight-semibold);
  color: var(--dim);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border-strong);
  padding: 0.375rem 0.5rem;
}

.analytics-table td {
  border-bottom: 1px solid var(--border);
  padding: 0.4rem 0.5rem;
  color: var(--fg);
}

.analytics-table tr:last-child td { border-bottom: none; }

.analytics-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--fg);
}

.analytics-r--good { color: var(--live); }

.analytics-r--bad  { color: var(--bad); }

/* ── Log section ────────────────────────────────────────────────────── */

.analytics-log {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.analytics-rec-link {
  color: var(--ink);
  text-decoration: none;
}

.analytics-rec-link:hover { text-decoration: underline; }

/* Phase G (2026-05-27) — marketing apex extracted to first-class layout. */

/*
 * Lodestone — layouts/marketing.css
 *
 * Phase G (2026-05-27) — marketing apex, extracted from the inline \3c style>
 * block that lived in templates/marketing.html. That block was ~500 lines
 * of inline CSS referencing tokens that didn't exist (--space-7, --surface-1,
 * --border-card, --shadow-card, --shadow-elevated), causing a 660px void
 * above the hero and an invisible wordmark. This file is the structural fix.
 *
 * Design direction: SYNTHESIS.md 2026-05-27 — three-persona review.
 * Composition: single column, hairline-separated sections, NO card chrome.
 * Register: engine-narrating-itself — same operator prose as the cockpit.
 * Ticker: DELETED (decoration pretending to be life, violates v5 contract).
 *
 * Token contract: all custom properties are declared in tokens.css :root.
 * Any property here that resolves to undefined will fail stylelint CI.
 * Do not add inline token declarations to the template.
 *
 * Wired into static/css/style.css via @import 'layouts/marketing.css';
 * alongside cockpit.css / auth.css / analytics.css.
 */

/* ==========================================================================
   PAGE SHELL
   ========================================================================== */

.mkt-body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--canvas);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ==========================================================================
   NAVIGATION — wordmark left, sign-in right.
   ========================================================================== */

.mkt-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--space-4) var(--space-5) 0;
  width: 100%;
}

.mkt-nav__signin {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--ink);
  text-decoration: none;
  font-weight: var(--weight-medium);
  transition: color var(--dur-tick) ease;
}

.mkt-nav__signin:hover { color: var(--ink-hover); }

.mkt-nav__signin:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* ==========================================================================
   HERO WASH — absolute-positioned backdrop. z-index 0.
   The wash must be position:absolute so it paints behind the content
   column without taking space in normal flow. The fix for the 660px void:
   the old .mkt-main > * selector caught the wash and made it
   position:relative in flow. The new selector targets only .mkt-column.
   ========================================================================== */

.hero-wash {
  position: absolute;
  inset: 0 0 auto 0;
  height: 70vh;
  background:
    radial-gradient(60% 50% at 50% 10%, rgb(var(--ink-rgb) / 0.07), transparent 70%),
    radial-gradient(30% 25% at 85% 5%,  rgb(var(--ink-rgb) / 0.04), transparent 70%);
  pointer-events: none;
  z-index: 0;
}

/* ==========================================================================
   MAIN COLUMN — the editorial spine.
   position:relative is needed so hero-wash (position:absolute) is contained
   within this stacking context. Only .mkt-column children get z-index:1 —
   the wash itself is excluded.
   ========================================================================== */

.mkt-main {
  flex: 1;
  width: 100%;
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--space-7) var(--space-5) var(--space-7);
  position: relative;   /* stacking context for .hero-wash */
}

/* Lift only the editorial column above the wash — NOT .hero-wash itself.
   This is the structural fix for the z-index/flow collision.
   Old: .mkt-main > *   ← caught .hero-wash → 660px void.
   New: .mkt-main > .mkt-column  ← wash excluded, stays in position:absolute. */

.mkt-main > .mkt-column {
  position: relative;
  z-index: 1;
}

/* ==========================================================================
   EDITORIAL COLUMN — single-column, hairline-separated sections.
   No card chrome. Sections are separated by border-top (hairline).
   ========================================================================== */

.mkt-column {
  display: flex;
  flex-direction: column;
}

/* All sections except the first hero get a hairline top border. */

.mkt-section {
  padding-top: var(--space-5);
  padding-bottom: var(--space-5);
  border-top: 1px solid var(--border);
}

/* Hero section — no top border; gets additional breathing room below. */

.mkt-section--hero {
  border-top: none;
  padding-top: 0;
  padding-bottom: var(--space-6);
}

/* ==========================================================================
   REVEAL-ON-SCROLL — IntersectionObserver hook in marketing.js.
   ========================================================================== */

.reveal {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 500ms var(--ease-arrival), transform 500ms var(--ease-arrival);
  will-change: opacity, transform;
}

.reveal[data-revealed="true"] {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   HERO TYPOGRAPHY — above the fold.
   ========================================================================== */

.mkt-hero__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--dim);
  margin: 0 0 var(--space-3);
}

.mkt-hero__h1 {
  font-family: var(--font-sans);
  font-size: var(--text-hero);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: -0.02em;
  color: var(--fg);
  margin: 0 0 var(--space-3);
  max-width: 18ch;
}

.mkt-hero__positioning {
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-snug);
  color: var(--dim);
  margin: 0;
  max-width: 44ch;
}

/* ==========================================================================
   SILENCE PULL-QUOTE — honest silence as content.
   Sits below the hero positioning, above the fold on a standard desktop.
   ========================================================================== */

.mkt-silence {
  padding-top: var(--space-4);
}

.mkt-silence__quote {
  font-family: var(--font-sans);
  font-size: var(--text-rationale);
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0 0 var(--space-3);
  max-width: 30ch;
}

.mkt-silence__sub {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-base);
  color: var(--dim);
  margin: 0;
  max-width: 52ch;
}

/* ==========================================================================
   SECTION HEADING + BODY — shared typography for below-fold sections.
   ========================================================================== */

.mkt-section__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--dim);
  margin: 0 0 var(--space-2);
}

.mkt-section__heading {
  font-family: var(--font-sans);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--fg);
  margin: 0 0 var(--space-3);
  line-height: var(--leading-snug);
}

.mkt-section__body {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-base);
  color: var(--dim);
  margin: 0 0 var(--space-3);
  max-width: 60ch;
}

.mkt-section__body:last-child { margin-bottom: 0; }

/* ==========================================================================
   FEATURE LIST — "What it doesn't do" / "What it does".
   Plain prose lines with a small geometric pip. No bullets.
   ========================================================================== */

.mkt-feature-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.mkt-feature-list li {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: var(--leading-base);
  color: var(--dim);
  padding-left: var(--space-4);
  position: relative;
}

.mkt-feature-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  opacity: 0.55;
}

/* "What it doesn't do" — muted pips */

.mkt-feature-list--doesnt li::before { background: var(--off); }

/* "What it does" — brand pips */

.mkt-feature-list--does li::before { background: var(--ink); }

/* ==========================================================================
   SOURCES GRID — six streams, 2-column.
   ========================================================================== */

.mkt-sources {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}

.mkt-source-item {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
}

.mkt-source-pip {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--live);
  margin-top: 0.45em;
  animation: source-breathe var(--pulse-dur, 2400ms) ease-in-out infinite;
}

.mkt-source-pip--slow  { --pulse-dur: 8000ms; }

.mkt-source-pip--vslow { --pulse-dur: 15000ms; }

@keyframes source-breathe {
  0%   { opacity: 1;    box-shadow: 0 0 0 0   rgb(var(--live-rgb) / 0.5); }
  60%  { opacity: 0.5;  box-shadow: 0 0 0 5px rgb(var(--live-rgb) / 0); }
  100% { opacity: 1;    box-shadow: 0 0 0 0   rgb(var(--live-rgb) / 0); }
}

.mkt-source-body { min-width: 0; }

.mkt-source-name {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--fg);
  display: block;
  margin-bottom: 2px;
}

.mkt-source-cadence {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  margin-left: 0.4em;
}

.mkt-source-desc {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  line-height: var(--leading-base);
  color: var(--dim);
  margin: 0;
}

/* ==========================================================================
   COCKPIT DEMO — three freshness states.
   Mirrors the cockpit's row register. NOT card-chrome style.
   ========================================================================== */

.mkt-demo-feed {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.mkt-demo-rec {
  display: grid;
  grid-template-columns: 6px 1fr auto;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border-radius: var(--radius-md);
  border-left: 2px solid var(--border);
}

.mkt-demo-rec__pip {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid currentColor;
  flex-shrink: 0;
}

.mkt-demo-rec__body { min-width: 0; }

.mkt-demo-rec__pair {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--fg);
}

.mkt-demo-rec__dir {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
  margin-left: var(--space-2);
}

.mkt-demo-rec__rationale {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--dim);
  margin: 2px 0 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mkt-demo-rec__state {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--dim);
  text-align: right;
  white-space: nowrap;
}

/* ACTIVE state */

.mkt-demo-rec--live {
  border-left: 2px solid var(--live);
}

.mkt-demo-rec--live .mkt-demo-rec__pip {
  background: var(--live);
  border-color: var(--live);
  box-shadow: 0 0 0 0 rgb(var(--live-rgb) / 0.5);
  animation: demo-pip-live 2600ms ease-in-out infinite;
}

.mkt-demo-rec--live .mkt-demo-rec__state { color: var(--live); }

@keyframes demo-pip-live {
  0%   { box-shadow: 0 0 0 0   rgb(var(--live-rgb) / 0.5); }
  60%  { box-shadow: 0 0 0 5px rgb(var(--live-rgb) / 0); }
  100% { box-shadow: 0 0 0 0   rgb(var(--live-rgb) / 0); }
}

/* DECAYING state */

.mkt-demo-rec--decay {
  border-left: 2px solid var(--warn);
}

.mkt-demo-rec--decay .mkt-demo-rec__pip {
  background: var(--warn);
  border-color: var(--warn);
}

.mkt-demo-rec--decay .mkt-demo-rec__state { color: var(--warn); }

/* INVALIDATED state */

.mkt-demo-rec--inv {
  opacity: 0.4;
}

.mkt-demo-rec--inv .mkt-demo-rec__pair,
.mkt-demo-rec--inv .mkt-demo-rec__rationale {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.mkt-demo-rec--inv .mkt-demo-rec__pip {
  background: transparent;
  border-color: var(--off);
}

/* ==========================================================================
   DEMO LEVELS — entry / SL / TP row under the rec.
   ========================================================================== */

.mkt-demo-levels {
  display: flex;
  gap: var(--space-4);
  margin-top: var(--space-2);
  padding-left: calc(6px + var(--space-3)); /* align with rec body column */
}

.mkt-demo-level {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.mkt-demo-level__label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  text-transform: lowercase;
}

.mkt-demo-level__value {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}

.mkt-demo-level__value--sl { color: var(--bad); }

.mkt-demo-level__value--tp { color: var(--live); }

/* ==========================================================================
   CTA — invite line + sign-in link.
   ========================================================================== */

.mkt-cta__heading {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-regular);
  color: var(--fg);
  margin: 0 0 var(--space-2);
}

.mkt-cta__aside {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--off);
  margin: var(--space-2) 0 0;
}

.mkt-cta__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  transition: color var(--dur-tick) ease;
}

.mkt-cta__link:hover { color: var(--ink-hover); }

.mkt-cta__link:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* ==========================================================================
   FOOTER
   ========================================================================== */

.mkt-footer {
  border-top: 1px solid var(--border);
}

.mkt-footer__inner {
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--space-4) var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.mkt-footer__copy {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
  margin: 0;
}

.mkt-footer__domain {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--off);
}

/* ==========================================================================
   RESPONSIVE — single breakpoint at 720px.
   ========================================================================== */

@media (max-width: 720px) {
  .mkt-nav {
    padding-top: var(--space-3);
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  .mkt-main {
    padding-top: var(--space-5);
    padding-bottom: var(--space-5);
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  .mkt-hero__h1 { font-size: var(--text-rationale); }
  .mkt-silence__quote { font-size: var(--text-lg); }
  .mkt-sources { grid-template-columns: 1fr; }
  .mkt-footer__inner { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
  .mkt-demo-levels { flex-wrap: wrap; }
}

/* ==========================================================================
   REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* layouts/rec-page.css + components/rec-card.css are linked directly
   from trade_ticket.html (per-page rather than bundle) so they don't
   appear in this @import list. */

/*
 * Lodestone — style-legacy.css
 *
 * Layout + motion vocabulary from SPEC §4.
 * CVD-tuned per §22.2.
 *
 * §22.8 note: light-mode colour-scheme is a non-goal per spec. Only dark mode
 * is supported. No @media (prefers-color-scheme: light) overrides are present.
 *
 * Rebrand A3: this file is imported by static/css/style.css as the
 * final entry in the chain (tokens → fonts → base → layout → legacy).
 *
 * Rebrand B1 (2026-05-25): the legacy :root token block that used to live
 * here was deleted in its entirety. Every legacy token had a 1:1 match in
 * Marcus's static/css/tokens.css; the legacy block had been shadowing
 * Marcus's values through source order. Removing it lets Marcus's tokens
 * govern.
 *
 * The legacy @media (prefers-reduced-motion) :root override was also removed
 * (duplicates tokens.css's). The `.brand-mark__bracket--left.is-pulsing`
 * reduced-motion selector rule was preserved and lives in the merged block
 * below.
 *
 * Tokens this file still consumes via var(--…) that come from tokens.css:
 *   surfaces: --canvas, --surface, --surface-2, --border, --bg
 *   ink:      --fg, --dim, --off, --ink, --ink-rgb, --ink-low
 *   status:   --live, --warn, --bad, --live-rgb, --warn-rgb, --bad-rgb
 *   halo:     --live-halo-rest-inset, --live-halo-rest-glow
 *   hover:    --row-hover-tint
 *   focus:    --focus-ring-width, --focus-ring-offset
 *   fonts:    --font-mono, --font-sans, --font-feat-mono, --font-feat-sans
 *   type:     --text-xs, --text-sm, --text-base, --text-lg, --text-hero
 *   space:    --space-1..5, --gap, --gap-sm, --gap-xs, --gap-lg, --gap-xl
 *   motion:   --dur-tick, --dur-row, --dur-terminal, --dur-pulse,
 *             --ease-arrival, --ease-terminal, --ease-glow, --stagger-row
 *
 * Tokens Marcus added that this file does NOT yet consume (Phase C+ will):
 *   --hairline, --off-2, --ink-hover, --ink-strong, --amber, --amber-rgb,
 *   --amber-low, --focus-ring-color, --row-active-tint, --leading-*,
 *   --weight-*, --text-md, --text-rationale, --radius-sm, --radius-md.
 *
 * Known live-but-unresolved reference: `var(--radius)` is used at ~10 sites
 * (e.g. .btn line ~1380). The legacy --radius token was deleted by an
 * earlier sweep ("DELETED" note in the prior :root block); call-sites
 * currently render with border-radius: 0 as a result. Phase C/E will
 * rewrite these to var(--radius-sm) / var(--radius-md). Out of B1 scope —
 * this is a no-op for B1 (value stays unresolved either way).
 *
 * Rebrand B2 (2026-05-25): audit pass for legacy indigo (#6366F1) and
 * legacy Klein blue (#1B2BBB) hex literals across templates and live
 * static assets. Result: zero raw-hex references to either value outside
 * Marcus's documentation prose in tokens.css. All ink-rendering surfaces
 * resolve through var(--ink) which tokens.css now sets to #7C7CFF (the
 * louder rebrand indigo). The favicon SVG set (already shipped at A5)
 * uses #7C7CFF directly. The marketing.html inline diagram references
 * var(--color-brand) — Tailwind's theme token — and will be swept in
 * Phase D2 alongside the rest of the Tailwind rip-out; out of B2 scope.
 *
 * Contrast spot-check (B2): --ink #7C7CFF on --canvas #100D0A reads 6.9:1
 * per tokens.css contrast audit — AAA large / AA small. Holds.
 */

/* C3: .bk atom + .brand-mark + their reduced-motion carve-outs moved to
   components/bk.css and components/brand-mark.css. */

/* C1: Reset + base + page-title + skip-link moved to base.css + skip-link.css. */

/* C2: header/main/footer landmarks moved to layout.css. */

/* ==========================================================================
   §17 — system status strip.
   ========================================================================== */

[role="status"][aria-label="System status"] {
  display: flex;
  gap: var(--gap);
  align-items: center;
  flex-wrap: wrap;
  padding: var(--gap-xs) var(--gap);
  background-color: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: 0.8rem;
  font-family: var(--font-mono);
}

.status-component {
  display: flex;
  align-items: center;
  gap: var(--gap-xs);
  color: var(--dim);
}

.status-pip {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  transition: background-color var(--dur-tick) var(--ease-default);
}

.status-pip--ok,
.status-pip--live  { background-color: var(--live); }

.status-pip--warn  { background-color: var(--warn); }

.status-pip--bad   { background-color: var(--bad); }

.status-pip--off   { background-color: var(--off); }

.status-label {
  color: var(--dim);
}

.status-age {
  font-size: 0.75em;
}

/* C7: .exposure-line moved to components/preamble.css. */

/* C4: .mode-toggle + .btn-mode moved to components/buttons.css. */

/* ==========================================================================
   §18.1 — recommendation rows.
   ========================================================================== */

[role="region"][aria-label="Recommendations"] {
  margin-top: var(--gap);
}

.rec-list {
  list-style: none;
}

/* §18.1.1 — per-row staleness pip + refresh countdown */

.rec-row {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: var(--gap-sm);
  padding: var(--gap-sm) var(--gap);
  cursor: pointer;
  transition:
    border-color var(--dur-row) var(--ease-default),
    opacity var(--dur-terminal) var(--ease-default);
  position: relative;
}

.rec-row:hover,
.rec-row:focus-within {
  border-color: var(--dim);
}

/* Keyboard focus picks up the global :focus-visible outline.
   Removed explicit .rec-row:focus rule that fired on mouse click too. */

/* §18.1 two-line row format */

.rec-thesis {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  flex-wrap: wrap;
}

.rec-structural {
  font-size: 0.8rem;
  color: var(--dim);
  margin-top: var(--gap-xs);
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  flex-wrap: wrap;
}

/* Status glyphs → accessible names via aria-label (§22.5) */

.rec-status-glyph {
  font-size: 1rem;
  flex-shrink: 0;
}

/* C8: .status-ACTIVE/PENDING/DECAYING/EXPIRED/INVALIDATED moved to
   components/bracket-pills.css. */

/* §18.1.1 staleness pip */

.rec-stale-pip {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  transition: background-color var(--dur-tick) var(--ease-default);
}

/* Freshness state pip — paints --live (lime), not brand --accent.
   Same collision class as .status-ACTIVE / .text-ok / .rec-dir-bracket--LONG:
   the brand cyan must never indicate "fresh data" or "ok state". */

.rec-stale-pip--fresh   { background-color: var(--live); }

.rec-stale-pip--stale   { background-color: var(--warn); }

.rec-stale-pip--expired { background-color: var(--bad); }

/* §18.1.7 intel drill-down — hidden by default, toggled by JS */

.rec-detail {
  display: none;
  margin-top: var(--gap-sm);
  padding-top: var(--gap-sm);
  border-top: 1px solid var(--border);
  font-size: 0.8rem;
  color: var(--dim);
}

.rec-row[aria-expanded="true"] .rec-detail {
  display: block;
}

/* §18.1.2 row-state transition keyframes */

@keyframes row-enter {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes row-invalidate {
  0%   { opacity: 1; }
  30%  { background-color: rgb(var(--bad-rgb) / 0.12); }
  100% { opacity: 0.45; }
}

@keyframes row-expire {
  0%   { opacity: 1; }
  100% { opacity: 0.35; }
}

@keyframes row-live {
  /* Rev 19 UX-stage — was rgba(31,184,122,*), the pre-Rev-9 --ok colour.
     Now uses --ok-rgb so the brand-green flash matches the actual palette
     rather than ghost-emitting the old token. */
  0%   { background-color: rgb(var(--live-rgb) / 0); }
  25%  { background-color: rgb(var(--live-rgb) / 0.15); }
  100% { background-color: rgb(var(--live-rgb) / 0); }
}

/* State-transition CSS classes applied by web.js on htmx:afterSwap.
   Rev 19 UX-stage — purposeful easing tokens (Sonnet R2 motion cohesion).
   Arrivals get --ease-arrival; terminal departures get --ease-terminal. */

.rec-row--entering {
  animation: row-enter var(--dur-row) var(--ease-arrival) both;
}

/* Batch-arrival stagger — when ≥2 rows enter on the same swap, space the
   animation starts by --stagger-row so the eye reads "system reacting to
   events", not "table reload". CSS-only via :nth-child; up to 6 rows in
   one batch (beyond that the stagger flattens — diminishing returns). */

.rec-row--entering:nth-child(2) { animation-delay: calc(var(--stagger-row) * 1); }

.rec-row--entering:nth-child(3) { animation-delay: calc(var(--stagger-row) * 2); }

.rec-row--entering:nth-child(4) { animation-delay: calc(var(--stagger-row) * 3); }

.rec-row--entering:nth-child(5) { animation-delay: calc(var(--stagger-row) * 4); }

.rec-row--entering:nth-child(6) { animation-delay: calc(var(--stagger-row) * 5); }

.rec-row--live-flash {
  animation: row-live var(--dur-row) var(--ease-default) both;
}

/* Terminal row states */

.rec-row[data-curr-status="INVALIDATED"] {
  animation: row-invalidate var(--dur-terminal) var(--ease-terminal) both;
  border-color: rgb(var(--bad-rgb) / 0.4);
}

.rec-row[data-curr-status="EXPIRED"] {
  animation: row-expire var(--dur-terminal) var(--ease-terminal) both;
  opacity: 0.45;
}

/* C8: .rec-dir-bracket family moved to components/bracket-pills.css. */

/* Pair label on L1 — mono bold, the eye-anchor next to the direction. */

.rec-pair {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* The rationale on L1 — must read as the focal point, not as a footnote.
   Rev 19 Opus R2: L1 is thesis-first; the rationale gets full --fg weight. */

.rec-rationale {
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  flex: 1;
  min-width: 0;
}

.rec-rationale--historical {
  color: var(--dim);
  font-style: italic;
  font-size: 0.85rem;
}

/* C8: .rec-meta-* atoms (sep / pips / conf / trigger / asymmetry / vol)
   moved to components/bracket-pills.css. */

/* Kill-scenarios block on the trade ticket. Surfaced inside the
   [ why this idea ] section under the LLM reasoning paragraph. */

.ticket-kill-scenarios {
  margin-top: 0.85rem;
}

.ticket-kill-scenarios__title {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  margin: 0 0 0.35rem 0;
  font-weight: 600;
}

.ticket-kill-scenarios__list {
  list-style: none;
  padding-left: 0;
  margin: 0;
}

.ticket-kill-scenarios__item {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  margin: 0.15rem 0;
  line-height: 1.4;
}

/* C6: orphan .pair-tile__tier-chip + __tier-line + __risk-flags moved to
   components/pair-tile.css. */

/* C8: .rec-meta-risk-flag moved to components/bracket-pills.css. */

/* R7 signal-gate refactor (2026-05-25) — trade ticket [ risk flags ]
   block under [ why this idea ]. Mirrors the kill-scenarios block in
   structure + spacing so the two sections feel like the same surface
   (machine-verifiable quant flags + LLM narrative kill scenarios)
   rather than fighting each other for layout authority. */

.ticket-risk-flags {
  margin-top: 0.85rem;
}

.ticket-risk-flags__title {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  margin: 0 0 0.35rem 0;
  font-weight: 600;
}

.ticket-risk-flags__list {
  list-style: none;
  padding-left: 0;
  margin: 0;
}

.ticket-risk-flags__item {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  margin: 0.15rem 0;
  line-height: 1.4;
}

.ticket-risk-flags__item--material      { color: var(--warn); }

.ticket-risk-flags__item--informational { color: var(--dim); }

.ticket-risk-flags__detail {
  font-size: 0.78rem;
  margin-left: 0.25rem;
}

.ticket-context-meta {
  margin: 0.25rem 0 0.5rem 0;
  font-size: 0.85rem;
}

/* C8: .rec-stale-counter / .rec-confidence / .rec-event-countdown /
   .refresh-countdown moved to components/bracket-pills.css. */

/* C7: preamble + since-last-open + calendar-peek moved to
   components/preamble.css + components/market-status.css +
   components/calendar-peek.css. */

/* C7: activity-canvas + market-status-banner moved to
   components/activity-canvas.css + components/market-status.css. */

/* ==========================================================================
   §18.1 historical rec rows (R4 ship-now, 2026-05-25).

   .rec-row--historical: dimmed visual register. The status glyphs and
   direction brackets reuse the existing colour palette (status-EXPIRED /
   status-INVALIDATED / rec-dir-bracket). The row itself dims to distinguish
   historicals from live recs without hiding them.

   .rec-stale-label: small mono label inline with the pair — EXPIRED /
   INVALIDATED in the terminal-state colour. Communicates state without
   needing a separate column.
   ========================================================================== */

/* Historical row — overall dim. Does not override the glyph colours. */

.rec-row--historical {
  opacity: 0.72;
}

.rec-row--historical:hover,
.rec-row--historical:focus {
  opacity: 0.92;
}

/* STALE label — monospace, small, colour matches status. */

.rec-stale-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.05em 0.3em;
  border: 1px solid var(--border);
  color: var(--off);
}

/* INVALIDATED gets the --bad (red) colour to distinguish from EXPIRED. */

.rec-row--historical[data-state="invalidated"] .rec-stale-label {
  color: var(--bad);
  border-color: var(--bad);
}

.rec-row--historical[data-state="expired"] .rec-stale-label {
  color: var(--off);
}

/* C9: .rec-history-zero-state + .disclaimer-gate moved to
   components/alerts.css. */

/* C5: .form-card + .form-group + .form-error + .prefs-tz-note moved to
   components/forms.css. C4: .btn family moved to components/buttons.css. */

/* ==========================================================================
   Tables — journal, invites, status detail.
   ========================================================================== */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  font-family: var(--font-mono);
  margin-bottom: var(--gap);
}

.data-table th,
.data-table td {
  text-align: left;
  padding: var(--gap-xs) var(--gap-sm);
  border-bottom: 1px solid var(--border);
}

.data-table th {
  color: var(--dim);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}

.data-table td {
  color: var(--fg);
  vertical-align: middle;
  /* Rev 20 (Opus R1 #2) — tnum on every data-table cell so price columns
     and timestamps don't jitter as digits advance. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.data-table tr:hover td {
  background-color: rgba(255, 255, 255, 0.02);
}

/* ==========================================================================
   Invite list.
   ========================================================================== */

.invite-code {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  /* v5 §3 — code IS data, not brand. Reads at --fg, paper-warm white. */
  color: var(--fg);
  letter-spacing: 0.04em;
}

.invite-used {
  color: var(--off);
  text-decoration: line-through;
}

/* ==========================================================================
   §23.5 — journal analytics: CSS-only bar chart.
   ========================================================================== */

.bar-chart {
  display: flex;
  flex-direction: column;
  gap: var(--gap-sm);
  margin: var(--gap) 0;
}

.bar-chart__row {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  font-size: 0.85rem;
  font-family: var(--font-mono);
}

.bar-chart__label {
  min-width: 12ch;
  color: var(--dim);
  text-align: right;
  flex-shrink: 0;
}

.bar-chart__track {
  flex: 1;
  height: 20px;
  background-color: var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.bar-chart__fill {
  height: 100%;
  border-radius: var(--radius);
  transition: width var(--dur-row) var(--ease-default);
}

.bar-chart__fill--ok      { background-color: var(--live); }

.bar-chart__fill--warn    { background-color: var(--warn); }

.bar-chart__fill--bad     { background-color: var(--bad); }

.bar-chart__value {
  min-width: 5ch;
  color: var(--fg);
}

/* C1: Section headings + text utility classes moved to base.css. */

/* Sound toggle button */

.sound-toggle {
  background: none;
  border: 1px solid var(--border);
  color: var(--dim);
  /* Rev-17 UX-stage — §22.10 touch-target floor. */
  min-height: 44px;
  min-width: 44px;
  padding: 10px 14px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.8rem;
  font-family: var(--font-mono);
  transition: border-color var(--dur-tick), color var(--dur-tick);
}

.sound-toggle:hover {
  border-color: var(--fg);
  color: var(--fg);
}

/* Keyboard focus inherits the global :focus-visible outline (--ink) — the
   focus ring is one of the canonical Klein-blue slots per v5 §3. */

.sound-toggle[aria-pressed="true"] {
  /* "On" reads as state (data earned it). --fg keeps the brand budget intact. */
  border-color: var(--fg);
  color: var(--fg);
}

/* Nav links in header */

.header-nav {
  display: flex;
  align-items: center;
  gap: var(--gap);
  font-size: 0.85rem;
}

.header-nav a {
  color: var(--dim);
  /* Rev-17 UX-stage — §22.10 touch-target floor. Inline-block + padding so
     anchors hit the 44 × 44 minimum on phones. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 10px;
}

.header-nav a:hover {
  /* Subtle hover — colour shift only. The active-page treatment below
     uses the accent underline; hover earns no chrome, just clarity. */
  color: var(--fg);
  text-decoration: none;
}

/* Rev 20 (Codex R1 #1) — active page indicator. The aria-current attribute is
   the source of truth (Jinja sets it in base.html); CSS surfaces it visually.
   Bottom-border in --ok mirrors the focus-visible token so the cockpit register
   stays consistent. */

.header-nav a[aria-current="page"] {
  color: var(--fg);
  border-bottom: 2px solid var(--ink);
}

/* C4: .btn-run-cycle moved to components/buttons.css. */

/* Status detail health table — legacy classes (most call-sites have moved
   to .bk bracket atoms in step 6). Remaining .health-ok cells render the
   "last success" timestamp as --fg per v5 §3 — state vs brand separation. */

.health-ok   { color: var(--fg); }

.health-warn { color: var(--warn); }

.health-bad  { color: var(--bad); }

/* Quota bar */

.quota-bar {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  margin-bottom: var(--gap);
  font-size: 0.85rem;
  font-family: var(--font-mono);
}

.quota-bar__label { color: var(--dim); }

.quota-bar__count { color: var(--fg); }

/* C5: .logout-form + .radio-group + .suggested-action moved to
   components/forms.css. */

/* ==========================================================================
   Trade-ticket worksheet (Rev 18 — small-circle trading-safety move).
   Per-rec broker-ready ticket page at /trade-ticket/<rec_id>. Sir's audit
   ask: every field he needs to fill in to take a rec to TradingView /
   MT4 / cTrader / OANDA / IG. Derived fields show auto; risk fields
   visually distinguished (--surface background) so it's clear they're
   user-supplied.
   ========================================================================== */

/* Legacy rec-page + trade-ticket layout rules DELETED 2026-05-26 per
   team-review-2026-05-26-post-phase-e R1 (Yuki + Marcus + Sloan all
   converged): these old rules competed with the new layouts/rec-page.css
   and produced the text-on-text overlap Sir caught in production. The
   new rec-page owns its own layout via static/css/layouts/rec-page.css
   + static/css/components/rec-card.css (linked directly from
   trade_ticket.html). Old wrappers (.rec-page, .rec-page__main,
   .rec-page__sidebar*, .trade-ticket) are no longer rendered by any
   live template — leaving them defined here was dead weight that won
   the cascade against the new rules.  */

.ticket-header {
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--gap-sm);
  margin-bottom: var(--gap);
}

.ticket-header h1 {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  margin: 0 0 var(--gap-xs) 0;
}

/* Title row holds the H1 alongside the Share button (creative review
   Item 9, 2026-05-26). Stack-then-row layout keeps the button left-of-
   wrap at narrow widths instead of stretching the H1. */

.ticket-header__title-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-sm);
  flex-wrap: wrap;
  margin-bottom: var(--gap-xs);
}

.ticket-header__title-row h1 {
  margin-bottom: 0;
  flex: 1 1 auto;
}

.rec-share-btn {
  flex: 0 0 auto;
}

.ticket-sub {
  font-size: 0.8rem;
  margin: var(--gap-xs) 0;
}

.ticket-section {
  margin-bottom: var(--gap);
  padding: var(--gap);
  background: var(--surface);
  border: 1px solid var(--border);
}

.ticket-section h2 {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  margin: 0 0 var(--gap-sm) 0;
  color: var(--fg);
  border-bottom: 1px dotted var(--border);
  padding-bottom: var(--gap-xs);
}

/* User-fill sections get a tinted left border so it's obvious Sir owns them */

.ticket-section--user {
  border-left: 3px solid var(--warn);
}

.ticket-section--checks {
  /* v5 §3 — checks section is sectioning, not brand. --fg keeps it
     visible without burning a Klein-blue slot. */
  border-left: 3px solid var(--fg);
}

.ticket-dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--gap-xs) var(--gap);
  font-size: 0.85rem;
  margin: 0;
}

.ticket-dl dt {
  color: var(--dim);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding-top: 0.1rem;
}

.ticket-dl dd {
  margin: 0;
  color: var(--fg);
}

.ticket-fillin {
  color: var(--dim);
  font-style: italic;
}

/* Links inside body text must be distinguishable beyond colour alone —
   axe-core `link-in-text-block` (Rev 19 Cluster G+ closure). Underlining
   inline anchors satisfies WCAG 1.4.1 without disturbing the cockpit
   register of standalone .btn / .header-nav anchors elsewhere.
   Rev 20 (Codex R1 secondary #2) — selector extended to cover .text-dim,
   .empty-state, and journal-analytics intro <p>. The link-in-text-block
   contrast issue Codex flagged at journal_analytics.html:17 is dominated
   by the underline once it's in place. */

.ticket-fillin a,
.ticket-dl dd a,
.ticket-section a,
.empty-state a,
.text-dim a,
p.text-dim a,
.help-text a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.ticket-tp-ladder {
  list-style: none;
  padding: 0;
  margin: var(--gap-xs) 0;
}

.ticket-tp-ladder li {
  padding: 0.2rem 0;
  font-size: 0.85rem;
}

.ticket-checklist {
  list-style: none;
  padding: 0;
  margin: 0;
}

.ticket-checklist li {
  padding: 0.5rem 0;
  border-bottom: 1px dotted var(--border);
  font-size: 0.85rem;
}

.ticket-checklist li:last-child {
  border-bottom: 0;
}

.ticket-checklist input[type="checkbox"] {
  margin-right: 0.5rem;
  /* Touch target floor §22.10 */
  width: 20px;
  height: 20px;
  vertical-align: middle;
}

.ticket-checklist label {
  cursor: pointer;
}

/* C9: .warn-box family moved to components/alerts.css. */

.ticket-footer {
  margin-top: var(--gap);
  padding-top: var(--gap-sm);
  border-top: 1px dotted var(--border);
  font-size: 0.75rem;
}

.text-bad { color: var(--bad); }

.text-warn { color: var(--warn); }

/* §18.1 cluster-sibling line — Rev 19 Cluster G+. Renders under the rec
   row's L2 when two+ open recs share a driver_cluster_id. Indented +
   --dim so it reads as supplementary context, not a fourth primary field. */

.rec-cluster-sibling {
  margin-top: var(--gap-xs);
  margin-left: 1.5rem;
  color: var(--dim);
  font-size: 0.78rem;
  font-family: var(--font-mono);
}

/* ==========================================================================
   Rev 20 — UX cohesion R1 → R2 implementation.
   Aliveness pulse, HTMX loading + error feedback, toast, mobile breakpoint.
   ========================================================================== */

/* v5 §6 — cockpit-pulse DELETED. The aliveness pulse violated the
   "delight only when the data earned it" rule: the dot flashed on
   every poll regardless of whether anything changed. The brand-mark
   weight-pulse (§4) is now the only ambient signal. The DOM node is
   left in dashboard.html as a 0-byte hook; web.js may still toggle
   the classes harmlessly. */

.cockpit-pulse,
.cockpit-pulse--active {
  display: none;
}

/* Sonnet R1 #2 — HTMX loading state. The hx-indicator on #intel-container
   never had a CSS hook, so the indicator silently never fired. The
   .htmx-request class is added to the request element AND any
   hx-indicator targets. We dim #intel-container while a swap is in flight. */

#intel-container.htmx-request {
  opacity: 0.6;
  transition: opacity 0.12s var(--ease-arrival);
}

/* C9: .htmx-error-banner + .panel-error + .run-cycle-toast moved to
   components/alerts.css. */

/* C4: .btn-run-cycle disabled state moved to components/buttons.css. */

/* C2: ≤640 px page-wide responsive collapse moved to layout.css. */

/* ==========================================================================
   Chart panel + position-sizing widget (trade_ticket.html)
   ========================================================================== */

/* Chart container — full-width mount point for Lightweight Charts. */

.rec-chart-container {
  width: 100%;
  height: 420px;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--canvas);
  overflow: hidden;
  position: relative;
}

/* Chart fallback — shown inside the container when JS does not hydrate.
   chart.js sets data-chart-ready="true" on #rec-chart-container once
   candles are painted; this hides the message at that point.
   If JS fails silently or the feed returns no bars, the copy stays visible
   rather than leaving a black void (R4 ship-now #2, 2026-05-26). */

.chart-fallback {
  font-size: 0.78rem;
  color: var(--dim);
  text-align: center;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  white-space: nowrap;
}

.rec-chart-container[data-chart-ready="true"] .chart-fallback {
  display: none;
}

/* Legend row beneath the chart. */

.rec-chart-legend {
  margin-top: 0.4rem;
  font-size: 0.78rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.rec-chart-legend__sl {
  color: var(--bad);
  font-variant-numeric: tabular-nums;
}

.rec-chart-legend__tp {
  color: var(--live);
  font-variant-numeric: tabular-nums;
}

.rec-chart-legend__now {
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* Semantic colour aliases for the widget — not on --good (doesn't exist),
   use --live for positive outcomes, --bad for loss. */

.text-good {
  color: var(--live);
}

.text-bad {
  color: var(--bad);
}

/* Position-sizing widget card. */

.chart-widget {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 1rem 1.25rem;
  max-width: 560px;
}

/* Risk-input row: label + $ prefix + number input + suffix text. */

.chart-widget__input-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.chart-widget__label {
  color: var(--fg);
  font-size: 0.88rem;
  white-space: nowrap;
}

.chart-widget__input-wrap {
  display: flex;
  align-items: center;
  border: 1px solid var(--ink);
  border-radius: 2px;
  background: var(--canvas);
  padding: 0 0.5rem;
  gap: 0.25rem;
}

.chart-widget__prefix {
  color: var(--ink);
  font-family: var(--font-mono, monospace);
  font-size: 0.88rem;
  line-height: 1;
}

.chart-widget__input {
  background: transparent;
  border: none;
  outline: none;
  color: var(--fg);
  font-family: var(--font-mono, monospace);
  font-size: 0.92rem;
  width: 7ch;
  padding: 0.3rem 0;
  text-align: right;
  -moz-appearance: textfield;
}

.chart-widget__input::-webkit-inner-spin-button,
.chart-widget__input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.chart-widget__input:focus {
  color: var(--ink);
}

.chart-widget__input-suffix {
  font-size: 0.82rem;
  white-space: nowrap;
}

/* Definition list — two-column grid, mono values. */

.chart-widget__dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 1rem;
  font-size: 0.85rem;
  margin: 0 0 0.75rem 0;
}

.chart-widget__dl dt {
  color: var(--dim);
}

.chart-widget__dl dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.chart-widget__note {
  font-size: 0.75rem;
  margin-top: 0.25rem;
}

/* Responsive — collapse widget to full width on small screens. */

@media (max-width: 600px) {
  .chart-widget {
    max-width: 100%;
    padding: 0.75rem;
  }

  .rec-chart-container {
    height: 300px;
  }

  .chart-widget__input-row {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ==========================================================================
   Journal — pending trades (JOURNALED / PLACED status treatment)
   Trade ticket — [ why this idea ] context section
   ========================================================================== */

/* Flash banner — brief confirmation after save-from-rec or mark-placed. */

.journal-flash {
  background: color-mix(in srgb, var(--ink) 12%, var(--surface));
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  color: var(--fg);
  font-size: 0.875rem;
  padding: 0.5rem 0.875rem;
  margin-bottom: 1rem;
}

/* Journal status pills. */

.journal-status {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  padding: 0.1em 0.4em;
  border-radius: 3px;
}

.journal-status--journaled {
  background: color-mix(in srgb, var(--dim) 15%, transparent);
  color: var(--dim);
  border: 1px solid var(--border);
}

.journal-status--placed {
  background: color-mix(in srgb, var(--ink) 18%, transparent);
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--ink) 45%, transparent);
}

/* Row-level visual dim for JOURNALED (not yet executed). */

.journal-row--journaled td {
  opacity: 0.75;
}

/* Row-level visual pop for PLACED (live in broker). */

.journal-row--placed td {
  opacity: 1;
}

/* Save-to-Journal button + note in the position-sizing section. */

.ticket-journal-form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0;
  flex-wrap: wrap;
}

.ticket-journal-btn {
  white-space: nowrap;
}

.ticket-journal-note {
  font-size: 0.8rem;
  margin-top: 0.5rem;
}

/* Place/Skip CTA pair — creative review Item 4 (2026-05-26). The pair
   sits side-by-side; the skip disclosure opens inline beneath the
   summary chip when expanded. */

.ticket-cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.75rem;
  margin-top: 1rem;
}

.ticket-skip-disclosure {
  display: inline-block;
}

.ticket-skip-summary {
  cursor: pointer;
  list-style: none;
}

.ticket-skip-summary::-webkit-details-marker {
  display: none;
}

.ticket-skip-form {
  margin-top: 0.75rem;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgb(var(--ink-rgb) / 0.04);
}

.ticket-skip-fieldset {
  border: none;
  padding: 0;
  margin: 0 0 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.ticket-skip-legend {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.35rem;
}

.ticket-skip-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
  cursor: pointer;
}

.ticket-skip-submit {
  margin-top: 0.25rem;
}

/* [ why this idea ] context section. */

.ticket-section--context {
  border-left: 2px solid color-mix(in srgb, var(--ink) 40%, transparent);
  padding-left: 1rem;
}

.ticket-context-reasoning {
  color: var(--fg);
  font-size: 0.9rem;
  line-height: 1.6;
  margin-bottom: 0.75rem;
}

.ticket-intel-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.ticket-intel-item {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.ticket-intel-headline {
  font-size: 0.875rem;
  color: var(--fg);
}

a.ticket-intel-headline {
  color: var(--ink);
  text-decoration: none;
}

a.ticket-intel-headline:hover {
  text-decoration: underline;
}

.ticket-intel-byline {
  font-family: var(--font-mono);
  font-size: 0.75rem;
}

/* ==========================================================================
   §25 — Market Sessions widget.
   ========================================================================== */

.sessions-widget {
  background-color: var(--surface);
  border: 1px solid var(--border);
  padding: 1rem 1rem 0.75rem;
  margin-bottom: var(--gap);
  font-family: var(--font-mono);
  position: relative;
  overflow: hidden;
}

/* ── Header ── */

.sessions-widget__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.sessions-widget__title {
  font-size: var(--text-xs);
  color: var(--dim);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.sessions-widget__tz-wrap {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.sessions-widget__tz-label {
  font-size: var(--text-xs);
  color: var(--dim);
  letter-spacing: 0.06em;
}

.sessions-widget__tz-value {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--fg);
}

/* ── 24-hour axis labels ── */

.sessions-axis {
  position: relative;   /* labels are absolutely positioned by JS */
  height: 1.1rem;       /* reserve vertical space for the label row */
  font-size: 0.7rem;
  color: var(--off);
  letter-spacing: 0.04em;
  margin-bottom: 0.35rem;
  /* left padding matches the label column (6rem + 0.5rem gap) so axis
     aligns with the track area, mirroring the old flex padding. */
  padding: 0 0 0 6.5rem;
  overflow: visible;
  pointer-events: none;
  user-select: none;
}

.sessions-axis span[data-label-slot] {
  position: absolute;
  transform: translateX(-50%);
  white-space: nowrap;
  top: 0;
}

/* ── Session rows container (relative for the "now" overlay) ── */

.sessions-rows {
  position: relative;
}

/* ── Individual session row ── */

.sessions-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 2rem;
  margin-bottom: 0.25rem;
}

.sessions-row__label {
  width: 6rem;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.sessions-row__code {
  font-size: var(--text-xs);
  color: var(--dim);
  letter-spacing: 0.08em;
  line-height: 1;
}

.sessions-row__local {
  font-size: 0.65rem;
  color: var(--off);
  line-height: 1;
  white-space: nowrap;
}

/* Open state: brighten the local time display */

.sessions-row--open .sessions-row__local {
  color: var(--dim);
}

/* ── Track (the horizontal bar area) ── */

.sessions-row__track {
  position: relative;
  flex: 1;
  height: 100%;
  background-color: var(--canvas);
  border: 1px solid var(--border);
}

/* ── Session bar ── */

.sessions-bar {
  position: absolute;
  top: 0;
  bottom: 0;
  /* left and width are set inline by sessions.js (sliding window). */
  left: 0%;
  width: 0%;
  background-color: rgb(var(--ink-rgb) / 0.45);
  border-radius: 1px;
  transition: background-color var(--dur-tick) ease;
}

/* Hover state */

.sessions-bar:hover {
  background-color: rgb(var(--ink-rgb) / 0.9);
  cursor: default;
}

/* Per-session opacity variation (same indigo, different lightness — no extra hues).
   Closed state is the default (dim). Open state is set when JS adds --open class. */

.sessions-row--sydney   .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.50); }

.sessions-row--tokyo    .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.38); }

.sessions-row--london   .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.65); }

.sessions-row--newyork  .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.42); }

.sessions-row--sydney.sessions-row--open  .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.82); }

.sessions-row--tokyo.sessions-row--open   .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.65); }

.sessions-row--london.sessions-row--open  .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.92); }

.sessions-row--newyork.sessions-row--open .sessions-bar { background-color: rgb(var(--ink-rgb) / 0.72); }

/* ── "Now" vertical indicator ── */

.sessions-now {
  position: absolute;
  top: 0;
  bottom: 0;
  /* left is set inline by sessions.js */
  width: 0;
  pointer-events: none;
  z-index: 2;
}

.sessions-now__pin {
  position: absolute;
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  background-color: var(--canvas);
  border: 1px solid var(--ink);
  padding: 0.1rem 0.35rem;
  white-space: nowrap;
}

.sessions-now__day {
  font-size: 0.6rem;
  color: var(--ink);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.2;
}

.sessions-now__time {
  font-size: 0.6rem;
  color: var(--ink);
  line-height: 1.2;
}

.sessions-now__line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 1px;
  background-color: var(--ink);
  opacity: 0.7;
}

/* Smooth glide on each minute tick when motion is OK.
   - .sessions-now pin: quick 0.4s settle (just a layout re-measure).
   - .sessions-bar: slides leftward over the full 60s tick interval.
   - .sessions-volume__svg: same 60s slide so volume curve stays in sync. */

@media (prefers-reduced-motion: no-preference) {
  .sessions-now {
    transition: left 0.4s ease;
  }

  .sessions-bar {
    transition: left 60s linear, width 60s linear,
                background-color var(--dur-tick) ease;
  }

  .sessions-volume__svg {
    transition: transform 60s linear;
  }
}

/* Reduced-motion: bars and SVG snap on each tick; no slide. */

@media (prefers-reduced-motion: reduce) {
  .sessions-bar {
    transition: background-color var(--dur-tick) ease;
  }

  .sessions-volume__svg {
    transition: none;
  }
}

/* ── Volume curve ── */

.sessions-volume {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.sessions-volume__curve {
  width: 100%;
  overflow: hidden;
}

.sessions-volume__svg {
  width: 100%;
  height: 48px;
  display: block;
}

.sessions-volume__fill {
  fill: rgb(var(--ink-rgb) / 0.08);
}

.sessions-volume__line {
  stroke: rgb(var(--ink-rgb) / 0.35);
  stroke-width: 1.5;
}

.sessions-volume__dot {
  fill: var(--ink);
}

.sessions-volume__label-row {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.7rem;
  margin-top: 0.25rem;
  flex-wrap: wrap;
}

.sessions-volume__caption {
  color: var(--dim);
}

.sessions-volume__value {
  color: var(--fg);
  font-weight: 600;
}

.sessions-volume__value[data-volume="low"]    { color: var(--dim); }

.sessions-volume__value[data-volume="medium"] { color: var(--warn); }

.sessions-volume__value[data-volume="high"]   { color: var(--ink); }

/* ── Responsive ── */

@media (max-width: 600px) {
  .sessions-axis {
    padding-left: 5rem;   /* 4.5rem label + 0.5rem gap */
    font-size: 0.6rem;
  }

  .sessions-row__label {
    width: 4.5rem;
  }
}

/* C6: .pair-grid container moved to components/pair-tile.css. */

/* C9: .pair-grid-error + .cockpit-empty-state family moved to
   components/alerts.css. */

/* C6: .pair-tile family (base + variants + children) moved to
   components/pair-tile.css. */

/* ==========================================================================
   Pair detail page — /pair/<slug>
   ========================================================================== */

.pair-detail {
  max-width: 860px;
  margin: 0 auto;
  padding: var(--gap) 0;
}

.pair-detail__header {
  margin-bottom: var(--gap);
}

.pair-detail__title {
  font-size: 1.4rem;
  margin: 0 0 0.4rem 0;
}

.pair-detail__price-row {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
}

.pair-detail__price {
  font-size: 1.1rem;
}

.pair-detail__arrow {
  font-size: 0.9rem;
  color: var(--dim);
}

.pair-detail__feed {
  font-size: 0.78rem;
}

.pair-detail__section {
  margin-bottom: var(--gap-lg);
}

.pair-detail__rec-list {
  list-style: none;
  padding: 0;
  margin: var(--gap-sm) 0 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap-sm);
}

.pair-detail__rec-row {
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: var(--gap-sm) var(--gap);
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.pair-detail__rec-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.pair-detail__rec-status {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.pair-detail__rec-rationale {
  font-size: 0.83rem;
  line-height: 1.4;
  margin: 0;
}

.pair-detail__rec-levels {
  font-size: 0.77rem;
}

.pair-detail__empty {
  font-size: 0.85rem;
  padding: var(--gap-sm) 0;
}

.pair-detail__footer {
  margin-top: var(--gap-lg);
  padding-top: var(--gap-sm);
  border-top: 1px solid var(--border);
}

/* Status pill on pair-detail rec list */

.status-pill--live    { color: var(--live); }

.status-pill--decaying { color: var(--warn); }

.status-pill--expired  { color: var(--off); }

.status-pill--invalidated { color: var(--bad); }

.status-pill--pending  { color: var(--warn); }
