/* Ori case study, Market Pulse production skin.
   Ground truth: vireonapp_migrated — SectionRail.jsx (dot rail + chip-bar),
   MPAct.jsx (act label row), PulseRadialHero.jsx + RadialGauge.jsx (ring
   gauge + sub-panels), MPMasthead.jsx (meta chips, session dot),
   MarketAnchorsRow.jsx (stat cards), tideTokens.js (all tokens).
   Accent teal-to-aqua is garnish only. Tier palette is the canonical
   src/lib/source-tiers.js set. No amber, gold, or orange anywhere.
   Motion: TIDE ease cubic-bezier(0.16, 1, 0.3, 1), nothing under 0.4s.
   Reduced motion collapses to 0.2s and nothing is ever hidden. */

/* 1. Tokens */

:root {
  /* Surfaces */
  --bg:    #0B0E13;
  --shell: #0D1017;

  /* Vireon BRAND lockup, verbatim from the app sidebar (Layout.jsx).
     Reserved for the wordmark. Distinct from the TIDE content accent:
     the brand is purple, the product surface is teal. */
  --brand-purple: #A774FF;
  --brand-blue:   #4EC8FF;

  /* Accent. Garnish only: never a large fill, never body text. */
  --teal: #2DD4BF;
  --aqua: #22D3EE;
  --accent-grad: linear-gradient(90deg, #2DD4BF, #22D3EE);

  /* Deep-teal ring ramp (RadialGauge / caution dial). Not for text. */
  --ring-teal: #14B8A6;
  --ring-teal-deep: #0D9488;

  /* Text ramp */
  --heading:      rgba(255, 255, 255, 0.92);
  --heading-soft: rgba(255, 255, 255, 0.88);
  --body:         rgba(230, 236, 245, 0.68);
  --body-muted:   rgba(230, 236, 245, 0.62);
  /* --meta carries real content (the demo's honesty caption, the source
     count, the footer link), so it is held at WCAG AA 4.5:1 against the
     shell. --meta-faint is genuinely decorative (hairlines, rule marks)
     and is exempt; never put a glyph on it. */
  --meta:         rgba(170, 180, 198, 0.72);  /* incidental labels, AA-safe */
  --meta-faint:   rgba(160, 170, 190, 0.45);  /* non-text decoration only   */

  /* Production label ramp (MPAct / sub-panel eyebrows) */
  --lab-name:    rgba(255, 255, 255, 0.55);
  --lab-hint:    rgba(255, 255, 255, 0.56);
  --lab-eyebrow: rgba(255, 255, 255, 0.50);
  --lab-strong:  rgba(255, 255, 255, 0.64);

  /* Chips */
  --chip-active-bg:     rgba(45, 212, 191, 0.10);
  --chip-active-text:   rgba(255, 255, 255, 0.95);
  --chip-active-border: rgba(45, 212, 191, 0.22);
  --chip-inactive:      rgba(170, 180, 198, 0.74);

  /* Borders, fills */
  --border-card:  rgba(255, 255, 255, 0.06);
  --border-sub:   rgba(255, 255, 255, 0.055);
  --border-glass: rgba(255, 255, 255, 0.11);
  --fill-card: linear-gradient(180deg, rgba(255, 255, 255, 0.048), rgba(255, 255, 255, 0.024));
  --fill-chip: linear-gradient(180deg, rgba(255, 255, 255, 0.048), rgba(255, 255, 255, 0.035));
  --fill-sub:  rgba(255, 255, 255, 0.028);
  --fill-panel: rgba(255, 255, 255, 0.03);
  --border-panel: rgba(255, 255, 255, 0.05);
  --track:     rgba(255, 255, 255, 0.06);

  /* Glass */
  --glass:      rgba(13, 16, 23, 0.75);
  --blur-glass: blur(16px) saturate(155%);
  --blur-label: blur(14px) saturate(160%);

  /* Radii */
  --r-shell: 22px;
  --r-card:  16px;
  --r-sub:   12px;
  --r-pill:  999px;

  /* Shadows */
  --shadow-shell: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 12px 40px rgba(0, 0, 0, 0.35);
  --shadow-card:  0 6px 24px rgba(0, 0, 0, 0.16);
  --shadow-chip:  0 4px 28px rgba(0, 0, 0, 0.08), inset 0 1px 1.5px rgba(255, 255, 255, 0.05);
  --shadow-glass: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 16px 48px rgba(0, 0, 0, 0.5);
  --shadow-panel: inset 0 1px 1px rgba(255, 255, 255, 0.04);
  --edge-light:   rgba(255, 255, 255, 0.05);

  /* Canonical tier palette (src/lib/source-tiers.js). Semantic only:
     tier chips, weight gauges, calculator rows. */
  --tier-a: #58E3A4;
  --tier-b: #93C5FD;
  --tier-c: #9EB8DC;
  --tier-d: #9CA3AF;
  --neutral: #8A94A6;

  /* Formula components. Non-directional, so no data tones here:
     publisher quality (0.5) teal, outlet corroboration (0.3) aqua,
     tier diversity (0.2) the system neutral. */
  --comp-pq: var(--teal);
  --comp-oc: var(--aqua);
  --comp-sd: var(--neutral);
  /* The system neutral reads fine as a 17px weight glyph but disappears as
     a 3-4px bar: a Tier Diversity row at 100 looked identical to an empty
     track while its teal and aqua siblings glowed. Bars get a lifted
     neutral so "full" is unmistakable; the hue stays non-directional. */
  --comp-sd-bar: #B4BFD1;

  /* Type */
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Menlo", "Cascadia Code", monospace;

  /* Motion: the TIDE contract */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-reveal: 0.4s;
  --dur-normal: 0.5s;
  --dur-slow:   0.55s;
  /* Entrance-only durations. --dur-grand is the wordmark's arrival: the
     one element in the title sequence allowed more mass than the shared
     0.55s rise. --dur-chrome is the dot rail's per-dot assembly; chrome
     gets the light end of the scale (still over the 0.4s floor), never
     the hero's weight. */
  --dur-grand:  0.85s;
  --dur-chrome: 0.45s;
  /* Demo drawer: the height opens slower than content fades, and the fill
     is delayed so the two layers read as sequential, not simultaneous. */
  --dur-demo-open:    0.62s;
  --delay-demo-fill:  0.16s;

  /* Measure */
  --measure:  66ch;
  --wide:     60rem;
  --wide-mid: 52rem;
  --gutter:   clamp(1.25rem, 5vw, 4rem);

  /* Sticky-header scroll offset shared by CSS and the scroll-spy. */
  --top-offset: 88px;
}

/* 2. Base */

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

html {
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  position: relative;
  background: var(--bg);
  color: var(--heading);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.7;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1;
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { text-wrap: balance; color: var(--heading); }
p { text-wrap: pretty; }

::selection {
  background: rgba(45, 212, 191, 0.28);
  color: rgba(255, 255, 255, 0.95);
}

:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
  border-radius: 3px;
}

main:focus { outline: none; }

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

/* Production deep-link offset: anchored jumps clear the sticky chrome. */
.act { scroll-margin-top: var(--top-offset); }
.hero { scroll-margin-top: 1.75rem; }

/* Every numeric value on a TIDE surface reads as tabular. */
.tabular,
.stat-value, .lat-v, .score, .count, .actlab-num,
.bar-head output, .calc-row-weight, .scale-n, .scale-v,
.pursuit-step, .dial-of {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* 3. Atmosphere. A quiet accent-family glow at the page head, and film
   grain over everything: it kills gradient banding on the dark ground
   and gives it a print-like surface. */

body::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: min(110vh, 60rem);
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(48rem 32rem at 62% -14rem, rgba(45, 212, 191, 0.07), rgba(45, 212, 191, 0) 62%),
    radial-gradient(40rem 28rem at 14% -10rem, rgba(34, 211, 238, 0.045), rgba(34, 211, 238, 0) 60%);
}

body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0.026;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* 4. Scroll progress (injected by app.js), accent gradient. */

.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 60;
  pointer-events: none;
}

.progress span {
  display: block;
  height: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--accent-grad);
}

/* 5. Skip link: hidden until focused, then a glass pill top-left. */

