/* =====================================================
   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);
}

/* Background helpers (badge pulse dot, demo after-icon chip). */
.bg-accent-token {
  background: var(--accent);
}
.bg-accent-light-token {
  background: var(--accent-light);
}

/* Top hairline border used by comparison rows and footer dividers. */
.border-subtle-t {
  border-top: 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 {
  color: var(--accent-text);
  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%);
  transition: transform 150ms ease-out;
}
.skip-link:focus {
  transform: translateY(0);
  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 {
  --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);
  /* 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 full-rainbow conic
     (--eg-rainbow) remains the LOGO/brand mark only. */
  --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;
}

/* =====================================================
   IMAGE OUTLINES (Principle 11)
   Subtle 1px outline on screenshots / hero images / OG
   art. Pure black/white only (never tinted neutrals).
   ===================================================== */
/* Dark-mode only (#223): a faint light hairline on the dark canvas. */
.image-outline,
img.image-outline,
.image-outline img {
  outline: 1px solid oklch(1 0 0 / 0.1);
  outline-offset: -1px;
}

/* =====================================================
   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;
}

/* =====================================================
   ANIMATED BRAND HOVER (#217, hover reworked #219)
   Nav + footer brand only. At rest: the rainbow gradient
   triangle + plain wordmark (same look as the static mark).
   On hover: the triangle BODY goes transparent and only a
   ~2px rainbow stroke outline remains (the page bg shows
   through the interior), with the gradient FLOWING around
   that outline (--eg-angle spins a full turn, 2s linear).
   The rainbow "recording core" still spins through the
   center hole (1.98s/turn, mirroring the app's recording
   animation), and a single shimmer band sweeps the wordmark.
   Pure CSS — no JS, no inline styles (CSP). At rest the
   triangle is masked by /evoglyph-glyph-mask.svg (silhouette
   with the center circle punched out); on hover the mask
   swaps to /evoglyph-glyph-stroke-mask.svg (outer outline
   only), leaving just the animated edge.
   ===================================================== */

/* Animatable start-angle for the triangle's conic gradient. At rest it sits at
   210deg (identical to --eg-rainbow's fixed start), so the rest look is
   unchanged; on hover it animates a full turn to flow the gradient around the
   stroke outline. Registered so CSS can interpolate the <angle> (plain custom
   properties animate as discrete strings and would not flow). */
@property --eg-angle {
  syntax: '<angle>';
  initial-value: 210deg;
  inherits: false;
}

/* The icon's angular rainbow, declared once so the triangle
   fill and the core share identical stops. The core keeps this
   fixed-angle version (it spins via a transform, not the angle).
   The triangle uses the same stops but starts from --eg-angle so
   the gradient itself can rotate on hover. */
.eg-brand {
  --eg-rainbow: conic-gradient(
    from 210deg,
    #ff7bfd 9%,
    #ff8875 21%,
    #ffbc80 28%,
    #ecff74 35%,
    #99ffa6 41%,
    #85ffe1 49%,
    #7fffff 62%,
    #6cbeff 75%,
    #798dfe 84%,
    #b57cf6 96%,
    #ff7bfd 100%
  );
}

/* Mark footprint. The mask viewBox was padded from `0 0 17 16` to
   `-1.5 -1.5 20 19` (#221) so the hover STROKE outline (stroke-width 1.7, half
   = 0.85) is no longer clipped at the box edges — the 1.5-unit pad on every
   side comfortably clears the stroke's outer half. The padding insets the
   17x16 glyph to 85% of the box width, so the box is enlarged by 1/0.85 to keep
   the visible triangle at the same ~1.5x prominence it had at 30x28.5. The new
   aspect (20:19) matches the padded viewBox so the mask still maps 1:1. */
.eg-mark {
  position: relative;
  flex-shrink: 0;
  width: 35.3px; /* 30 / 0.85 — restores the inset glyph to ~30px wide */
  height: 33.5px; /* 35.3 * 19/20 — aspect matches the -1.5 -1.5 20 19 viewBox */
  /* Subtle rest glow to echo the source PNG's bloom. */
  filter: drop-shadow(0 0 4px rgb(255 255 255 / 0.18));
}

