/* =====================================================
   EVOGLYPH - Self-hosted fonts (#97)
   Replaces Google Fonts so browsers do not ping
   fonts.googleapis.com / fonts.gstatic.com on page load.
   See fonts/README.md for source URLs.
   ===================================================== */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/InterVariable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('/fonts/JetBrainsMono.woff2') format('woff2-variations');
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Quicksand';
  src: url('/fonts/Quicksand-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* =====================================================
   EVOGLYPH - Component & token helpers (#165, Epic 3)
   These replace the per-element inline style="…" attributes
   that previously set colors/borders straight from the legacy
   :root tokens. Re-homing them into classes is what lets the
   CSP drop style-src 'unsafe-inline' (Epic 10). Each helper is
   APPEARANCE-PRESERVING: it consumes the SAME legacy token the
   inline declaration referenced, so the rendered result is
   byte-identical. The dark-palette flip (new --color-* tokens)
   is Epic 6, not here.

   Text-color helpers. Used on text nodes and on inline SVG icons
   whose paths use stroke/fill="currentColor" (so `color` cascades
   into the glyph) — exactly what the inline `color: var(--…)` did.
   ===================================================== */
.t-primary {
  color: var(--text-primary);
}
.t-secondary {
  color: var(--text-secondary);
}
.t-tertiary {
  color: var(--text-tertiary);
}
.t-accent {
  color: var(--accent);
}
.t-accent-text {
  color: var(--accent-text);
}

/* Top hairline border used by comparison rows and footer dividers. */
.border-subtle-t {
  border-top: 1px solid var(--border-subtle);
}
/* Matching bottom hairline (e.g. the hero proof-strip band). */
.border-subtle-b {
  border-bottom: 1px solid var(--border-subtle);
}

/* Demo waveform bars: solid fill in the tertiary token. The per-bar
   opacity variants (0.6–1.0) stay as Tailwind opacity-* utilities on
   each bar so the staggered look is preserved exactly. */
.wave-bar-fill {
  background: var(--text-tertiary);
}

/* Hero "See the benchmark →" link — underlined accent text. */
.hero-benchmark-link {
  /* #557: one token rung above the note's tertiary body text — reads as a
     link without popping bright white out of a muted footnote. */
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* =====================================================
   EVOGLYPH - Skip-link (#101)
   Hidden until focused via Tab; lets keyboard users jump
   past the nav directly into the main landmark.
   ===================================================== */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  padding: 8px 16px;
  background: var(--accent);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: 0 0 8px 0;
  z-index: 1000;
  transform: translateY(-100%);
  /* Also hide via opacity, not just the off-screen transform: the element
     physically sits just above the page top, and iOS Safari's rubber-band
     overscroll on a fast scroll-to-top renders that region — which flashed the
     link over the header (#495). opacity:0 keeps it invisible there while
     staying keyboard-focusable (unlike visibility/display, which drop it from
     the tab order). */
  opacity: 0;
  transition:
    transform 150ms ease-out,
    opacity 150ms ease-out;
}
.skip-link:focus {
  transform: translateY(0);
  opacity: 1;
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* =====================================================
   EVOGLYPH - Design Tokens (dark-flip, Epic 6 / #168)

   The legacy hardcoded-hex token system is RETIRED. These legacy
   names (--bg-*, --text-*, --accent*, --border-*) now ALIAS the
   canonical dark-first OKLCH semantic tokens defined in src/main.css
   (the @theme --color-* layer). Every component below consumes the
   legacy names, so re-pointing the aliases flips the whole site to
   the premium dark palette in one block.

   Dark-mode only (#223): there is no light palette and no theme
   toggle. The aliases below resolve through the single dark --color-*
   token set in src/main.css; `color-scheme: dark` there locks native
   UI to dark as well.

   Shadows are kept subtle: on a dark canvas, depth comes mostly from
   hairline borders (Raycast/Linear style), so the drop-shadows are
   light-touch dark-tinted values rather than the old strong shadows.
   ===================================================== */

:root {
  /* Command Mode accent (#662). Mirrors the in-app command state, and the demo
     loop's own blue, so a command-state element on the site reads as the same
     thing it means in the product. Lives here rather than src/main.css @theme
     because only legacy .eg-* component classes consume it — no Tailwind
     utility form is needed. Values proposed by the design handoff and PENDING
     OWNER CONFIRMATION; today the only consumer is .eg-newchip.
     NEVER use this as a section background or a general accent: the brand
     gradient (--eg-mbc) remains the site's accent. */
  --eg-command: #2f7bff;
  --eg-command-text: #8fc2ff;
  --eg-command-border: rgba(143, 194, 255, 0.4);

  --bg-primary: var(--color-canvas);
  --bg-secondary: var(--color-surface-1);
  --bg-tertiary: var(--color-surface-2);
  --bg-card: var(--color-surface-1);
  --bg-card-hover: var(--color-surface-2);
  --border-primary: var(--color-border-hairline);
  --border-subtle: var(--color-border-subtle);
  --text-primary: var(--color-text-primary);
  --text-secondary: var(--color-text-secondary);
  --text-tertiary: var(--color-text-muted);
  /* The PRIMARY accent is the MBC gradient (--eg-mbc); --accent is the
     SECONDARY periwinkle (hue 274) solid used only where a gradient can't go
     — tint washes, dots, checks, link underlines. Dark-mode only (#223). White
     label text on the periwinkle fill is AA, so accent-text resolves to
     text-primary (near-white on the dark canvas) for elements that sit on the
     canvas, while buttons that fill with --accent pair it with explicit white
     in the component rules below. */
  --accent: var(--color-accent);
  /* accent-light / accent-muted are TINT SURFACES (icon chips, highlighted
     cards, soft glows), not solid accent fills. Express them as low-alpha
     mixes of the accent into transparent so they read as a faint periwinkle
     wash on the dark canvas (#223, dark-mode only) while keeping a "barely
     there" weight. */
  --accent-light: color-mix(in oklch, var(--color-accent) 16%, transparent);
  --accent-text: var(--color-text-primary);
  --accent-muted: color-mix(in oklch, var(--color-accent) 10%, transparent);
  /* Sky-blue accent (= the MBC gradient's mid stop #6cbeff) used by the
     redesign for numbered-step circles, the cleanup arrow, and the pipeline
     nodes — distinct from the periwinkle icon accent. High-contrast on the
     dark canvas (AA for small text). */
  --accent-blue: #6cbeff;
  /* Deeper, more saturated blue for the founder-pricing badges (#499) — set
     apart from the green value/feature cues. Lifted just enough off a pure
     #0055ff to clear WCAG AA (~5:1 on the card surface) for the small,
     uppercase badge text; #0055ff itself only reaches ~3.4:1. */
  --accent-blue-deep: #3b7bff;
  /* The MBC gradient token (--eg-mbc) + --eg-spectrum are the
     PRIMARY UI accent (#221, MBC #222). They are declared in src/main.css @theme
     so they compile into dist/tailwind.css and resolve on every page; the
     component rules below consume them directly. The brand mark itself is
     now a static PNG (evoglyph-mark.png), not a CSS gradient (#430). */
  --shadow-sm: 0 1px 2px oklch(0 0 0 / 0.3);
  --shadow-md: 0 4px 14px oklch(0 0 0 / 0.4);
  --shadow-lg: 0 16px 48px oklch(0 0 0 / 0.5);
}

/* =====================================================
   EVOGLYPH - Custom Styles
   ===================================================== */

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

/* NOTE (#215): `scroll-behavior: smooth` was removed. It only affects
   programmatic/anchor jumps (which script.js already handles via
   scrollIntoView), never wheel/trackpad scroll, and a global smooth-scroll can
   hurt perceived scroll performance. In-page anchor smoothness is unchanged. */

body {
  font-family:
    'Inter',
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background-color: var(--bg-primary);
  color: var(--text-primary);
  overflow-x: hidden;
}

/* Backdrop layering (#215): the <body> stays OPAQUE (the canvas above). The
   fixed `.site-backdrop` paints at z-index:0 — OVER the body background but
   UNDER the page content, which is lifted to z-index:1 here so it scrolls over
   the fixed aurora. Keeping the body opaque is deliberate: it preserves the
   contrast-resolution behaviour axe relies on (see src/main.css). The backdrop
   itself is the FIRST body child and is excluded from the lift.

   `position: relative` is only added where an element is statically positioned;
   the section/footer already carry Tailwind's `.relative`, so z-index alone
   takes effect there. The nav already sits at z-50. */
body[data-page='home'] > section,
body[data-page='home'] > footer,
/* developers wraps its sections in <main> (unlike home, whose sections are direct body children) */
body[data-page='developers'] > footer,
body[data-page='developers'] > main > section {
  position: relative;
  z-index: 1;
}

/* =====================================================
   TEXT WRAPPING (Principle 10)
   - balance for short headings (no widows/orphans)
   - pretty for long-form paragraphs
   ===================================================== */
h1,
h2,
h3 {
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

/* =====================================================
   TYPOGRAPHY
   ===================================================== */

.wordmark {
  font-family: 'Quicksand', 'Inter', sans-serif;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

/* Static PNG mark used by the app surfaces (login / dashboard /
   admin) and the non-partial docs pages. The nav + footer
   partials now use the animated .eg-mark construct below (#217),
   but these pages are intentionally out of scope, so this rule
   stays to keep their static mark unchanged. */
.evoglyph-logo {
  flex-shrink: 0;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}

.evoglyph-logo:hover,
a:hover .evoglyph-logo {
  opacity: 1;
}

/* =====================================================
   BRAND MARK (nav + footer). Static holographic glyph PNG
   + plain wordmark. The animated hover treatment (#217 /
   #219) was removed pre-launch (#430); a lighter motion may
   return later. The full CSS construct is in git history.
   ===================================================== */
.eg-mark {
  flex-shrink: 0;
  width: 34px;
  height: auto;
  /* Subtle bloom echoing the glyph's own glow. */
  filter: drop-shadow(0 0 4px rgb(255 255 255 / 0.18));
}

.eg-wordmark {
  color: var(--text-primary);
}

/* Header-only: a subtle, bottom-weighted white glow under the
   wordmark. Footer wordmark intentionally untouched (#336). */
#navbar .eg-wordmark {
  text-shadow:
    0 1px 6px rgba(255, 255, 255, 0.42),
    0 3px 16px rgba(255, 255, 255, 0.28);
}

.inline-code {
  font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
  font-size: 0.85em;
  padding: 0.15em 0.45em;
  border-radius: 5px;
  background: var(--accent-muted);
  border: 1px solid var(--border-primary);
  color: var(--accent-text);
}

/* =====================================================
   NAVBAR
   ===================================================== */

#navbar {
  background: transparent;
}

#navbar.scrolled {
  /* Near-opaque solid instead of a translucent glass pane. A blurred-backdrop
     pane is CPU-rendered on Safari/iOS (the macOS audience) — a known perf
     trap (decision L3). 92% surface-1 over the canvas reads as a clean
     elevated bar with no compositing-cost blur pass. */
  background: color-mix(in oklch, var(--color-surface-1) 92%, transparent);
  border-bottom: 1px solid var(--border-subtle);
}

.nav-link {
  color: var(--text-secondary);
  position: relative;
  /* Min 40x40 hit area (Principle 16) without changing visual size.
     The pseudo-element extends the click target. */
}

.nav-link::before {
  content: '';
  position: absolute;
  inset: -10px -6px;
  /* Hit-target overlay only - no background. Adjacent nav-links sit
     in a flex row with gap-8 (32px), so 6px horizontal extension
     never overlaps neighbours. */
}

.nav-link:hover {
  color: var(--text-primary);
}

/* Active-page highlight for the single-source nav (#164). The nav partial
   carries data-nav="features|pricing|benchmark|changelog|docs|home" on its
   orientation links; each page sets data-page="…" on <body>. The matching
   link is promoted to the primary text colour, replacing the per-page inline
   active styles that used to live in the duplicated navs. */
body[data-page='features'] [data-nav='features'],
body[data-page='pricing'] [data-nav='pricing'],
body[data-page='benchmark'] [data-nav='benchmark'],
body[data-page='changelog'] [data-nav='changelog'],
body[data-page='docs'] [data-nav='docs'],
body[data-page='home'] [data-nav='home'] {
  color: var(--text-primary);
}

/* The brand UI accent is the MBC gradient (#221, MBC #222). The nav CTA — like
   all primary CTAs — is a transparent pill with a ~3px MBC gradient BORDER and a
   white label (legible on the dark nav). The gradient lives on a ::before
   pseudo (see the shared .cta-primary/.nav-cta/.pricing-btn-primary rule below)
   so the LABEL is not masked out. Only colour/label state lives here. */
.nav-cta {
  color: oklch(0.99 0 0);
  /* Declared here (not only as a markup utility) so BOTH instances — the
     desktop nav pill and the mobile-menu copy, which carries no `transition`
     utility — get the same interruptible hover/press motion as .cta-primary. */
  transition-property: background-color, box-shadow, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.nav-cta:active {
  transform: scale(0.96);
}

.nav-cta:hover {
  color: oklch(0.99 0 0);
}

/* =====================================================
   HERO
   ===================================================== */

/* NOTE (#215): the in-hero `.grid-overlay` was removed at the owner's request.
   The hero background now comes solely from the fixed `.site-backdrop` aurora
   (src/main.css), with no grid lines. */

/* =====================================================
   CTA BUTTONS
   ===================================================== */

/* ---------- MBC gradient-border CTAs (#221, MBC #222) ----------
   Shared by every primary call-to-action: hero "Download for macOS", the nav
   CTA, the featured-plan pricing button, the closing CTA, and the token-system
   .btn-primary. The button itself is transparent (the dark page/section shows
   through the interior); a ::before pseudo paints the 3px MBC ring.

   Why a pseudo and not `border-image`: the ring is drawn by filling the pseudo
   with the gradient as a `border-box` background, then masking AWAY the
   content-box rectangle (mask-composite: exclude) so only a 3px frame remains.
   Crucially the gradient is on the PSEUDO, never on the element, so the button's
   real text/icon children are NOT clipped to the gradient (a background-clip:text
   approach would erase the white label). pointer-events:none keeps clicks live. */
.cta-primary,
.nav-cta,
.pricing-btn-primary {
  position: relative;
  background: transparent;
  border: 0;
  box-shadow: none;
}

.cta-primary::before,
.nav-cta::before,
.pricing-btn-primary::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: var(--eg-mbc) border-box;
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  transition:
    filter 200ms cubic-bezier(0.2, 0, 0, 1),
    opacity 200ms cubic-bezier(0.2, 0, 0, 1);
}

/* Hover: a faint rainbow-tint interior fill + a soft gradient glow, and the ring
   brightens a touch. Subtle — the page still reads through the interior. The
   tint uses a representative spectrum hue so the wash isn't muddy. */
.cta-primary:hover,
.nav-cta:hover,
.pricing-btn-primary:hover {
  background: color-mix(in oklch, var(--eg-spectrum) 12%, transparent);
  box-shadow: 0 8px 28px color-mix(in oklch, var(--eg-spectrum) 28%, transparent);
}

.cta-primary:hover::before,
.nav-cta:hover::before,
.pricing-btn-primary:hover::before {
  filter: brightness(1.12) saturate(1.1);
}

.cta-primary:hover {
  transform: translateY(-1px);
}

/* Keyboard focus: a clear ring that doesn't rely on the (decorative) gradient
   border, so it reads for low-vision users in both themes. */
.cta-primary:focus-visible,
.nav-cta:focus-visible,
.pricing-btn-primary:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

/* Primary CTA: the brand accent is the rainbow gradient (#221). The button is a
   TRANSPARENT pill with a ~3px rainbow gradient border and a white centred
   label. State (lift, press, glow) lives in the shared rule below; the colour
   is white text on the dark page showing through the transparent interior. */
.cta-primary {
  color: oklch(0.99 0 0);
  transition-property: background-color, box-shadow, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.cta-primary:active {
  transform: scale(0.96);
}

.cta-secondary {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  color: var(--text-secondary);
  transition-property: background-color, border-color, color, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.cta-secondary:hover {
  background: var(--bg-card-hover);
  border-color: var(--text-tertiary);
  color: var(--text-primary);
}

.cta-secondary:active {
  transform: scale(0.96);
}

/* Optical alignment (make-interfaces-feel-better / surfaces): a button that
   pairs a leading download icon with a text label feels balanced when the
   icon side carries ~2px less padding than the text side. Applied to the
   download CTAs (hero + final) which use the .cta-with-icon marker. The
   base px-* utility is dropped from those anchors in favour of this rule. */
.cta-with-icon {
  padding-left: 1.75rem; /* 28px text side */
  padding-right: 1.625rem; /* 26px icon side (= text - 2px) */
}

/* =====================================================
   HERO TERMINAL
   ===================================================== */

/* The terminal/code-sample surfaces are deliberately a darker "device" panel
   that reads the same in both themes (a code window is always dark). They are
   re-expressed off the ink ramp so they sit a notch below the canvas with a
   hairline edge — and the blurred backdrop is removed (decision L3). */
.terminal-header {
  background: oklch(1 0 0 / 0.03);
  border-bottom: 1px solid oklch(1 0 0 / 0.06);
}

/* Recording indicator */
.recording-pulse {
  animation: pulse-rec 1.5s ease-in-out infinite;
}

@keyframes pulse-rec {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(0.85);
  }
}

/* Pulse dot in badge */
.pulse-dot {
  animation: pulse-badge 2s ease-in-out infinite;
}

@keyframes pulse-badge {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.4;
  }
}

/* =====================================================
   SECTION LABELS
   ===================================================== */

.section-label {
  color: var(--accent-text);
}

/* =====================================================
   FEATURE CARDS
   ===================================================== */

.feature-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, border-color, transform, box-shadow;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.feature-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-primary);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.feature-icon-bg {
  background: var(--accent-light);
  border: 1px solid var(--accent-muted);
}

/* =====================================================
   COMPARISON TABLE
   ===================================================== */

.comparison-header {
  background: var(--bg-secondary);
}

.comparison-row:hover {
  background: var(--bg-card-hover);
}

.comparison-row {
  border-color: var(--border-subtle);
}

/* =====================================================
   PRICING CARDS
   ===================================================== */

.pricing-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, border-color, transform, box-shadow;
  transition-duration: 400ms;
  /* easeOutQuint — a smooth, settled lift/glow (was a snappier 300ms ease). */
  transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}

/* The tile lifts + glows ONLY when its CTA is hovered/focused (#480), never on
   bare tile hover — the affordance must match the actually-clickable element
   (the button), not the whole non-clickable tile. :has() lets the card react to
   its own button's state; it pairs with the CTA fill-in below for one cohesive
   active look triggered by a single element. */
.pricing-card:has(.pricing-cta:hover),
.pricing-card:has(.pricing-cta:focus-visible) {
  transform: translateY(-4px);
  border-color: var(--border-primary);
  box-shadow:
    var(--shadow-lg),
    0 0 60px -30px color-mix(in oklch, var(--accent-blue) 45%, transparent);
}

/* .pricing-btn-primary / .pricing-btn-secondary are retained for thank-you.html
   (the only remaining consumer); the pricing tiles themselves now use the
   interactive .pricing-cta below (#480 dropped the featured tile + badge). */
.pricing-btn-primary {
  color: oklch(0.99 0 0);
  transition-property: background-color, box-shadow, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.pricing-btn-primary:active {
  transform: scale(0.96);
}

.pricing-btn-secondary {
  background: var(--bg-secondary);
  border: 1px solid var(--border-primary);
  color: var(--text-secondary);
  transition-property: background-color, border-color, color, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.pricing-btn-secondary:hover {
  background: var(--bg-tertiary);
  border-color: var(--text-tertiary);
  color: var(--text-primary);
}

.pricing-btn-secondary:active {
  transform: scale(0.96);
}

/* Pricing CTAs (#480) — one uniform treatment across all three tiles, no
   precedence. BASE: a transparent pill with the MBC gradient OUTLINE (the
   ::before ring, masked) and a white label. ACTIVE (the parent .pricing-card is
   hovered, or the CTA is keyboard-focused): the gradient FILL (::after) fades in
   and the label flips white→dark ink — landing on the look the Monthly tile had.
   Hover is driven at the CARD level so pointing anywhere on a tile lights its
   CTA; :focus-visible mirrors it for keyboard users. Both pseudos sit at
   z-index:-1 (isolation:isolate scopes the stack) so the label stays on top. */
.pricing-cta {
  position: relative;
  isolation: isolate;
  background: transparent;
  border: 0;
  color: oklch(0.99 0 0);
  /* Label color fades white→dark in lockstep with the fill (::after) so the two
     read as one gradual change; ease-in-out (not ease-out) avoids the
     front-loaded "pop". The press (:active) transform stays snappy. */
  transition:
    color 500ms ease-in-out,
    transform 200ms cubic-bezier(0.2, 0, 0, 1);
}

/* BASE — gradient outline ring (same mask trick as .cta-primary). */
.pricing-cta::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  padding: 2px;
  background: var(--eg-mbc) border-box;
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* ACTIVE — gradient fill, painted above the ring (later pseudo) but below the
   label; fades in on the active states below. */
.pricing-cta::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--eg-mbc);
  opacity: 0;
  pointer-events: none;
  /* Gradual 500ms ease-in-out fade — not a front-loaded pop. */
  transition: opacity 500ms ease-in-out;
}

.pricing-cta:hover,
.pricing-cta:focus-visible {
  color: oklch(0.18 0.02 264);
}

.pricing-cta:hover::after,
.pricing-cta:focus-visible::after {
  opacity: 1;
}

.pricing-cta:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.pricing-cta:active {
  transform: scale(0.98);
}

/* Reduced motion: keep the state change but make it instant (no fades). */
@media (prefers-reduced-motion: reduce) {
  .pricing-cta,
  .pricing-cta::after {
    transition: none;
  }
}

/* D4 beta-promo callout: visually distinct from the public pricing cards,
   uses the accent-muted surface so it reads as a labeled secondary offer. */
.pricing-promo-callout {
  background: var(--accent-muted);
  border: 1px solid var(--accent);
  box-shadow: var(--shadow-sm);
}

.pricing-promo-eyebrow {
  color: var(--accent-text);
}

/* =====================================================
   DOWNLOAD SECTION
   ===================================================== */

/* Developer install block (#211): the curl / Homebrew commands beneath the
   primary .dmg CTA. A hairline divider separates it from the button so the
   DMG stays visually dominant; the commands themselves reuse .use-case-example
   (a dark code-sample panel that reads correctly in both themes). */
.dev-install {
  padding-top: 2rem;
  border-top: 1px solid var(--border-primary);
}

/* =====================================================
   FAQ
   ===================================================== */

.faq-item {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  transition: border-color 0.2s ease;
}

.faq-item:hover {
  border-color: var(--text-tertiary);
}

.faq-item.open {
  border-color: var(--accent);
  background: var(--accent-muted);
}

.faq-trigger {
  cursor: pointer;
  background: transparent;
  border: none;
  color: inherit;
}

/* FAQ open/close — animate the panel HEIGHT so it grows/shrinks to its exact
   content height. The grid-template-rows 0fr→1fr trick interpolates to the real
   content height; unlike a max-height transition it never has to guess a ceiling
   and so never snaps at the end (the symptom this replaced). script.js strips
   the markup `.hidden` on init so the panel stays in grid layout and the height
   can transition — collapse is then driven purely by `.faq-item.open`. No-JS
   keeps the panel collapsed (grid 0fr, or `.hidden`). */
.faq-content {
  display: grid;
  grid-template-rows: 0fr;
  /* The markup's `pb-5` is overridden to 0 while collapsed and restored (and
     animated) when open, in lockstep with the row height — so the panel
     collapses fully to 0 (a child with its own padding-bottom can't shrink
     below that padding in a 0fr track and would leave a residual gap).
     Horizontal `px-6` stays on the box. */
  padding-bottom: 0;
  transition:
    grid-template-rows 300ms cubic-bezier(0.33, 1, 0.68, 1),
    padding-bottom 300ms cubic-bezier(0.33, 1, 0.68, 1);
}

.faq-item.open .faq-content {
  grid-template-rows: 1fr;
  padding-bottom: 1.25rem; /* = pb-5 */
}

/* The content child clips its overflow as the row collapses; min-height:0 lets
   the grid track shrink to 0 below the child's intrinsic height. visibility
   keeps collapsed content out of the a11y tree + tab order — it is transitioned
   so the text stays visible through the close animation, then hides at the end. */
.faq-content > * {
  overflow: hidden;
  min-height: 0;
  visibility: hidden;
  transition: visibility 300ms;
  transition-behavior: allow-discrete;
}

.faq-item.open .faq-content > * {
  visibility: visible;
}

.faq-icon {
  transition: transform 0.25s ease;
}

.faq-item.open .faq-icon {
  transform: rotate(180deg);
}

/* =====================================================
   HERO ENTRANCE (load animation — not scroll-driven)
   ===================================================== */

/* The hero fades in on load via a keyframe animation that SETTLES at the
   visible state (forwards). It is independent of any scroll observer, so it
   keeps working after the JS reveal observer was retired (Epic 5, #167).
   Visible-by-default safety: reduced-motion users get the static end state
   below; the `forwards` fill means even a slow start lands at opacity:1. */
.fade-in {
  opacity: 0;
  transform: translateY(20px);
  animation: fadeInUp 0.7s ease forwards;
}

.fade-in-delay-1 {
  animation-delay: 0.1s;
}
.fade-in-delay-2 {
  animation-delay: 0.2s;
}
.fade-in-delay-3 {
  animation-delay: 0.35s;
}
.fade-in-delay-4 {
  animation-delay: 0.5s;
}
.fade-in-delay-5 {
  animation-delay: 0.65s;
}

@keyframes fadeInUp {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Reduced-motion: skip the hero entrance entirely, show the final state. */
@media (prefers-reduced-motion: reduce) {
  .fade-in,
  [class*='fade-in-delay'] {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  /* FAQ + mobile menu: instant open/close, no height/fade animation. The grid
     row snaps and the child's visibility flips with no transition. */
  .faq-content,
  .faq-content > *,
  #mobile-menu {
    transition: none !important;
    translate: 0 0 !important;
  }
}

/* NOTE on scroll reveals (the `.reveal` system): these now live exclusively in
   src/main.css and are driven by a one-shot IntersectionObserver in script.js —
   it adds `.is-visible` the first time each element enters the viewport, then
   stops observing it (no scroll-timeline). The base `.reveal` state is shown by
   default (no-JS / reduced-motion safe) and only flips to hidden-until-revealed
   once `js-reveal` is set on <html>. The earlier `animation-timeline: view()`
   scroll-driven variant was dropped (#215) because it re-evaluated every reveal
   on every scroll event; the legacy `.reveal.visible` + `.reveal-delay-*` rules
   that once lived here were removed in Epic 5 (#167). */

/* =====================================================
   MOBILE MENU
   ===================================================== */

#mobile-menu {
  /* Surface re-homed from the inline style on the partial (#165). The
     blurred-backdrop pass is REMOVED here (decision L3 — a CPU-rendered
     blur is a Safari/iOS perf trap on the macOS audience). Visually kept
     close: the same 95%-of-bg solid the inline declaration used, just
     without the compositing-cost pass. */
  border-top: 1px solid var(--border-subtle);
  /* Fully opaque so the open menu never shows the bright hero through it at the
     top of the page (#458). */
  background: var(--bg-primary);
  /* Same allow-discrete treatment as .faq-content: script.js toggles `.hidden`
     (display:none), so the old `opacity, transform, max-height` list never
     animated. Now the menu fades/slides 8px through the display flip; engines
     without allow-discrete keep the instant toggle. */
  opacity: 1;
  translate: 0 0;
  transition-property: opacity, translate, display;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
  transition-behavior: allow-discrete;
}

#mobile-menu.hidden {
  opacity: 0;
  translate: 0 -8px;
}

@starting-style {
  #mobile-menu {
    opacity: 0;
    translate: 0 -8px;
  }
}

/* =====================================================
   HERO STATS STRIP
   ===================================================== */

.hero-stat-value {
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* =====================================================
   TABULAR NUMERALS (Principle 9)
   Any element rendering dynamic numbers should use
   tabular-nums so digits don't jitter as values change.
   Apply via .tabular-nums utility OR built into the
   component class itself.
   ===================================================== */
.pricing-card .text-4xl,
.docs-meta time,
table th[scope='row'],
table td {
  font-variant-numeric: tabular-nums;
}

/* Changelog version pills (h2 lines like "## [0.1.0] - 2026-05-11"
   rendered by changelog-render.js from CHANGELOG.md). Tabular
   numerals keep version numbers and dates aligned across releases. */
.docs-body h2 {
  font-variant-numeric: tabular-nums;
}

/* =====================================================
   TESTIMONIALS
   ===================================================== */

.testimonial-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, border-color, transform, box-shadow;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.testimonial-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-primary);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.testimonial-quote {
  color: var(--text-tertiary);
}

.testimonial-card blockquote {
  font-size: 0.95rem;
  line-height: 1.65;
}

/* =====================================================
   FOOTER
   ===================================================== */

.site-footer {
  background: var(--bg-secondary);
  border-color: var(--border-subtle);
}

.footer-link {
  position: relative;
  display: inline-block;
}

.footer-link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s ease;
}