.skip-link {
  position: fixed;
  top: 0.9rem;
  left: 0.9rem;
  z-index: 100;
  padding: 0.7rem 1.15rem;
  border-radius: var(--r-pill);
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--border-glass);
  box-shadow: var(--shadow-glass);
  color: var(--chip-active-text);
  font-size: 0.8125rem;
  font-weight: 500;
  text-decoration: none;
  transform: translateY(-250%);
  transition: transform var(--dur-reveal) var(--ease);
}

.skip-link:focus-visible {
  transform: none;
  outline-offset: 1px;
}

/* 6. Desktop dot rail — the production SectionRail. Bare dots on the
   right edge, NO persistent text pill. The glass label floats in on
   hover and keyboard focus only; the active dot enlarges to the accent
   gradient with a glow. It is chrome that floats, never a pill baked
   into the rail. */

.dotrail { display: none; }

@media (min-width: 48rem) {
  .dotrail {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 12px;
    position: fixed;
    top: 50%;
    right: 22px;
    transform: translateY(-50%);
    z-index: 40;
  }

  /* Padding rather than gap carries most of the vertical rhythm, so the
     hit target is a comfortable 22px tall while the dots still read as
     spaced roughly 20px apart. */
  .dot-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-block: 4px;
    border-radius: var(--r-pill);
    text-decoration: none;
    outline: none;
  }

  .dot-link:focus-visible {
    outline: 2px solid var(--teal);
    outline-offset: 2px;
    border-radius: var(--r-pill);
  }

  /* Floating glass label (mockup .pglbl). Hidden at rest, revealed on
     hover AND focus; opacity 0 -> 1, translateX 6px -> 0, 400ms TIDE ease. */
  .dot-label {
    pointer-events: none;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1;
    color: rgba(255, 255, 255, 0.9);
    padding: 5px 10px;
    border-radius: 8px;
    background: var(--glass);
    -webkit-backdrop-filter: var(--blur-label);
    backdrop-filter: var(--blur-label);
    border: 1px solid rgba(255, 255, 255, 0.09);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transform: translateX(6px);
    transition:
      opacity 400ms var(--ease),
      transform 400ms var(--ease);
  }

  @supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
    .dot-label { background: rgba(13, 16, 23, 0.94); }
  }

  .dot-link:hover .dot-label,
  .dot-link:focus-visible .dot-label {
    opacity: 1;
    transform: none;
  }

  /* The ACTIVE section keeps its label. The other five stay bare dots.
     The production SectionRail spec says bare dots and no persistent pill,
     and that holds for the rail as a whole: five of six are still bare. What
     it was guarding against was a permanent column of text down the margin,
     which this does not create. What it cost was discoverability, because a
     reader who never hovers reads six unlabeled dots as decoration rather
     than navigation. One label answers "where am I" and reveals that the
     rail is navigable at all, at the price of a single line of chrome. */
  .dot-link[aria-current] .dot-label {
    opacity: 1;
    transform: none;
    /* Reads as a status, not as a tooltip waiting to be dismissed: quieter
       ink and no border, so hovering it still produces a distinct state. */
    color: var(--lab-strong);
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* Hovering the active dot promotes its label back to the full glass chip,
     so the rail still answers a deliberate hover everywhere. */
  .dot-link[aria-current]:hover .dot-label,
  .dot-link[aria-current]:focus-visible .dot-label {
    color: rgba(255, 255, 255, 0.9);
    background: var(--glass);
    border-color: rgba(255, 255, 255, 0.09);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
    -webkit-backdrop-filter: var(--blur-label);
    backdrop-filter: var(--blur-label);
  }

  /* The dot (mockup .pgdot): 11px muted at rest, scales and brightens on
     hover; the active dot enlarges to 14px on the 135deg accent gradient
     + glow. Sized up from the original 8/10 because at 8px the inactive
     dots read as dust against the near-black ground on a large display. */
  .dot {
    flex-shrink: 0;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.32);
    transition: all 400ms var(--ease);
  }

  .dot-link:not([aria-current]):hover .dot {
    transform: scale(1.18);
    background: rgba(255, 255, 255, 0.55);
  }

  .dot-link[aria-current] .dot {
    width: 14px;
    height: 14px;
    background: linear-gradient(135deg, var(--teal), var(--aqua));
    box-shadow: 0 0 16px rgba(45, 212, 191, 0.55);
  }
}

/* 7. Mobile chip-bar — the production SectionRail second face. A sticky
   glass strip that scrolls horizontally and auto-centers the active pill
   (app.js). Navigation is never simply hidden on mobile. */

.chipbar {
  position: sticky;
  top: 0;
  z-index: 40;
}

@media (min-width: 48rem) {
  .chipbar { display: none; }
}

.chipbar-track {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  /* Trimmed from 8px so the taller pills do not push the sticky bar down
     the page; the bar grows by 6px total, not 13px. */
  padding: 4px 16px;
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border-bottom: 1px solid var(--border-glass);
  scrollbar-width: none;
}

.chipbar-track::-webkit-scrollbar { display: none; }

/* The track scrolls horizontally and at 390px it clipped the last chip
   mid-word with no hint that more existed. A short fade on each edge is
   the standard affordance and costs no layout. */
@supports (mask-image: linear-gradient(90deg, #000, #000)) {
  .chipbar-track {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 1.1rem, #000 calc(100% - 1.1rem), transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 1.1rem, #000 calc(100% - 1.1rem), transparent 100%);
  }
}

@supports not ((backdrop-filter: blur(16px)) or (-webkit-backdrop-filter: blur(16px))) {
  .chipbar-track { background: rgba(13, 16, 23, 0.94); }
}

/* The chip-bar is the ONLY navigation on mobile, so its pills are held to
   the 44px minimum touch target rather than sized to their text. They were
   31px, which is a comfortable miss on a moving thumb. Growing the pill
   rather than adding invisible padding keeps the hit area and the visible
   target identical, which is the part that actually prevents mis-taps. */
.chip-link {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 6px 14px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  background: transparent;
  color: var(--chip-inactive);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
  text-decoration: none;
  transition:
    color var(--dur-reveal) var(--ease),
    background var(--dur-reveal) var(--ease),
    border-color var(--dur-reveal) var(--ease);
}

.chip-link[aria-current] {
  background: var(--chip-active-bg);
  color: var(--chip-active-text);
  border-color: var(--chip-active-border);
}

.chip-link:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
}

/* 8. Back to top: glass disc, bottom right, shown once the reader is
   deep in the page (toggled by app.js). */

.to-top {
  position: fixed;
  right: 1.1rem;
  bottom: 1.1rem;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--r-pill);
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--border-glass);
  box-shadow: var(--shadow-glass);
  color: var(--heading-soft);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease),
    color var(--dur-reveal) var(--ease),
    visibility 0s linear var(--dur-reveal);
}

@supports not ((backdrop-filter: blur(16px)) or (-webkit-backdrop-filter: blur(16px))) {
  .to-top { background: rgba(13, 16, 23, 0.94); }
}

.to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease),
    color var(--dur-reveal) var(--ease),
    visibility 0s;
}

.to-top:hover { color: var(--chip-active-text); }

.to-top:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* 9. Layout skeleton: sections are centered as a whole, content is
   left-anchored inside them. Prose holds the measure; feature blocks
   extend to the wide grid from the same left edge. */

.hero,
.act,
footer {
  max-width: calc(var(--wide) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.hero > *,
.act > *,
footer > * {
  max-width: var(--measure);
}

.hero > .meta,
.hero > .stat-strip,
.act > .act-head,
.act > .table-wrap,
.act > .split,
.act > .calc,
.act > .guardrails,
.act > .oridemo,
.act > .latency-grid {
  max-width: min(100%, var(--wide));
}

.act > .formula,
.act > .component-list,
.act > .scale-viz,
.act > .pursuit,
.act > .findings {
  max-width: min(100%, var(--wide-mid));
}

/* The act head runs wide so the hairline reaches the right edge, but
   the headline and lede inside it hold the reading measure. */
.act-head h2,
.act-head .lede { max-width: var(--measure); }

/* 10. Hero */

.hero {
  position: relative;
  padding-block: clamp(5rem, 15vh, 9rem) clamp(2.75rem, 7vh, 4.25rem);
  border-bottom: 1px solid var(--border-card);
}

.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--lab-name);
  margin: 0 0 2.5rem;
}

.eyebrow::before {
  content: "";
  width: 1.5rem;
  height: 2px;
  border-radius: var(--r-pill);
  background: var(--accent-grad);
}