/* The triangle: rainbow conic gradient, clipped to the glyph
   silhouette (center circle is a hole in the mask). Uses the same
   stops as --eg-rainbow but starts from --eg-angle so the gradient
   can flow on hover. At rest --eg-angle is 210deg => identical look. */
.eg-mark__tri {
  position: absolute;
  inset: 0;
  background-image: conic-gradient(
    from var(--eg-angle),
    #ff7bfd 9%,
    #ff8875 21%,
    #ffbc80 28%,
    #ecff74 35%,
    #99ffa6 41%,
    #85ffe1 49%,
    #7fffff 62%,
    #6cbeff 75%,
    #798dfe 84%,
    #b57cf6 96%,
    #ff7bfd 100%
  );
  -webkit-mask: url('/evoglyph-glyph-mask.svg') center / contain no-repeat;
  mask: url('/evoglyph-glyph-mask.svg') center / contain no-repeat;
}

/* On hover the triangle BODY goes transparent: the mask swaps from
   the filled silhouette to the outer-triangle STROKE outline, so only
   a ~2px rainbow edge shows and the interior reveals the page bg. The
   gradient flows around that edge via the --eg-angle animation below. */
.eg-brand:hover .eg-mark__tri,
.eg-brand:focus-visible .eg-mark__tri {
  -webkit-mask: url('/evoglyph-glyph-stroke-mask.svg') center / contain no-repeat;
  mask: url('/evoglyph-glyph-stroke-mask.svg') center / contain no-repeat;
}

/* The "recording core": a rainbow disc sized + positioned to
   sit inside the glyph's center hole. With the padded viewBox
   (-1.5 -1.5 20 19, #221) the hole center (8.18, 6) maps to
   (8.18+1.5)/20 ≈ 48.4% / (6+1.5)/19 ≈ 39.5%; the ~8-unit-wide
   hole is 8/20 = 40% of the box width (42% of its height).
   Hidden at rest, behind the triangle. */
.eg-mark__core {
  position: absolute;
  width: 40%;
  height: 42%;
  left: 48.4%;
  top: 39.5%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  /* Rest: the center reads as an empty hole — the triangle's punched-out
     center shows the dark page background through it. On hover it fills with
     the spinning rainbow "recording core". */
  background-image: var(--eg-rainbow);
  opacity: 0;
  transition: opacity 0.25s ease;
  z-index: 0; /* behind the triangle, shows through the hole */
}

.eg-mark__tri {
  z-index: 1;
}

.eg-brand:hover .eg-mark__core,
.eg-brand:focus-visible .eg-mark__core {
  opacity: 1;
}

/* Wordmark shimmer. Two stacked backgrounds clipped to the
   text: an off-screen rainbow band over a flat currentColor
   fill. At rest the band sits off-screen so the text renders
   in the normal wordmark color. On hover it sweeps once. */
.eg-wordmark {
  background-image:
    linear-gradient(
      110deg,
      transparent 0%,
      #ff7bfd 10%,
      #ff8875 21%,
      #ffbc80 27%,
      #ecff74 34%,
      #99ffa6 39%,
      #85ffe1 47%,
      #7fffff 59%,
      #6cbeff 71%,
      #798dfe 79%,
      #b57cf6 90%,
      transparent 100%
    ),
    linear-gradient(currentColor, currentColor);
  background-size:
    6rem 100%,
    100% 100%;
  background-repeat: no-repeat;
  background-position:
    -7rem center,
    0 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--text-primary);
}

@media (prefers-reduced-motion: no-preference) {
  /* Flow the gradient around the stroke outline: spin --eg-angle a full
     turn (210 -> 570 == 210 + 360), looping while hovered. The stroke
     shape is fixed; only the gradient rotates within it. */
  .eg-brand:hover .eg-mark__tri,
  .eg-brand:focus-visible .eg-mark__tri {
    animation: eg-flow 2s linear infinite;
  }

  .eg-brand:hover .eg-mark__core,
  .eg-brand:focus-visible .eg-mark__core {
    animation: eg-rec-spin 1.98s linear infinite;
  }

  .eg-brand:hover .eg-wordmark,
  .eg-brand:focus-visible .eg-wordmark {
    animation: eg-shimmer 1.1s ease-in-out 1;
  }
}