.footer-link:hover::after {
  transform: scaleX(1);
}

.footer-social {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-primary);
  /* Min 40x40 hit area (surfaces §Minimum Hit Area): the visible tile is
     36px (w-9 h-9). The ::after extender adds 2px per side without changing
     the visual; the row's gap-2 (8px) leaves 4px clear between extended
     targets, so neighbours never overlap. */
  position: relative;
}

.footer-social::after {
  content: '';
  position: absolute;
  inset: -2px;
}

.footer-social:hover {
  background: var(--accent-light);
  border-color: var(--accent);
  transform: translateY(-1px);
}

/* =====================================================
   SMOOTH SCROLL OFFSET
   ===================================================== */

#features,
#pricing,
#download,
#how-it-works,
#use-cases,
#testimonials,
#built-for-mac {
  scroll-margin-top: 80px;
}

/* =====================================================
   BEFORE/AFTER DEMO
   ===================================================== */

.demo-panel {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
}

.demo-before {
  opacity: 0.85;
}

/* Contrast lift (#215): the demo-before's two small muted labels ("Recording"
   and "Raw voice input") are `t-tertiary` (the muted token) at 12px, further
   dimmed by this panel's 0.85 opacity and sitting on a slightly-lit pill. With
   the ambient backdrop now visible behind the semi-transparent panel, that
   combination dips just under WCAG AA (4.5:1 for small text). Promoting just
   these labels to the secondary token clears AA while keeping the panel's
   intentional "raw/dim" look (the 0.85 opacity is preserved). The body text
   below already uses the secondary token, so this only touches the two labels. */