/* The wordmark carries the canonical Vireon BRAND lockup gradient, verbatim
   from the app sidebar (Layout.jsx): purple #A774FF into blue #4EC8FF. The
   brand mark is purple; page content stays TIDE teal. The gradient is widened
   and mirrored so the slow drift never runs off either end of the word.
   Solid brand purple where text clipping is unsupported. */
.hero h1 {
  font-size: clamp(4rem, 13vw, 8.75rem);
  line-height: 0.92;
  letter-spacing: -0.05em;
  font-weight: 600;
  margin: 0 0 0.75rem;
  color: var(--brand-purple);
  /* The background box must hug the glyphs. As a full-width block the h1
     measured 960px while "Vireon" spans roughly half of it, so the drifting
     gradient window landed mostly on empty box and the word rendered as flat
     blue. Sizing to the text maps the lockup across the word itself. */
  width: fit-content;
  max-width: 100%;
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero h1 {
    /* Pinned, exactly as the app sidebar renders it. The drift is gone: a
       220% mirrored gradient swept the purple end off the word about 1.5s
       into every 16s cycle, so the canonical lockup spent most of its life
       reading as flat blue. Narrowing the window and sizing the box to the
       glyphs both helped and neither fixed it, because a 100deg gradient
       across a wide, short box puts nearly all of the transition outside
       the letterforms. A lockup that is only correct part of the time is
       not the lockup, and no animation here was worth that. */
    background: linear-gradient(100deg, #A774FF 0%, #4EC8FF 100%);
    background-size: 100% 100%;
    background-position: 0% 50%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.deck-lead {
  font-size: clamp(1.375rem, 3vw, 1.875rem);
  line-height: 1.28;
  letter-spacing: -0.026em;
  font-weight: 500;
  color: var(--heading-soft);
  margin: 0 0 2rem;
}

/* Identity mark: the formula's weights, drawn. 50 / 30 / 20. */
.mark {
  display: flex;
  gap: 4px;
  height: 3px;
  margin-bottom: 2.5rem;
}

.mark span {
  height: 100%;
  border-radius: var(--r-pill);
  transform-origin: 0 50%;
}

.mark .mark-pq { width: 4.25rem; background: var(--comp-pq); }
.mark .mark-oc { width: 2.55rem; background: var(--comp-oc); }
.mark .mark-sd { width: 1.7rem;  background: var(--comp-sd-bar); }

.deck {
  font-size: 1.1875rem;
  line-height: 1.66;
  color: var(--body);
  margin: 0 0 2.5rem;
  max-width: 58ch;
}

/* Meta chip row — the production .meta pill grammar (MPMasthead):
   two-tone chips on the chip gradient fill, a tiny bright category
   over a near-white value, faint bullet separators between pills. */

.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 2.75rem;
}

.meta > div {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 14px;
  border-radius: 20px;
  background: var(--fill-chip);
  box-shadow: var(--shadow-chip);
}

@media (min-width: 40rem) {
  .meta { gap: 10px 26px; }

  .meta > div:not(:last-child)::after {
    content: "\2022";
    position: absolute;
    right: -17px;
    top: 50%;
    transform: translateY(-50%);
    color: rgba(255, 255, 255, 0.28);
    font-size: 12px;
    line-height: 1;
  }
}

.meta dt {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.60);
}

.meta dd {
  margin: 0;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.94);
}

/* The session-style live dot on the Status chip. The pulse is gated:
   JS-on only, and removed under reduced motion. */
.live-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 7px;
  background: var(--ring-teal);
  box-shadow: 0 0 8px rgba(20, 184, 166, 0.45);
  vertical-align: 0.02em;
}

@keyframes live-pulse {
  0%, 100% { opacity: 0.85; }
  50%      { opacity: 1; }
}

/* The pulse is held until the title sequence has landed (~1.55s, after
   the stat strip's fourth beat): the last event of the entrance is not an
   element arriving but the page's heartbeat starting. During the hold the
   dot sits at its resting ink, so nothing reads as missing. Returning
   visitors (html.entered) skip the sequence, so the hold goes with it. */
html.js .live-dot { animation: live-pulse 2.6s var(--ease) 1.55s infinite; }
html.js.entered .live-dot { animation-delay: 0s; }

/* Stat cards — the production anchor-card grammar (MarketAnchorsRow):
   card gradient fill, hairline border, big tabular value, driver line. */

.stat-strip {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 40rem) { .stat-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) { .stat-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.stat-strip li {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 15px 16px 14px;
  border-radius: var(--r-card);
  background: var(--fill-card);
  border: 1px solid var(--border-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
}

.stat-value {
  display: block;
  font-size: 1.5625rem;
  line-height: 1;
  letter-spacing: -0.015em;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.95);
}

.stat-label {
  display: block;
  font-size: 12px;
  font-weight: 450;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.68);
}

/* Hero entrance: a title sequence in three movements. JS-gated so the
   no-script page is simply static and visible; transform and opacity
   only, so the sequence cannot shift layout (load CLS stays 0.000).

   Movement 1, the masthead (0 to ~0.95s). The eyebrow's accent rule
   draws first: the opening pixel of motion is a wipe, an authored
   gesture, not a fade. It hands off to the wordmark, which is the one
   element granted more mass than the shared rise: deeper travel, a hair
   of scale settle, --dur-grand instead of --dur-slow, origin pinned
   near its left baseline so the settle reads as weight landing, not
   zoom. Reading is never gated on the choreography: this ease front-
   loads its motion, so the headline is legible by ~0.4s and the deck by
   ~0.75s, well inside the 1.2s readability budget.

   Movement 2, the evidence (~0.26 to 1.45s). Deck-lead, mark, deck,
   meta keep the reading order. The identity mark draws its three
   weights as one continuous overlapping gesture (each bar starts as the
   previous one is ~90% home). The meta chips arrive as a single unit,
   because they are one fact row, not four claims. The stat strip breaks
   from a block into four beats, left to right: those numbers are the
   page's opening argument and they should stamp in like ledger entries,
   not fade in like furniture.

   Movement 3, chrome (~0.9 to 1.75s). Navigation arrives only after
   content: the dot rail assembles top to bottom off the right edge, the
   mobile chip-bar fades up. The reader watches the page finish
   composing, then sees the controls. The closing beat is not an element
   at all: the Status chip's live dot starts pulsing at 1.55s (see
   .live-dot above), so the sequence resolves on the page's heartbeat.

   Replays: the whole sequence is skipped via html.entered (set inline
   in the head) once this tab session has seen it, and on any URL that
   carries a hash, because a deep-linked reader lands mid-page and
   should never watch chrome assemble around content they are already
   reading. */

@keyframes hero-rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* The wordmark's own arrival. 0.985 is deliberately at the threshold of
   perception: any smaller reads as a zoom effect, which is exactly the
   AI-fintech tell this page avoids. */