@keyframes eg-flow {
  to {
    --eg-angle: 570deg;
  }
}

@keyframes eg-rec-spin {
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

@keyframes eg-shimmer {
  to {
    background-position:
      calc(100% + 7rem) center,
      0 0;
  }
}

.gradient-text {
  /* Hero accent word — the MBC gradient clipped to the text (#221, MBC #222). The
     magenta/blue/cyan stops are vivid AND legible on the dark canvas (every stop
     ≥ 8.8:1). Dark-mode only (#223), so no light-surface deepened variant. */
  background: var(--eg-mbc);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--eg-spectrum); /* fallback if background-clip unsupported */
  font-weight: 900;
}

.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);
}

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

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

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

/* =====================================================
   HERO SOUND WAVE
   ===================================================== */

.hero-wave-container {
  width: 100%;
  overflow: hidden;
}

.hero-wave {
  width: 100%;
  height: 80px;
}

@media (min-width: 768px) {
  .hero-wave {
    height: 120px;
  }
}

.hero-wave-path {
  fill: none;
  transition: d 0.1s ease;
}

/* 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 DOWNLOAD MODULE (#213 — download-first hero)
   The dominant above-the-fold CTA. Primary .dmg button + a compact,
   lower-emphasis developer-install disclosure so the one-click DMG stays
   visually dominant.
   ===================================================== */

.hero-download {
  max-width: 42rem;
}

/* Developer-install <details> disclosure. Quiet by default so it never
   competes with the DMG button above it. The wrapper itself is borderless;
   the inner code rows (.use-case-example) own their own radius, so no
   concentric-radius math is needed on it. */
.hero-dev-install-summary {
  cursor: pointer;
  color: var(--text-tertiary);
  font-weight: 500;
  list-style: none;
  width: fit-content;
  /* Min hit area (surfaces §Minimum Hit Area): the visible text is short, so
     pad the row out to a >=40px tap target without shifting the label. */
  min-height: 40px;
  padding-block: 0.5rem;
  transition-property: color;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}

/* Hide the native disclosure triangle (we render our own caret) across
   engines so the summary reads as a quiet text link. */
.hero-dev-install-summary::-webkit-details-marker {
  display: none;
}
.hero-dev-install-summary::marker {
  content: '';
}

.hero-dev-install-summary:hover {
  color: var(--text-secondary);
}

/* Interruptible rotation on the caret — a CSS transition (not a keyframe) so
   toggling open/closed mid-animation retargets smoothly (animations §
   Interruptible Animations). */