.demo-before .t-tertiary {
  color: var(--text-secondary);
}

/* "Clean output" panel — its outline is now an MBC gradient ring (#221, MBC
   #222), marking it as the on-brand "after" result. The ring is a ::before mask
   (so the panel's animated text content isn't clipped to the gradient); the 1px
   solid border from .demo-panel is dropped in favour of the 1.5px gradient frame.
   The faint periwinkle interior tint is kept so the panel still reads as
   accented. */
.demo-after {
  position: relative;
  background: var(--accent-muted);
  border-color: transparent;
}

.demo-after::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: var(--eg-mbc) border-box;
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

.demo-arrow {
  color: var(--text-tertiary);
  animation: arrowPulse 2s ease-in-out infinite;
}

@keyframes arrowPulse {
  0%,
  100% {
    opacity: 0.5;
    transform: translateX(0);
  }
  50% {
    opacity: 1;
    transform: translateX(4px);
  }
}

@media (min-width: 768px) {
  .demo-arrow {
    animation-name: arrowPulseH;
  }
  @keyframes arrowPulseH {
    0%,
    100% {
      opacity: 0.5;
      transform: translateX(0);
    }
    50% {
      opacity: 1;
      transform: translateX(6px);
    }
  }
}

/* Demo transformation loop: conveys raw speech -> polished text.
   Visible-by-default — the base state is the final, fully-revealed text, so
   browsers without animation support (and reduced-motion users) see the
   finished output. The keyframes start hidden and settle to the base state. */
.demo-after-text {
  clip-path: inset(0 0 0 0);
  opacity: 1;
  animation: demoReveal 7s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}

@keyframes demoReveal {
  /* raw input still "settling" */
  0%,
  12% {
    clip-path: inset(0 100% 0 0);
    opacity: 0.25;
  }
  /* polished text wipes in */
  45% {
    clip-path: inset(0 0 0 0);
    opacity: 1;
  }
  /* hold the clean result, then loop */
  92%,
  100% {
    clip-path: inset(0 0 0 0);
    opacity: 1;
  }
}

.demo-after-badge {
  opacity: 1;
  animation: demoBadge 7s ease-out infinite;
}