@keyframes hero-mass {
  from { opacity: 0; transform: translateY(26px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

@keyframes mark-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes rail-in {
  from { opacity: 0; transform: translateX(10px); }
  to   { opacity: 1; transform: none; }
}

/* Opacity only: the chip-bar is sticky glass and a transform would both
   look wrong on a pinned strip and create a containing block under it. */
@keyframes chrome-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

html.js .hero > * {
  animation: hero-rise var(--dur-slow) var(--ease) both;
}

/* Timing map, movement 1 + 2. Delays are the full choreography in one
   place; durations are the shared rise unless noted. */
html.js .hero .eyebrow    { animation-delay: 0s;    }
html.js .hero .deck-lead  { animation-delay: 0.26s; }
html.js .hero .mark       { animation-delay: 0.34s; }
html.js .hero .deck       { animation-delay: 0.40s; }
html.js .hero .meta       { animation-delay: 0.55s; }

/* The eyebrow's rule wipes while its label fades up around it; the wipe
   runs a beat behind the fade so its start is actually visible. */
.eyebrow::before { transform-origin: 0 50%; }
html.js .eyebrow::before {
  animation: mark-draw var(--dur-normal) var(--ease) 0.05s both;
}

html.js .hero h1 {
  transform-origin: 0 80%;
  animation: hero-mass var(--dur-grand) var(--ease) 0.10s both;
}

html.js .hero .mark span {
  animation: mark-draw var(--dur-normal) var(--ease) both;
}

html.js .hero .mark .mark-pq { animation-delay: 0.44s; }
html.js .hero .mark .mark-oc { animation-delay: 0.58s; }
html.js .hero .mark .mark-sd { animation-delay: 0.70s; }

/* Stat strip: container stays put, the four cards carry the animation
   as beats. 80ms apart is close enough to read as one gesture, far
   enough apart to count. */
html.js .hero .stat-strip { animation: none; }

html.js .hero .stat-strip li {
  animation: hero-rise var(--dur-slow) var(--ease) both;
}

html.js .hero .stat-strip li:nth-child(1) { animation-delay: 0.66s; }
html.js .hero .stat-strip li:nth-child(2) { animation-delay: 0.74s; }
html.js .hero .stat-strip li:nth-child(3) { animation-delay: 0.82s; }
html.js .hero .stat-strip li:nth-child(4) { animation-delay: 0.90s; }

/* Movement 3: the rail assembles top to bottom, sliding in from the
   edge it lives on. 50ms per dot: at six dots anything slower turns a
   flourish into a wait. */
html.js .dotrail .dot-link {
  animation: rail-in var(--dur-chrome) var(--ease) both;
}

html.js .dotrail .dot-link:nth-child(1) { animation-delay: 1.05s; }
html.js .dotrail .dot-link:nth-child(2) { animation-delay: 1.10s; }
html.js .dotrail .dot-link:nth-child(3) { animation-delay: 1.15s; }
html.js .dotrail .dot-link:nth-child(4) { animation-delay: 1.20s; }
html.js .dotrail .dot-link:nth-child(5) { animation-delay: 1.25s; }
html.js .dotrail .dot-link:nth-child(6) { animation-delay: 1.30s; }

/* The mobile chip-bar is still tappable while transparent; 0.9s of
   invisible chrome is an acceptable price for content-first order. */
html.js .chipbar {
  animation: chrome-fade var(--dur-normal) var(--ease) 0.9s both;
}

/* One showing per tab session. html.entered (head script) drops every
   entrance animation so reloads, back-navigations, and deep links get
   the settled page instantly. Kept after every delay rule above so the
   shorthand wins each longhand tie. The live dot is deliberately absent
   from this list: its pulse must keep running (its delay reset lives
   with the .live-dot rules). */
html.js.entered .hero > *,
html.js.entered .hero h1,
html.js.entered .eyebrow::before,
html.js.entered .hero .mark span,
html.js.entered .hero .stat-strip li,
html.js.entered .dotrail .dot-link,
html.js.entered .chipbar {
  animation: none;
}

/* 11. Acts — the production MPAct rhythm: 100px between acts, 30px
   under the masthead, and every act opens with the .actlab row: a
   tracked teal number, the act name in small caps, a fading gradient
   hairline to the right edge, and a right-aligned orientation hint. */

main > .act { margin-top: 100px; }
main > .act:first-of-type { margin-top: 30px; }

.act-head { margin-bottom: clamp(2rem, 5.5vh, 3rem); }

.actlab {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 2px 18px;
}

.actlab-num {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: rgba(45, 212, 191, 0.75);
  flex-shrink: 0;
}

.actlab-name {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--lab-name);
  flex-shrink: 0;
}

.actlab-rule {
  flex: 1;
  height: 1px;
  min-width: 2rem;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0));
}

.actlab-hint {
  display: none;
  font-size: 11.5px;
  font-weight: 450;
  color: var(--lab-hint);
  text-align: right;
}

@media (min-width: 40rem) {
  .actlab-hint { display: inline; }
}

/* The close act's number slot carries the identity mark instead. */
.actlab-mark {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

.actlab-mark i {
  display: block;
  height: 2px;
  border-radius: var(--r-pill);
}

.actlab-mark i:nth-child(1) { width: 9px; background: var(--comp-pq); }
.actlab-mark i:nth-child(2) { width: 5px; background: var(--comp-oc); }
.actlab-mark i:nth-child(3) { width: 4px; background: var(--comp-sd); }

.act h2 {
  font-size: clamp(2rem, 5vw, 3rem);
  line-height: 1.05;
  letter-spacing: -0.038em;
  font-weight: 600;
  margin: 0;
}

.act h3 {
  font-size: clamp(1.3125rem, 2.6vw, 1.625rem);
  line-height: 1.24;
  letter-spacing: -0.028em;
  font-weight: 600;
  margin: 4rem 0 1.25rem;
}

.act h4 {
  font-size: 1.0625rem;
  line-height: 1.4;
  letter-spacing: -0.018em;
  font-weight: 600;
  margin: 0 0 0.6rem;
}

.act p {
  margin: 0 0 1.4rem;
  color: var(--body);
}

.act p strong { color: var(--heading); font-weight: 600; }
.act em { font-style: italic; color: var(--heading-soft); }

.act .lede {
  font-size: clamp(1.1875rem, 2.4vw, 1.4375rem);
  line-height: 1.52;
  letter-spacing: -0.022em;
  color: var(--heading-soft);
  margin: 1.5rem 0 0;
}

/* 12. Split cards */

.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 14px;
  margin: 2.5rem 0 0;
}

.split-card {
  background: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
  padding: 1.6rem 1.7rem;
}

.split-card h3 {
  margin: 0 0 0.3rem;
  font-size: 1.1875rem;
  letter-spacing: -0.02em;
}

.split-card .split-role {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--lab-eyebrow);
  margin: 0 0 1.05rem;
}

.split-card p:last-child {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.62;
  color: var(--body);
}

/* 13. Tables */

.table-wrap {
  overflow-x: auto;
  margin: 2.25rem 0;
  border: 1px solid var(--border-card);
  border-radius: var(--r-card);
  background: var(--fill-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
  -webkit-overflow-scrolling: touch;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.tier-table  { min-width: 38rem; }
.plain-table { min-width: 26rem; }
.caps-table  { min-width: 34rem; }

thead th {
  text-align: left;
  font-size: 0.65625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--lab-strong);
  padding: 1.05rem 1.4rem;
  border-bottom: 1px solid var(--border-card);
  white-space: nowrap;
}

tbody td {
  padding: 0.95rem 1.4rem;
  border-bottom: 1px solid var(--border-sub);
  color: var(--body);
  vertical-align: middle;
  line-height: 1.5;
}

tbody tr:last-child td { border-bottom: none; }

/* Numerals: sans + tabular, right-aligned like a ledger (production
   surfaces carry tabular-nums on the system face, never a mono family). */
.tier-table th:nth-child(2),
.tier-table td:nth-child(2) { text-align: right; }

.tier-table td:nth-child(2),
.tier-table td:nth-child(3),
.caps-table td:not(:first-child),
.plain-table td:not(:first-child) {
  font-size: 0.875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  color: var(--heading-soft);
}

.caps-table th:not(:first-child),
.caps-table td:not(:first-child),
.plain-table th:not(:first-child),
.plain-table td:not(:first-child) { text-align: right; }

.caps-table td.na {
  font-size: 0.875rem;
  font-weight: 450;
  font-style: italic;
  color: var(--body-muted);
}

/* Weight cells carry a small gauge of the same value, in the exact
   canonical tier color. */
.w-cell {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}

.w-bar {
  position: relative;
  width: 4.25rem;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--track);
  overflow: hidden;
  flex-shrink: 0;
}

.w-bar::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--w) * 1%);
  border-radius: var(--r-pill);
}

.w-bar.w-a::before { background: var(--tier-a); }
.w-bar.w-b::before { background: var(--tier-b); }
.w-bar.w-c::before { background: var(--tier-c); }
.w-bar.w-d::before { background: var(--tier-d); }
.w-bar.w-x::before { background: transparent; }

td.yes { color: var(--heading-soft); white-space: nowrap; }
td.no  { color: var(--body-muted); white-space: nowrap; }

td.yes::before,
td.no::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  margin-right: 0.6rem;
  vertical-align: 1px;
}

td.yes::before { background: var(--teal); }
td.no::before  { border: 1px solid var(--meta-faint); }

.excluded-row td {
  color: var(--body-muted);
  background: repeating-linear-gradient(
    -45deg,
    rgba(255, 255, 255, 0.02) 0 1px,
    transparent 1px 8px
  );
}

.excluded-row td:nth-child(2),
.excluded-row td:nth-child(3) { color: var(--body-muted); }