.hero-dev-install-caret {
  transition-property: transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.hero-dev-install[open] .hero-dev-install-caret {
  transform: rotate(90deg);
}

/* Price-transparency link in the hero (replaces the old "View Pricing"
   competing button). Accent-coloured text affordance, generous tap height. */
.hero-pricing-link {
  color: var(--accent-text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: color-mix(in oklch, var(--color-accent) 45%, transparent);
}

.hero-pricing-link:hover {
  text-decoration-color: var(--color-accent);
}

/* =====================================================
   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-card {
  background: oklch(0.13 0.014 264);
  border: 1px solid var(--color-border-hairline);
  box-shadow: var(--shadow-lg);
}

.terminal-header {
  background: oklch(1 0 0 / 0.03);
  border-bottom: 1px solid oklch(1 0 0 / 0.06);
}

.terminal-body {
  background: oklch(0.1 0.012 264);
  color: oklch(0.92 0.01 264);
}

/* Waveform animation */
.wave-bar {
  animation: wave 1.4s ease-in-out infinite;
}

.wave-bar:nth-child(1) {
  animation-delay: 0s;
}
.wave-bar:nth-child(2) {
  animation-delay: 0.1s;
}
.wave-bar:nth-child(3) {
  animation-delay: 0.2s;
}
.wave-bar:nth-child(4) {
  animation-delay: 0.3s;
}
.wave-bar:nth-child(5) {
  animation-delay: 0.4s;
}
.wave-bar:nth-child(6) {
  animation-delay: 0.15s;
}
.wave-bar:nth-child(7) {
  animation-delay: 0.25s;
}
.wave-bar:nth-child(8) {
  animation-delay: 0.05s;
}
.wave-bar:nth-child(9) {
  animation-delay: 0.35s;
}
.wave-bar:nth-child(10) {
  animation-delay: 0.45s;
}
.wave-bar:nth-child(11) {
  animation-delay: 0.1s;
}
.wave-bar:nth-child(12) {
  animation-delay: 0.3s;
}

@keyframes wave {
  0%,
  100% {
    transform: scaleY(1);
    opacity: 0.6;
  }
  50% {
    transform: scaleY(0.35);
    opacity: 1;
  }
}

/* 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);
  }
}

/* Cursor blink */
.cursor-blink {
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

/* 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);
}

/* =====================================================
   STEP CARDS
   ===================================================== */

.step-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);
}

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

.step-number {
  color: var(--border-subtle);
  font-variant-numeric: tabular-nums;
  user-select: none;
}

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

/* =====================================================
   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-card-highlight {
  background: var(--accent-muted);
  border-color: var(--border-primary);
}

.feature-card-highlight:hover {
  background: var(--accent-light);
  border-color: var(--border-primary);
}

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

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

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

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

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

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

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

.check-yes {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  /* The check glyph itself is the MBC gradient (clipped to text) and a thin
     gradient ring is drawn by ::before (#225). `color` is a visible periwinkle
     fallback so the ✓ still shows if background-clip:text is unsupported;
     supporting browsers paint it transparent (revealing the clipped gradient). */
  background: var(--eg-mbc);
  -webkit-background-clip: text;
  background-clip: text;
  color: var(--eg-spectrum);
  -webkit-text-fill-color: transparent;
  font-size: 12px;
  font-weight: 700;
}

.check-yes::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  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;
}

.check-no {
  color: var(--text-tertiary);
  font-size: 14px;
}

.check-partial {
  color: var(--text-tertiary);
}

/* =====================================================
   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: 300ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.pricing-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-primary);
  box-shadow: var(--shadow-lg);
}

.pricing-card-featured {
  background: var(--accent-muted);
  border-color: var(--accent);
  box-shadow:
    0 0 0 1px var(--accent-muted),
    var(--shadow-lg);
}

.pricing-card-featured:hover {
  background: var(--accent-light);
  border-color: var(--accent);
  box-shadow:
    0 0 0 1px var(--accent-muted),
    var(--shadow-lg);
}

/* "Most Popular" badge on the featured pricing card. A small filled chip is
   too small for a hairline gradient outline to read, so this one keeps a SOLID
   fill — but off the retired blue: a subtle MBC gradient fill with dark text
   (#221, MBC #222). (Judgment call per #221: tiny badges stay filled, prominent
   buttons get the outline.) */
.popular-badge {
  background: var(--eg-mbc);
  color: oklch(0.18 0.02 264);
  box-shadow: 0 4px 14px oklch(0 0 0 / 0.35);
}

/* Featured-plan CTA mirrors the hero primary: transparent interior, ~3px
   MBC gradient border (shared rule below), white label. */
.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);
}

/* 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);
}

/* =====================================================
   PRIVACY SECTION
   ===================================================== */

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

.privacy-glow {
  background: radial-gradient(ellipse 60% 60% at 80% 50%, var(--accent-muted) 0%, transparent 70%);
}

.privacy-quote {
  font-style: italic;
  line-height: 1.6;
}

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

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

.download-glow {
  background:
    radial-gradient(ellipse 70% 70% at 30% 50%, var(--accent-muted) 0%, transparent 60%),
    radial-gradient(ellipse 50% 50% at 70% 50%, var(--accent-muted) 0%, transparent 60%);
}

/* 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);
}

.dev-install .use-case-example code {
  /* Commands are the content here, so let them sit at full mono size rather
     than the 0.9em used for the inline use-case samples elsewhere. */
  font-size: 0.85rem;
  white-space: nowrap;
}

/* =====================================================
   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-content {
  transition-property: max-height, opacity;
  transition-duration: 250ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.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;
  }
}

/* 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);
  background: color-mix(in oklch, var(--bg-primary) 95%, transparent);
  transition-property: opacity, transform, max-height;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

/* =====================================================
   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.
   ===================================================== */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