@keyframes demoBadge {
  0%,
  30% {
    opacity: 0.35;
    transform: translateY(2px);
  }
  50%,
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.demo-before-text {
  animation: demoBeforeDim 7s ease-in-out infinite;
}

@keyframes demoBeforeDim {
  0%,
  12% {
    opacity: 1;
  }
  /* dip as the cleanup happens, then return */
  40% {
    opacity: 0.55;
  }
  70%,
  100% {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .demo-after-text,
  .demo-after-badge,
  .demo-before-text {
    animation: none;
    clip-path: none;
    opacity: 1;
    transform: none;
  }
}

/* Offscreen pause (Epic 5, #167): a tiny IntersectionObserver in script.js
   adds `.paused` to the looping demo (and the fixed backdrop aurora, #215) when
   it scrolls out of view, so an infinite compositor animation isn't burning
   cycles on a panel nobody is looking at. Re-entering view removes the class
   and the loop resumes. */
.demo-after-text.paused,
.demo-after-badge.paused,
.demo-before-text.paused,
.site-backdrop-aurora.paused {
  animation-play-state: paused;
}

/* When PAUSED, the looping demo must rest at its SETTLED, fully-readable output
   — not whatever keyframe it happened to freeze on. `animation-play-state:
   paused` freezes the CURRENT frame, and at page load that is the 0% frame
   (opacity 0.25, mid-wipe), which is below the fold on a short viewport and so
   never plays forward. Forcing the finished state here keeps the offscreen demo
   readable and makes good on the "content stays visible either way" intent. The
   running animation when the demo is in view is unchanged.

   `!important` is required: a running-but-paused CSS animation still applies its
   current keyframe at a higher precedence than a normal declaration, so without
   it the `demoReveal` 0% frame (opacity 0.25) would keep winning. */
.demo-after-text.paused {
  clip-path: inset(0 0 0 0) !important;
  opacity: 1 !important;
}
.demo-after-badge.paused,
.demo-before-text.paused {
  opacity: 1 !important;
  transform: none !important;
}

/* =====================================================
   BUILT FOR MAC
   ===================================================== */

.mac-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, transform, box-shadow;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.mac-card:hover {
  background: var(--bg-card-hover);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

/* =====================================================
   PRIVACY GRID
   ===================================================== */

.privacy-claim-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, transform, box-shadow;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.privacy-claim-card:hover {
  background: var(--bg-card-hover);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.privacy-verify {
  font-size: 12px;
  color: var(--text-tertiary);
  font-style: italic;
}

/* =====================================================
   RESPONSIVE
   ===================================================== */

@media (max-width: 640px) {
  .wordmark {
    font-size: 1.1rem;
  }

  .terminal-card {
    font-size: 12px;
  }

  .comparison-card table {
    font-size: 12px;
  }

  .hero-stat-value {
    font-size: 1.4rem;
  }

  .use-case-example {
    font-size: 11px;
    padding: 10px 12px;
  }

  .testimonial-card {
    padding: 1.5rem;
  }

  .spec-card {
    padding: 1.5rem;
  }
}

/* =====================================================
   CONSOLIDATED HOMEPAGE SECTIONS (#213)
   Features pillars, the compact how-it-works flow strip, the
   differentiation cards, and the Specs models/pipeline cards. All share the
   established card surface (var(--bg-card) + hairline border + layered
   shadow) and the same hover lift as .feature-card / .step-card, so the new
   consolidated sections read as one system rather than a flutter of grids.
   Transitions name explicit properties (never `transition: all`).
   ===================================================== */

/* Four-pillar row + the differentiation cards: same surface as feature cards. */
.pillar-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
  transition-property: background-color, border-color, transform, box-shadow;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.pillar-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-primary);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

/* Pillar + differentiator icon GLYPHS render in the MBC gradient (#222). The
   line icons are inline SVGs with stroke="currentColor" (via .t-accent); this
   CSS `stroke` overrides that presentation attribute and points it at the shared
   <linearGradient id="eg-mbc-icon"> sprite (index.html), so each glyph picks up
   the magenta→blue→cyan sweep. Covers the pillar, differentiator,
   and privacy-claim ("Every claim is verifiable") icon grids (#224). The Specs
   models/pipeline block has no line icons, so it is unaffected.
   CSP-safe: a CSS property, not an inline style. */
.pillar-card .feature-icon-bg svg,
.privacy-claim-card .feature-icon-bg svg {
  stroke: url(#eg-mbc-icon);
}

/* Compact how-it-works flow: a lighter inset surface so the three steps read
   as a single strip, not three more feature cards. */
.flow-step {
  background: color-mix(in oklch, var(--bg-card) 60%, transparent);
  border: 1px solid var(--border-subtle);
  transition-property: background-color, border-color, transform;
  transition-duration: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.flow-step:hover {
  background: var(--bg-card);
  border-color: var(--border-primary);
  transform: translateY(-2px);
}

/* Oversized step ordinal — echoes the .step-number treatment (muted, tabular,
   non-selectable) but inline at the head of each flow step. */
.flow-step-num {
  flex-shrink: 0;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
  user-select: none;
}

/* Specs cards: same card surface, no hover lift (these are dense reference
   blocks, not affordances). */
.spec-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-sm);
}

/* Pipeline step badge: small accent-tinted numeral chip aligned to its row. */
.spec-pipeline-num {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 7px;
  background: var(--accent-muted);
  border: 1px solid var(--border-primary);
  color: var(--accent-text);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* =====================================================
   SELECTION
   ===================================================== */

::selection {
  /* Accent-tinted highlight that reads in both themes (was a near-black
     overlay, invisible on the dark canvas). */
  background: color-mix(in oklch, var(--color-accent) 35%, transparent);
  color: var(--text-primary);
}

/* =====================================================
   SCROLLBAR
   ===================================================== */

::-webkit-scrollbar {
  width: 6px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--text-tertiary);
  border-radius: 3px;
  opacity: 0.3;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--text-secondary);
}

/* =====================================================
   LANDING REDESIGN (#417) — refined direction
   Section restructure + restrained type scale. New component classes used by
   the rebuilt index.html. Color/border/shadow all flow through the @theme
   tokens (src/main.css), so nothing here hardcodes a palette value. Dark-only,
   CSP-safe (class-based, no inline styles), motion gated by reduced-motion.
   ===================================================== */

/* ---------- Type scale (landing-scoped, not global @theme) ----------
   Localized here so other marketing pages' text-display/headline utilities are
   undisturbed. Display clamps 40–54px (build note); section title 32–42px. */
.hero-headline {
  font-family:
    'Inter',
    -apple-system,
    sans-serif;
  font-weight: 600;
  font-size: clamp(2.5rem, 4.8vw, 3.5rem);
  /* Looser than the 1.03 display default — more air between the headline's
     lines carries the hero's vertical rhythm now that the inter-group gaps are
     smaller (#568). */
  line-height: 1.15;
  letter-spacing: -0.038em;
}
.section-title {
  font-family:
    'Inter',
    -apple-system,
    sans-serif;
  font-weight: 600;
  font-size: clamp(1.875rem, 4vw, 2.75rem);
  line-height: 1.08;
  letter-spacing: -0.03em;
}
/* Monospace eyebrow — the "reads 2026" cue. 0.16em tracking per the spec. */
.eyebrow-mono {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
/* Numbers are always mono (credibility cue). */
.num-mono {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* Refined gradient accent: MBC clipped to text WITHOUT forcing 900 weight, so
   the hero "fully local" stays at the display weight (600). Distinct from the
   legacy .gradient-text (which forces 900 for the bolder direction). */
.gradient-accent {
  background: var(--eg-mbc);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--eg-spectrum); /* fallback if background-clip:text unsupported */
}

/* ---------- Hero ---------- */
.hero-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.hero-stat {
  padding: 0 0.75rem;
  border-left: 1px solid var(--border-primary);
  /* #557: content centred in its cell on every breakpoint (was flush-left on
     desktop). first-child keeps the symmetric padding so centring is true. */
  text-align: center;
}
.hero-stat:first-child {
  border-left: 0;
}
.hero-stat-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.25rem, 2.3vw, 1.5rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}
.hero-stat-label {
  font-size: 0.75rem;
  color: var(--text-tertiary);
  line-height: 1.3;
}
@media (max-width: 720px) {
  .hero-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem 0;
  }
  .hero-stat:nth-child(3) {
    border-left: 0;
  }
  /* Tighter symmetric cell padding for the half-width mobile cells (#550).
     Centering itself now comes from the base .hero-stat rule (#557). */
  .hero-stat {
    padding: 0 0.75rem;
  }
  /* #557: horizontal rule under the first row so the 2×2 reads as a real
     grid. Same hairline as the vertical dividers; the cells' padding-bottom
     matches the row gap so the rule sits centred between the rows. Declared
     after the symmetric-padding rule above so this padding-bottom wins at
     equal specificity. */
  .hero-stat:nth-child(-n + 2) {
    border-bottom: 1px solid var(--border-primary);
    padding-bottom: 1.25rem;
  }
  /* Row 1's vertical divider must stop short of the row rule (no T junction).
     A border-left spans the padded box right down to the rule, so swap it for
     an inset pseudo line that ends where the content does — 1.25rem above the
     rule, mirroring the 1.25rem row-gap that row 2's divider gets below it. */
  .hero-stat:nth-child(2) {
    border-left: 0;
    position: relative;
  }
  .hero-stat:nth-child(2)::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 1.25rem;
    left: 0;
    width: 1px;
    background: var(--border-primary);
  }
}

/* Moat pills under the CTA row + small emerald-check status pills. */
.moat-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border-primary);
  background: var(--bg-card);
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.moat-pill .check {
  color: var(--color-emerald);
  font-weight: 700;
}

/* =====================================================
   HERO — 2-column redesign (#439): copy left, product-demo card right.
   ===================================================== */
.hero-grid {
  display: grid;
  /* The right column (video + stats) is ~4rem wider than the balanced split so
     the video snugs closer to the CTA row (#568). The 52.5/47.5 percentages
     carry the original ratio; the −1.75rem on each side pays for the 3.5rem
     column-gap; the ∓4rem shifts ~4rem of width from the copy to the video.
     (col1 + col2 + gap = 100%, no overflow.) Held at 4rem: an 8rem shift
     narrowed the copy enough to wrap the CTA row onto two lines (#583). */
  grid-template-columns:
    calc(52.5% - 1.25rem - 4rem)
    calc(47.5% - 1.25rem + 4rem);
  column-gap: 2.5rem;
  /* Vertically CENTER the two columns so their midpoints line up — the copy
     (col 1) and the video+stats stack (col 2, .hero-right) read as balanced
     whichever is taller (#568). */
  align-items: center;
}
.hero-copy {
  grid-column: 1;
  text-align: left;
  /* Nudge the copy up to fine-tune the two columns' vertical balance (#568). */
  transform: translateY(-1rem);
}
/* Right column: the demo video with the proof strip (stats + latency note)
   stacked beneath it as one vertically-centred unit. Wrapping them (rather than
   placing them in two grid rows) decouples the stack from the copy's height, so
   align-items:center can balance the two columns' centres. On mobile the grid
   collapses and this flows after the copy (copy → video → stats) (#568). */
.hero-right {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  gap: 3rem; /* video ↔ stats */
  min-width: 0;
  /* Raise the demo+stats stack so it sits a touch higher than the copy column's
     centre (was translateY(1rem) down; now 2rem higher for a top-weighted hero). */
  transform: translateY(-1rem);
}
.hero-lead {
  font-size: 1.125rem;
  line-height: 1.9;
  max-width: 48ch;
  margin-top: 2.25rem;
}
.hero-cta-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.875rem;
  margin-top: 1.875rem;
}
/* Hero-only spacing (#580/#583). The system-requirements tile sits between the
   pricing lead and the CTA row; give it equal air above and below (tile
   margin-top == CTA margin-top) so it reads as vertically centred in that gap.
   The free-trial line returned below the CTAs and uses the base .hero-trial
   margin. Both scoped to .hero-copy so the shared components (developers.html,
   #get-started) keep their own spacing. */
.hero-copy .sysreq-card {
  margin-top: 2.5rem;
}
.hero-copy .hero-cta-row {
  margin-top: 2.5rem;
}

/* Install-via-terminal toggle — a transparent ghost button (distinct from the
   bg-filled .cta-secondary), with a chevron that flips when the panel opens. */
.term-toggle {
  background: transparent;
  border: 1px solid var(--color-border-strong);
  color: var(--text-primary);
  cursor: pointer;
  transition:
    border-color 150ms ease,
    background-color 150ms ease;
}
.term-toggle:hover {
  border-color: color-mix(in oklch, white 32%, transparent);
  background: color-mix(in oklch, white 3%, transparent);
}
.term-toggle:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}
.term-chevron {
  color: var(--text-tertiary);
  transition: transform 200ms cubic-bezier(0.2, 0, 0, 1);
}
.term-toggle[aria-expanded='true'] .term-chevron {
  transform: rotate(180deg);
}

/* Install-via-terminal disclosure. The `hidden` attr collapses it for no-JS (UA
   display:none); script.js strips `hidden` on init so the panel is CSS-collapsed
   via the default below, then the toggle animates height smoothly with the
   grid-template-rows 0fr→1fr technique. (display:none can't transition, which is
   why the open state is a class, not :not([hidden]).) CSP-safe: the toggle flips
   the `.is-open` class — no inline-style writes. */
