/* World News — Apple-style, mixed theme: dark cinematic hero, light sections */

:root {
  --accent: #0071e3;          /* page accent on light backgrounds */
  --accent-hero: #2997ff;     /* brighter accent for the dark hero & globe */
  --accent-ink: #ffffff;
  --bg: #ffffff;
  --panel: #f5f5f7;
  --card: #ffffff;
  --card-sub: #f5f5f7;
  --hairline: #d2d2d7;
  --text: #1d1d1f;
  --muted: #6e6e73;
  --dim: #86868b;
  --orange: #ff9f0a;
  --green: #248a3d;
  --green-hero: #30d158;
  --red: #ff453a;
  --purple: #bf5af2;
  --hero-bg: #000000;
  --hero-text: #f5f5f7;
  --hero-muted: #a1a1a6;
  --hero-dim: #6e6e73;
  --radius: 28px;          /* large surfaces: app window, feature rail cards */
  --radius-card: 22px;     /* standard content cards / consoles / panels */
  --max: 1200px;
  --copy: 980px;
  /* One card language, applied everywhere a panel sits on a page surface:
     a single soft hairline, one two-layer elevation, one lifted-hover
     elevation, and one motion easing. Keeps every card visually related. */
  --card-border: rgba(0, 0, 0, 0.06);
  --shadow-card: 0 1px 2px rgba(20, 22, 40, 0.04), 0 12px 32px -18px rgba(20, 22, 40, 0.12);
  --shadow-card-hover: 0 2px 4px rgba(20, 22, 40, 0.05), 0 24px 48px -20px rgba(20, 22, 40, 0.18);
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
button, input, textarea { font: inherit; }
h1, h2, h3, h4, p { margin: 0; }
section { scroll-margin-top: 64px; }

.container { width: min(var(--max), calc(100% - 44px)); margin-inline: auto; }
.copy-width { width: min(var(--copy), calc(100% - 44px)); margin-inline: auto; }

/* ---------- Nav (light over hero, frosted-light when scrolled) ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  /* Height comes from .nav-inner (48px); the phone dropdown (.nav-menu) is
     inside the bar and needs it to grow when open. */
  color: var(--hero-text);
  transition: background 0.4s ease, border-color 0.4s ease, color 0.3s ease;
  border-bottom: 1px solid transparent;
  /* Promote the bar to its own composited layer so its frosted
     `backdrop-filter` reliably paints above the GPU-composited demo embed
     (a plain transform doesn't create a backdrop-root, so the blur is kept). */
  transform: translateZ(0);
}
.nav.scrolled {
  background: rgba(251, 251, 253, 0.78);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: rgba(0, 0, 0, 0.08);
  color: var(--text);
}
.nav-inner {
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.nav-brand { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.nav-links {
  display: flex;
  align-items: center;
  gap: 32px;
  font-size: 12px;
  opacity: 0.85;
}
.nav-links a:hover { opacity: 1; }
.nav-cta {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 14px;
  border-radius: 980px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 500;
}
.nav-cta:hover { filter: brightness(1.1); }
.nav-actions { display: flex; align-items: center; gap: 14px; }
.nav-auth {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 14px;
  border-radius: 980px;
  /* Inherit the nav's text colour so the pill is visible on BOTH the dark
     hero (light text) and the scrolled light bar (dark text). */
  color: inherit;
  border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
  background: transparent;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
}
.nav-auth:hover {
  border-color: color-mix(in srgb, currentColor 70%, transparent);
  background: color-mix(in srgb, currentColor 10%, transparent);
}

/* Hamburger — phones/tablets only (nav-links are hidden ≤980px). Two bars
   morph into a cross when the dropdown is open. */
.nav-burger {
  display: none;
  position: relative;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.nav-burger span {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.28s cubic-bezier(0.3, 0.7, 0.3, 1);
}
.nav-burger span:first-child { transform: translate(-50%, calc(-50% - 4.5px)); }
.nav-burger span:last-child { transform: translate(-50%, calc(-50% + 4.5px)); }
.nav.menu-open .nav-burger span:first-child { transform: translate(-50%, -50%) rotate(45deg); }
.nav.menu-open .nav-burger span:last-child { transform: translate(-50%, -50%) rotate(-45deg); }

/* Dropdown panel under the bar — same frosted-light material as the scrolled
   nav, so it reads on the dark hero and light sections alike. */
.nav-menu {
  display: none;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  background: rgba(251, 251, 253, 0.88);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  color: var(--text);
  padding: 6px 22px 14px;
}
.nav.menu-open .nav-menu { display: grid; }
.nav-menu a {
  padding: 13px 2px;
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.01em;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.nav-menu a:last-of-type { border-bottom: 0; }
.nav-menu .nav-menu-cta { color: var(--accent); font-weight: 600; }
/* While the dropdown is open the bar itself must sit on the same light glass
   (over the black hero the default transparent bar would clash with the panel). */
.nav.menu-open {
  background: rgba(251, 251, 253, 0.88);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: transparent;
  color: var(--text);
}

/* ---------- Type ---------- */
.eyebrow {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
.display {
  font-size: clamp(44px, 6.6vw, 88px);
  line-height: 1.04;
  font-weight: 600;
  letter-spacing: -0.025em;
}
.headline {
  font-size: clamp(32px, 4.3vw, 56px);
  line-height: 1.08;
  font-weight: 600;
  letter-spacing: -0.021em;
}
.subhead {
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.45;
  font-weight: 400;
  color: var(--muted);
  text-wrap: pretty;
}
.accent-text { color: var(--accent-hero); }

/* ---------- Buttons ---------- */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 26px;
  border: 0;
  border-radius: 980px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 17px;
  font-weight: 500;
  cursor: pointer;
  transition: filter 0.2s ease;
}
.btn-primary:hover { filter: brightness(1.12); }
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 26px;
  border-radius: 980px;
  border: 1px solid rgba(0, 0, 0, 0.3);
  color: var(--text);
  font-size: 17px;
  font-weight: 500;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.btn-ghost:hover { border-color: rgba(0, 0, 0, 0.55); background: rgba(0, 0, 0, 0.04); }
.link-arrow { color: var(--accent); font-size: 17px; font-weight: 400; }
.link-arrow::after { content: " ›"; }
.link-arrow:hover { text-decoration: underline; }

/* ---------- Hero (dark stage) ---------- */
.hero {
  position: relative;
  padding-top: 96px;
  padding-bottom: 0;
  overflow: hidden;
  text-align: center;
  background: var(--hero-bg);
  color: var(--hero-text);
}
.hero .eyebrow { color: var(--hero-dim); }
.hero .subhead { color: var(--hero-muted); }
.hero .link-arrow { color: var(--accent-hero); }
/* Frosted-glass CTA on the dark hero (solid accent fill is kept everywhere
   else the button appears — contact form, demo — so this override is
   hero-scoped). */
.hero .btn-primary {
  background: rgba(255, 255, 255, 0.12);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--hero-text);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.hero .btn-primary:hover {
  filter: none;
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.32);
}
.hero-copy { position: relative; z-index: 3; display: grid; justify-items: center; gap: 18px; }
.hero-copy .subhead { max-width: 720px; margin-top: 4px; }
.hero-actions { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 14px 28px; margin-top: 14px; }

/* Two-tone band that carries the live-app window across the black→white fold.
   The gradient splits exactly at 50% and the equal 54px vertical padding
   centres the frame on that seam, so the app window sits half on the dark hero
   and half on the light page regardless of viewport width. Padding stays equal
   top/bottom so the seam-centring holds; tightened from 72px to pull the live
   product up as a continuation of the headline.
   The lower half is --bg: the proof-metrics strip directly below shares the
   same white surface, so demo + metrics read as one module before the tinted
   #platform journey begins. */
.hero-demo {
  padding: 54px 0;
  background: linear-gradient(to bottom, var(--hero-bg) 0 50%, var(--bg) 50% 100%);
  scroll-margin-top: 64px;
}
/* The hero window has two faces, switched at RUNTIME rather than by environment.
   Default (nothing mounted yet, and the fallback if the embed never loads) is
   the .demo-poster — a real clickable link to the product. js/predictions.js
   lazy-mounts the live app iframe when the window approaches the viewport and
   adds body.demo-embed-live, which flips the faces: the embed shows, the poster
   hides. This runs in BOTH environments now (local → :5173, prod →
   app.<domain>); body.demo-local still marks localhost, but only for things that
   are genuinely env-specific. Fullscreen / scrim / exit belong to the live face
   only — over the poster the scrim would float "Tap to open the demo" on top of
   what is already a tappable link. */
.hero-demo .demo-body { position: relative; }
.hero-demo .demo-embed { display: none; }
.hero-demo .demo-poster { display: flex; }
body:not(.demo-embed-live) .hero-demo .demo-fs { display: none; }
body.demo-embed-live .hero-demo .demo-embed { display: block; }
body.demo-embed-live .hero-demo .demo-poster { display: none; }
body:not(.demo-embed-live) .hero-demo .demo-scrim,
body:not(.demo-embed-live) .hero-demo .demo-exit { display: none; }

/* Production window face: a clickable link-poster that opens the real product. */
.demo-poster {
  aspect-ratio: 16 / 9;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  text-decoration: none;
  border-radius: 16px;
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(130% 120% at 50% -10%, #1c2c4c 0%, #0d1220 55%, #070a12 100%);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.demo-poster:hover { transform: translateY(-3px); box-shadow: 0 40px 90px -50px rgba(41, 151, 255, 0.6); }
.demo-poster__inner { display: grid; gap: 14px; justify-items: center; max-width: 620px; }
.demo-poster__eyebrow {
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.demo-poster__title {
  font-size: clamp(22px, 3.4vw, 40px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.08;
}
.demo-poster__cta {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 6px;
  padding: 12px 22px; border-radius: 999px; font-size: 15px; font-weight: 600;
  color: #fff; background: var(--accent);
}
.demo-poster:hover .demo-poster__cta { filter: brightness(1.08); }
.demo-poster__cta svg { transition: transform 0.2s ease; }
.demo-poster:hover .demo-poster__cta svg { transform: translateX(3px); }
/* Wider than the pre-restructure window (was 1760) so the product reads big in
   the hero, but with a real gutter on each side — the window must read as a
   floating card on the band, not a full-bleed stripe. */
.hero-demo .demo-stage { width: min(1840px, calc(100% - clamp(48px, 8vw, 160px))); margin: 0 auto; }

/* Browser-style window: a glass title strip on top, the live app below — no
   frost "mat". The window itself straddles the black→white fold; the frame just
   clips its corners and carries the depth shadow. */
.hero-demo .demo-frame {
  --win-r: 28px;
  padding: 0;
  border-radius: var(--win-r);
  overflow: hidden;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    0 60px 120px -60px rgba(0, 0, 0, 0.9),
    0 0 90px -30px rgba(41, 151, 255, 0.22);
}
/* The glass browser strip: same dark-glass material as the globe's .event-chip
   (white sheen over a translucent dark base + blur), so the window's title bar
   reads as real glass over the app, not a matte frost. Light dots/caption. */
.hero-demo .demo-bar {
  position: relative;
  height: 44px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 24px;
  /* The glass pane must carry its own top radius: the frame's overflow clip
     rounds the bar's box, but the backdrop-filter region stays rectangular,
     so on the curve the corners showed frame background instead of glass. */
  border-radius: calc(var(--win-r) - 1px) calc(var(--win-r) - 1px) 0 0;
  background:
    linear-gradient(155deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.03)),
    rgba(16, 18, 23, 0.52);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
.hero-demo .demo-bar i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.34);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.hero-demo .demo-bar > span {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  padding: 0;
  border-radius: 0;
  background: transparent;
  border: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  font-size: 12px;
  color: var(--hero-dim);
  text-shadow: none;
  pointer-events: none;
}
.hero-demo .demo-fs {
  margin-left: auto;
  color: var(--hero-dim);
  background: transparent;
  border: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.hero-demo .demo-fs:hover {
  color: #fff;
  background: transparent;
  border-color: transparent;
}
/* The app fills the rest; the body carries the bottom radii itself (same
   reason as the bar above — don't rely on the frame clip alone) and clips
   the embed/poster/scrim, which stay square. */
.hero-demo .demo-body {
  border-radius: 0 0 calc(var(--win-r) - 1px) calc(var(--win-r) - 1px);
  overflow: hidden;
}
.hero-demo .demo-embed,
.hero-demo .demo-poster { border-radius: 0; }

/* Layer pills (removed from hero) */

.globe-stage {
  position: relative;
  width: clamp(320px, 96vw, 1060px);
  height: clamp(280px, 52vw, 560px);
  margin: 22px auto 0;
  overflow: hidden;
}
.globe-stage canvas {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: clamp(320px, 96vw, 1060px);
  height: clamp(320px, 96vw, 1060px);
  cursor: grab;
  touch-action: none;
  contain: layout paint size;
}
.globe-stage canvas:active { cursor: grabbing; }
.globe-stage .globe-overlay {
  pointer-events: none;
  cursor: default;
  z-index: 2;
}
.globe-stage .globe-fade {
  pointer-events: none;
  position: absolute;
  inset: auto 0 0 0;
  height: 160px;
  background: linear-gradient(to bottom, transparent, var(--hero-bg) 88%);
}
.globe-fallback {
  display: none;
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: clamp(320px, 96vw, 1060px);
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 38% 32%, rgba(255, 255, 255, 0.1), transparent 42%),
    radial-gradient(circle at 50% 50%, #1c1c21 0 62%, #0a0a0c 70%, transparent 71%);
}
.globe-stage.no-webgl canvas { display: none; }
.globe-stage.no-webgl .globe-fallback { display: block; }

/* Shared chip atoms (dark glass) */
.chip-top {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--hero-muted);
}
.chip-dot {
  position: relative;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-hero);
  color: var(--accent-hero);
  box-shadow: 0 0 12px var(--accent-hero);
}
/* Shift the hero dot left and add space so its pulse ring never reaches the text. */
.event-chip .chip-dot { margin-left: -6px; margin-right: 7px; }
.chip-dot.orange { background: var(--orange); color: var(--orange); box-shadow: 0 0 12px var(--orange); }
.chip-dot.green { background: var(--green-hero); color: var(--green-hero); box-shadow: 0 0 12px var(--green-hero); }
.chip-dot.purple { background: var(--purple); color: var(--purple); box-shadow: 0 0 12px var(--purple); }
@media (prefers-reduced-motion: no-preference) {
  .chip-dot.pulse::after {
    content: "";
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    border: 1.5px solid currentColor;
    animation: dot-ring 2s ease-out infinite;
  }
  @keyframes dot-ring {
    0% { transform: scale(0.6); opacity: 0.9; }
    70% { transform: scale(2.6); opacity: 0; }
    100% { transform: scale(2.6); opacity: 0; }
  }
}
.chip-city { color: var(--hero-text); }
.chip-headline { font-size: 15px; color: var(--hero-text); line-height: 1.35; text-wrap: balance; }
.event-chip .chip-headline { min-height: 20px; }

.event-chip {
  position: absolute;
  z-index: 4;
  left: 50%;
  top: 44px;
  transform: translateX(-50%);
  display: grid;
  gap: 5px;
  justify-items: center;
  min-width: 280px;
  max-width: min(86vw, 420px);
  padding: 13px 24px 17px;
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  /* Dark fill is load-bearing for legibility: the chip sits directly on the
     busy globe (city glows, arcs). 0.10 alpha made headlines dissolve into
     the orange hotspots — keep the glass look but with real contrast. */
  background:
    linear-gradient(155deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.03)),
    rgba(16, 18, 23, 0.52);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  box-shadow:
    0 22px 55px -26px rgba(0, 0, 0, 0.9),
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    inset 0 0 0 1px rgba(255, 255, 255, 0.06);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
  opacity: 1;
  transition: opacity 0.45s ease, transform 0.45s ease;
}
.event-chip.swap { opacity: 0; transform: translateX(-50%) translateY(6px); }

/* Floating side chips removed — activity now lives on the globe itself. */
.sat-meta { display: grid; gap: 5px; }
.sparkline { display: flex; align-items: flex-end; gap: 3px; height: 24px; margin-top: 2px; }
.sparkline i { width: 5px; border-radius: 2px; background: rgba(255, 159, 10, 0.75); }

/* (chip-float removed with the side chips) */

/* The final-CTA globe (.contact-globe / .globe-light) was removed — the closing
   section is now the two-column offer + form (.cta-final, see below). The
   generic .globe-stage / .event-chip / .chip-* atoms above are kept because the
   retrieval console still uses .chip-dot.pulse. */

/* ---------- Proof bar (scale metrics attached to the live demo) ---------- */
/* Matches the demo frame's content width, so the five numbers read as the
   demo's own footing — not a detached strip. The block stays borderless on the
   shared white surface; the tinted #platform section separates what follows. */
.stats {
  width: min(1840px, calc(100% - clamp(48px, 8vw, 160px)));
  margin: 10px auto 0;
  padding: 0 0 72px;
  background: var(--bg);
  border: 0;
}
.stats-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  background: transparent;
}
.stat {
  display: grid;
  gap: 4px;
  justify-items: center;
  align-content: center;
  text-align: center;
  padding: 20px 14px;
  background: var(--bg);
  border: 0;
}
.stat b { font-size: clamp(22px, 2.3vw, 30px); font-weight: 700; letter-spacing: -0.02em; color: var(--text); font-variant-numeric: tabular-nums; line-height: 1.1; }
.stat span { font-size: 13px; color: var(--dim); line-height: 1.3; }

/* ---------- Sections ---------- */
.section { padding: 120px 0; }
.section.tinted { background: var(--panel); }
.section-head { display: grid; gap: 16px; max-width: 820px; margin: 0 auto 56px; text-align: center; justify-items: center; }
.section-head .subhead { max-width: 640px; }

/* ---------- Forecast cards — region-based, equal height ---------- */
/* A card is fixed regions stacked top→bottom: A metadata · B question ·
   C context · D anchor comparison · E crowd · F action. Every region reserves
   its height, so all cards are exactly equal height and nothing jumps when a
   value updates (no more flex:1 gap pushing the probabilities to the floor). */
.fcard {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 20px 18px;
  border-radius: var(--radius-card);
  background: var(--card);
  border: 1px solid var(--card-border);
  box-shadow: var(--shadow-card);
}

/* A. Metadata: category (left) · deadline/state (right). */
.fcard-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.fcard-cat { color: var(--dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fcard-state { flex: 0 0 auto; color: var(--dim); white-space: nowrap; }
.fcard-state--upcoming { color: var(--accent); }

/* B. Question — reserve a fixed number of lines so every card is equal height and
   the anchor bars sit at a consistent position (probabilities kept high, not
   floated to the floor). Desktop/tablet clamp to 2 lines; mobile to 3. */
.fcard-q {
  font-size: 16.5px; font-weight: 600; line-height: 1.34; letter-spacing: -0.01em;
  text-wrap: pretty;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  min-height: calc(2 * 1.34em);
}

/* C. Contextual signal (one line) + the anchor-spread chip on the right. */
.fcard-context { display: flex; align-items: center; gap: 10px; min-height: 22px; }
.fcard-ctx { flex: 1 1 auto; min-width: 0; font-size: 12px; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fcard-spread {
  flex: 0 0 auto; font-size: 11px; font-weight: 600;
  padding: 3px 9px; border-radius: 999px; white-space: nowrap;
  color: var(--muted); background: var(--panel);
}
.fcard-spread.spread--close { color: var(--green); background: rgba(36, 138, 61, 0.09); }
.fcard-spread.spread--strong { color: #b23200; background: rgba(201, 52, 0, 0.10); }

/* D. Anchor comparison (the hero) + E. crowd (secondary). Shared row grammar:
   avatar · name · bar · %. Anna = accent blue, Boris = warm red, crowd = green. */
.fcard-anchors { display: flex; flex-direction: column; gap: 12px; }
.prob-row { position: relative; display: flex; align-items: center; gap: 11px; min-width: 0; }
.prob-ava {
  width: 32px; height: 32px; flex: 0 0 32px;
  border-radius: 50%; object-fit: cover;
  box-shadow: 0 0 0 2px var(--card), 0 0 0 3.5px currentColor;
}
.prob-ava--crowd {
  width: 24px; height: 24px; flex: 0 0 24px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; background: #eef1f4; box-shadow: none;
}
.anchor-row.anna { color: var(--accent); }
.anchor-row.boris { color: #c93400; }
.anchor-row .prob-name { font-size: 14px; font-weight: 600; color: var(--text); flex: 0 0 auto; min-width: 50px; }
.anchor-row .prob-pct { font-size: 16px; font-weight: 700; }
.prob-name { font-size: 13px; font-weight: 600; flex: 0 0 auto; min-width: 50px; }
.prob-name em { font-style: normal; font-weight: 500; color: var(--dim); font-size: 12px; }
.prob-bar {
  position: relative; flex: 1; height: 6px; border-radius: 999px;
  background: #e6e6ec; overflow: hidden;
}
.prob-bar span { display: block; height: 100%; border-radius: inherit; background: currentColor; transition: width 0.45s cubic-bezier(0.22, 0.61, 0.36, 1); }
.prob-pct { font-size: 13px; font-weight: 700; flex: 0 0 42px; text-align: right; font-variant-numeric: tabular-nums; }

/* E. Crowd row: deliberately smaller / softer than the two anchors. */
.crowd-row { color: #4d9d66; min-height: 24px; margin-top: 1px; }
.crowd-row .prob-name { color: var(--muted); font-size: 12.5px; }
.crowd-row .prob-pct { color: var(--muted); font-size: 13px; }
.crowd-row .prob-bar { height: 5px; }
.crowd-empty { font-size: 12.5px; color: var(--dim); }

/* Anchor rationale — revealed only on hover/focus, so it never grows the card. */
.anchor-row.has-tip { border-radius: 10px; outline: none; }
.anchor-row.has-tip:focus-visible { box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.18); }
.prob-tip {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 6;
  background: #1d1d1f; color: #fff; font-size: 12px; line-height: 1.42; font-weight: 500;
  letter-spacing: 0; text-transform: none;
  padding: 8px 11px; border-radius: 10px;
  box-shadow: 0 12px 26px -12px rgba(0, 0, 0, 0.55);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
  pointer-events: none;
}
.prob-tip::before {
  content: ""; position: absolute; left: 22px; top: -5px;
  border: 5px solid transparent; border-top: 0; border-bottom-color: #1d1d1f;
}
.anchor-row.has-tip:hover .prob-tip,
.anchor-row.has-tip:focus-visible .prob-tip {
  opacity: 1; visibility: visible; transform: translateY(0);
  transition: opacity 0.16s ease, transform 0.16s ease;
}

/* F. Action / resolution region — reserved height across all states. */
.fcard-action { margin-top: 2px; min-height: 68px; display: flex; flex-direction: column; justify-content: flex-end; gap: 8px; }
.pm-call-label { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); }
.forecast-cta { text-align: center; margin-top: 40px; }

/* ---------- Live prediction funnel (js/predictions.js) ---------- */
/* Live-forecasts area: visible by default, JS sets [hidden] only when the
   feature.landing.predictions_live flag is explicitly off. Compact eyebrow sits
   directly above the rail (no more large centred "Current predictions" block),
   so the cards enter the viewport sooner. */
.pm-live { margin-top: 28px; }
.pm-live[hidden] { display: none; }
/* Aligned with the rail's 4px inset. The 28px bottom margin collapses with the
   rail's 8px top margin; + the track's 6px top padding the first card lands
   ~34px below the row (§ 32–48px card offset). */
.pm-live-eyebrow { display: flex; align-items: center; gap: 14px; margin-bottom: 28px; padding: 0 4px; }
.pm-live-updated { font-size: 13px; color: var(--dim); font-variant-numeric: tabular-nums; }
.pm-live-badge {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 12px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: #c93400; background: rgba(201, 52, 0, 0.08);
}
.pm-live-badge i {
  width: 7px; height: 7px; border-radius: 50%; background: #c93400;
  animation: pm-live-pulse 1.6s ease-in-out infinite;
}
@keyframes pm-live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.8); }
}
@media (prefers-reduced-motion: reduce) {
  .pm-live-badge i { animation: none; }
}
.pm-status { grid-column: 1 / -1; text-align: center; color: var(--dim); font-size: 15px; padding: 28px 0; }

/* Reduced header height: tighten the headline→paragraph→rail whitespace so the
   predictions section leads with its cards (§ ~20–25% shorter header). */
#predictions { padding-top: 104px; }
#predictions .section-head { margin-bottom: 34px; gap: 14px; }
#predictions .section-head .subhead { max-width: 620px; }
/* "Built on the platform" transition: the eyebrow + one quiet context line
   above the headline frame the anchors as an application example, not the
   core product. Sized between eyebrow and subhead so it stays restrained. */
#predictions .app-note { font-size: 15px; line-height: 1.5; color: var(--dim); }

/* Hover: a restrained 3px lift + slightly deeper shadow (no scale, no tilt). */
.pm-card { transition: box-shadow 0.28s var(--ease-out), transform 0.28s var(--ease-out), border-color 0.28s var(--ease-out); }
.pm-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-card-hover); border-color: rgba(0, 0, 0, 0.09); }
@media (prefers-reduced-motion: reduce) { .pm-card:hover { transform: none; } }

/* Pill button — used by the error-state "Try again" inside the carousel. */
.pm-expand-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--hairline); background: var(--card); color: var(--text);
  border-radius: 980px; padding: 10px 20px;
  font-size: 14px; font-weight: 600; cursor: pointer;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.pm-expand-btn:hover { border-color: color-mix(in srgb, var(--text) 30%, transparent); background: var(--panel); }

/* Open call: equal-width Yes / No. The answer label leads; the odds ride along
   as a secondary detail. Generous touch target + clear hover/press/focus. */
.pm-vote { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pm-btn {
  display: flex; align-items: baseline; justify-content: center; gap: 7px;
  border-radius: 13px; padding: 12px; min-height: 48px;
  cursor: pointer;
  background: var(--panel);
  border: 1px solid rgba(0, 0, 0, 0.08);
  color: var(--text);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.06s ease, box-shadow 0.15s ease;
}
.pm-btn-ans { font-size: 14px; font-weight: 700; }
.pm-btn-odds { font-size: 12px; font-weight: 600; color: var(--dim); font-variant-numeric: tabular-nums; }
.pm-yes:hover { border-color: var(--green); background: rgba(36, 138, 61, 0.07); }
.pm-yes:hover .pm-btn-ans { color: var(--green); }
.pm-no:hover { border-color: #c93400; background: rgba(201, 52, 0, 0.06); }
.pm-no:hover .pm-btn-ans { color: #c93400; }
.pm-btn:active { transform: scale(0.98); }
.pm-btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.22); }
.pm-yes.on { border-color: var(--green); background: rgba(36, 138, 61, 0.08); color: var(--green); }
.pm-no.on  { border-color: #c93400; background: rgba(201, 52, 0, 0.07); color: #c93400; }

/* Upcoming / closed: a single, unmistakably disabled state. */
.pm-locked {
  min-height: 48px; display: flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: 13px; padding: 10px 12px; text-align: center;
  font-size: 13px; font-weight: 600; color: var(--dim);
  background: var(--panel); border: 1px dashed rgba(0, 0, 0, 0.14);
}
.pm-locked--soon { color: var(--muted); }
.pm-locked-when { color: var(--dim); font-weight: 600; }

/* Resolved: concise result panel replaces the betting controls. */
.pm-result {
  min-height: 48px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border-radius: 13px; padding: 8px 12px; text-align: center;
}
.pm-result-main { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 700; }
.pm-result-main i { font-style: normal; font-size: 13px; }
.pm-result-sub { font-size: 11px; font-weight: 500; color: var(--muted); }
.pm-result--yes { background: rgba(36, 138, 61, 0.09); color: var(--green); }
.pm-result--no { background: rgba(201, 52, 0, 0.08); color: #c93400; }

/* Resolved / closed cards read as secondary: muted surface + dimmed question,
   probability data stays fully readable. */
.pm-card--resolved, .pm-card--closed { background: var(--card-sub); box-shadow: 0 3px 12px -12px rgba(0, 0, 0, 0.14); }
.pm-card--resolved .fcard-q, .pm-card--closed .fcard-q { color: var(--muted); }

/* Value that just changed on air — brief highlight (§ microinteractions). */
.pm-flash.prob-pct { animation: pm-flash-pct 1.1s ease; }
.prob-bar.pm-flash { animation: pm-flash-bar 1.1s ease; }
@keyframes pm-flash-pct {
  0% { color: var(--accent); transform: scale(1.18); }
  40% { color: var(--accent); }
  100% { color: inherit; transform: scale(1); }
}
@keyframes pm-flash-bar {
  0%, 100% { box-shadow: none; }
  30% { box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.28); }
}
@media (prefers-reduced-motion: reduce) {
  .pm-flash.prob-pct, .prob-bar.pm-flash { animation: none; }
}

/* Live-update animations for the keyed card sync in predictions.js: new cards
   drop in (staggered via inline animation-delay), removed cards fold away.
   Bars slide through the .prob-bar span width transition above. */
.pm-card--enter { animation: pm-card-in 0.45s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
.pm-card--exit { animation: pm-card-out 0.35s ease both; pointer-events: none; }
/* One-shot entry when the rail first scrolls into view: fade up with a stagger
   (delay set inline by predictions.js); bars fill from zero once. */
.pm-card--intro { animation: pm-card-in 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
@keyframes pm-card-in {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
}
@keyframes pm-card-out {
  to { opacity: 0; transform: scale(0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .pm-card--enter, .pm-card--exit, .pm-card--intro { animation: none; }
  .pm-card--exit { opacity: 0; }
}

/* ---------- Prediction carousel ---------- */
/* Horizontal scroll-snap rail: any number of cards. Desktop shows 3 complete
   cards + a subtle peek of the next; tablet ~1.8; mobile 1 + a small peek
   (native touch swipe). Navigation lives BELOW the rail — never over a card. */
.pm-carousel { position: relative; margin-top: 8px; --pm-card-w: calc((100% - 3 * 20px) / 3.12); }
.pm-carousel-track {
  display: flex;
  align-items: stretch;       /* equal card height per row → no vertical jump */
  gap: 20px;
  min-height: 390px;          /* reserve the card region: loading↔loaded↔empty
                                 all keep the section height stable */
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 4px; /* snap cards to the padded edge, not the scrollport */
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  padding: 6px 4px 20px;      /* room for card shadow/lift + snap gutter */
  scrollbar-width: none;
}
.pm-carousel-track::-webkit-scrollbar { display: none; }
.pm-carousel-track:focus-visible { outline: 2px solid rgba(0, 113, 227, 0.4); outline-offset: -2px; border-radius: 14px; }
.pm-carousel-track > .pm-card {
  flex: 0 0 var(--pm-card-w);
  scroll-snap-align: start;
  min-height: 358px;          /* reserve height so short/long titles don't jump */
}

/* Navigation row under the rail: position indicator + prev/next, right-aligned
   with a gutter so nothing overlaps card content. Hidden when nothing overflows. */
.pm-carousel-nav { display: flex; align-items: center; justify-content: flex-end; gap: 16px; margin-top: 6px; padding: 0 4px; min-height: 40px; }
.pm-carousel:not(.has-overflow) .pm-carousel-nav { visibility: hidden; }
.pm-carousel-count { font-size: 13px; font-weight: 600; color: var(--dim); font-variant-numeric: tabular-nums; }
.pm-carousel-count[hidden] { display: none; }
.pm-carousel-btns { display: flex; gap: 8px; }
.pm-carousel-arrow {
  width: 44px; height: 44px; border-radius: 50%; /* ≥44px touch target */
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--hairline); background: var(--card); color: var(--text);
  font-size: 22px; line-height: 1; cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
}
.pm-carousel-arrow:hover:not(:disabled) { background: var(--panel); border-color: color-mix(in srgb, var(--text) 24%, transparent); }
.pm-carousel-arrow:disabled { opacity: 0.35; cursor: default; }
.pm-carousel-arrow:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.22); }

/* Narrower desktop: ~2.55 cards (two complete + half the third) instead of
   squeezing three below a readable card width. */
@media (max-width: 1140px) { .pm-carousel { --pm-card-w: calc((100% - 2 * 20px) / 2.55); } }
/* Tablet: ~1.8 cards (second mostly shown, third peeking). */
@media (max-width: 980px) { .pm-carousel { --pm-card-w: calc((100% - 20px) / 1.85); } }
/* Touch: one card + ~10% peek, native swipe. Arrows give way to the counter. */
@media (max-width: 600px) {
  .pm-carousel { --pm-card-w: calc(100% - 32px); } /* one card + ~10% peek */
  .pm-carousel-track { margin-inline: -22px; padding-inline: 22px; scroll-padding-inline: 22px; min-height: 412px; }
  .pm-carousel-track > .pm-card { min-height: 380px; }
  .pm-carousel-btns { display: none; }
  .pm-carousel-nav { justify-content: center; }
  /* One card is wide → give long questions a 3rd line before clamping. */
  .fcard-q { -webkit-line-clamp: 3; min-height: calc(3 * 1.34em); }
}

/* Full-width status / fallback lines inside the flex track. */
.pm-carousel-track > .pm-status,
.pm-carousel-track > .pm-fallback { flex: 1 1 100%; }
.pm-fallback { text-align: center; padding: 40px 16px 48px; display: grid; gap: 10px; justify-items: center; }
.pm-fallback-lead { font-size: 18px; font-weight: 600; color: var(--text); }
.pm-fallback-sub { font-size: 15px; color: var(--dim); }
.pm-fallback--error .pm-fallback-lead { color: var(--text); }

/* "Example forecast" badge on the sample cards shown when production data is
   empty — makes clear these are illustrative, not live calls. */
.pm-card--example { position: relative; }
.pm-example-badge {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px;
  color: var(--dim); background: var(--panel); border: 1px solid var(--hairline);
}

/* Loading skeleton — same region footprint as a real card so the swap doesn't
   shift (metadata · question · context · 3 rows · action). */
.pm-card--skeleton { pointer-events: none; gap: 12px; }
.pm-card--skeleton .sk { border-radius: 8px; background: #e7e7ec; }
.pm-card--skeleton .sk-top { height: 12px; width: 60%; }
.pm-card--skeleton .sk-q { height: 17px; width: 92%; }
.pm-card--skeleton .sk-q2 { width: 70%; }
.pm-card--skeleton .sk-ctx { height: 12px; width: 55%; }
.pm-card--skeleton .sk-rows { display: grid; gap: 14px; margin-top: 2px; }
.pm-card--skeleton .sk-row { height: 14px; width: 100%; }
.pm-card--skeleton .sk-action { height: 48px; border-radius: 13px; margin-top: auto; }
.pm-card--skeleton .sk { animation: pm-sk-shimmer 1.4s ease-in-out infinite; }
@keyframes pm-sk-shimmer { 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) { .pm-card--skeleton .sk { animation: none; } }

/* Dev-only controls (js/predictions.js mountDevPanel) — rendered ONLY in
   local/preview mock mode, so it never ships to the production UI. Collapsible;
   the collapsed state is remembered in localStorage and stays clear of the
   first card / carousel controls (bottom-left, controls sit bottom-right). */
.pm-devpanel {
  position: fixed; left: 16px; bottom: 16px; z-index: 300; max-width: min(92vw, 420px);
  display: grid; gap: 8px; padding: 10px 12px; border-radius: 16px;
  background: #1d1d1f; color: #fff; font-size: 12px;
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
}
.pm-devpanel-head {
  display: flex; align-items: center; gap: 8px;
  background: transparent; border: 0; padding: 2px; margin: 0;
  color: inherit; cursor: pointer; text-align: left;
}
.pm-devpanel-head b { font-size: 11px; letter-spacing: 0.14em; color: #ffd60a; }
.pm-devpanel-head span { color: rgba(255, 255, 255, 0.55); }
.pm-devpanel-caret { margin-left: auto; font-style: normal; color: rgba(255, 255, 255, 0.6); transition: transform 0.18s ease; }
.pm-devpanel.is-collapsed .pm-devpanel-caret { transform: rotate(-90deg); }
.pm-devpanel.is-collapsed .pm-devpanel-body { display: none; }
/* Collapsed (the default) = a small "PREVIEW ▸" pill: subtitle drops so the
   trigger stays tiny and never distracts from the cards / screenshots. */
.pm-devpanel.is-collapsed { padding: 8px 14px; border-radius: 999px; }
.pm-devpanel.is-collapsed .pm-devpanel-head span { display: none; }
.pm-devpanel-body { display: grid; gap: 8px; }
.pm-devpanel-scenarios { display: flex; flex-wrap: wrap; gap: 6px; }
.pm-devpanel button:not(.pm-devpanel-head) {
  border: 1px solid rgba(255, 255, 255, 0.22); background: transparent; color: #fff;
  border-radius: 999px; padding: 4px 10px; font-size: 12px; cursor: pointer; white-space: nowrap;
}
.pm-devpanel button:not(.pm-devpanel-head):hover { background: rgba(255, 255, 255, 0.12); }
.pm-devpanel button.on { background: #fff; color: #1d1d1f; border-color: #fff; }
.pm-devpanel-sim { display: flex; }
.pm-devpanel-sim button { border-color: rgba(255, 214, 10, 0.5); color: #ffd60a; }
.pm-devpanel-sim button.on { background: #ffd60a; color: #1d1d1f; border-color: #ffd60a; }

/* ---------- How it works (#how-it-works): input → transformation → output ----------
   One connected pipeline, not three feature columns. Three white stage cards on
   the tinted band, joined by thin arrowed connectors; inside each card a flat
   recessed canvas ends in a white outcome panel — the panels are bottom-anchored
   so all three outcomes sit on one baseline. Copy zones (title/description)
   reserve their line counts on desktop so the canvases start level too.
   Flat surfaces only: no gradients, no glass — that language belongs to the
   Journey rail vignettes above. */
#how-it-works .section-head { margin-bottom: 56px; }
.hiw {
  --hiw-gap: 56px;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--hiw-gap);
  align-items: stretch;
}
.hiw-stage {
  --lift: 0px;
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-card);
  padding: 26px 26px 22px;
  box-shadow: 0 30px 60px -44px rgba(0, 0, 0, 0.16), 0 2px 6px -3px rgba(0, 0, 0, 0.04);
  transform: translateY(var(--lift));
  transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
}
/* The middle stage is the heart of the platform — it rides a touch higher
   with a slightly deeper shadow, so the eye lands there first. Quiet, not
   dramatic; the stacked (mobile) layout flattens it back. */
.hiw-stage[data-stage="2"] {
  --lift: -10px;
  z-index: 1;
  border-color: rgba(0, 0, 0, 0.08);
  box-shadow: 0 38px 72px -46px rgba(0, 0, 0, 0.24), 0 4px 10px -5px rgba(0, 0, 0, 0.05);
}
/* Hover/focus: the touched stage sharpens, siblings recede a step, and both
   connectors adjacent to it pick up the accent. Pure enhancement — the flow
   reads identically without it (and on touch, where hover never fires). */
.hiw-stage:hover, .hiw-stage:focus-within {
  transform: translateY(calc(var(--lift) - 2px));
  box-shadow: 0 36px 66px -44px rgba(0, 0, 0, 0.2), 0 4px 10px -5px rgba(0, 0, 0, 0.06);
}
.hiw:has(.hiw-stage:hover) .hiw-stage:not(:hover) { opacity: 0.72; }
/* Stage label leads, the number recedes — CONNECT / UNDERSTAND / ACT is the
   three-second read; 01/02/03 only orders it. */
.hiw-head { display: flex; align-items: center; gap: 10px; }
.hiw-num { order: 1; font-size: 11px; font-weight: 650; color: var(--dim); letter-spacing: 0.08em; font-variant-numeric: tabular-nums; }
.hiw-tag { font-size: 12.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text); }
.hiw-head::after { content: ""; flex: 1; height: 1px; background: var(--hairline); opacity: 0.6; }
.hiw-title { margin-top: 14px; font-size: 21px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.25; min-height: 2.5em; text-wrap: balance; }
.hiw-desc { margin-top: 6px; font-size: 14px; line-height: 1.5; color: var(--muted); min-height: 4.5em; text-wrap: pretty; }

/* Stage-to-stage connector: a thin arrowed line in the grid gap at card
   mid-height, owned by the stage it leads INTO (stages 02 and 03). The <i> is
   the travelling signal dot — ambient, and running only while `.hiw.is-live`
   (observer in atlas.js pauses it off-screen). Decorative: aria-hidden. */
.hiw-link { position: absolute; top: 50%; right: 100%; width: var(--hiw-gap); height: 0; pointer-events: none; }
/* One hairline that fades out at both card edges; direction is carried by the
   travelling dot (and by the 01→02→03 order when motion is off). ::after is an
   identical accent copy that crossfades in when an adjacent stage is hovered. */
.hiw-link::before, .hiw-link::after {
  content: "";
  position: absolute;
  left: 1px;
  right: 1px;
  top: -0.5px;
  height: 1px;
  background: linear-gradient(90deg, rgba(134, 134, 139, 0), rgba(134, 134, 139, 0.55) 26%, rgba(134, 134, 139, 0.55) 74%, rgba(134, 134, 139, 0));
}
.hiw-link::after {
  background: linear-gradient(90deg, rgba(0, 113, 227, 0), rgba(0, 113, 227, 0.6) 26%, rgba(0, 113, 227, 0.6) 74%, rgba(0, 113, 227, 0));
  opacity: 0;
  transition: opacity 0.35s ease;
}
.hiw-stage:hover .hiw-link::after, .hiw-stage:focus-within .hiw-link::after,
.hiw-stage:hover + .hiw-stage .hiw-link::after, .hiw-stage:focus-within + .hiw-stage .hiw-link::after { opacity: 1; }
/* The travelling signal — a soft blue comet gliding card-to-card. */
.hiw-link i {
  position: absolute;
  left: 0;
  top: -2.5px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 9px 2px rgba(0, 113, 227, 0.3);
  opacity: 0;
}

/* The recessed diagram canvas. Flat panel tint (echoes the section band inside
   the white card), soft inset lip, fixed floor height so the three diagrams
   stay optically equal; grid-stretch equalises the rest. */
.hiw-canvas {
  margin-top: 20px;
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 248px;
  background: var(--panel);
  border-radius: 16px;
  padding: 14px;
  box-shadow: inset 0 1px 0 rgba(29, 29, 31, 0.04), inset 0 8px 16px -14px rgba(29, 29, 31, 0.07);
}

/* Source / output chips */
.hiw-chips { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.hiw-chips li {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 9px;
  padding: 5px 10px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  white-space: nowrap;
}
.hiw-chips li i { flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: var(--c, var(--accent)); }
/* Output chips recede — they are the supporting branches; the briefing panel
   below is the destination that should win the eye. */
.hiw-chips--outputs { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.hiw-chips--outputs li { flex: 0 0 auto; font-size: 11px; font-weight: 600; color: var(--muted); padding: 4px 9px; border-color: rgba(0, 0, 0, 0.05); box-shadow: none; }

/* Converge / fan wires: stretchy SVG (preserveAspectRatio none + non-scaling
   strokes) that absorbs the flexible height between chips and outcome panel,
   so the wires always physically connect the two. */
.hiw-wires { display: block; width: 82%; margin-inline: auto; height: 30px; min-height: 24px; flex: 1 1 auto; }
.hiw-wires path { fill: none; stroke: rgba(29, 29, 31, 0.17); stroke-width: 1px; }
/* Centre spine of the ACT fan — the main flow line into the briefing. */
.hiw-wires .hiw-spine { stroke: rgba(29, 29, 31, 0.26); }
/* Accent flow overlays: invisible at rest (and under reduced motion); the
   motion block below turns them into travelling dashes while on screen. */
.hiw-wires .hiw-flowpath { stroke: var(--accent); opacity: 0; }

/* Outcome panels — each stage's bottom line, shared baseline via margin-top. */
.hiw-out {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.07);
  border-radius: 13px;
  padding: 10px 13px;
  box-shadow: 0 12px 24px -18px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.04);
}
.hiw-out-head { display: flex; align-items: center; gap: 7px; min-width: 0; }
.hiw-out b { font-size: 12.5px; font-weight: 650; letter-spacing: -0.01em; color: var(--text); }
.hiw-state { margin-left: auto; font-size: 10px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--green); }
.hiw-live { flex: 0 0 auto; position: relative; width: 8px; height: 8px; border-radius: 50%; background: #30d158; }
.hiw-live::after { content: ""; position: absolute; inset: -1px; border-radius: 50%; border: 1.5px solid #30d158; opacity: 0; }

/* Stage 02: the processing rail. A thin spine runs from "Raw signal" through
   every operation and on down into the Structured-event panel. */
.hiw-ops { position: relative; list-style: none; margin: 0; padding: 0; flex: 1 0 auto; display: flex; flex-direction: column; justify-content: space-between; gap: 4px; }
.hiw-ops::before { content: ""; position: absolute; left: 7px; top: 8px; bottom: -10px; width: 1px; background: #c6c6cc; }
.hiw-op { position: relative; display: flex; align-items: center; gap: 9px; font-size: 11.5px; font-weight: 600; color: var(--text); }
.hiw-op i {
  position: relative;
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.14);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* Completed-operation tick (drawn, not an icon font). */
.hiw-op i::after {
  content: "";
  position: absolute;
  left: 3.5px;
  top: 4px;
  width: 6px;
  height: 3px;
  border-left: 1.5px solid var(--accent);
  border-bottom: 1.5px solid var(--accent);
  transform: rotate(-45deg);
}
.hiw-op--raw i::after { content: none; }
.hiw-op--raw i { border-color: rgba(255, 159, 10, 0.65); }
.hiw-op--raw i::before { content: ""; position: absolute; inset: 3.5px; border-radius: 50%; background: var(--orange); }
/* The evolving object, colour-coded through the pipeline: orange raw signal →
   blue structured event / connected context → green briefing Ready. */
.hiw-out--event { display: grid; gap: 7px; border-color: rgba(0, 113, 227, 0.24); }
.hiw-meta { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, max-content); gap: 4px; }
.hiw-meta li { font-size: 10px; font-weight: 600; color: var(--muted); background: var(--panel); border-radius: 6px; padding: 3px 7px; }
.hiw-meta li::before { content: "+ "; color: var(--green); }

/* Stage 03: connected context node + emphasised briefing outcome. */
.hiw-context {
  display: flex;
  align-items: center;
  gap: 8px;
  width: max-content;
  max-width: 100%;
  margin: 2px auto 0;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.07);
  border-radius: 999px;
  padding: 6px 14px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.hiw-context b { font-size: 12px; font-weight: 650; letter-spacing: -0.01em; }
.hiw-node { flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.hiw-chips--outputs { margin-top: 10px; }
/* The destination: the finished briefing, softly green and a step heavier
   than every other panel in the section. */
.hiw-out--brief {
  border-color: rgba(36, 138, 61, 0.38);
  background: #f3faf5;
  padding: 12px 14px;
  box-shadow: 0 16px 30px -20px rgba(36, 138, 61, 0.35), 0 1px 3px rgba(0, 0, 0, 0.04);
}
.hiw-out--brief b { font-size: 13px; }
.hiw-state--done { background: rgba(36, 138, 61, 0.12); padding: 3px 9px; border-radius: 999px; }
.hiw-checkmark {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(36, 138, 61, 0.14);
  color: var(--green);
}
.hiw-checkmark svg { width: 11px; height: 11px; display: block; }

/* Entrance choreography — one left-to-right sequence (~3.3s total), fired once
   by the shared reveal observer adding `.in`. Base styles above are the FINAL
   state; every animation runs `backwards` from a hidden/idle first frame, so
   with reduced motion (or no JS) the section simply renders finished. */
.hiw-flows { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  html.js .hiw.reveal.in .hiw-stage { animation: hiw-rise 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
  html.js .hiw.reveal.in .hiw-stage[data-stage="2"] { animation-delay: 0.12s; }
  html.js .hiw.reveal.in .hiw-stage[data-stage="3"] { animation-delay: 0.24s; }
  /* 1 — sources appear… */
  html.js .hiw.reveal.in .hiw-chips:not(.hiw-chips--outputs) li { animation: hiw-pop 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(0.12s + var(--i) * 0.055s); }
  /* 2 — …their signals draw down into one stream… */
  html.js .hiw.reveal.in .hiw-merge path:not(.hiw-flowpath) { stroke-dasharray: 40; animation: hiw-draw 0.5s ease-out 0.62s backwards; }
  html.js .hiw.reveal.in .hiw-merge .hiw-flows { opacity: 1; transition: opacity 0.6s ease 1.05s; }
  html.js .hiw.reveal.in .hiw-out--stream { animation: hiw-rise-sm 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) 0.85s backwards; }
  /* 3 — the processing rail checks operations off in order… */
  html.js .hiw.reveal.in .hiw-op { animation: hiw-op-in 0.4s ease backwards; animation-delay: calc(1.05s + var(--i) * 0.12s); }
  html.js .hiw.reveal.in .hiw-op i::after { animation: hiw-fade 0.28s ease backwards; animation-delay: calc(1.18s + var(--i) * 0.12s); }
  /* 4 — …and the structured event lands… */
  html.js .hiw.reveal.in .hiw-out--event { animation: hiw-rise-sm 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) 2s backwards; }
  html.js .hiw.reveal.in .hiw-meta li { animation: hiw-pop 0.35s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(2.12s + var(--i) * 0.06s); }
  /* 5 — …context fans out to destinations… */
  html.js .hiw.reveal.in .hiw-context { animation: hiw-rise-sm 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) 2.3s backwards; }
  html.js .hiw.reveal.in .hiw-fan path:not(.hiw-flowpath) { stroke-dasharray: 40; animation: hiw-draw 0.5s ease-out 2.45s backwards; }
  html.js .hiw.reveal.in .hiw-fan .hiw-flows { opacity: 1; transition: opacity 0.6s ease 2.7s; }
  html.js .hiw.reveal.in .hiw-chips--outputs li { animation: hiw-pop 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(2.55s + var(--i) * 0.07s); }
  /* 6 — …and the briefing reaches Ready. */
  html.js .hiw.reveal.in .hiw-out--brief { animation: hiw-rise-sm 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) 2.9s backwards; }
  html.js .hiw.reveal.in .hiw-checkmark { animation: hiw-check 0.45s cubic-bezier(0.34, 1.4, 0.64, 1) 3.15s backwards; }
  html.js .hiw.reveal.in .hiw-out--brief .hiw-state { animation: hiw-fade 0.35s ease 3.2s backwards; }

  /* Ambient afterwards — everything loops slow and quiet, and everything is
     paused unless `.is-live` (the pipeline is actually on screen):
     a comet gliding card-to-card, dashes drifting down the wires, source dots
     breathing, a pulse sliding down the processing rail, the live dot pinging. */
  .hiw-link i { animation: hiw-travel 3.2s linear infinite; animation-play-state: paused; }
  .hiw-stage[data-stage="3"] .hiw-link i { animation-delay: -1.6s; }
  .hiw-wires .hiw-flowpath { opacity: 0.5; stroke-dasharray: 2.4 37.6; animation: hiw-wireflow 2.6s linear infinite; animation-play-state: paused; }
  .hiw-flows path:nth-of-type(2) { animation-delay: -0.9s; }
  .hiw-flows path:nth-of-type(3) { animation-delay: -1.7s; }
  .hiw-flows path:nth-of-type(4) { animation-delay: -0.5s; }
  .hiw-chips:not(.hiw-chips--outputs) li i { animation: hiw-dot-pulse 3.6s ease-in-out infinite; animation-delay: calc(var(--i) * 0.45s); animation-play-state: paused; }
  .hiw-ops::before {
    background-image: linear-gradient(to bottom, rgba(0, 113, 227, 0), rgba(0, 113, 227, 0.75) 50%, rgba(0, 113, 227, 0));
    background-size: 100% 26px;
    background-repeat: no-repeat;
    background-position: 0 -26px;
    animation: hiw-rail 3.4s linear infinite;
    animation-play-state: paused;
  }
  .hiw-live::after { animation: hiw-ping 3.2s cubic-bezier(0, 0, 0.2, 1) infinite; animation-play-state: paused; }
  .hiw.is-live .hiw-link i,
  .hiw.is-live .hiw-wires .hiw-flowpath,
  .hiw.is-live .hiw-chips li i,
  .hiw.is-live .hiw-ops::before,
  .hiw.is-live .hiw-live::after { animation-play-state: running; }
}
@keyframes hiw-rise { from { opacity: 0; transform: translateY(18px); } }
@keyframes hiw-rise-sm { from { opacity: 0; transform: translateY(8px); } }
@keyframes hiw-pop { from { opacity: 0; transform: translateY(5px) scale(0.92); } }
@keyframes hiw-fade { from { opacity: 0; } }
@keyframes hiw-op-in { from { opacity: 0.15; transform: translateX(-5px); } }
@keyframes hiw-draw { from { stroke-dashoffset: 40; } to { stroke-dashoffset: 0; } }
@keyframes hiw-check { from { opacity: 0; transform: scale(0.4); } }
@keyframes hiw-wireflow { to { stroke-dashoffset: -40; } }
@keyframes hiw-dot-pulse { 50% { transform: scale(1.4); } }
@keyframes hiw-rail {
  0% { background-position: 0 -26px; }
  62%, 100% { background-position: 0 calc(100% + 26px); }
}
@keyframes hiw-travel {
  0% { transform: translateX(0); opacity: 0; }
  14% { opacity: 1; }
  80% { opacity: 1; }
  94%, 100% { transform: translateX(calc(var(--hiw-gap) - 5px)); opacity: 0; }
}
@keyframes hiw-ping {
  0% { transform: scale(1); opacity: 0.6; }
  55%, 100% { transform: scale(2.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hiw-stage, .hiw-link::after { transition: none; }
  .hiw-stage:hover, .hiw-stage:focus-within { transform: translateY(var(--lift)); }
  .hiw:has(.hiw-stage:hover) .hiw-stage:not(:hover) { opacity: 1; }
}

/* Tablet band (three-up survives, everything a notch tighter). */
@media (max-width: 1120px) and (min-width: 981px) {
  .hiw { --hiw-gap: 32px; }
  .hiw-stage { padding: 22px 20px 18px; }
  .hiw-title { font-size: 19px; }
  .hiw-desc { font-size: 13.5px; min-height: 6em; }
  .hiw-canvas { padding: 12px; }
  .hiw-chips li { padding: 5px 8px; font-size: 11px; }
}
/* Stacked pipeline (portrait tablet + phones): one stage per block, the same
   fading connector + comet turned vertical in the row gap, copy reserves
   released, the hero lift flattened (stacked cards sit level). */
@media (max-width: 980px) {
  #how-it-works .section-head { margin-bottom: 40px; }
  .hiw { grid-template-columns: 1fr; row-gap: 52px; max-width: 560px; margin-inline: auto; }
  .hiw-stage { --lift: 0px !important; }
  .hiw-title { min-height: 0; }
  .hiw-desc { min-height: 0; }
  .hiw-canvas { min-height: 0; }
  .hiw-wires { height: 34px; flex: 0 0 auto; }
  .hiw-ops { gap: 5px; }
  .hiw-link { top: auto; bottom: 100%; right: auto; left: 50%; width: 0; height: 52px; }
  .hiw-link::before, .hiw-link::after {
    left: -0.5px;
    right: auto;
    top: 1px;
    bottom: 1px;
    height: auto;
    width: 1px;
    background: linear-gradient(180deg, rgba(134, 134, 139, 0), rgba(134, 134, 139, 0.55) 26%, rgba(134, 134, 139, 0.55) 74%, rgba(134, 134, 139, 0));
  }
  .hiw-link::after { background: linear-gradient(180deg, rgba(0, 113, 227, 0), rgba(0, 113, 227, 0.6) 26%, rgba(0, 113, 227, 0.6) 74%, rgba(0, 113, 227, 0)); }
  .hiw-link i { left: -2.5px; top: 0; }
  .hiw:has(.hiw-stage:hover) .hiw-stage:not(:hover) { opacity: 1; }
}
@media (max-width: 980px) and (prefers-reduced-motion: no-preference) {
  @keyframes hiw-travel {
    0% { transform: translateY(0); opacity: 0; }
    14% { opacity: 1; }
    80% { opacity: 1; }
    94%, 100% { transform: translateY(47px); opacity: 0; }
  }
}
@media (max-width: 420px) {
  .hiw-stage { padding: 22px 18px 18px; }
  .hiw-chips { grid-template-columns: 1fr 1fr; }
  .hiw-chips li { font-size: 11px; padding: 5px 8px; }
}

/* ---------- Demo frame (white on tinted) ---------- */
/* Full-bleed stage: the product is a desktop, full-screen app, so the embed
   breaks out of the 1200px column to near-viewport width (capped so it stays
   sane on ultra-wide displays). */
/* Pin the live-app embed into its own stacking context *below* the fixed nav
   (z-index 50). The embed is a GPU-composited iframe (WebGL globe); without an
   explicit low z-index its promoted layer paints over the fixed nav in Chrome,
   which wipes out the nav's frosted `backdrop-filter` glass as you scroll past.
   Same class of GPU-compositing bug the 2D map hit (see project notes). */
.demo-stage { width: min(1760px, calc(100% - 48px)); margin-inline: auto; position: relative; z-index: 0; scroll-margin-top: 64px; }
.demo-frame {
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: 0 40px 80px -50px rgba(0, 0, 0, 0.35);
}
.demo-bar {
  position: relative;
  height: 44px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 18px;
  border-bottom: 1px solid var(--hairline);
  background: #fafafc;
}
.demo-bar i { width: 11px; height: 11px; border-radius: 50%; background: #d2d2d7; }
/* Center the caption on the whole bar regardless of the buttons around it.
   Direct child only — must not catch the label span inside the .demo-fs button. */
.demo-bar > span {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* The caption is a full sentence now — clamp it so it never runs under the
     window dots on the left or the fullscreen button on the right. */
  max-width: calc(100% - 152px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  color: var(--hero-dim);
  pointer-events: none;
}
.demo-fs {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--hero-dim);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.18s ease;
}
.demo-fs:hover { color: var(--accent); }
.demo-fs:active { color: var(--accent); }
.demo-fs svg { display: block; }
.demo-body { padding: 0; position: relative; }

/* Interaction gate for the live embed (local face only — the prod poster keeps
   .demo-scrim/.demo-exit hidden via body:not(.demo-embed-live), see top of file).
   The embedded app's map captures wheel/touch and would trap the pointer in the
   iframe, so the page couldn't scroll past the demo. A scrim — a *parent-page*
   element laid over the iframe, which stays pointer-events:none until opted in —
   catches the gesture, so wheel/scroll lands on the page and keeps working.
   Click/tap flips .demo-frame.interactive and hands the pointer to the map:
     • desktop (fine pointer) → interactive *in place* (inline); the chrome "Exit
       demo" / Esc / moving the cursor off the window hand scroll back.
     • touch (pointer: coarse, media query below) → the frame goes full-screen,
       page scroll locked via body.demo-open; exit via ✕ / Esc. */
.demo-scrim {
  display: grid;
  place-items: center;
  position: absolute;
  inset: 0;
  z-index: 3;
  margin: 0;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: transparent;            /* desktop: keep the live preview visible */
  -webkit-tap-highlight-color: transparent;
}
/* Outer focus-mode exit — a labelled chip that lives in the window chrome
   (.demo-bar), shown only while interactive. Because it sits in the header row
   (never absolutely positioned over the embedded app), it can't cover the
   product's own panel close button. It sits NEXT TO the ⤢ fullscreen button,
   never replacing it: hiding ⤢ once the visitor clicked into the map meant
   "open fullscreen" silently stopped existing after the first interaction.
   margin-left is 0 here — ⤢ (which precedes it in the bar) carries the auto
   margin that pushes the pair right. On touch, where ⤢ is hidden entirely,
   the coarse-pointer block below gives the auto margin back to this chip.

   Styled as quiet bar text, matching .demo-fs — not a filled pill. The pill
   read as the loudest thing in the window chrome, competing with the product
   itself; exiting a demo is a utility affordance, not a call to action. */
.demo-exit {
  margin-left: 0;
  display: none;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--hero-dim);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.18s ease;
}
.demo-exit:hover { color: var(--hero-text); }
.demo-exit:focus-visible { outline: 2px solid var(--accent-hero); outline-offset: 2px; }
.demo-exit svg { display: block; }
.demo-frame.interactive .demo-exit { display: inline-flex; }
/* Until the visitor opts in, the map must not eat scroll/wheel gestures. */
.demo-frame:not(.interactive) .demo-embed { pointer-events: none; }
.demo-frame.interactive .demo-scrim { display: none; }

/* ---- Native fullscreen: the WINDOW goes fullscreen, not the bare iframe ----
   Fullscreening the <iframe> alone rendered nothing but the app, and since the
   iframe stays pointer-events:none until .interactive, the app arrived frozen
   with no visible way to wake it. .demo-frame is the fullscreen element instead,
   and predictions.js flips .interactive on entry so the map is live immediately.

   Fullscreen means fullscreen: the browser-window chrome (dots, caption, ⤢,
   Exit demo) is hidden — a fake title bar pinned over a real fullscreen app is
   just a strip of wasted screen. Esc is the way out (the browser's own native
   affordance, and the parent Esc handler defers to it while fullscreen). */
.demo-frame:fullscreen {
  --win-r: 0px;
  display: flex;
  flex-direction: column;
  width: 100vw;
  height: 100vh;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: #0b0b0d;
}
.demo-frame:fullscreen .demo-bar { display: none; }
.demo-frame:fullscreen .demo-body { flex: 1 1 auto; min-height: 0; }
.demo-frame:fullscreen .demo-embed { width: 100%; height: 100%; aspect-ratio: auto; }

/* Frosted chip shared by the touch pill and the desktop hover hint. */
.demo-scrim-pill,
.demo-scrim-hint {
  display: inline-flex;
  align-items: center;
  padding: 12px 22px;
  border-radius: 980px;
  border: 1px solid rgba(255, 255, 255, 0.30);
  background: rgba(18, 20, 26, 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  color: #f5f5f7;
  font-size: 15px;
  font-weight: 500;
  box-shadow: 0 14px 34px -18px rgba(0, 0, 0, 0.7);
}
/* Desktop: the tap pill is touch-worded — show the (hover-revealed) hint. */
.demo-scrim-pill { display: none; }
.demo-scrim-hint {
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.demo-scrim:hover .demo-scrim-hint,
.demo-scrim:focus-visible .demo-scrim-hint { opacity: 1; transform: none; }

@media (pointer: coarse) {
  /* No hover on touch — show the gate as a solid, always-visible tap affordance. */
  .demo-scrim {
    background: linear-gradient(to bottom, rgba(8, 10, 14, 0.10), rgba(8, 10, 14, 0.32));
  }
  .demo-scrim-hint { display: none; }
  .demo-scrim-pill { display: inline-flex; opacity: 1; transform: none; }

  /* Bigger touch target for the chrome exit control when the frame is
     full-screen (it still lives in the bar, not over the app). ⤢ is dropped on
     touch (see below), so the exit chip takes over the right-pushing margin. */
  .demo-frame.interactive .demo-exit { margin-left: auto; padding: 8px 14px; font-size: 13px; }

  /* Tap-to-open ⇒ the live app fills the whole phone screen instead of a
     325px postage stamp (the app's own mobile layout finally has room). The
     stage's z-index is raised above the nav via body.demo-open (JS), since the
     stage establishes a z:0 stacking context that would otherwise trap the
     fixed frame under the fixed nav. "Exit demo" in the bar returns to the card. */
  .demo-frame.interactive {
    position: fixed;
    inset: 0;
    z-index: 200;
    margin: 0;
    border: 0;
    border-radius: 0;
    /* Zeroes the hero window's rounded bar/body corners edge-to-edge
       (children derive their radii from this var). */
    --win-r: 0px;
    display: flex;
    flex-direction: column;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    box-shadow: none;
  }
  .demo-frame.interactive .demo-bar { flex: 0 0 auto; padding-top: env(safe-area-inset-top); height: auto; min-height: 44px; }
  /* On phones the scrim IS the single "open" affordance (→ in-page fullscreen);
     the bar's ⤢ (which falls back to a new tab on iOS) would be a confusing
     second path, so drop it on touch entirely. */
  .demo-fs { display: none; }
  .demo-frame.interactive .demo-body { flex: 1 1 auto; min-height: 0; }
  .demo-frame.interactive .demo-embed { aspect-ratio: auto; width: 100%; height: 100%; }
}
body.demo-open { overflow: hidden; }
body.demo-open .demo-stage { z-index: 200; }
.demo-body image-slot { display: block; width: 100%; aspect-ratio: 16 / 9; }
.demo-embed { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; background: #0b0b0d; }
.demo-actions { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 14px 28px; margin-top: 36px; }

/* ---------- Ask anything: retrieval console ---------- */
.rag-split { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center; }
.rag-copy { display: grid; gap: 16px; justify-items: start; }
.rag-copy h3 { font-size: clamp(28px, 3vw, 40px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; text-wrap: balance; }
.rag-lede { color: var(--muted); font-size: 18px; text-wrap: pretty; }
/* Three value points: quiet icon tiles, Linear-style. */
.rag-points { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 22px; }
.rag-points li { display: flex; align-items: flex-start; gap: 14px; }
.rag-point-ic {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin-top: 1px;
  border-radius: 10px;
  background: rgba(0, 113, 227, 0.09);
  color: var(--accent);
}
.rag-point-ic svg { width: 17px; height: 17px; display: block; }
.rag-points b { display: block; font-size: 15.5px; font-weight: 650; letter-spacing: -0.01em; }
.rag-points p { margin-top: 2px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.rag-models { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.rag-models li { font-size: 11.5px; font-weight: 600; color: var(--muted); background: var(--panel); border: 1px solid rgba(0, 0, 0, 0.05); border-radius: 999px; padding: 3px 10px; }

/* The console: workspace bar → query → retrieval trace → grounded answer.
   Everything below is the FINISHED state; the choreography further down runs
   `backwards` from hidden first frames (same contract as #how-it-works). */
.rag-console {
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--card);
  border: 1px solid var(--card-border);
  box-shadow: var(--shadow-card);
  font-size: 13px;
}
.rag-bar { display: flex; align-items: center; gap: 8px; padding: 12px 18px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); }
.rag-bar .chip-dot { flex: 0 0 auto; background: var(--green); color: var(--green); box-shadow: none; }
.rag-bar b { font-size: 12.5px; font-weight: 650; letter-spacing: -0.01em; }
.rag-bar-meta { margin-left: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); font-size: 10.5px; color: var(--dim); }
.rag-kicker { font-family: var(--mono); font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim); }
.rag-query { display: grid; gap: 5px; padding: 14px 18px 13px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); }
.rag-question { font-size: 15.5px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.35; text-wrap: balance; }

/* Trace: a thin rail runs through the step markers; a soft accent overlay
   grows down it while the steps complete. */
.rag-trace { position: relative; list-style: none; margin: 0; padding: 15px 18px 17px; display: grid; gap: 13px; }
.rag-trace::before, .rag-trace::after { content: ""; position: absolute; left: 25px; top: 26px; bottom: 28px; width: 1px; }
.rag-trace::before { background: rgba(0, 0, 0, 0.1); }
.rag-trace::after { background: rgba(0, 113, 227, 0.4); transform-origin: top; }
.rag-step { display: flex; align-items: flex-start; gap: 10px; }
.rag-dot {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 1.5px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.14);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* One-shot ping ring at the moment a step goes active. */
.rag-dot::before { content: ""; position: absolute; inset: -3px; border-radius: 50%; border: 1.5px solid var(--accent); opacity: 0; }
/* Completed-step tick (drawn, not an icon font). */
.rag-dot::after {
  content: "";
  position: absolute;
  left: 3.5px;
  top: 4px;
  width: 6px;
  height: 3px;
  border-left: 1.5px solid var(--accent);
  border-bottom: 1.5px solid var(--accent);
  transform: rotate(-45deg);
}
.rag-step-body { flex: 1; min-width: 0; display: grid; gap: 8px; }
.rag-step-row { display: flex; align-items: center; gap: 8px; min-height: 18px; }
.rag-step-label { font-size: 12.5px; font-weight: 600; letter-spacing: -0.005em; }
.rag-step-meta { margin-left: auto; font-family: var(--mono); font-size: 10.5px; color: var(--dim); }
.rag-rank + .rag-step-meta { margin-left: 0; }
/* Working ellipsis — visible only during the search window (base = hidden). */
.rag-busy { display: inline-flex; align-items: center; gap: 3px; opacity: 0; }
.rag-busy i { width: 3.5px; height: 3.5px; border-radius: 50%; background: var(--dim); }
/* Tiny descending histogram beside "Ranking sources". */
.rag-rank { margin-left: auto; display: inline-flex; align-items: flex-end; gap: 2px; height: 10px; }
.rag-rank i { width: 3px; border-radius: 1.5px; background: var(--accent); transform-origin: bottom; }
.rag-rank i:nth-child(1) { height: 10px; }
.rag-rank i:nth-child(2) { height: 7px; opacity: 0.55; }
.rag-rank i:nth-child(3) { height: 5px; opacity: 0.3; }
.rag-hits { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.rag-hits li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  background: var(--panel);
  border: 1px solid rgba(0, 0, 0, 0.05);
  border-radius: 8px;
  padding: 4px 10px;
}
.rag-hits li i { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--c, var(--accent)); }

/* The result zone: softly tinted footer of the console. */
.rag-answer { display: grid; gap: 10px; padding: 15px 18px 16px; background: var(--card-sub); border-top: 1px solid rgba(0, 0, 0, 0.06); }
.rag-answer-head { display: flex; align-items: center; }
.rag-grounded {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--green);
  background: rgba(36, 138, 61, 0.12);
  border-radius: 999px;
  padding: 3px 9px;
}
.rag-grounded::before { content: ""; width: 5px; height: 3px; margin-top: -2px; border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); }
.rag-answer-text { font-size: 14.5px; line-height: 1.55; text-wrap: pretty; }
.rag-ref { font-family: var(--mono); font-size: 9.5px; font-weight: 600; color: var(--accent); line-height: 0; margin-left: 2px; }
.rag-conf { display: flex; align-items: center; gap: 10px; }
.rag-conf-label { font-family: var(--mono); font-size: 10.5px; color: var(--dim); }
.rag-conf-bar { flex: 1; height: 4px; border-radius: 999px; background: rgba(0, 0, 0, 0.08); overflow: hidden; }
.rag-conf-bar i { display: block; height: 100%; width: var(--conf, 92%); border-radius: inherit; background: var(--accent); transform-origin: left; }
.rag-conf-val { font-family: var(--mono); font-size: 11.5px; font-weight: 700; }
.rag-cites { display: flex; flex-wrap: wrap; gap: 6px; }
.rag-cites a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 8px;
  padding: 4px 10px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.rag-cites a b { font-family: var(--mono); font-size: 9.5px; font-weight: 700; color: var(--accent); }
.rag-cites a:hover { transform: translateY(-1px); border-color: rgba(0, 113, 227, 0.45); box-shadow: 0 4px 10px -4px rgba(0, 0, 0, 0.15); }
.rag-updated { font-family: var(--mono); font-size: 10.5px; color: var(--dim); }

/* Choreography — one-shot (~4s), fired when the shared observer lands `.in`:
   search (busy dots + source chips pop) → read → rank (bars) → compose, the
   accent rail growing alongside; then the answer rises, confidence fills to
   92% and the citation chips land. Steps wait ghosted at 25% via `backwards`
   fill. Reduced motion / no JS: the finished console, nothing moves. */
@media (prefers-reduced-motion: no-preference) {
  html.js .rag-console.reveal.in .rag-trace::after { animation: rag-rail 2.6s cubic-bezier(0.4, 0, 0.4, 1) 0.25s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(1) { animation: rag-wake 0.35s ease 0.25s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(1) .rag-dot::before { animation: rag-ping 0.8s ease-out 0.25s; }
  html.js .rag-console.reveal.in .rag-step:nth-child(1) .rag-dot::after { animation: rag-tick 0.3s cubic-bezier(0.34, 1.4, 0.64, 1) 1.3s backwards; }
  html.js .rag-console.reveal.in .rag-busy { animation: rag-window 1s linear 0.28s; }
  html.js .rag-console.reveal.in .rag-busy i { animation: rag-blink 0.45s ease-in-out 0.3s 3 alternate; }
  html.js .rag-console.reveal.in .rag-busy i:nth-child(2) { animation-delay: 0.44s; }
  html.js .rag-console.reveal.in .rag-busy i:nth-child(3) { animation-delay: 0.58s; }
  html.js .rag-console.reveal.in .rag-hits li { animation: rag-pop 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(0.4s + var(--i) * 0.11s); }
  html.js .rag-console.reveal.in .rag-step:nth-child(2) { animation: rag-wake 0.35s ease 1.4s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(2) .rag-dot::before { animation: rag-ping 0.8s ease-out 1.4s; }
  html.js .rag-console.reveal.in .rag-step:nth-child(2) .rag-dot::after { animation: rag-tick 0.3s cubic-bezier(0.34, 1.4, 0.64, 1) 1.85s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(2) .rag-step-meta { animation: rag-meta-in 0.35s ease 1.85s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(3) { animation: rag-wake 0.35s ease 1.95s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(3) .rag-dot::before { animation: rag-ping 0.8s ease-out 1.95s; }
  html.js .rag-console.reveal.in .rag-step:nth-child(3) .rag-dot::after { animation: rag-tick 0.3s cubic-bezier(0.34, 1.4, 0.64, 1) 2.5s backwards; }
  html.js .rag-console.reveal.in .rag-rank i { animation: rag-bar-in 0.3s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(2s + var(--i) * 0.1s); }
  html.js .rag-console.reveal.in .rag-step:nth-child(3) .rag-step-meta { animation: rag-meta-in 0.35s ease 2.5s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(4) { animation: rag-wake 0.35s ease 2.6s backwards; }
  html.js .rag-console.reveal.in .rag-step:nth-child(4) .rag-dot::before { animation: rag-ping 0.8s ease-out 2.6s; }
  html.js .rag-console.reveal.in .rag-step:nth-child(4) .rag-dot::after { animation: rag-tick 0.3s cubic-bezier(0.34, 1.4, 0.64, 1) 3.05s backwards; }
  /* The tinted result slot stays visible from the start; only its content
     rises in — an empty white void at the card's foot read as a bug. */
  html.js .rag-console.reveal.in .rag-answer > * { animation: rag-rise 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) 3.15s backwards; }
  html.js .rag-console.reveal.in .rag-conf-bar i { animation: rag-fill 0.55s cubic-bezier(0.25, 0.6, 0.3, 1) 3.45s backwards; }
  html.js .rag-console.reveal.in .rag-conf-val { animation: rag-fade 0.35s ease 3.85s backwards; }
  html.js .rag-console.reveal.in .rag-cites a { animation: rag-pop 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; animation-delay: calc(3.55s + var(--i) * 0.09s); }
  html.js .rag-console.reveal.in .rag-updated { animation: rag-fade 0.4s ease 4s backwards; }
}
@keyframes rag-wake { from { opacity: 0.25; transform: translateX(-4px); } }
@keyframes rag-tick { from { opacity: 0; transform: rotate(-45deg) scale(0.4); } }
@keyframes rag-ping { 0% { opacity: 0.75; transform: scale(0.5); } 100% { opacity: 0; transform: scale(2); } }
@keyframes rag-pop { from { opacity: 0; transform: translateY(5px) scale(0.92); } }
@keyframes rag-rise { from { opacity: 0; transform: translateY(9px); } }
@keyframes rag-fade { from { opacity: 0; } }
@keyframes rag-meta-in { from { opacity: 0; transform: translateY(2px); } }
@keyframes rag-rail { from { transform: scaleY(0); } }
@keyframes rag-fill { from { transform: scaleX(0); } }
@keyframes rag-bar-in { from { transform: scaleY(0); } }
@keyframes rag-window { 0%, 100% { opacity: 0; } 12%, 86% { opacity: 1; } }
@keyframes rag-blink { from { opacity: 0.25; } }
@media (max-width: 600px) {
  .rag-console { border-radius: 18px; }
  .rag-bar, .rag-query, .rag-answer { padding-inline: 14px; }
  .rag-trace { padding-inline: 14px; }
  .rag-trace::before, .rag-trace::after { left: 21px; }
  .rag-bar-meta { display: none; }
  .rag-points { gap: 18px; }
}
.cap-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 36px 28px; }
.cap { padding-top: 18px; border-top: 1px solid var(--hairline); display: grid; gap: 5px; align-content: start; }
.cap h4 { font-size: 17px; font-weight: 600; }
.cap p { font-size: 14px; color: var(--dim); line-height: 1.45; }

/* ---------- Feature rail (Apple-style horizontal gallery) ---------- */
.rail-section { overflow: hidden; --rail-pad: max(calc(22px + env(safe-area-inset-left, 0px)), calc((100vw - var(--max)) / 2)); --rail-gap: 24px; }
/* One vertical rhythm for the whole unit: headline → 24 → steps → 10 → track
   → 22 → chapter title → 10 → sub → ~36 → cards. */
.rail-head { display: grid; gap: 20px; justify-items: start; text-align: left; margin-bottom: 8px; }

/* Narrative steps (01 Connect … 04 Decide): a progress indicator over the
   rail, not static labels — upcoming (quiet) / is-active (ink + accent) /
   is-done (settled). Steps and track share one max-content block so the
   track is exactly as long as the navigation (JS sizes it to end at "04"). */
.rail-steps-wrap { justify-self: stretch; margin-top: 4px; }
.rail-steps-inner { display: grid; gap: 10px; width: max-content; padding-bottom: 6px; }
.rail-steps { display: flex; gap: 36px; list-style: none; margin: 0; padding: 0; }
.rail-step {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  border: 0;
  background: none;
  padding: 6px 0 2px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #b4b4b9;
  transition: color 0.3s ease, font-weight 0.3s ease;
}
/* Invisible bold twin under each label reserves the active weight's width,
   so the weight change can never nudge the row (or the dots under it). */
.rail-step span::after {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 640;
}
.rail-step b { font-weight: 600; font-size: 11px; font-variant-numeric: tabular-nums; opacity: 0.55; transition: color 0.3s ease, opacity 0.3s ease; }
.rail-step:hover { color: var(--muted); }
.rail-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
.rail-step.is-done { color: var(--dim); }
.rail-step.is-done b { opacity: 0.8; }
.rail-step.is-active { color: var(--text); font-weight: 640; }
.rail-step.is-active b { opacity: 1; color: var(--accent); }
/* The track supports the story rather than competing with it: hairline-thin,
   soft blue fill, small nodes under each step number. The fill is a scaleX
   (compositor-only); its longer transition is what makes the scroll-driven
   fill read as one interpolated motion instead of steps. */
.rail-progress { position: relative; height: 2px; border-radius: 999px; background: rgba(0, 0, 0, 0.07); }
.rail-progress i {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(0, 113, 227, 0.55);
  display: block;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.45s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.rail-progress-dot {
  position: absolute;
  top: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: #d4d4d8;
  box-shadow: 0 0 0 2.5px var(--panel);
  transition: background 0.35s ease, transform 0.35s ease, box-shadow 0.35s ease;
}
.rail-progress-dot.is-reached { background: rgba(0, 113, 227, 0.55); }
.rail-progress-dot.is-active {
  background: var(--accent);
  transform: translate(-50%, -50%) scale(1.3);
  box-shadow: 0 0 0 2.5px var(--panel), 0 0 0 5.5px rgba(0, 113, 227, 0.12);
}

/* Chapter head: the messaging of the active step. All four copies are
   grid-stacked in one cell (height = tallest copy at any breakpoint), and
   the active one crossfades in with a quiet rise, sub trailing ~70ms.
   Class-driven, so a throttled/hidden tab can never strand it mid-swap. */
.rail-chapter { margin-top: 22px; display: grid; }
.rail-chapter-slide {
  grid-area: 1 / 1;
  display: grid;
  gap: 10px;
  justify-items: start;
  align-content: start;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, visibility 0s linear 0.18s;
}
.rail-chapter-slide.is-active { opacity: 1; visibility: visible; transition: opacity 0.1s ease; }
@keyframes rail-chapter-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
.rail-chapter-slide.is-active .rail-chapter-title { animation: rail-chapter-in 0.42s cubic-bezier(0.22, 0.61, 0.36, 1) both; }
.rail-chapter-slide.is-active .rail-chapter-sub { animation: rail-chapter-in 0.42s cubic-bezier(0.22, 0.61, 0.36, 1) 0.07s both; }
.rail-chapter-title { font-size: clamp(24px, 2.6vw, 34px); line-height: 1.12; font-weight: 600; letter-spacing: -0.02em; }
.rail-chapter-sub { font-size: 17px; line-height: 1.5; font-weight: 400; color: var(--muted); max-width: 560px; text-wrap: pretty; }

/* One consistent kicker colour per category */
.rcard[data-cat="data"] .rcard-kicker { color: #0071e3; }
.rcard[data-cat="ai"] .rcard-kicker { color: #bf5af2; }
.rcard[data-cat="analysis"] .rcard-kicker { color: #5e5ce6; }
.rcard[data-cat="map"] .rcard-kicker { color: #248a3d; }
.rcard[data-cat="workspace"] .rcard-kicker { color: #00798c; }
.rcard[data-cat="monitoring"] .rcard-kicker { color: #c93400; }
.rcard[data-cat="deployment"] .rcard-kicker { color: #6e6e73; }
.rail {
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 28px var(--rail-pad) 30px;
  scroll-padding-inline: var(--rail-pad);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  outline: 0;
  cursor: grab;
}
.rail:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 12px; }
.rail.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; -webkit-user-select: none; }
.rail.is-dragging .rcard { pointer-events: none; }
.rail::-webkit-scrollbar { display: none; }
/* ~3.2 cards per view: the next card always peeks in from the right edge to
   invite horizontal scrolling (clamped so cards stay sane on extreme widths).
   Solid white on the tinted band — no gradient, no glass — with a hairline
   border and a two-layer shadow (contact + ambient) for clean depth. */
.rcard {
  flex: 0 0 min(86vw, clamp(370px, calc((100vw - var(--rail-pad) - 3 * var(--rail-gap)) / 3.22), 470px));
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  padding: 28px 28px 0;
  border-radius: var(--radius);
  background: #fff;
  border: 1px solid var(--card-border);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition:
    transform 0.32s cubic-bezier(0.2, 0.6, 0.2, 1),
    box-shadow 0.32s cubic-bezier(0.2, 0.6, 0.2, 1),
    opacity 0.5s ease;
  will-change: transform;
}
/* Cards outside the active chapter rest slightly; the chapter owns its cards. */
.rcard.is-off { opacity: 0.62; }
.rcard.is-off:hover { opacity: 1; }
.rcard:hover {
  transform: translateY(-3px) scale(1.006);
  box-shadow: var(--shadow-card-hover);
}
.rcard-kicker { font-size: 12px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; margin-bottom: 12px; }
.rcard h3 { font-size: 22px; font-weight: 600; letter-spacing: -0.018em; line-height: 1.18; margin-bottom: 8px; text-wrap: balance; }
/* Clamped to two lines (three on smaller cards) with the same height reserved,
   so every card's text region — and therefore the media line — stays level. */
.rcard > p:not(.rcard-kicker) {
  font-size: 15px;
  color: rgba(60, 60, 67, 0.62);
  line-height: 1.45;
  text-wrap: pretty;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  min-height: 2.9em;
}
.rcard-media { margin: 14px -28px 0; margin-top: auto; padding-top: 16px; height: 208px; }
.rcard-media image-slot { display: block; width: 100%; height: 100%; }

/* Card illustrations (CSS vignettes): a recessed "stage" clearly separated
   from the card body, with a gentle zoom on card hover. Every scene is
   composed on a fixed 384×178 canvas (.vg-scene) centred inside the fluid
   card, so absolute chips and SVG connectors stay pixel-aligned at any
   card width. */
.vg {
  position: relative;
  width: 100%;
  height: 100%;
  background: radial-gradient(130% 120% at 30% 0%, #dfe7f9 0%, #edf0f7 62%);
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(29, 29, 31, 0.06),
    inset 0 10px 18px -14px rgba(29, 29, 31, 0.08);
  transition: transform 0.28s cubic-bezier(0.2, 0.6, 0.2, 1), filter 0.28s ease;
  transform-origin: 50% 100%;
}
.rcard:hover .vg { transform: scale(1.02); filter: brightness(1.03) saturate(1.03); }
/* Each chapter shifts the stage tint a step — connect (blue), understand
   (violet), explore (green), decide (graphite). Same language, own identity. */
.rcard[data-step="2"] .vg { background: radial-gradient(130% 120% at 30% 0%, #e5e3f8 0%, #efeff6 62%); }
.rcard[data-step="3"] .vg { background: radial-gradient(130% 120% at 30% 0%, #ddebe4 0%, #edf1ef 62%); }
.rcard[data-step="4"] .vg { background: radial-gradient(130% 120% at 30% 0%, #e6e6ee 0%, #efeff3 62%); }
.vg-scene { position: absolute; top: 0; left: 50%; width: 384px; height: 100%; transform: translateX(-50%); }

/* Illustration micro-motion: signals flow along wires as spaced dots (one
   dash period per loop = seamless), pins ping, switches flip. Everything is
   slow and quiet; hover only adds the finishing beat. */
.vg-flow { stroke-linecap: round; stroke-dasharray: 0.1 11; animation: vg-flow 1.8s linear infinite; }
.vg-flow--slow { animation-duration: 2.8s; }
@keyframes vg-flow { to { stroke-dashoffset: -11.1; } }
circle.vg-ping { transform-box: fill-box; transform-origin: center; animation: vg-ping 2.8s cubic-bezier(0, 0, 0.2, 1) infinite; }
@keyframes vg-ping {
  0% { transform: scale(0.5); opacity: 0.9; }
  75%, 100% { transform: scale(2); opacity: 0; }
}
.vg-dot--ping { position: relative; }
.vg-dot--ping::after {
  content: "";
  position: absolute;
  inset: -1.5px;
  border-radius: 50%;
  border: 1.5px solid #ff453a;
  animation: vg-ping 2.8s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.vg-switch { position: relative; display: block; width: 28px; height: 16px; border-radius: 999px; background: #e3e3e8; flex: 0 0 auto; transition: background 0.35s ease; }
.vg-switch i { position: absolute; left: 2px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18); transition: transform 0.35s cubic-bezier(0.22, 0.61, 0.36, 1); }
.vg-switch.is-on { background: #30d158; }
.vg-switch.is-on i { transform: translateX(12px); }
.rcard:hover .vg-switch--arm { background: #30d158; }
.rcard:hover .vg-switch--arm i { transform: translateX(12px); }
.vg-nudge, .vg-nudge-back { transition: transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1); }
svg .vg-nudge, svg .vg-nudge-back { transform-box: fill-box; }
.rcard:hover .vg-nudge { transform: translate(7px, 5px); }
.rcard:hover .vg-nudge-back { transform: translate(-6px, -4px); }
.vg-layer { transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1); }
.vg-layer--a { transform: rotate(-4deg); }
.vg-layer--b { transform: rotate(-1deg); }
.vg-layer--c { transform: rotate(2deg); }
.rcard:hover .vg-layer--a { transform: rotate(-7deg) translate(-6px, 4px); }
.rcard:hover .vg-layer--c { transform: rotate(3.5deg) translate(5px, -4px); }
.vg-playhead { transition: left 1.6s cubic-bezier(0.22, 0.61, 0.36, 1); }
.vg-playfill { transition: right 1.6s cubic-bezier(0.22, 0.61, 0.36, 1); }
.rcard:hover .vg-playhead { left: 68%; }
.rcard:hover .vg-playfill { right: 32%; }
.vg-rise { transition: transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1); }
.rcard:hover .vg-rise { transform: translateY(-3px); }
.vg-spark { transform-origin: 50% 100%; transition: transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1); }
.rcard:hover .vg-spark { transform: scaleY(1.05); }
.vg-net { position: absolute; left: 0; top: 0; }
.vg-dots { position: absolute; inset: 0; background-image: radial-gradient(circle, #c9d0e0 1.5px, transparent 1.5px); background-size: 15px 15px; }
.vg-chip {
  position: absolute;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border-radius: 11px;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.46), rgba(255, 255, 255, 0.20));
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow:
    0 8px 20px -12px rgba(0, 0, 0, 0.24),
    inset 0 1px 0 rgba(255, 255, 255, 0.85);
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.vg-chip small { font-weight: 400; color: var(--dim); font-size: 11px; }
.vg-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.vg-halo { position: absolute; border-radius: 50%; }
.vg-bar { position: absolute; border-radius: 999px; }
.vg-card {
  position: absolute;
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 14px;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.50), rgba(255, 255, 255, 0.24));
  -webkit-backdrop-filter: blur(18px) saturate(185%);
  backdrop-filter: blur(18px) saturate(185%);
  border: 1px solid rgba(255, 255, 255, 0.62);
  box-shadow:
    0 16px 32px -18px rgba(0, 0, 0, 0.26),
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.vg-line { height: 7px; border-radius: 4px; background: #e3e3e8; }
.vg-spark { display: flex; align-items: flex-end; gap: 4px; height: 34px; }
.vg-spark i { width: 7px; border-radius: 3px; }
/* Full-width variant: slim bars distributed across the whole row instead of
   clustering left under a full-width card. */
.vg-spark--fill { justify-content: space-between; }
.vg-spark--fill i { flex: 0 0 auto; }
/* Paddles: quiet 44px targets that only speak on hover/press. */
.rail-nav { display: flex; justify-content: flex-end; gap: 10px; margin-top: 2px; }
.rail-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(29, 29, 31, 0.08);
  background: #fff;
  box-shadow: 0 1px 2px rgba(20, 22, 40, 0.05), 0 6px 16px -8px rgba(20, 22, 40, 0.14);
  color: var(--text);
  display: grid;
  place-items: center;
  padding: 0;
  cursor: pointer;
  transition: background 0.2s ease, box-shadow 0.25s ease, transform 0.3s cubic-bezier(0.34, 1.3, 0.64, 1), opacity 0.25s ease;
}
.rail-btn svg { width: 15px; height: 15px; display: block; }
.rail-btn:hover:not(:disabled) { transform: translateY(-1px) scale(1.04); box-shadow: 0 2px 4px rgba(20, 22, 40, 0.05), 0 10px 22px -10px rgba(20, 22, 40, 0.22); }
.rail-btn:active:not(:disabled) { transform: scale(0.94); transition-duration: 0.1s; box-shadow: 0 1px 2px rgba(20, 22, 40, 0.06); }
.rail-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.rail-btn:disabled { opacity: 0.3; cursor: default; box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .rcard, .vg, .rail-btn, .rail-progress i, .rail-progress-dot,
  .rail-step, .rail-chapter-slide,
  .vg-switch, .vg-switch i, .vg-nudge, .vg-nudge-back, .vg-layer,
  .vg-playhead, .vg-playfill, .vg-rise, .vg-spark { transition: none; }
  .rail-chapter-slide.is-active .rail-chapter-title,
  .rail-chapter-slide.is-active .rail-chapter-sub { animation: none; }
  .rcard:hover { transform: none; }
  .rcard:hover .vg { transform: none; filter: none; }
  .rcard:hover .vg-nudge, .rcard:hover .vg-nudge-back { transform: none; }
  .rcard:hover .vg-playhead { left: 54%; }
  .rcard:hover .vg-playfill { right: 46%; }
  .rcard:hover .vg-rise, .rcard:hover .vg-spark { transform: none; }
  .vg-flow, circle.vg-ping, .vg-dot--ping::after { animation: none; }
  .vg-dot--ping::after { content: none; }
}

/* ---------- Sources & models (#sources, the ecosystem section) ---------- */
/* Two top-ruled groups side by side; two short hairlines converge from under
   the columns into the Shared Context card (sources → context ← models).
   The connector is decorative only (empty pseudo-elements + aria-hidden). */
.eco-grid { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(48px, 7vw, 96px); }
/* Hero: tighter headline↔sub gap and a readable measure. */
#sources .section-head { gap: 12px; }
#sources .subhead { max-width: 660px; line-height: 1.55; }
/* Descriptions recede a touch from their labels (eyebrow tracking is global). */
#sources .eco-sub, #sources .eco-facets { opacity: 0.94; }
.eco-group {
  display: grid;
  gap: 10px;
  align-content: start;
  justify-items: center;
  text-align: center;
}
/* Top rule frames the group heading — deliberately shorter than the column,
   identical width on both sides (columns are equal, so 82% resolves equal). */
.eco-group::before { content: ""; width: 82%; height: 1px; background: var(--hairline); margin-bottom: 14px; }
.eco-sub { color: var(--muted); font-size: 15px; max-width: 42ch; text-wrap: pretty; }
.eco-logos { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 16px 44px; }
.eco-logos li { display: inline-flex; align-items: center; gap: 10px; opacity: 0.5; transition: opacity 0.25s ease, transform 0.25s ease; }
.eco-logos li:hover { opacity: 0.95; transform: scale(1.02); }
/* --h tunes per-logo optical height (set inline where a mark runs dense/wide). */
.eco-logos img { display: block; height: var(--h, 22px); width: auto; filter: brightness(0); }
/* Optical-weight trims: the FT badge is a filled dark square and Bloomberg a
   heavy wordmark — both read heavier than their neighbours, so damp them. */
.eco-logos img[alt="Financial Times"] { filter: grayscale(1) contrast(0.7); opacity: 0.72; }
.eco-logos img[alt="Bloomberg"] { opacity: 0.82; }
.eco-logos b { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; color: #000; white-space: nowrap; }
/* Model providers: compact two-column grid on desktop so the group carries
   the same visual weight as the newsroom block; the odd last item spans. */
.eco-logos--models {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, max-content));
  justify-content: center;
  justify-items: center;
  align-items: center;
  gap: 24px 52px;
  margin-top: 12px;
}
.eco-logos--models li:last-child:nth-child(odd) { grid-column: 1 / -1; }
/* Informational capability labels — quiet fill, no border, no hover: they
   list supported input types and must not read as filter controls. */
.eco-chips { list-style: none; margin: 13px 0 0; padding: 0; display: grid; grid-template-columns: repeat(4, max-content); justify-content: center; justify-items: center; gap: 5px; }
.eco-chips li {
  font-size: 12px;
  color: var(--muted);
  background: var(--card-sub);
  border: 1px solid transparent;
  border-radius: 7px;
  padding: 5px 10px;
  transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.eco-chips li:hover { transform: translateY(-1px); border-color: var(--hairline); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); }
.eco-join { margin-top: 44px; display: flex; flex-direction: column; align-items: center; text-align: center; }
/* Shared Context card — the section's conclusion: white, softly elevated,
   with a faint inner top highlight; two converging hairlines land ON the card's
   top edge (rotation shortens the vertical extent by height×(1−cos32°) ≈ 8px,
   hence the −8px offset; scaleY draws them downward from the groups on reveal). */
.eco-context {
  position: relative;
  display: grid;
  gap: 7px;
  justify-items: center;
  max-width: 672px;
  padding: 22px 44px 18px;
  border-radius: var(--radius-card);
  background: var(--card);
  border: 1px solid var(--card-border);
  box-shadow: var(--shadow-card), inset 0 1px 0 rgba(255, 255, 255, 0.8);
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.eco-context:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-card-hover), inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
.eco-context::before,
.eco-context::after {
  content: "";
  position: absolute;
  bottom: calc(100% - 8px);
  width: 1px;
  height: 50px;
  background: rgba(0, 0, 0, 0.09);
  transform: rotate(var(--a));
  transform-origin: top center;
}
.eco-context::before { --a: -31deg; left: calc(50% - 196px); }
.eco-context::after { --a: 31deg; left: calc(50% + 196px); }
.eco-context-eyebrow { display: inline-flex; align-items: center; gap: 8px; }
.eco-node { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.eco-outcome { font-size: 20px; font-weight: 600; letter-spacing: -0.015em; text-wrap: balance; }
.eco-facets { list-style: none; display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 12px; margin: 2px 0 0; padding: 0; font-size: 13px; font-weight: 500; color: var(--muted); }
.eco-facets li { display: inline-flex; align-items: center; gap: 12px; }
.eco-facets li + li::before { content: ""; width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: 0.7; }
.eco-legal { margin-top: 13px; font-size: 11px; line-height: 1.4; color: var(--dim); opacity: 0.78; max-width: 62ch; text-wrap: pretty; }
@media (prefers-reduced-motion: no-preference) {
  /* Choreography (one-shot, nothing loops): heading/groups ride the .reveal
     transition with a small stagger, the card lifts in last; then the lines
     draw down into the card, a tiny blue pulse travels along each line, and
     the node blinks once as it arrives. */
  html.js .eco-group.reveal:nth-child(2) { transition-delay: 0.08s; }
  html.js .eco-join.reveal { transition-delay: 0.16s; }
  html.js .eco-group.reveal.in .eco-logos li,
  html.js .eco-group.reveal.in .eco-chips li {
    animation: eco-rise 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
    animation-delay: calc(0.12s + var(--i, 0) * 0.045s);
  }
  html.js .eco-join.reveal.in .eco-context::before,
  html.js .eco-join.reveal.in .eco-context::after {
    animation:
      eco-line-in 0.35s cubic-bezier(0.22, 0.61, 0.36, 1) 0.25s backwards,
      eco-line-pulse 0.5s linear 0.62s 1;
  }
  html.js .eco-join.reveal.in .eco-node { animation: eco-node-pulse 0.5s ease-out 1.02s 1 backwards; }
}
@keyframes eco-rise { from { opacity: 0; transform: translateY(9px); } }
@keyframes eco-line-in {
  from { transform: rotate(var(--a, 0deg)) scaleY(0); }
  to { transform: rotate(var(--a, 0deg)) scaleY(1); }
}
/* A short accent streak slides down the 1px line once (background-image over
   the hairline background-color; clipped at both ends by the element box). */
@keyframes eco-line-pulse {
  from {
    background-image: linear-gradient(to bottom, rgba(0, 113, 227, 0), rgba(0, 113, 227, 0.85) 50%, rgba(0, 113, 227, 0));
    background-size: 100% 16px;
    background-repeat: no-repeat;
    background-position: 0 -16px;
  }
  to {
    background-image: linear-gradient(to bottom, rgba(0, 113, 227, 0), rgba(0, 113, 227, 0.85) 50%, rgba(0, 113, 227, 0));
    background-size: 100% 16px;
    background-repeat: no-repeat;
    background-position: 0 52px;
  }
}
@keyframes eco-node-pulse {
  from { box-shadow: 0 0 0 0 rgba(0, 113, 227, 0.4); }
  to { box-shadow: 0 0 0 11px rgba(0, 113, 227, 0); }
}
@media (max-width: 980px) {
  .eco-grid { grid-template-columns: 1fr; row-gap: 44px; }
  .eco-logos--models { display: flex; flex-wrap: wrap; gap: 22px 36px; }
  .eco-join { margin-top: 46px; }
  /* Stacked: converging diagonals are meaningless — one vertical drop that
     still terminates at the card's top edge (no rotation → no −8px offset). */
  .eco-context::after { content: none; }
  .eco-context::before { --a: 0deg; left: 50%; height: 36px; bottom: 100%; }
}
@media (max-width: 600px) {
  .eco-logos { gap: 18px 26px; }
  .eco-chips { grid-template-columns: repeat(3, max-content); }
  .eco-context { padding: 22px 22px 20px; }
  /* Wrapped facet lines would start with a stray separator dot — drop them. */
  .eco-facets { gap: 6px 18px; }
  .eco-facets li + li::before { content: none; }
}

/* ---------- FAQ (compact objection-handling, before the final CTA) ----------
   A narrow, restrained band — clearly smaller than the product sections and the
   CTA below it — that clears last questions and hands the visitor to the form.
   White (--bg) to separate from the tinted #predictions above; the CTA below is
   tinted, so the ending reads FAQ (quiet) → CTA (strong) → footer (quiet).
   `.section.faq-section` specificity keeps this padding over the responsive
   `.section` rules further down. */
.section.faq-section { padding: 88px 0 72px; background: var(--bg); }
.faq-wrap { width: min(860px, calc(100% - 44px)); margin-inline: auto; }
.faq-head { margin-bottom: 26px; }
.faq-title {
  font-size: clamp(22px, 2.4vw, 28px);
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.015em;
}
.faq details { border-bottom: 1px solid var(--hairline); }
.faq details:first-of-type { border-top: 1px solid var(--hairline); }
.faq summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  padding: 18px 2px;
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+";
  flex: 0 0 auto;
  font-size: 22px; font-weight: 300; line-height: 1;
  color: var(--dim);
  transition: transform 0.25s var(--ease-out);
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.faq details p { padding: 0 2px 20px; color: var(--muted); max-width: 68ch; }
/* Smooth open/close height where the browser can animate to auto (Chromium
   ::details-content + interpolate-size). Elsewhere it toggles instantly — no
   layout jump either way. Skipped under reduced-motion. Single-open behaviour
   comes from the shared name="faq" on each <details> (native accordion). */
@media (prefers-reduced-motion: no-preference) {
  .faq { interpolate-size: allow-keywords; }
  .faq details::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 0.28s var(--ease-out), content-visibility 0.28s allow-discrete;
    transition-behavior: allow-discrete;
  }
  .faq details[open]::details-content { block-size: auto; }
}

/* ---------- Final CTA (two-column: offer + form) ----------
   The last content before the footer. Tinted (--panel) so it separates from the
   white FAQ above and flows into the tinted footer below. Aligned to the global
   .container like the product sections; strong but controlled — headline smaller
   than the hero. A faint decorative glow (::before) stands in for the removed
   globe: pure CSS, adds no height, hidden on mobile, out of the a11y tree. */
.section.cta-final { position: relative; padding: 96px 0 96px; background: var(--panel); overflow: hidden; }
.cta-final::before {
  content: "";
  position: absolute;
  right: -6%;
  bottom: -34%;
  width: 620px;
  height: 620px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, rgba(0, 113, 227, 0.10), rgba(0, 113, 227, 0) 68%);
  pointer-events: none;
  z-index: 0;
}
.cta-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.02fr 0.98fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}
.cta-offer { display: grid; gap: 18px; align-content: start; }
.cta-headline {
  font-size: clamp(30px, 3.5vw, 44px);
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: -0.02em;
  max-width: 15ch;
}
.cta-lede { font-size: clamp(16.5px, 1.3vw, 19px); line-height: 1.5; color: var(--muted); max-width: 48ch; }
.offer-card {
  margin-top: 4px;
  padding: 22px 24px;
  border: 1px solid var(--card-border);
  border-radius: var(--radius-card);
  background: var(--card);
}
.offer-card-title {
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--dim); margin-bottom: 14px;
}
.offer-list { list-style: none; display: grid; gap: 12px; }
.offer-list li {
  display: flex; align-items: flex-start; gap: 11px;
  font-size: 15.5px; line-height: 1.4; color: var(--text);
}
.offer-check {
  flex: 0 0 auto; width: 20px; height: 20px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: rgba(36, 138, 61, 0.12); color: var(--green);
}
.offer-check svg { width: 12px; height: 12px; display: block; }
.cta-form-card {
  padding: clamp(22px, 2.6vw, 30px);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-card);
  background: var(--card);
  box-shadow: var(--shadow-card);
}
.contact-form { display: grid; gap: 15px; }
.contact-form .field { display: grid; gap: 6px; }
.contact-form label { font-size: 13px; font-weight: 600; color: var(--text); }
.field-opt { font-weight: 400; color: var(--dim); }
.contact-form input, .contact-form textarea {
  width: 100%;
  border: 1px solid var(--hairline);
  border-radius: 12px;
  background: var(--bg);
  color: var(--text);
  padding: 12px 14px;
  font-size: 16px;               /* ≥16px so iOS never zooms on focus */
  line-height: 1.4;
  outline: 0;
  resize: vertical;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.contact-form input:focus, .contact-form textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.15); }
.contact-form ::placeholder { color: var(--dim); }
.contact-form .field.has-error input,
.contact-form .field.has-error textarea { border-color: var(--red); }
.contact-form .field.has-error input:focus,
.contact-form .field.has-error textarea:focus { box-shadow: 0 0 0 3px rgba(255, 69, 58, 0.16); }
.field-error { font-size: 12.5px; line-height: 1.35; color: #c93400; }
.field-error[hidden] { display: none; }
.cta-submit { width: 100%; margin-top: 2px; }
.form-note { font-size: 12.5px; line-height: 1.4; color: var(--dim); }
.form-note.is-error { color: #c93400; }
.form-note.is-success { color: var(--green); }

/* ---------- Footer ---------- */
.footer { background: var(--panel); border-top: 1px solid var(--hairline); padding: 44px 0 48px; font-size: 13px; color: var(--dim); }
/* Two columns (Product | Company) — the Resources column held only
   placeholder links and was removed with them. Left-packed so they align to the
   page's content edge instead of stretching apart across the full width. */
.footer-cols { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); gap: 96px; padding-bottom: 36px; }
.footer-cols h4 { color: var(--dim); font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 12px; }
.footer-cols a { display: block; padding: 5px 0; color: var(--muted); }
.footer-cols a:hover { color: var(--text); }
.footer-legal { border-top: 1px solid var(--hairline); padding-top: 22px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }

/* ---------- Reveal ---------- */
@media (prefers-reduced-motion: no-preference) {
  html.js .reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.8s cubic-bezier(0.22, 0.61, 0.36, 1), transform 0.8s cubic-bezier(0.22, 0.61, 0.36, 1); }
  html.js .reveal.in { opacity: 1; transform: none; }
}

/* ---------- Responsive ---------- */
/* Tablet: a deliberate two-up rail (second card fully visible, third peeking).
   The 370px floor keeps the fixed 384px scene canvas from clipping its chips. */
@media (max-width: 1080px) and (min-width: 601px) {
  .rcard { flex-basis: min(74vw, clamp(370px, calc((100vw - var(--rail-pad) - 2 * var(--rail-gap)) / 2.12), 480px)); }
}
/* Narrower cards wrap the description onto a third line — allow it (and keep
   reserving two, so the media line stays level across cards). */
@media (max-width: 1080px) {
  .rcard > p:not(.rcard-kicker) { -webkit-line-clamp: 3; }
}
@media (max-width: 980px) {
  .cap-grid { grid-template-columns: repeat(2, 1fr); }
  .rag-split { grid-template-columns: 1fr; gap: 36px; }
  .nav-links { display: none; }
  .nav-burger { display: block; }
  .section { padding: 96px 0; }
}
/* Final CTA: stack once two comfortable columns no longer fit. Offer stays
   above the form (source order); decorative glow off so it never adds height. */
@media (max-width: 900px) {
  .cta-grid { grid-template-columns: 1fr; gap: 34px; align-items: start; }
  .cta-headline { max-width: 22ch; }
  .cta-lede { max-width: 64ch; }
  .cta-final::before { display: none; }
}
@media (max-width: 600px) {
  .hero { padding-top: 82px; }
  /* Let the headline wrap naturally on narrow screens — the desktop's forced
     "…Earth. / Live" break plus a natural wrap gave three ragged lines. */
  .display br { display: none; }
  .display { font-size: clamp(34px, 10vw, 48px); }
  .hero-copy .subhead { font-size: 18px; }
  .hero-actions { flex-direction: column; }
  .hero-actions .btn-primary, .hero-actions .btn-ghost { width: 100%; max-width: 320px; }
  .cap-grid { grid-template-columns: 1fr; }
  .demo-fs__label { display: none; }
  .demo-fs { padding: 0; }
  /* A 16:9 desktop viewport at ~340px wide is an unusable postage stamp;
     portrait lets the app's own mobile layout breathe. The poster keeps a
     shorter portrait so its CTA is not stranded below a tall empty panel. */
  .demo-embed { aspect-ratio: 3 / 4; }
  .demo-poster { aspect-ratio: 4 / 5; }
  /* Proof bar: 2-col grid on phones (the 5th metric spans the full width). */
  .stats { width: calc(100% - 32px); padding-bottom: 52px; }
  .stats-row { grid-template-columns: repeat(2, 1fr); }
  .stat { padding: 16px 12px; }
  .stat:last-child { grid-column: 1 / -1; }
  .stat b { font-size: 24px; }
  .footer-cols { grid-template-columns: 1fr 1fr; }
  .event-chip { min-width: 0; width: max-content; max-width: 88vw; }
  /* Tidy the top bar on phones: keep brand + Sign in + burger, drop the
     duplicate "Request access" (the dropdown and hero both carry it). */
  .nav-inner { gap: 12px; }
  .nav-brand { white-space: nowrap; }
  .nav-cta { display: none; }
  .nav-auth { white-space: nowrap; }
  /* Tighter vertical rhythm + smaller headline steps for phone screens. */
  .section { padding: 76px 0; }
  .section-head { margin-bottom: 44px; gap: 14px; }
  .headline { font-size: clamp(30px, 8.6vw, 40px); }
  .subhead { font-size: 17px; }
  .rcard { flex-basis: 84vw; }
  .rail { padding-top: 24px; }
  .rail-head { margin-bottom: 6px; gap: 14px; }
  /* The step row becomes a horizontal selector: one line, scrollable, bleeding
     to the screen edge so the trailing steps invite a swipe. The wrap is the
     scroller, so the progress track underneath travels with the labels. */
  .rail-steps-wrap { overflow-x: auto; scrollbar-width: none; margin-inline: -22px; padding-inline: 22px; }
  .rail-steps-wrap::-webkit-scrollbar { display: none; }
  .rail-steps { gap: 24px; }
  .rail-step { font-size: 12px; white-space: nowrap; }
  .rail-chapter { margin-top: 16px; }
  .rail-chapter-slide { gap: 8px; }
  .rail-chapter-title { font-size: clamp(22px, 6.4vw, 28px); }
  .rail-chapter-sub { font-size: 16px; }
  /* Cards are narrower than the 384px scene canvas on phones — shrink the
     whole composition uniformly instead of clipping its edges. */
  .vg-scene { transform: translateX(-50%) scale(0.82); transform-origin: 50% 50%; }
  /* Compact FAQ + final CTA on phones: tighter bands, single-column form,
     full-width button. Labels stay visible; inputs keep their 16px font. */
  .section.faq-section { padding: 60px 0 48px; }
  .faq-title { font-size: clamp(21px, 6vw, 25px); }
  .faq summary { font-size: 16px; padding: 16px 2px; }
  .section.cta-final { padding: 60px 0 64px; }
  .cta-headline { font-size: clamp(26px, 7.4vw, 34px); }
  .cta-lede { font-size: 16.5px; }
  .offer-card { padding: 18px 18px; }
  .cta-form-card { padding: 20px 18px; }
}