.speed-bar,
.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;
}

.version-pill {
  font-variant-numeric: tabular-nums;
}

/* =====================================================
   USE CASES
   ===================================================== */

.use-case-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);
}

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

.use-case-accent {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 50% at 100% 0%, var(--accent-muted) 0%, transparent 60%);
  pointer-events: none;
  opacity: 0.6;
  transition: opacity 0.3s ease;
}

.use-case-card:hover .use-case-accent {
  opacity: 1;
}

/* Code-sample box (dark-mode only, #223): a dark code surface a notch below
   the canvas. */
.use-case-example {
  background: oklch(0.1 0.012 264);
  border: 1px solid oklch(1 0 0 / 0.05);
  border-radius: 10px;
  padding: 14px 16px;
  line-height: 1.6;
  word-wrap: break-word;
  color: oklch(0.92 0.01 264);
}

.use-case-example code {
  font-size: 0.9em;
}

/* =====================================================
   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);
}

.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;
}

/* =====================================================
   SPEED COMPARISON
   ===================================================== */

.speed-bar {
  height: 44px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  padding: 0 16px;
  font-weight: 600;
  font-size: 14px;
  transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.speed-bar-typing {
  background: var(--border-primary);
  color: var(--text-secondary);
  width: 0;
}

.speed-bar-evoglyph {
  /* The "180 WPM" bar is the on-brand winner — filled with the MBC gradient
     (#222) instead of the flat indigo accent. The MBC pastels are light, so the
     label flips to dark ink (≥ 7.6:1 on every stop) rather than the white it used
     on the dark indigo. The neutral .speed-bar-typing bar is unchanged. */
  background: var(--eg-mbc);
  color: oklch(0.18 0.02 264);
  width: 0;
}

.speed-bar-typing.animated {
  width: 22%;
}

.speed-bar-evoglyph.animated {
  width: 100%;
}

@media (max-width: 640px) {
  .speed-bar {
    height: 36px;
    font-size: 12px;
  }
}

/* =====================================================
   APP GRID
   ===================================================== */

.app-grid-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px 8px;
  /* Concentric radius (surfaces §Concentric Border Radius): the inner tile is
     14px and sits 12px from the item's top edge, so the outer radius is
     14 + 12 = 26px. (Was 12px — an inverted/mismatched pair that read as off.) */
  border-radius: 26px;
  transition-property: background-color, transform;
  transition-duration: 200ms;
  transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

.app-grid-item:hover {
  background: var(--accent-muted);
}

.app-grid-icon {
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  /* Frosted light app-tile so dark/monochrome glyphs (GitHub, Terminal, iTerm2,
     Cursor, Linear, ChatGPT) read on the dark canvas (#196). */
  background: oklch(0.96 0.008 264);
  /* Image outline (surfaces §Image Outlines): a neutral 1px edge so the tiles
     read with consistent depth. PURE black/white only — the tile is a light
     surface even on the dark canvas, so it takes the light-mode black hairline.
     Never a tinted/palette neutral (would read as dirt on the edge). */
  outline: 1px solid oklch(0 0 0 / 0.1);
  outline-offset: -1px;
  box-shadow:
    0 1px 2px oklch(0 0 0 / 0.25),
    inset 0 1px 0 oklch(1 0 0 / 0.55);
  transition:
    transform 200ms cubic-bezier(0.2, 0, 0, 1),
    box-shadow 200ms cubic-bezier(0.2, 0, 0, 1);
}

.app-grid-item:hover .app-grid-icon {
  transform: translateY(-2px);
  box-shadow:
    0 6px 16px oklch(0 0 0 / 0.3),
    inset 0 1px 0 oklch(1 0 0 / 0.55);
}

.app-grid-icon svg {
  width: 30px;
  height: 30px;
}

.app-grid-label {
  font-size: 11px;
  color: var(--text-tertiary);
  text-align: center;
  line-height: 1.2;
}

/* =====================================================
   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-card {
    padding: 1.5rem;
  }

  .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,
.diff-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,
.diff-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,
.diff-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);
}