/* Reconciles the domain column against the 378 quoted in the prose, so a
   reader who adds the column finds the answer at the table rather than two
   paragraphs later. */
.table-note {
  margin: 0.85rem 0 0;
  max-width: var(--measure);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--body-muted);
}

/* 14. Tier chips: solid canonical fills, near-black glyph. */

.tier-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 8px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #0B0E13;
  flex-shrink: 0;
}

.tier-a { background: var(--tier-a); }
.tier-b { background: var(--tier-b); }
.tier-c { background: var(--tier-c); }
.tier-d { background: var(--tier-d); }

.tier-x {
  background: var(--fill-sub);
  border: 1px solid var(--border-sub);
  color: var(--body-muted);
}

/* `.act em` (below) styles emphasis as italic --heading-soft. The Ori demo
   marks its tier badges up as <em class="tier-chip">, which inherited that
   and rendered near-white glyphs on a bright mint chip: measured 1.52:1 on
   tier A, 1.69:1 on tier B, effectively invisible. A tier chip is a badge,
   never emphasis, so it re-asserts its own ink at a specificity `.act em`
   cannot reach. */
.act em.tier-chip { font-style: normal; color: #0B0E13; }
.act em.tier-x    { color: var(--body-muted); }

.excluded-row .tier-x { opacity: 0.85; }

/* 15. Formula. Deliberately mono: it is arithmetic, quoted. */

.formula {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.65rem 1rem;
  padding: 1.6rem 1.9rem;
  margin: 2.25rem 0;
  background-color: var(--shell);
  background-image: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
  font-family: var(--mono);
  font-size: clamp(0.9375rem, 1.9vw, 1.0625rem);
  line-height: 1.5;
  font-variant-numeric: tabular-nums lining-nums;
}

/* The operator is arithmetic being quoted, not decoration, so it sits on
   the AA-safe ramp rather than --meta-faint. */
.f-op { color: var(--meta); }
.f-term { color: var(--heading-soft); white-space: nowrap; }
.f-term .f-w { font-weight: 700; }
.f-pq .f-w { color: var(--comp-pq); }
.f-oc .f-w { color: var(--comp-oc); }
.f-sd .f-w { color: var(--comp-sd); }

@media (max-width: 34rem) {
  .f-term { white-space: normal; }
}

.component-list {
  list-style: none;
  padding: 0;
  margin: 0 0 2.5rem;
  display: grid;
  gap: 1.25rem;
}

@media (min-width: 48rem) {
  .component-list {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.75rem;
  }
}

.component-list li {
  color: var(--body);
  font-size: 0.9375rem;
  line-height: 1.62;
  padding-top: 1rem;
  border-top: 2px solid var(--border-card);
}

.component-list li:nth-child(1) { border-top-color: var(--comp-pq); }
.component-list li:nth-child(2) { border-top-color: var(--comp-oc); }
.component-list li:nth-child(3) { border-top-color: var(--comp-sd); }

.component-list strong { display: block; margin-bottom: 0.25rem; }
.c-pq { color: var(--comp-pq); }
.c-oc { color: var(--comp-oc); }
.c-sd { color: var(--comp-sd); }

/* 16. Calculator: the one shell-grade surface on the page. The readout
   is the production radial dial (PulseRadialHero / RadialGauge): a
   deep-teal ring sweeping to the confidence value, the number large and
   tabular in the center, and the three formula components as production
   sub-panels beside it. */

.calc {
  background-color: var(--shell);
  background-image: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-shell);
  box-shadow: var(--shadow-shell);
  overflow: hidden;
  margin: 3rem 0;
}

.calc-head {
  padding: 1.6rem clamp(1.25rem, 4vw, 2rem) 1.35rem;
  border-bottom: 1px solid var(--border-card);
}

.calc-head h4 { margin: 0 0 0.3rem; }

.calc .calc-head p {
  margin: 0;
  font-size: 0.875rem;
  color: var(--body-muted);
}

.calc-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
}

.calc-controls {
  padding: 1.9rem clamp(1.25rem, 4vw, 2rem);
  display: grid;
  gap: 1.15rem;
  align-content: center;
  border-right: 1px solid var(--border-card);
}

@media (max-width: 46rem) {
  .calc-body { grid-template-columns: 1fr; }
  .calc-controls {
    border-right: none;
    border-bottom: 1px solid var(--border-card);
  }
}

.calc-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.calc-row-label {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  font-size: 0.9375rem;
  line-height: 1.35;
  color: var(--heading-soft);
}

/* Weight pill — the production .mpill (uppercase tabular pill, neutral). */
.calc-row-weight {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.10em;
  color: rgba(255, 255, 255, 0.50);
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.025);
  border-radius: 6px;
  padding: 2.5px 7px;
  margin-top: 0.35rem;
  white-space: nowrap;
}

.stepper {
  display: flex;
  align-items: center;
  gap: 0.1rem;
  background: var(--fill-sub);
  border: 1px solid var(--border-sub);
  border-radius: var(--r-pill);
  padding: 0.2rem;
  flex-shrink: 0;
}

.stepper button {
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  color: var(--body-muted);
  font-size: 1.0625rem;
  line-height: 1;
  border-radius: var(--r-pill);
  cursor: pointer;
  font-family: var(--sans);
  transition:
    background var(--dur-reveal) var(--ease),
    color var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease),
    opacity var(--dur-reveal) var(--ease);
}

/* 44px is Apple's minimum touch target and the steppers were sitting at
   40px, close enough to look fine and small enough to mis-tap. The visual
   glyph is unchanged; only the hit area grows. */
@media (pointer: coarse) {
  .stepper button { width: 2.75rem; height: 2.75rem; }
}

.stepper button:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.07);
  color: var(--heading);
}

.stepper button:active:not(:disabled) {
  transform: scale(0.92);
}

.stepper button:disabled {
  opacity: 0.25;
  cursor: default;
}

.stepper button:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
}

.count {
  min-width: 1.75rem;
  text-align: center;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--heading);
}

/* The readout: dial + sub-panel bars, wrapping under pressure. */

.calc-readout {
  padding: 1.9rem clamp(1.25rem, 4vw, 2rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.75rem;
}

/* Ring gauge — the RadialGauge recipe: 186px viewport, 10px stroke,
   r = 78 (inset off the stroke edge), butt-capped hairline track,
   round-capped progress arc on the locked deep-teal ramp, and a
   12 o'clock start tick at the arc origin. */

.dial {
  position: relative;
  flex: 0 0 auto;
  width: 186px;
  height: 186px;
  margin-inline: auto;
}

.dial-ring {
  display: block;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.dial-track {
  fill: none;
  stroke: rgba(255, 255, 255, 0.07);
  stroke-width: 10;
}

.dial-arc {
  fill: none;
  stroke: url(#dial-grad);
  stroke-width: 10;
  stroke-linecap: round;
  transition: stroke-dasharray 0.2s linear;
}

.dial-tick {
  fill: var(--ring-teal);
  opacity: 0.9;
}

.dial-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  pointer-events: none;
}

.score {
  display: block;
  font-size: 3.375rem;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -0.035em;
  color: var(--teal);
  transition: opacity var(--dur-normal) var(--ease);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .score {
    background-image: linear-gradient(120deg, #2DD4BF 0%, #22D3EE 60%, #22D3EE 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.calc[data-empty="true"] .score { opacity: 0.35; }

.dial-of {
  font-size: 12px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.66);
  margin-top: 2px;
}

.calc .score-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(45, 212, 191, 0.85);
  margin: 5px 0 0;
}

/* Component bars as production sub-panels (the 7-DAY TRAJECTORY /
   CONFIDENCE IN THIS READ recipe): matte inset panel, small-caps
   eyebrow, bright tabular value, 4px rail. */

.bars {
  flex: 1 1 15rem;
  min-width: 0;
  display: grid;
  gap: 10px;
}

.bar-row {
  padding: 11px 13px 12px;
  border-radius: var(--r-sub);
  background: var(--fill-panel);
  border: 1px solid var(--border-panel);
  box-shadow: var(--shadow-panel);
}

.bar-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 8px;
}

.bar-head span {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--lab-eyebrow);
}

.bar-head output {
  font-size: 12.5px;
  font-weight: 650;
  color: rgba(255, 255, 255, 0.90);
}

.bar-track {
  height: 4px;
  background: var(--track);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  width: var(--fill, 0%);
  border-radius: var(--r-pill);
  transition: width var(--dur-slow) var(--ease);
}

.bar-fill.f-pq { background: var(--comp-pq); }
.bar-fill.f-oc { background: var(--comp-oc); }
.bar-fill.f-sd { background: var(--comp-sd-bar); }

.calc .calc-note {
  margin: 0;
  padding: 1.2rem clamp(1.25rem, 4vw, 2rem);
  border-top: 1px solid var(--border-card);
  background: var(--fill-sub);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--body);
  min-height: 3.6rem;
}