.term-panel {
  display: grid;
  grid-template-rows: 0fr;
  margin-top: 0;
  transition:
    grid-template-rows 320ms cubic-bezier(0.4, 0, 0.2, 1),
    margin-top 320ms cubic-bezier(0.4, 0, 0.2, 1);
}
.term-panel.is-open {
  grid-template-rows: 1fr;
  margin-top: 1rem; /* gap above the pills; 0 when closed so no residual space */
}
.term-panel-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  min-height: 0; /* let the grid row collapse all the way to 0 */
  overflow: hidden;
  visibility: hidden; /* dropped from tab order + AT while collapsed */
  transition: visibility 0s linear 320ms; /* hide only after the close finishes */
}
.term-panel.is-open .term-panel-inner {
  visibility: visible;
  transition: visibility 0s; /* reveal immediately on open */
}
@media (prefers-reduced-motion: reduce) {
  .term-panel {
    transition: none;
  }
  .term-panel-inner {
    transition: none;
  }
}
.term-cmd {
  display: inline-flex;
  align-items: center;
  gap: 0.875rem;
  text-align: left;
  background: var(--color-surface-2);
  border: 1px solid var(--border-primary);
  border-radius: 10px;
  padding: 0.8rem 1rem;
  cursor: pointer;
  transition:
    border-color 150ms ease,
    background-color 150ms ease;
}
.term-cmd:hover {
  border-color: color-mix(in oklch, var(--accent-blue) 40%, transparent);
  background: var(--color-surface-3);
}
.term-cmd-text {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--text-secondary);
  white-space: nowrap;
}
.term-cmd-state {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.term-cmd-copy {
  color: var(--text-tertiary);
}
.term-cmd-copied {
  display: none;
  align-items: center;
  gap: 0.3rem;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.6875rem;
  color: var(--color-emerald);
}
.term-cmd.copied .term-cmd-copy {
  display: none;
}
.term-cmd.copied .term-cmd-copied {
  display: inline-flex;
}

/* Free-trial line. */
.hero-trial {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  /* #557: matches .hero-proof .hero-stats' margin-top, so in the hero the
     trial line sits vertically centred between the CTA row and the stats
     band's top hairline. The #get-started reuse inherits the same value
     (+0.875rem over the old 1.25rem — the requested extra air below its
     CTAs). */
  margin-top: 2.125rem;
}
.hero-trial-icon {
  color: var(--color-success);
  flex-shrink: 0;
}
.hero-trial-label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.hero-trial-text {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  color: var(--text-tertiary);
}

/* System-requirements tile (#580, tile form #582). A compact bordered card:
   a centred header (chip glyph + "System requirements") over a single inline
   row of bullet-separated specs. No background fill (#583) — just the outline.
   Recap of the first FAQ answer. Sits under the hero CTAs; `.is-centered`
   centres the same tile in the pricing + final-CTA sections. Short tile —
   width hugs the content (fit-content). */
.sysreq-card {
  display: inline-block;
  width: fit-content;
  max-width: 100%;
  margin-top: 1.875rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border-primary);
  border-radius: 12px;
}
.sysreq-card.is-centered {
  display: block;
  margin-top: 2.5rem;
  margin-inline: auto;
}
/* #get-started (#583): match the tile's top gap to its bottom gap. The lead's
   mb-8 (2rem) already sits above; zeroing the tile's own top margin lets that
   2rem be the whole top gap, equal to the mb-8 (2rem) below it → even air
   above and below. Scoped by #id so the pricing tile keeps its 2.5rem top. */
#get-started .sysreq-card.is-centered {
  margin-top: 0;
}
.sysreq-card-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4375rem;
  margin-bottom: 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-secondary);
}
.sysreq-card-icon {
  color: var(--text-tertiary);
  flex-shrink: 0;
}
/* One inline row of specs, • separators between items. Wraps on narrow
   viewports; the separator TRAILS each item (::after on the inline-flex li) so
   it rides with its item and no wrapped line ever begins with an orphan bullet. */
.sysreq-card-specs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-tertiary);
}
.sysreq-card-specs li {
  display: inline-flex;
  align-items: center;
}
.sysreq-card-specs li:not(:last-child)::after {
  content: '•';
  margin: 0 0.5rem;
  color: var(--text-tertiary);
  opacity: 0.55;
}
/* The centred (pricing + final-CTA) tiles wrap their specs centred, matching the
   hero tile. The base specs row defaults to flex-start; the hero centres it via
   .hero-copy, so the .is-centered variant needs the same to look identical. */
.sysreq-card.is-centered .sysreq-card-specs {
  justify-content: center;
}
/* Mobile-only <br> — hidden on desktop, breaks at ≤720px (#550). Used in the
   free-trial line and the hero-lead pricing sentence. */
.mobile-br {
  display: none;
}
/* Inverse of .mobile-br (#557): a break that exists on desktop and dissolves
   under 720px — used by the hero latency note, whose mobile form is a narrow
   centred block that wraps naturally (a hard break there would orphan a
   word). */
@media (max-width: 720px) {
  .desktop-br {
    display: none;
  }
}
/* Hero sub-lead responsive breaks (#644). The sub-lead reads as four short
   lines in two ranges where the copy runs narrow: true mobile (≤720px, where
   the hero is stacked-and-centred) AND the 901–1105px two-column band, where
   the copy column is ~355–460px wide and the sentences would otherwise wrap at
   arbitrary points. Outside both ranges the breaks dissolve and the sub-lead
   flows as its natural two lines. Scoped to the sub-lead only — the pricing and
   free-trial .mobile-br breaks are deliberately untouched here. */
.sublead-br {
  display: none;
}
@media (max-width: 720px), (min-width: 901px) and (max-width: 1105px) {
  .sublead-br {
    display: inline;
  }
}
/* True-mobile refinements (#550). The hero copy's *centering* moved to the
   900px stack breakpoint (below) — it now kicks in the moment the demo video
   wraps under the copy, so the stacked hero is never left-aligned. What stays
   here at ≤720px is genuinely mobile: the free-trial line breaks after its
   label (icon + "Free trial:" on line one, the mono text on its own line), and
   the mobile-only <br>s appear. */
@media (max-width: 720px) {
  .hero-trial {
    flex-wrap: wrap;
    row-gap: 0.4375rem;
  }
  .hero-trial-text {
    flex-basis: 100%;
  }
  .mobile-br {
    display: inline;
  }
}

/* Stat-number unit (the s / % / x / mo after the figure) — smaller, muted. */
.hero-stat-unit {
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
.hero-proof .hero-stats {
  /* 0: the grid row-gap (3rem) now provides the gap between the video above and
     this stats band (#568). Was 2.125rem when the stats sat under the copy. */
  margin-top: 0;
  padding-top: 1.625rem;
  /* #557: symmetric bottom padding against the border-subtle-b hairline, so
     the stats read as a framed band (top rule / stats / bottom rule). */
  padding-bottom: 1.625rem;
  /* left-aligned hero: stats span the column, divided by left hairlines */
}
.hero-latency-note {
  text-align: center;
  max-width: none;
  /* 1.5rem below the stats band's bottom hairline. */
  margin: 1.5rem 0 0;
  font-size: 0.75rem;
}
/* #557: on the centred mobile stats layout (#550), centre the footnote BLOCK
   under the grid as well — the copy inside stays left-aligned (ragged right). */
@media (max-width: 720px) {
  .hero-latency-note {
    max-width: 17.5rem;
    margin-inline: auto;
  }
}

/* ---------- Hero product-demo video ---------- */
.hero-demo-wrap {
  /* Flex child of .hero-right (video over stats). position:relative keeps it as
     the fade-in transform target; the pause toggle anchors to .demo-video-frame
     below. The 3rem gap to the stats is owned by .hero-right's flex gap (#568). */
  position: relative;
  /* Fills the column (#583). Was 90% (10% narrower) to shrink the right stack
     toward the copy height (#568); now that the left column carries the
     system-requirements tile it is tall enough to balance a full-width video,
     which also aligns the video to the stats strip below it. */
  width: 100%;
  align-self: center;
}
.demo-video-frame {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--color-border-strong);
  box-shadow: 0 40px 80px -34px oklch(0 0 0 / 0.85);
}
/* Hero product demo (#492 → HTML embed). The demo is a self-contained HTML page
   loaded in a same-origin iframe. The frame rounds + clips the corners and casts
   the floating-card shadow; `aspect-ratio` reserves the box before the iframe
   loads so there is zero layout shift. 16:9 to match the demo's own canvas. */
.demo-embed {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  background: var(--color-canvas);
}

/* ---------- Merged voice/Command Mode demo (#662) ---------- */
/* The section-width demo loop under #features. Distinct from the hero embed
   above: it is centred at its own max-width rather than filling a grid column,
   and it carries a hairline border with no drop shadow (it sits on the flat
   section canvas, not floating over the hero). */
.voice-demo-wrap {
  display: flex;
  justify-content: center;
  margin-top: 3.25rem;
}
.voice-demo-frame {
  position: relative; /* anchors the .demo-expand affordance (#665) */
  width: 100%;
  max-width: 1120px;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--color-border-strong);
}
/* aspect-ratio reserves the box before the iframe loads (zero layout shift).
   1120/589 is the demo's own 1920x1010 stage, which it letterboxes into. */
.voice-demo-embed {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1120 / 589;
  border: 0;
  background: var(--color-canvas);
}

/* Alias anchors (#662). Zero-size markers that keep retired section ids
   addressable after their sections merged away, so inbound /#cleanup and /#how
   links still land on the successor section. Not display:none — a hidden
   element is not a scroll target. */
.anchor-alias {
  display: block;
  height: 0;
  width: 0;
  overflow: hidden;
}

/* Expand affordance — a glassy round button pinned to the demo frame's
   bottom-right. Opens the full-size lightbox (script.js). Mirrors the look of
   the old video pause toggle it replaces. */
.demo-expand {
  position: absolute;
  bottom: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  color: var(--text-primary);
  background: color-mix(in oklch, var(--color-surface-1) 72%, transparent);
  border: 1px solid var(--color-border-strong);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: pointer;
  transition: background 0.15s ease;
}
.demo-expand:hover {
  background: color-mix(in oklch, var(--color-surface-1) 92%, transparent);
}
.demo-expand:focus-visible {
  outline: 2px solid var(--accent-blue);
  outline-offset: 2px;
}

/* Hero demo lightbox (expand → full-screen takeover). Native <dialog> opened
   with showModal(): the browser supplies the top layer, focus-trap, and Esc for
   free (same pattern as the #554 download modal). The ::backdrop is the
   low-opacity black scrim over the rest of the page. The dialog is padding-free
   and shrink-wraps its frame, so a click whose target is the dialog element can
   only be the ::backdrop — script.js dismisses on it. The frame is the largest
   16:9 box that fits within 94vw × 90vh, so the demo fills the majority of the
   screen at full size. */
.demo-modal {
  margin: auto;
  padding: 0;
  border: 0;
  background: transparent;
  width: min(94vw, calc(90vh * 16 / 9));
  max-width: 94vw;
  overflow: visible;
}
/* Section (Command Mode) demo variant (#665): its stage is wider than the hero's
   16:9, so the dialog width cap tracks the embed's 1120/589. Toggled on open by
   script.js from data-demo-aspect="wide". The matching frame aspect override is
   defined AFTER .demo-modal-frame below so it wins at equal specificity. */
.demo-modal--wide {
  width: min(94vw, calc(90vh * 1120 / 589));
}
.demo-modal::backdrop {
  background: oklch(0 0 0 / 0.72);
}
.demo-modal-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--color-border-strong);
  box-shadow: 0 40px 120px -40px oklch(0 0 0 / 0.9);
  background: var(--color-canvas);
}
.demo-modal-frame--wide {
  aspect-ratio: 1120 / 589; /* overrides the 16/9 above (#665) */
}
.demo-modal-embed {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
.demo-modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  color: var(--text-primary);
  background: color-mix(in oklch, var(--color-surface-1) 80%, transparent);
  border: 1px solid var(--color-border-strong);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: pointer;
  transition: background 0.15s ease;
}
.demo-modal-close:hover {
  background: color-mix(in oklch, var(--color-surface-1) 96%, transparent);
}
.demo-modal-close:focus-visible {
  outline: 2px solid var(--accent-blue);
  outline-offset: 2px;
}
/* Lock the page behind the takeover. Toggled on <html> by script.js. */
.is-demo-modal-open {
  overflow: hidden;
}
/* Fade the scrim + frame in on open; instant close. Static for reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .demo-modal[open] {
    opacity: 1;
    transition: opacity 200ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  @starting-style {
    .demo-modal[open] {
      opacity: 0;
    }
  }
}

/* Hero stack on narrow screens: the right column (video + stats) drops below
   the copy. */