@keyframes note-in {
  from { opacity: 0.35; }
  to   { opacity: 1; }
}

.calc-note.is-fresh { animation: note-in var(--dur-slow) var(--ease) both; }

/* 17. Log-scale corroboration chart. The curve belongs to the outlet
   corroboration component, so it draws in that component's aqua. */

.scale-viz {
  display: grid;
  gap: 0.85rem;
  margin: 2.5rem 0 1rem;
}

.scale-item {
  display: grid;
  grid-template-columns: 2.25rem 1fr 2.5rem;
  align-items: center;
  gap: 1.1rem;
}

.scale-n, .scale-v {
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1;
}

.scale-n { color: var(--body-muted); text-align: right; }
.scale-v { color: var(--heading-soft); text-align: right; }

.scale-bar {
  position: relative;
  display: block;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--track);
  overflow: hidden;
}

.scale-bar::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--v) * 1%);
  border-radius: var(--r-pill);
  background: var(--comp-oc);
}

/* Bars draw in when the group reveals. */
html.js [data-reveal] .scale-bar::before {
  width: 0;
  transition: width 0.9s var(--ease);
}

html.js [data-revealed="true"] .scale-bar::before {
  width: calc(var(--v) * 1%);
}

html.js [data-revealed="true"] .scale-item:nth-child(2) .scale-bar::before { transition-delay: 0.07s; }
html.js [data-revealed="true"] .scale-item:nth-child(3) .scale-bar::before { transition-delay: 0.14s; }
html.js [data-revealed="true"] .scale-item:nth-child(4) .scale-bar::before { transition-delay: 0.21s; }
html.js [data-revealed="true"] .scale-item:nth-child(5) .scale-bar::before { transition-delay: 0.28s; }

.act .viz-caption {
  font-size: 0.8125rem;
  color: var(--body-muted);
  margin: 0 0 2.25rem;
}

/* 18. Latency stat cards — the production anchor-card anatomy: a
   small-caps label over a large tabular value on the card fill. The
   label sits on top purely visually (column-reverse), so the DOM and
   its reading order are untouched. */

.latency-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
  gap: 14px;
  margin: 2.25rem 0;
}

.lat {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: 8px;
  background: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
  padding: 15px 16px 14px;
}

.lat-v {
  display: block;
  font-size: 1.5625rem;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: rgba(255, 255, 255, 0.95);
}

.lat-l {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--lab-strong);
}

/* 19. Findings: open ledger, numbered spine */

.findings {
  list-style: none;
  counter-reset: f;
  padding: 0;
  margin: 2.25rem 0;
  border-bottom: 1px solid var(--border-sub);
}

.findings li {
  counter-increment: f;
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr);
  padding: 1.75rem 0;
  border-top: 1px solid var(--border-sub);
}

.findings li::before {
  content: "0" counter(f);
  grid-column: 1;
  grid-row: 1;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1.9;
  color: rgba(45, 212, 191, 0.75);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

.findings h4,
.findings p { grid-column: 2; }

.findings h4 { margin-bottom: 0.65rem; }

.findings p {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.65;
}

@media (max-width: 40rem) {
  .findings li {
    grid-template-columns: 1fr;
  }

  .findings li::before,
  .findings h4,
  .findings p { grid-column: 1; }

  .findings li::before {
    grid-row: auto;
    margin-bottom: 0.35rem;
  }
}

/* 20. Callout — the production reconcile inset: a bordered teal-tinted
   panel with a small-caps teal label. */

.callout {
  padding: 1rem 1.25rem 1.05rem;
  border-radius: var(--r-sub);
  background: rgba(20, 184, 166, 0.055);
  border: 1px solid rgba(20, 184, 166, 0.16);
  margin: 3rem 0;
}

.callout .callout-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(20, 184, 166, 0.85);
  margin: 0 0 0.5rem;
}

.act .callout p:last-child {
  margin: 0;
  color: rgba(255, 255, 255, 0.86);
  font-size: 0.9375rem;
  font-weight: 450;
  line-height: 1.62;
}

/* 21. Act 03: guardrails, a matched pair of guarantees. Small-caps
   teal slab labels (the evidence-card grammar) on the card fill. */

.guardrails {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 14px;
  margin: 2.5rem 0;
}

.guardrail {
  background: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-card);
  box-shadow: inset 0 1px 0 var(--edge-light), var(--shadow-card);
  padding: 1.6rem 1.7rem;
}

.guardrail-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(45, 212, 191, 0.80);
  margin: 0 0 1rem;
}

.guardrail h4 {
  margin: 0 0 0.6rem;
  font-size: 1.0625rem;
  line-height: 1.35;
}

.guardrail p:last-child {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.62;
  color: var(--body);
}

/* 22. Act 03: pursuit, a three-step process on a spine. */

.pursuit {
  list-style: none;
  padding: 0;
  margin: 2.5rem 0;
  display: grid;
  gap: 0;
}

.pursuit li {
  position: relative;
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  gap: 1.25rem;
  padding: 0 0 1.75rem;
}

.pursuit li:last-child { padding-bottom: 0.25rem; }

/* The connecting spine between step markers. */
.pursuit li::before {
  content: "";
  position: absolute;
  left: 1.3rem;
  top: 2.85rem;
  bottom: 0.25rem;
  width: 1px;
  transform: translateX(-0.5px);
  background: var(--border-card);
}

.pursuit li:last-child::before { display: none; }

.pursuit-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: var(--r-sub);
  background: var(--fill-sub);
  border: 1px solid var(--border-sub);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--teal);
}

.pursuit li p {
  margin: 0;
  padding-top: 0.55rem;
  font-size: 0.9375rem;
  line-height: 1.62;
  color: var(--body);
}

/* 23. Failures: case files on a label spine */

.failure {
  margin: 4rem 0 0;
  padding-top: 3rem;
  border-top: 1px solid var(--border-card);
}

.failure:first-of-type { border-top: none; padding-top: 0.5rem; }

.failure .failure-num {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--neutral);
  margin: 0 0 0.9rem;
}

.failure h3 {
  font-size: clamp(1.375rem, 2.8vw, 1.75rem);
  margin: 0 0 1.9rem;
}

.failure-dl {
  margin: 0 0 1.75rem;
  border-bottom: 1px solid var(--border-sub);
}

.failure-dl > div {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 1.5rem;
  padding: 1.15rem 0;
  border-top: 1px solid var(--border-sub);
}

.failure-dl dt {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--lab-eyebrow);
  padding-top: 0.25rem;
}

.failure-dl dd {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.62;
  color: var(--body);
}

.failure .principle {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 1.5rem;
  margin: 1.9rem 0 0;
  padding: 1.15rem 0 0;
  border-top: 1px solid var(--border-sub);
}

.principle .principle-label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(45, 212, 191, 0.80);
  padding-top: 0.25rem;
}

.act .principle .principle-text {
  font-size: 1rem;
  line-height: 1.62;
  color: var(--heading-soft);
}

@media (max-width: 44rem) {
  .failure-dl > div,
  .principle {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
}

/* 23b. Failure 02 propagation chain. The page's strongest material was
   also its only fully undrawn material: "the failure chains" was a prose
   sentence describing a shape. This draws it. One origin, one inheritance
   step, then a fan into the three surfaces that get corrupted at once,
   which is the actual insight and the thing a sentence buries.

   No new colour enters: hairlines and the label ramp only. The origin node
   carries the same diagonal hatch the excluded tier row uses, because both
   mean "this should never have been treated as evidence". */

/* Breaks out of the failure article's reading measure into the wide grid,
   from the same left edge. The prose stays at 66ch; the diagram uses the
   right field that was otherwise dead space at this width. */
.chain {
  margin: 2.5rem 0 0;
  width: min(var(--wide), calc(100vw - 2 * var(--gutter)));
  max-width: none;
}

.chain-flow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 2.4rem;
  align-items: center;
}

.chain-node,
.chain-out li {
  position: relative;
  border: 1px solid var(--border-card);
  border-radius: var(--r-sub);
  background: var(--fill-card);
  padding: 1rem 1.1rem;
}

.chain-origin {
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,0.028) 0 6px, transparent 6px 12px),
    var(--fill-card);
  border-style: dashed;
  border-color: var(--border-glass);
}

.chain-lab {
  margin: 0 0 0.45rem !important;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lab-eyebrow);
}

.chain h4 {
  margin: 0 0 0.35rem;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--heading-soft);
}

.chain p:not(.chain-lab) {
  margin: 0 !important;
  max-width: none;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--body-muted);
}

.chain-out {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Equal rows are load-bearing: they put the first and last card centres
     at exactly 1/6 and 5/6, which is what lets the spine be drawn in CSS
     without measuring anything. */
  grid-template-rows: repeat(3, 1fr);
  gap: 0.7rem;
}

/* Equal rows mean the shorter cards would otherwise pool dead space at the
   bottom, and the connector would meet text that is not where it points. */
.chain-out li {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Connectors. Hairline runs, chevron terminators, all in the meta ramp. */
.chain-flow :is(.chain-origin, .chain-lift)::before,
.chain-out::after,
.chain-out li::before {
  content: "";
  position: absolute;
  background: var(--border-glass);
}

/* origin -> lift, and lift -> the fan spine */
.chain-origin::before,
.chain-lift::before {
  top: 50%;
  right: -2.4rem;
  width: 2.4rem;
  height: 1px;
}

/* the fan spine: a vertical run touching the centre of the first and last
   outcome. Equal grid rows put those at exactly 1/6 and 5/6. */
.chain-out::after {
  left: -1.2rem;
  top: 16.667%;
  height: 66.666%;
  width: 1px;
}

/* spine -> each outcome */
.chain-out li::before {
  top: 50%;
  left: -1.2rem;
  width: 1.2rem;
  height: 1px;
}

.chain figcaption {
  margin: 1.1rem 0 0;
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--meta);
}

/* Below the three-column threshold the flow turns vertical: same nodes,
   same spine logic rotated, nothing hidden. */
@media (max-width: 62rem) {
  .chain-flow {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.9rem;
  }
  .chain-origin::before,
  .chain-lift::before {
    top: auto;
    bottom: -1.9rem;
    left: 1.6rem;
    right: auto;
    width: 1px;
    height: 1.9rem;
  }
  .chain-out::after {
    left: 1.6rem;
    top: -1.9rem;
    height: 1.9rem;
  }
  .chain-out li::before { display: none; }
}

/* 24. Close and footer */

.close .act-head { margin-bottom: 2.25rem; }
.close .act-head h2 { margin-bottom: 0; }

/* The one outbound action on the page. It carries the Vireon BRAND lockup
   (purple into blue), not the TIDE teal, because it points at the brand
   rather than at page content. The teal accent stays reserved for the
   product surface, exactly as in the hero. */

.close > .visit {
  max-width: min(100%, var(--wide));
  margin-top: 3.25rem;
  padding-top: 2.5rem;
  border-top: 1px solid var(--border-sub);
}

.visit {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
}

/* The host line is a caption on the button, not a third peer element, so
   it sits tight under it rather than on the block's rhythm. */
.visit-cta + .visit-host { margin-top: -0.7rem; }

.visit-lead {
  margin: 0;
  max-width: var(--measure);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--body);
}

.visit-cta {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
  padding: 1rem 1.45rem 1rem 1.6rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-glass);
  background: var(--fill-chip);
  box-shadow: var(--shadow-chip);
  color: var(--heading);
  text-decoration: none;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.008em;
  line-height: 1;
  overflow: hidden;
  transition:
    transform var(--dur-reveal) var(--ease),
    border-color var(--dur-reveal) var(--ease),
    box-shadow var(--dur-reveal) var(--ease);
}

/* Brand wash, held behind the label. Absent at rest so the button sits in
   the page's glass system, then fades up on intent. */
.visit-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(100deg, rgba(167, 116, 255, 0.20), rgba(78, 200, 255, 0.16));
  opacity: 0;
  transition: opacity var(--dur-normal) var(--ease);
}

.visit-cta:hover,
.visit-cta:focus-visible {
  transform: translateY(-2px);
  border-color: rgba(167, 116, 255, 0.34);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.30), inset 0 1px 1.5px rgba(255, 255, 255, 0.07);
}

.visit-cta:hover::before,
.visit-cta:focus-visible::before { opacity: 1; }

.visit-cta:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
}

/* The wordmark inside the label, same lockup treatment as .hero h1.
   Solid brand purple where text clipping is unsupported. */
.visit-cta b {
  font-weight: 600;
  color: var(--brand-purple);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .visit-cta b {
    background: linear-gradient(100deg, #A774FF 0%, #4EC8FF 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* The wash is a positioned pseudo-element, so the label and arrow are
   lifted into the same stacking context to stay above it. */
.visit-cta-text,
.visit-cta-arrow { position: relative; }

.visit-cta-arrow {
  display: flex;
  color: var(--lab-strong);
  transition:
    transform var(--dur-reveal) var(--ease),
    color var(--dur-reveal) var(--ease);
}

.visit-cta:hover .visit-cta-arrow,
.visit-cta:focus-visible .visit-cta-arrow {
  transform: translate(2px, -2px);
  color: var(--heading);
}

.visit-host {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--meta);
}

footer {
  margin-top: 100px;
  padding-block: 2.5rem 5.5rem;
  border-top: 1px solid var(--border-card);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.9rem 2.5rem;
}

footer p {
  margin: 0;
  max-width: 56ch;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: var(--body-muted);
}

.footer-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--meta);
  text-decoration: none;
  transition: color var(--dur-reveal) var(--ease);
}

.footer-link svg { transition: transform var(--dur-reveal) var(--ease); }

.footer-link:hover { color: var(--heading-soft); }
.footer-link:hover svg { transform: translate(1.5px, -1.5px); }

.footer-link:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
  border-radius: 4px;
}

/* 25. Scroll reveal. JS-gated: without script, nothing hides. */

html.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
}

html.js [data-reveal][data-revealed="true"] {
  opacity: 1;
  transform: none;
}

/* Grouped reveal: the container observes, children stagger. */
html.js [data-reveal="group"] {
  opacity: 1;
  transform: none;
  transition: none;
}

html.js [data-reveal="group"] > * {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
}

html.js [data-reveal="group"][data-revealed="true"] > * {
  opacity: 1;
  transform: none;
}

html.js [data-reveal="group"][data-revealed="true"] > *:nth-child(2) { transition-delay: 0.09s; }
html.js [data-reveal="group"][data-revealed="true"] > *:nth-child(3) { transition-delay: 0.18s; }
html.js [data-reveal="group"][data-revealed="true"] > *:nth-child(4) { transition-delay: 0.27s; }
html.js [data-reveal="group"][data-revealed="true"] > *:nth-child(5) { transition-delay: 0.36s; }
html.js [data-reveal="group"][data-revealed="true"] > *:nth-child(n+6) { transition-delay: 0.45s; }

/* 26. Reduced motion: the TIDE contract collapses durations to 0.2s,
   entrance choreography and perpetual pulses are removed outright, and
   nothing is ever hidden. The reveal observer also short-circuits in JS. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.2s !important;
    animation-delay: 0s !important;
    transition-duration: 0.2s !important;
    transition-delay: 0s !important;
  }

  html.js .hero > *,
  html.js .hero h1,
  html.js .eyebrow::before,
  html.js .hero .mark span,
  html.js .hero .stat-strip li,
  html.js .dotrail .dot-link,
  html.js .chipbar,
  html.js .live-dot {
    animation: none !important;
  }

  html.js [data-reveal],
  html.js [data-reveal="group"] > * {
    opacity: 1 !important;
    transform: none !important;
  }

  html.js [data-reveal] .scale-bar::before {
    width: calc(var(--v) * 1%);
    transition: none !important;
  }

  .dial-arc { transition: none !important; }
}

/* ───────────────────────────────────────────────────────────────────────────
   27. Ori demo. Was a second block numbered 16, colliding with the
   Calculator section above; it also sits after 26, so its reduced-motion
   overrides (.od-caret, .od-ring-arc) live up there rather than here.
   Renumbered rather than relocated: this file's own case study calls out
   comment drift as a defect class, and a stale number is exactly that.

   A vanilla recreation of the landing page's OriAIVisual
   (vireon_saas_funnel/src/components/landing/LyraAIVisual.jsx): typing ->
   thinking -> response, autoplaying once on scroll.

   Data honesty: the publishers and tiers are real registry entries and the
   score is computed by the production formula at runtime. Nothing here is a
   market claim, so no directional tones appear.
   ─────────────────────────────────────────────────────────────────────────── */