@media (max-width: 900px) {
  .hero-grid {
    grid-template-columns: 1fr;
    row-gap: 2.5rem;
  }
  /* Collapse to one column: copy then the video+stats stack, in DOM order
     (copy → video → stats) (#495, #568). */
  .hero-copy,
  .hero-right {
    grid-column: auto;
    transform: none;
  }
  .hero-demo-wrap {
    max-width: 30rem;
  }
  /* Center the hero copy at the SAME breakpoint the video wraps under it — was
     720px, raised to 900px so the 720–900px "stacked-but-left-aligned" disjoint
     is gone. #main-only in practice: #get-started centers via its own
     #get-started .hero-trial rule and developers.html via justify-center
     utilities, so these unscoped rules are redundant (harmless) for them. */
  .hero-copy {
    text-align: center;
  }
  .hero-lead {
    margin-inline: auto;
  }
  .hero-cta-row {
    justify-content: center;
  }
  .hero-trial {
    justify-content: center;
  }
  .hero-copy .sysreq-card {
    margin-inline: auto;
  }
  .hero-copy .sysreq-card-specs {
    justify-content: center;
  }
}
/* Stacked-hero reorder (#646). In the ≤900px stacked view, lift the demo video
   up between the title and the sub-lead, and hide the stats band + latency
   disclaimer entirely (see .hero-proof below). The title/demo/sub-lead need to
   be siblings to interleave, so flatten the two column wrappers with
   display:contents — their children promote to items of the single-column
   .hero-grid — then place everything with `order`. Scoped to #main so the
   shared hero classes (#get-started, developers.html) are untouched, and the
   >900px two-column layout keeps its original DOM + CSS verbatim.
   Note: `order` changes visual order only; DOM/reading order is unchanged. */
@media (max-width: 900px) {
  #main .hero-grid {
    /* Once flattened, the copy children's own margin-top values own the
       vertical rhythm; a grid row-gap would double up on them. */
    row-gap: 0;
  }
  #main .hero-copy,
  #main .hero-right {
    display: contents;
  }
  #main .hero-headline {
    order: 1;
  }
  #main .hero-demo-wrap {
    order: 2;
    /* title ↔ demo — mirrors the title ↔ sub-lead gap (.hero-lead margin-top). */
    margin-top: 2.25rem;
    /* Was align-self:center inside .hero-right's flex column; as a grid item the
       horizontal centre comes from justify-self. */
    justify-self: center;
  }
  #main .hero-lead {
    order: 3;
  }
  #main .hero-copy .sysreq-card {
    order: 4;
  }
  #main .hero-cta-row {
    order: 5;
  }
  #main .hero-trial {
    order: 6;
  }
  #main .hero-proof {
    /* Stats band + latency disclaimer are hidden in the stacked view (#646
       owner call): they're supplementary marketing figures shown on the wide
       hero and elsewhere on the site, and we don't want them stacked directly
       under the demo. Still present in the >900px two-column layout. */
    display: none;
  }
}

/* =====================================================
   CLEANUP — the model-provenance pill. The raw → cleaned before/after cards
   and the animated mark that sat here were removed in #662: the demo loop in
   #features shows the raw → cleaned transition directly, so the static cards
   were superseded. The pill survives beneath the demo.
   ===================================================== */
.cleanup-llm-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-secondary);
  padding: 0.6875rem 1rem;
  border-radius: 999px;
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface-1);
}
.cleanup-llm-pill svg {
  color: var(--eg-spectrum);
  flex-shrink: 0;
}
.cleanup-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}
.cleanup-stat-card {
  border: 1px solid var(--border-primary);
  border-radius: 14px;
  background: var(--bg-card);
  padding: 1.625rem;
  text-align: center;
}
.cleanup-stat-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 1.875rem;
  font-weight: 500;
  line-height: 1;
  color: var(--text-primary);
}
.cleanup-stat-unit {
  font-size: 0.9375rem;
  color: var(--text-tertiary);
}
.cleanup-stat-desc {
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-tertiary);
  margin-top: 0.75rem;
}
@media (max-width: 820px) {
  .cleanup-grid {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .cleanup-arrow-col {
    flex-direction: row;
    padding: 0.25rem 0;
  }
  .cleanup-arrow-line {
    display: none;
  }
  /* #557: the tiles stack vertically here, so the raw→clean arrow points
     down the flow instead of sideways. */
  .cleanup-arrow-circle {
    transform: rotate(90deg);
  }
  .cleanup-stats {
    grid-template-columns: 1fr;
  }
}

/* =====================================================
   HOW — two-key-press 3-step timeline (#439).
   ===================================================== */
/* =====================================================
   WHY — four reason cards (2x2). Reuses .pillar-card + .feature-icon-bg
   (the site's gradient feature-icon treatment), laid out horizontally (#439).
   ===================================================== */
.why-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}
.why-card {
  display: flex;
  gap: 1.25rem;
}
@media (max-width: 820px) {
  .why-grid {
    grid-template-columns: 1fr;
  }
}

/* =====================================================
   WHERE — verified-apps grid with real app logos (#439).
   ===================================================== */
.where-grid {
  display: grid;
  grid-template-columns: repeat(4, max-content);
  gap: 2.25rem 4rem;
  justify-content: center;
}
.where-cat {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 1.25rem;
}
.where-apps {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.where-app {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: color-mix(in oklch, var(--color-text-primary) 62%, var(--color-text-secondary));
}
.where-tile {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: var(--color-surface-2);
  border: 1px solid var(--border-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.where-tile img {
  width: 20px;
  height: 20px;
  object-fit: contain;
}
.where-note {
  margin-top: 2.5rem;
  padding-top: 1.625rem;
  border-top: 1px solid var(--border-subtle);
}
.where-note-inner {
  max-width: 66%;
  margin: 0 auto;
}
.where-note-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}
.where-note-row + .where-note-row {
  margin-top: 0.875rem;
}
.where-note-row p {
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--text-secondary);
  margin: 0;
}
.where-note-icon {
  flex-shrink: 0;
  margin-top: 1px;
}
.where-note-icon--ok {
  color: var(--color-success);
}
.where-note-icon--muted {
  color: var(--text-tertiary);
}
@media (max-width: 820px) {
  .where-grid {
    grid-template-columns: repeat(2, max-content);
    gap: 2rem 3rem;
    justify-content: center;
  }
  .where-note-inner {
    max-width: 100%;
  }
}

/* =====================================================
   COMPARE — 4-col grid, evoglyph column highlighted (#439).
   ===================================================== */
/* Cap the table width and center it so it doesn't sprawl across the full
   max-w-7xl container on wide screens (#467). */
.compare-wrap {
  max-width: 960px;
  margin-inline: auto;
}
.compare-grid {
  position: relative;
  display: grid;
  /* Fixed label column sized to fit the longest label with a little breathing
     room; the four comparison columns split the remaining width.
     --compare-label-w is the single source of truth shared with
     .compare-highlight's calc() below, so the track and the overlay can never
     drift (#467, widened #469; Voice Ink column added #601). */
  --compare-label-w: 16rem;
  /* minmax(0, 1fr) — NOT bare 1fr (min: auto) — so the four comparison tracks
     stay exact quarters even when a cell's min-content exceeds its share (narrow
     viewports). .compare-highlight's width calc() assumes exact quarters;
     auto-min tracks let the evoglyph column outgrow the ring, spilling cell
     content past its border (#550). */
  grid-template-columns: var(--compare-label-w) repeat(4, minmax(0, 1fr));
  font-size: 0.875rem;
}
/* Cells stretch to fill their row track and center their own content, so each
   row's top border lines up across every column even when a label wraps on
   mobile (#458). display:grid keeps the inline label+sub flow (unlike flex). */
.compare-cell,
.compare-head {
  display: grid;
  align-content: center;
}
/* The highlight card behind column 2 (evoglyph): starts after the fixed label
   column and spans exactly one comparison column. Derived from the same
   --compare-label-w as the track (inherited from .compare-grid) so the overlay
   tracks the columns at any table width — no hardcoded percentages (#467). */
.compare-highlight {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--compare-label-w);
  width: calc((100% - var(--compare-label-w)) / 4);
  border: 1px solid color-mix(in oklch, var(--accent-blue) 28%, transparent);
  border-radius: 14px;
  background: linear-gradient(
    180deg,
    color-mix(in oklch, var(--accent-blue) 10%, transparent),
    color-mix(in oklch, var(--accent-blue) 4%, transparent)
  );
  box-shadow: 0 0 50px -26px color-mix(in oklch, var(--accent-blue) 60%, transparent);
  z-index: 0;
  pointer-events: none;
}
.compare-head {
  padding: 1.125rem 1rem;
  text-align: center;
  /* Center each head's grid item horizontally too — the .compare-eg-name badge
     is a flex grid item that would otherwise stretch and pack its logo+wordmark
     at flex-start (left). text-align alone can't position a grid item (#464). */
  justify-items: center;
  font-weight: 600;
  color: var(--text-secondary);
}
.compare-cell {
  padding: 1rem;
  text-align: center;
  color: var(--text-secondary);
  border-top: 1px solid var(--border-subtle);
}
.compare-cell--label {
  text-align: left;
  color: color-mix(in oklch, var(--color-text-primary) 62%, var(--color-text-secondary));
}
/* evoglyph column sits above the highlight overlay. */
.compare-cell--eg {
  position: relative;
  z-index: 1;
}
.compare-eg-name {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  font-weight: 700;
  color: var(--text-primary);
}
.compare-eg-name img {
  display: block;
}
.compare-sub {
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
.compare-yes {
  color: var(--color-success);
  font-size: 0.875rem;
}
.compare-no {
  color: var(--text-tertiary);
  font-size: 0.875rem;
}
.compare-muted {
  font-size: 0.75rem;
  color: var(--text-secondary);
}
.compare-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-secondary);
}
.compare-num--eg {
  color: var(--text-primary);
}
.compare-built {
  font-size: 0.75rem;
  color: color-mix(in oklch, var(--color-text-primary) 62%, var(--color-text-secondary));
}
/* "Coming soon" marker for a built-but-not-yet-shipped evoglyph capability
   (command mode #601). Deliberately quiet: plain tertiary text, no pill/border,
   so it reads as a low-key roadmap note rather than an emphasized claim. */
/* Deal footnote under the grid (#520 r6/r7): explains the asterisk on the
   struck evoglyph prices. Small muted caption carrying the copy pill;
   centered, with clear separation from the table's bottom edge. */
.compare-footnote {
  margin-top: 1.75rem;
  font-size: 0.75rem;
  text-align: center;
  color: var(--text-tertiary);
}
/* Mobile: the deal parenthetical drops to its own line under the pill row
   instead of wrapping mid-phrase (#550). */
@media (max-width: 720px) {
  .compare-footnote-deal {
    display: block;
    margin-top: 0.3125rem;
  }
}
.compare-note {
  display: flex;
  align-items: flex-start;
  gap: 0.6875rem;
  margin-top: 1.75rem;
  padding: 1.125rem 1.25rem;
  border-radius: 12px;
  border: 1px solid var(--border-primary);
  background: var(--bg-card);
}
.compare-note-icon {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--eg-spectrum);
}
.compare-note p {
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0;
}
/* Mobile: let the grid scroll horizontally rather than crush 5 columns. */
@media (max-width: 720px) {
  .compare-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .compare-grid {
    /* Narrower label track + wider floor: (800px − 12.5rem) / 4 ≈ 150px per
       comparison column — the mono prices fit on one line inside the
       highlight ring (#550; widened for the Voice Ink column #601). */
    --compare-label-w: 12.5rem;
    min-width: 800px;
  }
  .compare-cell,
  .compare-head {
    padding-inline: 0.75rem;
  }
}

/* =====================================================
   PRICING — 3 tiers, mock layout (#439).
   ===================================================== */
.pricing-tier {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-secondary);
}
.pricing-price {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  margin: 1.125rem 0 0.375rem;
}
.pricing-amount {
  font-size: 2.75rem;
  line-height: 1;
  font-weight: 500;
  color: var(--text-primary);
}
.pricing-per {
  font-size: 0.875rem;
  color: var(--text-tertiary);
}
.pricing-desc {
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--text-tertiary);
  margin-bottom: 1.5rem;
}
.pricing-save {
  color: var(--color-success);
}
.pricing-features {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.pricing-feature {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.84rem;
  color: var(--text-secondary);
}
.pricing-check {
  flex-shrink: 0;
  color: var(--color-success);
}

/* BETA50 promotion presentation (#497 → #520). Struck standard price + per-card
   deep-blue code badge on the cards, plus one static "how to redeem" pill above
   the grid. Fully static markup — the promo code is applied by the buyer at
   Stripe checkout; ending the promo is a copy-only edit, no flag, no counter.
   The struck price is aria-hidden; the live price is announced. */
.pricing-price {
  flex-wrap: wrap;
}
/* Shared struck-original-price treatment (#520 r6): the deal is displayed as
   [struck standard] discounted everywhere a price appears numerically —
   tiles (.pricing-was), hero sub-lead + hero stat, final CTA, comparison
   column (.price-was, size inherits from context). The struck span is always
   aria-hidden so screen readers hear only the real price. */
.pricing-was,
.price-was {
  color: var(--text-tertiary);
  text-decoration: line-through;
  text-decoration-thickness: 2px;
}
/* Tile price row keeps its larger struck size + baseline alignment. */
.pricing-was {
  align-self: baseline;
  font-size: 1.375rem;
  font-weight: 500;
}
/* Inside the big hero stat number, the struck original scales down the same
   way the tiles scale theirs (~half the live price). The negative margin
   (#557) claws back most of the whitespace text node that separates the two
   prices in the markup — that space renders at the PARENT's monospace size
   (JetBrains Mono space = 0.6em of the num size ≈ 17px), which read as a
   weird hole between $2 and $1. In this span's own em (0.55 of the num):
   -0.64em leaves ≈ 0.25 num-em ≈ 7px at the desktop clamp, scaling down with
   it. If the whitespace between the spans is ever removed, drop this too. */
.hero-stat-num .price-was {
  font-size: 0.55em;
  font-weight: 500;
  margin-right: -0.64em;
}
/* Bright discounted price in the prose lines (hero sub-lead, final CTA,
   #520 r7): the live amount pops near-white from the gray body text while
   the struck original stays muted. Same token the headings use. */
.price-now {
  color: var(--text-primary);
}
/* r8: in the hero the bright amounts are links into #pricing (same
   destination as the nav "Pricing" link). Underline conventions mirror
   .hero-benchmark-link; hover mirrors the site's opacity pattern; keyboard
   focus keeps the UA focus ring like the other inline text links. */
.price-link {
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: opacity 0.2s;
}
.price-link:hover {
  opacity: 0.8;
}
.pricing-beta-badge {
  /* Small-caps white "50% off with" + the copy pill on one row, reading as a
     sentence ("50% OFF WITH [CODE: BETA50]" — mirrors the hero/footer
     language); the pill wraps under the text on narrow cards. r7: the r4/r6
     wide-gap experiments are reverted — the connector word "with" is what
     the design wanted, with natural word-like spacing around the pill. The
     pill resets the badge's uppercase/tracking internally, then re-applies
     the same small-caps treatment to its own CODE: label. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.4rem;
  flex-basis: 100%;
  margin-top: 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.pricing-promo {
  /* Plain standalone line under the section header (the old border-pill
     treatment moved onto the .promo-pill component itself). */
  max-width: min(100%, 38rem);
  margin: 0 auto 2.25rem;
  font-size: 0.875rem;
  text-align: center;
  color: var(--text-secondary);
}

/* =====================================================
   BETA50 COPY PILL (#520) — the one branded promo-code component.
   A pill-shaped <button> used inline wherever the code is named:
   CODE: in the small uppercase sans (white — same treatment as the tiles'
   "50% off" label) + BETA50 in JetBrains Mono in the #500 deep blue + a
   Lucide copy glyph. Click copies the code (see script.js
   [data-copy-code] module); .is-copied swaps the glyph to a check via
   display toggles only — no transitions, so the state change is
   reduced-motion safe by construction. The opaque ink-850 fill +
   hairline border read identically on the canvas and on cards. */
.promo-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.65rem 0.2rem 0.6rem;
  border: 1px solid var(--border-primary);
  border-radius: 999px;
  background: var(--bg-tertiary);
  cursor: pointer;
  vertical-align: baseline;
  white-space: nowrap; /* the pill never breaks internally */
  /* Neutralise inherited context (uppercase badge, hero tracking, …). */
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: normal;
  text-transform: none;
}
.promo-pill:hover {
  border-color: var(--color-border-strong);
  background: var(--bg-card-hover);
}
.promo-pill-label {
  /* Same small-caps sans treatment as the tiles' "50% off" label. Source
     text stays lowercase ("code:") and is uppercased here — the same
     mechanism the badge uses — overriding the pill's text-transform reset. */
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.promo-pill-code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* ~4.9:1 on the pill's ink-850 fill — passes WCAG AA for the 13px text
     (same #500 blue the tiles already clear at ~5:1 on ink-900 cards). */
  color: var(--accent-blue-deep);
}
.promo-pill-icon {
  flex-shrink: 0;
  color: var(--text-tertiary);
}
/* Copy ↔ check swap: both glyphs are in the DOM; the class shows one. */
.promo-pill .promo-pill-icon-check {
  display: none;
}
.promo-pill.is-copied .promo-pill-icon-copy {
  display: none;
}
.promo-pill.is-copied .promo-pill-icon-check {
  display: inline-block;
  color: var(--color-success);
}
/* Visually-hidden per-pill live region ("Copied" announcement). Self-contained
   rather than Tailwind's sr-only so the pill works on every page styles.css
   reaches. */
.promo-pill-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* =====================================================
   TECHNICAL OVERVIEW (#specs) — "Open at the core, custom where it counts."
   Ports the design-final mockup to CSP-safe classes (no inline style=). Raw
   values match the mockup's design tokens 1:1 so the section renders exactly
   as designed; mono elements use the site's --font-mono (JetBrains Mono) in
   place of the mockup's stand-in 'Geist Mono' (which the site does not load).
   This block supersedes the old .hood privacy under-the-hood panel that
   previously owned the #specs anchor. Design per the #578 brief.
   ===================================================== */
.eg-specs {
  position: relative;
  overflow: hidden;
  margin-top: 36px;
  padding: 92px 0 100px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
/* off-screen SVG holding the brand-gradient <defs> the icons stroke via url() */
.eg-gradient-def {
  position: absolute;
  width: 0;
  height: 0;
}
.eg-specs__glow {
  position: absolute;
  top: 120px;
  left: 50%;
  width: 880px;
  height: 480px;
  transform: translateX(-50%);
  background: radial-gradient(
    50% 50% at 50% 40%,
    rgba(123, 131, 242, 0.08),
    rgba(123, 131, 242, 0) 72%
  );
  pointer-events: none;
}
.eg-specs__inner {
  position: relative;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 32px;
}
.eg-specs__head {
  max-width: 720px;
  margin: 0 auto;
  text-align: center;
}
.eg-specs__eyebrow {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #6c6c76;
}
.eg-specs__title {
  margin: 18px 0 0;
  font-size: clamp(34px, 4.3vw, 52px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.022em;
  color: #f4f4f6;
  text-wrap: balance;
}
.eg-specs__intro {
  margin: 18px 0 0;
  font-size: 19px;
  line-height: 1.58;
  color: #a2a2ac;
  text-wrap: pretty;
}

/* two equal tiles */
.eg-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
  align-items: stretch;
  margin-top: 46px;
}
.eg-tile {
  display: flex;
  flex-direction: column;
  /* Grid item: allow it to shrink to its 1fr track instead of being sized up to
     its content's min-content, which would blow the single-column mobile grid
     past the viewport (#595). */
  min-width: 0;
  padding: 15px 30px 12px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 20px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.024), rgba(255, 255, 255, 0.007));
  box-shadow:
    0 26px 70px -36px rgba(0, 0, 0, 0.9),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition:
    border-color 0.25s ease,
    box-shadow 0.25s ease;
}
.eg-tile:hover {
  border-color: rgba(255, 255, 255, 0.13);
  box-shadow:
    0 30px 80px -34px rgba(0, 0, 0, 0.95),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.eg-tile__head {
  display: flex;
  /* Wrap on narrow screens so the eyebrow + tag stack instead of summing their
     nowrap widths — that sum otherwise forced the tile (and the 1fr grid track)
     wider than a phone viewport, clipping every row under .eg-specs'
     overflow:hidden (#595). */
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.eg-tile__eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #7f7f89;
  white-space: nowrap;
}
.eg-tile__tag {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: #83838d;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  padding: 5px 11px;
  white-space: nowrap;
}
.eg-tile__badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  white-space: nowrap;
}
.eg-tile__badge-mark {
  display: block;
  width: 16px;
  height: 16px;
  object-fit: contain;
}
.eg-tile__badge-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: #83838d;
}
.eg-tile__title {
  margin: 14px 0 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #f1f1f4;
}