.oridemo {
  margin: 3rem 0;
}

.od-shell {
  background-color: var(--shell);
  background-image: var(--fill-card);
  border: 1px solid var(--border-card);
  border-radius: var(--r-shell);
  box-shadow: var(--shadow-shell);
  overflow: hidden;
}

/* Query bar */
.od-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1.05rem 1.35rem;
  border-bottom: 1px solid var(--border-card);
  min-height: 3.75rem;
}

.od-glyph {
  display: inline-flex;
  flex-direction: column;
  gap: 2.5px;
  flex-shrink: 0;
}
.od-glyph i {
  display: block;
  height: 2px;
  border-radius: 999px;
  background: var(--comp-pq);
}
.od-glyph i:nth-child(1) { width: 14px; }
.od-glyph i:nth-child(2) { width: 9px;  background: var(--comp-oc); }
.od-glyph i:nth-child(3) { width: 5px;  background: var(--comp-sd); }

.od-query {
  flex: 1;
  margin: 0 !important;
  font-size: 0.9375rem;
  color: var(--heading-soft) !important;
  min-width: 0;
}

.od-caret {
  display: none;
  width: 1.5px;
  height: 1.05em;
  margin-left: 1px;
  vertical-align: -0.16em;
  background: var(--teal);
}
.oridemo[data-phase="typing"] .od-caret {
  display: inline-block;
  animation: od-blink 1s steps(1, end) infinite;
}
@keyframes od-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.od-replay {
  flex-shrink: 0;
  font: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--chip-inactive);
  background: var(--fill-chip);
  border: 1px solid var(--border-card);
  border-radius: var(--r-pill);
  padding: 0.34rem 0.8rem;
  cursor: pointer;
  transition: color var(--dur-reveal) var(--ease),
              border-color var(--dur-reveal) var(--ease);
}
.od-replay:hover { color: var(--chip-active-text); border-color: var(--chip-active-border); }
.od-replay:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* Two-layer reveal, per the ui-movement-protocol drawer contract: the
   container's HEIGHT animates first and the content fades in behind it.
   Previously these regions held their full height at opacity 0 from the
   first frame, so the demo opened as a card that was 84% reserved empty
   space (measured: 336px of a 398px shell during the typing phase) and
   .od-stages snapped away on replay via `display: none`. Collapsing with
   grid-template-rows 0fr -> 1fr animates real height on the TIDE curve
   with no measured max-height and no JS.

   `.od-clip` is a BARE clipping layer: no padding, no border, no
   background. That matters. `overflow: hidden` clips a box's content, not
   its own padding or borders, so styling the row child directly left ~127px
   of padding and two stray hairlines standing at 0fr. Pushing all of that
   one level down onto the styled element lets the clip collapse to a true
   zero. */
.od-collapse {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-demo-open) var(--ease);
}

.od-clip {
  min-height: 0;
  overflow: hidden;
}

.od-clip > * {
  opacity: 0;
  /* Content trails the height so the box opens, then fills. */
  transition: opacity var(--dur-normal) var(--ease) var(--delay-demo-fill);
}

/* Stages appear when thinking starts and stay through the answer. */
.oridemo[data-phase="thinking"] .od-stages-wrap,
.oridemo[data-phase="response"] .od-stages-wrap { grid-template-rows: 1fr; }

.oridemo[data-phase="response"] .od-response-wrap,
.oridemo[data-phase="response"] .od-sources-wrap { grid-template-rows: 1fr; }

.oridemo[data-phase="thinking"] .od-stages-wrap .od-clip > *,
.oridemo[data-phase="response"] .od-stages-wrap .od-clip > *,
.oridemo[data-phase="response"] .od-response-wrap .od-clip > *,
.oridemo[data-phase="response"] .od-sources-wrap .od-clip > * { opacity: 1; }

/* Thinking stages */
.od-stages {
  list-style: none;
  margin: 0;
  padding: 1.15rem 1.35rem;
  display: grid;
  gap: 0.6rem;
  border-bottom: 1px solid var(--border-card);
}

.od-stages li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8125rem;
  color: var(--meta);
  /* Was 0.4, which multiplied against the old --meta put pending stages
     below any legibility floor. Pending still reads as pending; it just
     reads. */
  opacity: 0.62;
  transition: opacity var(--dur-normal) var(--ease), color var(--dur-normal) var(--ease);
}
.od-stages li[data-done] { opacity: 1; color: var(--lab-strong); }

.od-tick {
  width: 13px;
  height: 13px;
  border-radius: 999px;
  border: 1px solid var(--border-glass);
  flex-shrink: 0;
  position: relative;
  transition: background var(--dur-normal) var(--ease), border-color var(--dur-normal) var(--ease);
}
.od-stages li[data-done] .od-tick {
  background: var(--accent-grad);
  border-color: transparent;
}
.od-stages li[data-done] .od-tick::after {
  content: "";
  position: absolute;
  left: 4px; top: 1.5px;
  width: 3px; height: 6px;
  border: solid #08130f;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

/* Response */
.od-response {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.75rem;
  align-items: center;
  padding: 1.5rem 1.35rem;
}
.oridemo:not([data-phase="response"]) .od-response { pointer-events: none; }

/* The measure sits on the text elements, not the column: `ch` resolves
   against the element's own font-size, so 60ch on the 17px container was
   still ~86 characters of 15px body copy. */
.od-headline, .od-body { max-width: 62ch; }

.od-headline {
  margin: 0 0 0.6rem !important;
  font-size: 1.0625rem;
  line-height: 1.4;
  letter-spacing: -0.018em;
  font-weight: 600;
  color: var(--heading) !important;
}
.od-body {
  margin: 0 !important;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--body) !important;
}

.od-receipts { text-align: center; flex-shrink: 0; }

.od-ring { position: relative; width: 116px; height: 116px; }
.od-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.od-ring-track {
  fill: none;
  stroke: var(--track);
  stroke-width: 8;
}
.od-ring-arc {
  fill: none;
  stroke: var(--ring-teal);
  stroke-width: 8;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.2s linear;
}
.od-ring-mid {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
}
.od-score {
  font-size: 1.875rem;
  font-weight: 600;
  letter-spacing: -0.035em;
  color: var(--heading);
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 1;
}
.od-of { font-size: 0.6875rem; color: var(--meta); }
.od-ringlab {
  margin: 0.55rem 0 0 !important;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lab-eyebrow) !important;
}

/* Sources */
.od-sources {
  padding: 1.15rem 1.35rem 1.35rem;
  border-top: 1px solid var(--border-card);
  background: var(--fill-sub);
}

.od-srclab {
  margin: 0 0 0.8rem !important;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lab-eyebrow) !important;
  font-variant-numeric: tabular-nums;
}

.od-srclist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.od-srclist li {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.8125rem;
  color: var(--body);
  background: var(--fill-chip);
  border: 1px solid var(--border-card);
  border-radius: var(--r-pill);
  padding: 0.3rem 0.6rem 0.3rem 0.32rem;
}
.od-mono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.35rem;
  height: 1.35rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--m);
  color: #0A0D12;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}
.od-srclist .tier-chip {
  width: 1.05rem;
  height: 1.05rem;
  font-size: 0.5625rem;
  font-style: normal;
}

.oridemo figcaption {
  margin: 0.85rem 0 0;
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--meta);
}

@media (max-width: 40rem) {
  .od-response { grid-template-columns: 1fr; gap: 1.25rem; justify-items: start; }
  .od-receipts { display: flex; align-items: center; gap: 0.9rem; text-align: left; }
  .od-ringlab { margin: 0 !important; }
}

@media (prefers-reduced-motion: reduce) {
  .od-caret { display: none !important; animation: none !important; }
  .od-ring-arc { transition: none; }
}