/* pipeline-component rows */
.eg-row {
  display: flex;
  gap: 15px;
  padding: 19px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.eg-row--first {
  margin-top: 12px;
}
.eg-icon {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 11px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: linear-gradient(180deg, #1c1d30, #111119);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.eg-logo {
  display: block;
  object-fit: contain;
}
.eg-logo--fluidaudio {
  width: 30px;
  height: 30px;
  border-radius: 7px;
}
.eg-logo--nvidia {
  /* the source PNG has a black plate; screen-blend drops it out on the dark tile */
  width: 32px;
  height: 32px;
  mix-blend-mode: screen;
}
.eg-logo--liquid {
  /* black wordmark inverted to white for the dark tile */
  width: 34px;
  height: auto;
  filter: invert(1);
}
.eg-row__body {
  flex: 1;
  min-width: 0;
}
.eg-row__titleline {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
}
.eg-row__name {
  font-size: 16.5px;
  font-weight: 600;
  color: #eeeef2;
}
.eg-license {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.03em;
  color: #9a9aa4;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  padding: 3px 9px;
  white-space: nowrap;
}
/* NEW tag on the two Command Mode #specs rows (#662). TEMPORARY — remove after
   a release cycle, along with the .eg-newchip spans in index.html.

   This is the site's only use of the in-app "command blue". In the product,
   blue is the signal that you are commanding rather than dictating, and the
   demo loop above already puts that blue on this page, so the tag ties the two
   new rows to the mode they belong to instead of reading as a third brand
   colour. Deliberately NOT --eg-mbc: the brand gradient means "evoglyph", not
   "command state". Scope is intentionally tiny (two chips) — the value is
   pending owner confirmation, so a wider rollout should not depend on it. */
.eg-newchip {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--eg-command-text);
  border: 1px solid var(--eg-command-border);
  border-radius: 5px;
  padding: 2px 6px;
  white-space: nowrap;
}
.eg-desc {
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: #93939d;
}
.eg-more {
  color: #93939d;
  text-decoration: none;
  white-space: nowrap;
}
.eg-more:hover {
  color: #ffffff;
}
.eg-more__label {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.eg-more svg {
  /* Tailwind preflight sets svg { display: block }, which drops this inline
     arrow onto its own line; restore inline flow so it sits after the label. */
  display: inline-block;
  vertical-align: -1px;
}
.eg-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 12px;
}
.eg-tile--custom .eg-tags {
  margin-top: 11px;
}
.eg-chip {
  font-family: var(--font-mono);
  font-size: 11px;
  color: #c2c2ca;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.03);
  border-radius: 7px;
  padding: 4px 9px;
  white-space: nowrap;
}
@media (max-width: 820px) {
  .eg-tiles {
    grid-template-columns: 1fr;
  }
}
/* Phone: tighten the section + tile horizontal insets so the single-column
   tiles have room (the desktop 32px + 30px insets ate a third of a 390px
   screen) and narrow-phone content clears the tag pill comfortably (#595). */
@media (max-width: 560px) {
  .eg-specs__inner {
    padding: 0 20px;
  }
  .eg-tile {
    padding: 15px 22px 12px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .eg-tile {
    transition: none;
  }
}

/* #439: privacy cards use smaller type than the why-cards (mock: 16px title,
   13.5px body), so scope these overrides to .privacy-claim-card only. */
.privacy-claim-card h3 {
  font-size: 1rem;
}
.privacy-claim-card p {
  font-size: 0.84rem;
}

/* =====================================================
   FAQ — single bordered container w/ divider rows (#439). Overrides the older
   separate-card .faq-item look; keeps the grid-height open/close animation.
   ===================================================== */
.faq-list {
  border: 1px solid var(--border-primary);
  border-radius: 16px;
  overflow: hidden;
  background: var(--bg-card);
}
.faq-list .faq-item {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  border-radius: 0;
}
.faq-list .faq-item:last-child {
  border-bottom: 0;
}
.faq-list .faq-item:hover {
  border-color: var(--border-subtle);
}
.faq-list .faq-item.open {
  background: transparent;
  border-color: var(--border-subtle);
}
.faq-list .faq-trigger span {
  font-size: 0.9375rem;
}
.faq-list .faq-content p {
  font-size: 0.875rem;
}

/* =====================================================
   FINAL CTA — card-less centered section + bottom glow (#439).
   ===================================================== */
.cta-title {
  font-family:
    'Inter',
    -apple-system,
    sans-serif;
  font-weight: 600;
  font-size: clamp(2.125rem, 5vw, 3.25rem);
  line-height: 1.04;
  letter-spacing: -0.035em;
  color: var(--text-primary);
}
.cta-glow {
  position: absolute;
  bottom: -180px;
  left: 50%;
  transform: translateX(-50%);
  width: 820px;
  max-width: 100%;
  height: 460px;
  pointer-events: none;
  background: radial-gradient(
    ellipse 55% 60% at 50% 100%,
    color-mix(in oklch, var(--accent-blue) 16%, transparent),
    color-mix(in oklch, #ff7bfd 9%, transparent) 45%,
    transparent 72%
  );
}
/* The final CTA reuses the hero free-trial component (#484). The hero instance
   is left-aligned inside the left-aligned hero column; here the section is
   centered, so center the flex row to match. */
#get-started .hero-trial {
  justify-content: center;
}
/* #439: CTA 'See pricing' reads as a clean transparent ghost w/ bright label
   (mock); the open FAQ caret takes the accent; carets are 16px (mock). */
#get-started .cta-secondary {
  background: transparent;
  color: var(--text-primary);
}
.faq-list .faq-icon {
  width: 16px;
  height: 16px;
}
.faq-list .faq-item.open .faq-icon {
  color: var(--accent);
}

/* #439: footer status badge uses mono type (matches the mock). */
.footer-status {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.6875rem;
  font-weight: 500;
}

/* #439: offset in-page anchor jumps for the fixed nav (the smooth-scroll JS
   uses scrollIntoView, which honours scroll-padding) so a nav link like
   Features -> #features lands below the bar, not hidden under it. Also what
   makes the .anchor-alias markers (#662) land correctly. */
html {
  scroll-padding-top: 5rem;
}

/* ---------- Menu-bar dictation HUD (replaces the old speed bar + sound wave) ----------
   A dark "device" panel echoing the app's menu-bar HUD. min-height reserves
   space so the animated content does not shift layout (CLS guard). */
.hud {
  position: relative;
  background: oklch(0.13 0.014 264);
  border: 1px solid var(--color-border-strong, var(--border-primary));
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.hud-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  background: oklch(1 0 0 / 0.03);
  border-bottom: 1px solid oklch(1 0 0 / 0.06);
}
.hud-status {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.hud-rec {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-emerald);
  flex-shrink: 0;
}
.hud-body {
  padding: 1.25rem 1.25rem 1.5rem;
}
/* Waveform: reuses the .wave-bar scaleY keyframe (defined above). Bars take the
   MBC gradient so the HUD reads as the brand's "listening" state. */
.hud-wave {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 40px;
}
.hud-wave .wave-bar {
  flex: 1;
  max-width: 5px;
  height: 100%;
  border-radius: 3px;
  background: var(--eg-mbc);
  transform-origin: center;
}
.hud-recognized {
  margin-top: 1rem;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text-primary);
}
.hud-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.85rem;
  padding: 0.2rem 0.55rem;
  border-radius: 7px;
  border: 1px solid var(--border-primary);
  background: oklch(1 0 0 / 0.03);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

/* ---------- Cleanup story: speed callout + coverage chips + disclaimer ---------- */
.speed-callout {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.speed-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: clamp(2.5rem, 6vw, 3.5rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  background: var(--eg-mbc);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--eg-spectrum);
}
.coverage-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border-primary);
  background: var(--bg-card);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.75rem;
  color: var(--text-secondary);
}
.latency-note {
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--text-tertiary);
}

/* ---------- 16-app chip strip (replaces the heavy SVG app grid) ---------- */
.app-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.75rem;
  border-radius: 8px;
  border: 1px solid var(--border-primary);
  background: var(--bg-card);
  font-size: 0.8125rem;
  color: var(--text-secondary);
  transition:
    border-color 150ms ease,
    color 150ms ease;
}
.app-chip:hover {
  border-color: var(--border-subtle);
  color: var(--text-primary);
}

/* ---------- "verify:" line — monospace, emerald (privacy grid) ---------- */
.verify-line {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--color-emerald);
}

/* ---------- Social proof: example ribbon + initials avatar ---------- */
.example-ribbon {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  border: 1px dashed var(--accent);
  background: var(--accent-muted);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.avatar-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--eg-mbc);
  color: oklch(0.18 0.02 264);
  font-weight: 700;
  font-size: 0.8125rem;
  flex-shrink: 0;
}
.star-rating {
  color: var(--color-emerald);
  letter-spacing: 0.1em;
}

/* ---------- Comparison: CSS grid (not a table), highlighted evoglyph column ----------
   Desktop: 5 columns (feature label + evoglyph + 3 competitors). The evoglyph
   column is tinted and its header wears the MBC gradient-ring. Mobile (<720px):
   collapses to label + evoglyph + ONE competitor chosen by the .cmp-switcher;
   script.js sets .cmp--wispr / --super / --open and the inactive competitor
   cells are hidden. No latency row (it left for its own callout in §cleanup). */
.cmp {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(4, minmax(0, 1fr));
  border: 1px solid var(--border-primary);
  border-radius: 16px;
  overflow: hidden;
  background: var(--bg-card);
}
.cmp-cell {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 1rem;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.9rem;
  min-width: 0;
}
.cmp-head {
  border-top: 0;
  font-weight: 600;
  color: var(--text-primary);
  justify-content: center;
}
.cmp-row-label {
  color: var(--text-secondary);
  justify-content: flex-start;
}
.cmp-value {
  justify-content: center;
  text-align: center;
  color: var(--text-secondary);
}
/* evoglyph column tint + gradient-ring header chip. */
.cmp-eg {
  background: var(--accent-muted);
  color: var(--text-primary);
}
.cmp-eg.cmp-head {
  position: relative;
}
.cmp-eg.cmp-head::before {
  content: '';
  position: absolute;
  inset: 0.5rem 0.4rem;
  border-radius: 999px;
  padding: 1.5px;
  background: var(--eg-mbc) border-box;
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.cmp-yes {
  color: var(--color-emerald);
  font-weight: 700;
}
.cmp-no {
  color: var(--text-tertiary);
}
.cmp-switcher {
  display: none;
}
@media (max-width: 720px) {
  .cmp {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr);
  }
  .cmp-switcher {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
  }
  .cmp-switch-btn {
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    border: 1px solid var(--border-primary);
    background: var(--bg-card);
    color: var(--text-secondary);
    font-size: 0.8125rem;
    transition:
      border-color 150ms ease,
      color 150ms ease,
      background-color 150ms ease;
  }
  .cmp-switch-btn[aria-pressed='true'] {
    border-color: var(--accent);
    background: var(--accent-muted);
    color: var(--text-primary);
  }
  /* Hide all competitor cells, then reveal the active one. */
  .cmp-c-wispr,
  .cmp-c-super,
  .cmp-c-open {
    display: none;
  }
  .cmp--wispr .cmp-c-wispr,
  .cmp--super .cmp-c-super,
  .cmp--open .cmp-c-open {
    display: flex;
  }
}

/* Reduced-motion: freeze the HUD waveform + decorative pulses at a static
   frame (build note: "paused under reduced-motion, show a static frame"). */
@media (prefers-reduced-motion: reduce) {
  .wave-bar,
  .recording-pulse,
  .cursor-blink,
  .pulse-dot {
    animation: none !important;
    transform: none !important;
  }
}

/* ---------- Nav: scroll tint (#417) + gradient Download (#439) ----------
   The bar is transparent over the canvas with a single hairline underline; on
   scroll a faint 6% white tint fades in (no blur — decision L3). The Download
   uses the shared MBC gradient-ring treatment (see the .cta-primary group
   above) to match the Jun-28 mock — the #417 solid-pill override is gone. */
#navbar {
  border-bottom: 1px solid var(--color-border-subtle);
  transition: background-color 300ms ease;
}
#navbar.scrolled {
  background: color-mix(in oklch, oklch(1 0 0) 6%, var(--color-canvas));
  border-bottom-color: var(--color-border-hairline);
}
/* Mobile menu toggle: the visible icon is 24px; extend the tap target to
   44x44 via a transparent ::after (surfaces §Minimum Hit Area), mirroring the
   .footer-social pattern. The toggle sits alone at the bar's right edge, so the
   extended target never overlaps a neighbour. */
#mobile-menu-toggle {
  position: relative;
}
#mobile-menu-toggle::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
/* #439: nav Download uses the shared MBC gradient-ring treatment (transparent
   pill + ring + white label, from the .cta-primary group above) to match the
   Jun-28 mock. The #417 solid-neutral-pill override was removed. */
.nav-cta:active {
  transform: scale(0.96);
}
.nav-cta:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

/* =====================================================
   ACCOUNT / AUTH SURFACE (account.html)

   Restores the auth-* component classes that previously lived in login.css
   (deleted in #415 along with the dead mock login/dashboard pages). account.html
   — the real account/sign-in page — still consumes them, so the page rendered
   borderless + nearly invisible without these rules (#503). Consolidated here
   rather than in a separate login.css: account.html already loads styles.css,
   and the build's content-hash rewrite keeps this single <link> in sync. A
   standalone login.css left a stale, 404-ing hashed href behind.

   Dark-mode only (#223); every value reads a shared semantic token.
   ===================================================== */

/* Soft accent-tinted radial wash behind the card. */
.auth-glow {
  background:
    radial-gradient(ellipse 60% 50% at 50% 0%, var(--accent-muted) 0%, transparent 60%),
    radial-gradient(ellipse 40% 40% at 80% 80%, var(--accent-muted) 0%, transparent 60%);
}

.auth-card {
  background: var(--bg-card);
  border: 1px solid var(--border-primary);
  box-shadow: var(--shadow-lg);
  transition-property: box-shadow, border-color, background-color;
  transition-duration: 0.2s;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

/* Email field + the license-key display box. The resting border meets WCAG
   1.4.11 non-text contrast (≈4.6:1 against both the field fill and the card
   behind it). The design system's hairline border (~2.5:1) does NOT clear the
   3:1 bar, which — combined with Tailwind Preflight's border-0 reset — left the
   field invisible on the dark canvas (#503). */
.auth-input {
  background: var(--bg-tertiary);
  border: 1px solid color-mix(in oklch, var(--color-text-primary) 22%, transparent);
  color: var(--text-primary);
  transition-property: background-color, border-color, box-shadow, color;
  transition-duration: 0.2s;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

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

.auth-input:focus {
  border-color: var(--accent);
  background: var(--bg-card-hover);
}

/* Keyboard focus indicator — same outline convention as the rest of the site. */
.auth-input:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.auth-link-btn {
  color: var(--text-secondary);
  cursor: pointer;
  transition-property: color, transform;
  transition-duration: 0.15s;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.auth-link-btn:hover {
  color: var(--accent-text);
}

.auth-link-btn:active {
  transform: scale(0.96);
}
