/* Self-hosted Inter (variable woff2). Mirror of styles.css — keep in sync. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/InterVariable.woff2") format("woff2");
}
/* Self-hosted Space Grotesk (variable woff2, latin subset) — the brand DISPLAY
   face, used for the wordmark, headings, nav, buttons and eyebrows only (see the
   --font-display rule below); body copy stays on Inter. Self-hosting is what makes
   it render on phones: a locally-installed copy only covers desktops that happen
   to have it, so without this @font-face mobile silently falls back to Inter. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/fonts/SpaceGrotesk-Variable.woff2") format("woff2");
}

:root {
  --bg-deep: #05060f;
  --bg-mid: #0b1026;
  --bg-glow: #1a1147;
  --accent: #7c5cff;
  --accent-2: #21d4fd;
  --star: #ffffff;
  --text: #e8ecff;
  --text-dim: #9aa3cc;
  --card: rgba(255, 255, 255, 0.04);
  --card-border: rgba(124, 92, 255, 0.22);
  /* Body/UI text — keep in sync with styles.css --font-sans. Inter for reading
     comfort and dense UI. */
  --font-sans: "Inter", "Segoe UI", "Helvetica Neue", system-ui, -apple-system, Roboto, sans-serif;
  /* Display face — Space Grotesk for brand + headings, falling back to the body
     stack if it hasn't loaded yet. Applied selectively below, not site-wide. */
  --font-display: "Space Grotesk", var(--font-sans);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

/* Skip-to-content link: visually hidden until focused, so keyboard users can
   jump past the header/nav. Sits above the sticky header (z-index 50). */
.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -3.5rem;
  z-index: 100;
  padding: 0.6rem 1rem;
  border-radius: 0 0 8px 8px;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: 0;
  outline: 2px solid #fff;
  outline-offset: 2px;
}
/* Global reduced-motion reset (mirrors styles.css): near-instant
   animations/transitions, no infinite loops, no smooth scroll. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .skip-link { transition: none; }
}

body {
  font-family: var(--font-sans);
  color: var(--text);
  background:
    radial-gradient(ellipse at 20% 0%, var(--bg-glow) 0%, transparent 45%),
    radial-gradient(ellipse at 85% 30%, #14213d 0%, transparent 50%),
    linear-gradient(180deg, var(--bg-mid) 0%, var(--bg-deep) 100%);
  /* Solid fallback under the gradient so contrast checkers measure against a real
     dark background (not assumed-white), which otherwise false-flags dim text. */
  background-color: var(--bg-deep);
  background-attachment: fixed;
  min-height: 100vh;
  position: relative;
  overflow-x: hidden;
  line-height: 1.6;
}

/* Display face (Space Grotesk) for the brand wordmark, headings, eyebrows,
   buttons and primary nav — the type that carries personality. Everything else
   (body copy, captions, dense UI) stays on Inter via the body rule above. These
   classes live across landing.css + site.css, but both stylesheets load together
   so a single rule here covers the whole marketing site. */
h1, h2, h3, h4,
.brand-name,
.hero__title, .page-hero__title, .section__title,
.eyebrow,
.btn,
.site-nav__links a, .nav-login {
  font-family: var(--font-display);
}

/* ---------- Starfield (fixed backdrop) ---------- */
.starfield { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.stars { position: absolute; inset: 0; background-repeat: repeat; }
.stars--small {
  background-image:
    radial-gradient(1px 1px at 20px 30px, var(--star), transparent),
    radial-gradient(1px 1px at 90px 60px, var(--star), transparent),
    radial-gradient(1px 1px at 160px 120px, #cdd6ff, transparent),
    radial-gradient(1px 1px at 230px 200px, var(--star), transparent),
    radial-gradient(1px 1px at 300px 80px, var(--star), transparent);
  background-size: 350px 350px; animation: drift 140s linear infinite; opacity: 0.6;
}
.stars--medium {
  background-image:
    radial-gradient(1.5px 1.5px at 50px 160px, #fff, transparent),
    radial-gradient(1.5px 1.5px at 180px 40px, #b9c4ff, transparent),
    radial-gradient(2px 2px at 120px 300px, #fff, transparent);
  background-size: 400px 400px; animation: drift 100s linear infinite, twinkle 5s ease-in-out infinite; opacity: 0.7;
}
.stars--large {
  background-image:
    radial-gradient(2px 2px at 100px 100px, #fff, transparent),
    radial-gradient(2.5px 2.5px at 320px 180px, var(--accent-2), transparent);
  background-size: 500px 500px; animation: drift 70s linear infinite, twinkle 3.5s ease-in-out infinite; opacity: 0.55;
}
@keyframes drift { from { transform: translateY(0); } to { transform: translateY(-350px); } }
@keyframes twinkle { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }

/* ---------- Layout primitives ---------- */
main, .nav, .site-footer { position: relative; z-index: 2; }

.btn {
  display: inline-block;
  padding: 0.8rem 1.6rem;
  border-radius: 999px;
  font-size: 0.98rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.2s, border-color 0.2s;
  border: 1px solid transparent;
}
.btn--primary {
  color: #fff;
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  box-shadow: 0 6px 20px rgba(124, 92, 255, 0.4);
}
.btn--primary:hover { transform: translateY(-2px); box-shadow: 0 10px 28px rgba(124, 92, 255, 0.55); }
.btn--ghost { color: var(--text); border-color: var(--card-border); background: rgba(255, 255, 255, 0.03); }
.btn--ghost:hover { border-color: var(--accent-2); }
.btn--sm { padding: 0.55rem 1.1rem; font-size: 0.9rem; }
.btn--lg { padding: 1rem 2.2rem; font-size: 1.1rem; }

/* ---------- Keyboard focus (WCAG 2.4.7) ----------
   The marketing pages previously exposed a visible focus ring only on the
   skip-link and the contact-form inputs. Give every interactive control a
   consistent, high-contrast focus-visible outline. Scoped to :focus-visible so
   pointer clicks stay ring-free; the contact inputs keep their own box-shadow
   focus (more specific rule in site.css) and are intentionally not overridden. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
.btn:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 3px;
}

/* Base link colour for the marketing pages. Without it, any link not caught by a
   component/context rule (nav, footer, .legal, .post, buttons — which all set
   their own colour and override this) falls back to the browser default
   blue/purple, unreadable on the dark background. `--accent-2` (#21d4fd) is the
   same cyan the legal/blog body links already use, so stray links stay on-brand;
   `:visited` is pinned to it so a clicked link doesn't drop to default purple. */
a {
  color: var(--accent-2);
}
a:visited {
  color: var(--accent-2);
}

/* ---------- Nav ---------- */
.nav {
  display: flex; align-items: center; justify-content: space-between;
  max-width: 1100px; margin: 0 auto; padding: 1.4rem 1.5rem;
}
.nav__brand { display: inline-flex; align-items: center; gap: 0.55rem; font-size: 1.3rem; font-weight: 800; color: #fff; text-decoration: none; letter-spacing: -0.02em; }
.brand-name {
  background: linear-gradient(95deg, #e8ecff 42%, #21d4fd 82%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* Solid-color fallback where background-clip:text is unsupported (else invisible). */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .brand-name { color: #fff; -webkit-text-fill-color: #fff; }
}
.nav__brand__mark { width: 1.5rem; height: 1.5rem; flex: none; }
.nav__links { display: flex; align-items: center; gap: 1.4rem; }
.nav__links a { color: var(--text-dim); text-decoration: none; font-size: 0.95rem; transition: color 0.2s; }
.nav__links a:hover { color: var(--text); }
.nav__login { font-weight: 600; }

/* ---------- Hero ---------- */
/* Transform-only entrance (no opacity fade): the hero holds the LCP text, and an
   opacity:0 start would defer Largest Contentful Paint until the animation runs.
   A translate doesn't gate LCP or cause layout shift, so the copy paints at once. */
.hero { max-width: 760px; margin: 0 auto; padding: 5rem 1.5rem 4rem; text-align: center; animation: hero-rise 0.8s ease both; }
.eyebrow { font-size: 0.78rem; letter-spacing: 0.32em; text-transform: uppercase; color: var(--accent-2); opacity: 0.85; margin-bottom: 1.2rem; }
.hero__title {
  font-size: clamp(2.6rem, 7vw, 4.6rem); font-weight: 800; letter-spacing: -0.02em; line-height: 1.04;
  background: linear-gradient(120deg, #fff 10%, var(--accent-2) 55%, var(--accent) 95%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 40px rgba(124, 92, 255, 0.25); margin-bottom: 1.4rem;
}
/* Solid-color fallback where background-clip:text is unsupported (else invisible). */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero__title { color: #fff; -webkit-text-fill-color: #fff; }
}
.hero__sub { font-size: clamp(1.05rem, 2.5vw, 1.3rem); color: var(--text-dim); max-width: 600px; margin: 0 auto 2.2rem; }
.cta-row { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; }
.cta-note { margin-top: 1rem; font-size: 0.85rem; color: var(--text-dim); }
/* Accent inline link inside body copy (centralizes the repeated
   color:var(--accent-2);text-decoration:none inline style). Underlines on
   hover/focus so the link keeps an affordance once color isn't enough. */
.inline-link { color: var(--accent-2); text-decoration: none; }
.inline-link:hover, .inline-link:focus-visible { text-decoration: underline; }
@keyframes fade-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes hero-rise { from { transform: translateY(16px); } to { transform: translateY(0); } }

/* ---------- Sections ---------- */
.section { max-width: 1000px; margin: 0 auto; padding: 4.5rem 1.5rem; }
.section--alt { max-width: 100%; background: rgba(124, 92, 255, 0.04); border-top: 1px solid var(--card-border); border-bottom: 1px solid var(--card-border); }
.section--alt > * { max-width: 1000px; margin-left: auto; margin-right: auto; }
.section__title { text-align: center; font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 800; letter-spacing: -0.01em; margin-bottom: 2.8rem; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.4rem; }
.card { background: var(--card); border: 1px solid var(--card-border); border-radius: 18px; padding: 2rem 1.6rem; transition: transform 0.2s, border-color 0.2s; }
.card:hover { transform: translateY(-4px); border-color: var(--accent-2); }
.card__num { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; color: var(--accent-2); }
.card h3 { font-size: 1.3rem; margin: 0.6rem 0 0.7rem; }
.card p { color: var(--text-dim); font-size: 0.98rem; }

.features { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.6rem 2.2rem; }
.feature h3 { font-size: 1.15rem; margin-bottom: 0.5rem; color: #fff; }
.feature h3::before { content: "→ "; color: var(--accent-2); }
.feature p { color: var(--text-dim); font-size: 0.96rem; }

/* ---------- Final CTA ---------- */
.final-cta { text-align: center; padding: 5rem 1.5rem; max-width: 700px; margin: 0 auto; }

/* ---------- Cursor glow (A + C) ---------- */
/* Enabled by site.js (it adds .cursor-fx to <html>) only when a fine pointer is
   present and the visitor hasn't asked for reduced motion. A · a soft brand
   aura trails the pointer (sits above the starfield at z-index 0, below the
   z-index 2 content). C · cards & buttons light toward the cursor on hover. */
.cursor-aura {
  position: fixed; top: 0; left: 0; width: 560px; height: 560px; border-radius: 50%;
  pointer-events: none; z-index: 1; opacity: 0; transition: opacity 0.35s ease;
  mix-blend-mode: screen; will-change: transform, opacity;
  background: radial-gradient(circle closest-side,
    rgba(124, 92, 255, 0.2) 0%, rgba(33, 212, 253, 0.09) 38%, rgba(33, 212, 253, 0) 72%);
}
html.cursor-fx .cursor-aura.is-live { opacity: 1; }

html.cursor-fx .card, html.cursor-fx .btn { position: relative; isolation: isolate; }
html.cursor-fx .card::before, html.cursor-fx .btn::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  z-index: -1; opacity: 0; transition: opacity 0.25s;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
    rgba(33, 212, 253, 0.18), rgba(124, 92, 255, 0.09) 40%, transparent 60%);
}
html.cursor-fx .btn::before {
  background: radial-gradient(120px circle at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, 0.25), transparent 55%);
}
html.cursor-fx .card:hover::before, html.cursor-fx .btn:hover::before { opacity: 1; }

@media (prefers-reduced-motion: reduce) { .cursor-aura { display: none; } }
.final-cta h2 { font-size: clamp(1.8rem, 5vw, 3rem); font-weight: 800; letter-spacing: -0.01em; margin-bottom: 0.9rem; }
.final-cta p { color: var(--text-dim); margin-bottom: 2rem; font-size: 1.1rem; }

/* ---------- Footer ---------- */
.site-footer { text-align: center; padding: 2.5rem 1.5rem; border-top: 1px solid var(--card-border); color: var(--text-dim); font-size: 0.85rem; }
.site-footer__links { margin-top: 0.5rem; }
.site-footer__links a { color: var(--text-dim); text-decoration: none; }
.site-footer__links a:hover { color: var(--accent-2); }

@media (max-width: 560px) {
  .nav { flex-direction: column; gap: 0.9rem; }
  .nav__links { gap: 1rem; flex-wrap: wrap; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; scroll-behavior: auto; }
}

/* ---------- Icon system ----------
   Gradient line-art icons live in the shared sprite /icons.svg, injected once
   per page by site.js (see the loader there). Use them as:
     <svg class="icon"><use href="#i-search"/></svg>
   The purple->cyan stroke gradient is baked into each sprite symbol, matching
   the four feature icons already inline in features.html. Add a new glyph by
   adding a <symbol> to icons.svg — no CSS change needed. */
.icon {
  width: 1.5rem; height: 1.5rem; flex: none;
  filter: drop-shadow(0 4px 14px rgba(124, 92, 255, 0.4));
}
/* Icon tile — a rounded, gradient-lit square that holds an .icon, mirroring the
   feature-row__visual panel on features.html but sized for a card header. */
.icon-tile {
  display: inline-grid; place-items: center;
  width: 3rem; height: 3rem;
  margin-bottom: 1rem;
  border-radius: 14px;
  background:
    radial-gradient(ellipse at 50% 25%, rgba(124, 92, 255, 0.3), transparent 70%),
    rgba(255, 255, 255, 0.02);
  border: 1px solid var(--card-border);
}
.icon-tile .icon { width: 1.7rem; height: 1.7rem; }
/* A mascot mark in an icon tile (e.g. Proto on the Find & score card). */
.icon-tile img { width: 2.2rem; height: 2.2rem; }
/* A card/feature with an icon tile drops the decorative "→" text prefix so the
   glyph carries the accent instead. */
.feature--icon h3::before { content: none; }
/* Inline icon anchoring a centered section title. */
.section__title .icon {
  width: 1.4rem; height: 1.4rem;
  vertical-align: -0.22em; margin-right: 0.55rem;
}
/* Small inline icon inside a pricing plan name. */
.plan__name .icon {
  width: 1.1rem; height: 1.1rem;
  vertical-align: -0.16em; margin-right: 0.4rem;
}

/* ---------- Hero film (home) ----------
   A looping inline-SVG short starring the whole crew: Proto sweeps in and spots
   a cold lead on Earth (scan pings), the demo card builds, then Astro escorts
   the lead up the trail to Stella (the destination star) as it warms — find ->
   build the proof -> warm up -> client won, the product's frozen-to-won arc as
   a 26s two-act allegory. Act 2: while Astro delivers lead 1, Proto is already
   hovering over a second cold lead further along Earth's rim; Astro swings back
   down and escorts that one too, while Proto exits the way he came — off to
   scan the next market — re-entering fresh on every loop.
   All motion is CSS keyframes on SVG elements (so it scales with the
   viewBox and honours reduced motion, which freezes it on the final "pipeline,
   on repeat" family portrait). Reuses proto.svg/astro.svg/stella.svg and the
   brand gradients. Decorative (aria-hidden bar the SVG's own label). */
.hero-film {
  width: min(600px, 96%);
  margin: 3rem auto 0;
  animation: fade-up 1.2s ease 0.15s both;
}
.hero-film svg {
  width: 100%; height: auto; display: block;
  filter: drop-shadow(0 14px 40px rgba(124, 92, 255, 0.28));
}
.hero-film text { font-family: var(--font-display); }
.hero-film .cap {
  fill: var(--text); font-size: 19px; font-weight: 600;
  text-anchor: middle; opacity: 0;
}
.hero-film .film-proto { animation: film-proto 26s ease-in-out infinite; }
.hero-film .film-astro { animation: film-astro 26s ease-in-out infinite; }
.hero-film .film-lead { animation: film-lead 26s ease-in-out infinite; }
.hero-film .film-lead2 { animation: film-lead2 26s ease-in-out infinite; }
.hero-film .film-lead .orb,
.hero-film .film-lead2 .orb {
  filter: drop-shadow(0 0 6px rgba(124, 92, 255, 0.8));
}
.hero-film .film-lead .orb { animation: film-warm 26s linear infinite; }
.hero-film .film-lead2 .orb { animation: film-warm2 26s linear infinite; }
.hero-film .film-trail {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: film-draw 26s ease-in-out infinite;
}
.hero-film .film-trail2 {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: film-draw2 26s ease-in-out infinite;
}
.hero-film .film-card,
.hero-film .film-card2,
.hero-film .film-star,
.hero-film .film-flare,
.hero-film .ping { transform-box: fill-box; transform-origin: center; }
.hero-film .film-card { animation: film-card 26s ease-in-out infinite; }
.hero-film .film-card2 { animation: film-card2 26s ease-in-out infinite; }
.hero-film .film-star { animation: film-star 26s ease-in-out infinite; }
.hero-film .film-stella {
  transform-box: fill-box; transform-origin: center;
  animation: bob 4.5s ease-in-out infinite;
}
.hero-film .film-flare { animation: film-flare 26s ease-out infinite; opacity: 0; }
.hero-film .ping { opacity: 0; }
/* Ping delays are positive offsets into the 26s loop: p0a/p0b are Proto's scan
   pulses over lead 1, p1-p3 track escort 1, p0c/p0d are the scan over lead 2,
   p4-p6 track escort 2. */
.hero-film .ping.p0a { animation: film-ping 26s ease-out infinite; animation-delay: 2.2s; }
.hero-film .ping.p0b { animation: film-ping 26s ease-out infinite; animation-delay: 3s; }
.hero-film .ping.p1 { animation: film-ping 26s ease-out infinite; animation-delay: 8.1s; }
.hero-film .ping.p2 { animation: film-ping 26s ease-out infinite; animation-delay: 9s; }
.hero-film .ping.p3 { animation: film-ping 26s ease-out infinite; animation-delay: 9.9s; }
.hero-film .ping.p0c { animation: film-ping 26s ease-out infinite; animation-delay: 10.3s; }
.hero-film .ping.p0d { animation: film-ping 26s ease-out infinite; animation-delay: 11.1s; }
.hero-film .ping.p4 { animation: film-ping 26s ease-out infinite; animation-delay: 16.5s; }
.hero-film .ping.p5 { animation: film-ping 26s ease-out infinite; animation-delay: 17.6s; }
.hero-film .ping.p6 { animation: film-ping 26s ease-out infinite; animation-delay: 18.7s; }
/* Captions play through ONCE (first loop), then stay hidden while the scene
   keeps looping — the story reads on the first pass, then it's ambient. */
.hero-film .cap1 { animation: film-cap1 26s linear 1 both; }
.hero-film .cap2 { animation: film-cap2 26s linear 1 both; }
.hero-film .cap3 { animation: film-cap3 26s linear 1 both; }
.hero-film .cap4 { animation: film-cap4 26s linear 1 both; }
.hero-film .cap5 { animation: film-cap5 26s linear 1 both; }
.hero-film .cap6 { animation: film-cap6 26s linear 1 both; }

/* Proto: sweeps in from deep space, hovers over lead 1 while scanning + the
   demo card builds, hands off to Astro, glides on to lead 2 and scans that one
   too, then exits the way he came once Astro returns — re-entering fresh on
   every loop. */
@keyframes film-proto {
  0% { transform: translate(-50px, 110px); opacity: 0; }
  3% { opacity: 1; }
  7%, 30% { transform: translate(118px, 164px); opacity: 1; }
  38%, 58% { transform: translate(183px, 205px); opacity: 1; }
  66% { transform: translate(-50px, 128px); opacity: 1; }
  67%, 100% { transform: translate(-50px, 128px); opacity: 0; }
}
/* Astro: rises from behind Earth at the handoff, escorts lead 1 to the star,
   pauses, then swings back down for lead 2 and escorts that one too. */
@keyframes film-astro {
  0%, 18% { transform: translate(34px, 308px); opacity: 0; }
  23% { opacity: 1; }
  26%, 28% { transform: translate(120px, 205px); opacity: 1; }
  31% { transform: translate(192px, 169px); }
  34% { transform: translate(264px, 133px); }
  38% { transform: translate(336px, 104px); }
  41% { transform: translate(408px, 90px); }
  43%, 50% { transform: translate(414px, 102px); }
  54% { transform: translate(300px, 190px); }
  58%, 60% { transform: translate(185px, 246px); }
  64% { transform: translate(257px, 211px); }
  68% { transform: translate(331px, 175px); }
  72% { transform: translate(405px, 137px); }
  75% { transform: translate(450px, 110px); }
  78%, 100% { transform: translate(414px, 102px); }
}
/* Lead 1 appears (dim + cold) as Proto's scan finds it, waits for the escort,
   then rides trail 1. */
@keyframes film-lead {
  0%, 7% { transform: translate(120px, 205px); opacity: 0; }
  10%, 28% { transform: translate(120px, 205px); opacity: 1; }
  31% { transform: translate(192px, 169px); }
  34% { transform: translate(264px, 133px); }
  38% { transform: translate(336px, 104px); }
  41% { transform: translate(408px, 90px); }
  43% { transform: translate(480px, 92px); opacity: 1; }
  46%, 100% { transform: translate(480px, 92px); opacity: 0; }
}
/* Lead 2 appears under Proto's second scan while escort 1 is mid-flight, waits
   for Astro to swing back, then rides trail 2. */
@keyframes film-lead2 {
  0%, 41% { transform: translate(185px, 246px); opacity: 0; }
  44%, 60% { transform: translate(185px, 246px); opacity: 1; }
  64% { transform: translate(257px, 211px); }
  68% { transform: translate(331px, 175px); }
  72% { transform: translate(405px, 137px); }
  75% { transform: translate(480px, 92px); opacity: 1; }
  79%, 100% { transform: translate(480px, 92px); opacity: 0; }
}
@keyframes film-warm {
  0%, 28% { fill: #3aa0ff; }
  33% { fill: #7c5cff; }
  38% { fill: #ffb020; }
  41%, 100% { fill: #ff4d6d; }
}
@keyframes film-warm2 {
  0%, 60% { fill: #3aa0ff; }
  65% { fill: #7c5cff; }
  70% { fill: #ffb020; }
  74%, 100% { fill: #ff4d6d; }
}
@keyframes film-draw { 0%, 26% { stroke-dashoffset: 100; } 42%, 100% { stroke-dashoffset: 0; } }
@keyframes film-draw2 { 0%, 58% { stroke-dashoffset: 100; } 75%, 100% { stroke-dashoffset: 0; } }
@keyframes film-card {
  0%, 15% { opacity: 0; transform: scale(0.3); }
  20%, 27% { opacity: 1; transform: scale(1); }
  32%, 100% { opacity: 0; transform: scale(0.8); }
}
/* Lead 2's demo builds while Astro is still up delivering lead 1. */
@keyframes film-card2 {
  0%, 45% { opacity: 0; transform: scale(0.3); }
  49%, 55% { opacity: 1; transform: scale(1); }
  60%, 100% { opacity: 0; transform: scale(0.8); }
}
/* Star pops at each arrival (~43% and ~77%) and ends a notch brighter after
   the second win. */
@keyframes film-star {
  0%, 38% { opacity: 0.45; transform: scale(1); }
  43% { opacity: 1; transform: scale(1.28); }
  47%, 72% { opacity: 0.9; transform: scale(1.1); }
  77% { opacity: 1; transform: scale(1.34); }
  81%, 100% { opacity: 1; transform: scale(1.16); }
}
@keyframes film-flare {
  0%, 39% { opacity: 0; transform: scale(0.4); }
  44% { opacity: 0.9; transform: scale(1); }
  50%, 73% { opacity: 0; transform: scale(1.6); }
  74% { opacity: 0; transform: scale(0.4); }
  78% { opacity: 0.9; transform: scale(1); }
  85%, 100% { opacity: 0; transform: scale(1.6); }
}
@keyframes film-ping {
  0% { opacity: 0.9; transform: scale(0.4); }
  5% { opacity: 0; transform: scale(2.4); }
  100% { opacity: 0; }
}
@keyframes film-cap1 { 0%, 3% { opacity: 0; } 6%, 13% { opacity: 1; } 16%, 100% { opacity: 0; } }
@keyframes film-cap2 { 0%, 16% { opacity: 0; } 19%, 26% { opacity: 1; } 29%, 100% { opacity: 0; } }
@keyframes film-cap3 { 0%, 29% { opacity: 0; } 32%, 39% { opacity: 1; } 42%, 100% { opacity: 0; } }
@keyframes film-cap4 { 0%, 42% { opacity: 0; } 45%, 49% { opacity: 1; } 52%, 100% { opacity: 0; } }
@keyframes film-cap5 { 0%, 52% { opacity: 0; } 55%, 62% { opacity: 1; } 65%, 100% { opacity: 0; } }
/* Closing caption holds through the second win + pause on the first loop. */
@keyframes film-cap6 { 0%, 76% { opacity: 0; } 80%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* Reduced motion: freeze on the composed "pipeline, on repeat" family portrait
   — the full story at a glance, no movement: Proto hovering over lead pin 2
   (his scanning post), both trails lit, Astro + Stella at the bright star.
   (The global reduced-motion reset stops the keyframes; these rules place the
   static end state.) */
@media (prefers-reduced-motion: reduce) {
  .hero-film .film-proto { transform: translate(183px, 205px); }
  .hero-film .film-astro { transform: translate(414px, 102px); }
  .hero-film .film-lead,
  .hero-film .film-lead2 { opacity: 0; }
  .hero-film .film-trail,
  .hero-film .film-trail2 { stroke-dashoffset: 0; }
  .hero-film .film-star { opacity: 1; }
  .hero-film .film-card,
  .hero-film .film-card2,
  .hero-film .film-flare,
  .hero-film .ping { opacity: 0; }
  .hero-film .cap6 { opacity: 1; }
}

/* ---------- Constellation divider (decorative section break) ----------
   A small connect-the-stars motif that breaks up long text runs, tying the
   sections back to the starfield backdrop. */
.constellation {
  display: block; width: min(300px, 66%); height: 30px;
  margin: 0.5rem auto; opacity: 0.85;
}
.constellation .c-star { animation: twinkle 4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.constellation .c-star:nth-child(2) { animation-delay: 1.2s; }
.constellation .c-star:nth-child(3) { animation-delay: 2.1s; }
.constellation .c-star:nth-child(4) { animation-delay: 0.6s; }

/* ---------- Stella mascot accent ----------
   The support-chat mascot doubling as a friendly brand accent above a CTA. */
.cta-stella {
  width: 66px; height: 66px; display: block; margin: 0 auto 1.1rem;
  filter: drop-shadow(0 8px 22px rgba(124, 92, 255, 0.45));
  animation: bob 4.5s ease-in-out infinite;
}
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }

/* ---------- Astro mascot ----------
   The AI mission-specialist persona introduced on the AI Assistant page. Sits in the page
   hero; the gentle bob is a CSS animation, so it honours reduced motion. */
.hero-astro {
  width: 96px; height: 96px; display: block; margin: 0 auto 1.5rem;
  filter: drop-shadow(0 10px 28px rgba(124, 92, 255, 0.5));
  animation: bob 4.5s ease-in-out infinite;
}
/* Smaller Astro for a feature callout. */
.hero-astro--sm { width: 74px; height: 74px; margin-bottom: 1.1rem; }
/* Inline Astro mark for a heading (e.g. the pricing add-on card). */
.astro-inline {
  width: 1.7rem; height: 1.7rem;
  vertical-align: -0.42em; margin-right: 0.5rem;
  filter: drop-shadow(0 3px 10px rgba(124, 92, 255, 0.45));
}

/* ---------- "Meet Astro" strip ----------
   A compact top-of-funnel teaser for the AI Assistant add-on: mascot + one line +
   a link. Deliberately small so it doesn't crowd the core-loop story on home. */
.astro-strip {
  max-width: 760px; margin: 0 auto;
  display: flex; align-items: center; gap: 1.7rem;
  padding: 1.5rem 1.8rem; border-radius: 18px;
  background:
    radial-gradient(ellipse at 0% 0%, rgba(124, 92, 255, 0.14), transparent 60%),
    var(--card);
  border: 1px solid var(--card-border);
}
.astro-strip__mark {
  width: 76px; height: 76px; flex: none;
  filter: drop-shadow(0 8px 22px rgba(124, 92, 255, 0.5));
  animation: bob 4.5s ease-in-out infinite;
}
.astro-strip__eyebrow {
  font-size: 0.74rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: 0.35rem;
}
.astro-strip h3 { font-size: 1.2rem; margin-bottom: 0.35rem; color: #fff; }
.astro-strip p { color: var(--text-dim); font-size: 0.96rem; margin-bottom: 0.6rem; }
@media (max-width: 560px) {
  .astro-strip { flex-direction: column; text-align: center; }
}

/* ---------- Live activity strip (home) ----------
   Real, polled counts across the operator's own tenants. Hidden until
   /live-stats.js confirms non-zero data (see the [hidden] attribute in markup),
   so it never renders an empty room. */
.live-stats { text-align: center; }
.live-stats__eyebrow {
  font-family: var(--font-display);
  font-size: 0.74rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 1.8rem;
  display: inline-flex; align-items: center; gap: 0.5rem;
}
.live-stats__dot {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: #28c840; box-shadow: 0 0 0 0 rgba(40, 200, 64, 0.6);
  animation: live-pulse 2.4s ease-out infinite;
}
@keyframes live-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(40, 200, 64, 0.55); }
  70%  { box-shadow: 0 0 0 0.55rem rgba(40, 200, 64, 0); }
  100% { box-shadow: 0 0 0 0 rgba(40, 200, 64, 0); }
}
.live-stats__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem; max-width: 760px; margin: 0 auto;
}
.live-stats__tile {
  background: var(--card); border: 1px solid var(--card-border);
  border-radius: 18px; padding: 1.8rem 1.2rem;
}
.live-stats__num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 5.5vw, 3.1rem); font-weight: 800; line-height: 1;
  letter-spacing: -0.02em;
  background: linear-gradient(120deg, #fff 10%, var(--accent-2) 55%, var(--accent) 95%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .live-stats__num { color: #fff; -webkit-text-fill-color: #fff; }
}
.live-stats__label {
  display: block; margin-top: 0.7rem;
  color: var(--text-dim); font-size: 0.9rem;
}
.live-stats__since {
  margin-top: 1.5rem;
  color: var(--text-dim); font-size: 0.82rem; opacity: 0.85;
}
@media (max-width: 560px) {
  .live-stats__grid { grid-template-columns: 1fr; gap: 1rem; }
}
@media (prefers-reduced-motion: reduce) {
  .live-stats__dot { animation: none; }
}

/* ---------- Works-with integration row (home) ----------
   Every chip maps to a shipped provider. Grouped by category. */
.works-with__groups {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1.8rem 2.2rem; max-width: 900px; margin: 0 auto;
}
.works-with__cat {
  font-family: var(--font-display);
  font-size: 0.74rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: 0.9rem;
  display: flex; align-items: center; gap: 0.45rem;
}
.works-with__cat .icon { width: 1.05rem; height: 1.05rem; }
.works-with__chips {
  list-style: none; display: flex; flex-wrap: wrap; gap: 0.55rem;
}
.works-with__chips li {
  font-size: 0.92rem; color: var(--text);
  padding: 0.42rem 0.85rem; border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--card-border);
  transition: border-color 0.2s, transform 0.2s;
}
.works-with__chips li:hover {
  border-color: var(--accent-2); transform: translateY(-2px);
}

/* ---------- Product screenshots ----------
   Real UI captures presented in a browser chrome (.ui-shot) or floated as a
   single card (.app-card-shot). The screenshots are genuine app renders. */
.ui-shot {
  max-width: 920px; margin: 2.6rem auto 0;
  border-radius: 14px; overflow: hidden;
  border: 1px solid var(--card-border); background: #0b1026;
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(124, 92, 255, 0.12);
  position: relative;
}
.ui-shot__bar {
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.65rem 1rem; background: #0e1533;
  border-bottom: 1px solid var(--card-border);
}
.ui-shot__dots { display: inline-flex; gap: 0.42rem; flex: none; }
.ui-shot__dots i { width: 0.72rem; height: 0.72rem; border-radius: 50%; display: block; }
.ui-shot__addr {
  flex: 1; text-align: center; font-family: var(--font-sans);
  font-size: 0.8rem; color: var(--text-dim);
  background: #05060f; border: 1px solid var(--card-border);
  border-radius: 7px; padding: 0.3rem 0.8rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* <picture> is only a WebP-with-PNG-fallback wrapper — it must not introduce a
   box, so the inner <img> keeps behaving exactly as the direct-child img rules
   below (and .app-card-shot's margin:auto centering) expect. */
picture { display: contents; }
.ui-shot img { display: block; width: 100%; height: auto; }
/* Soft fade at the bottom so a screenshot cropped mid-scroll reads as intentional. */
.ui-shot--fade::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 90px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(11, 16, 38, 0), #0b1026);
}
/* A single lead card, floated (it's a component, not a full page). */
.app-card-shot {
  display: block; width: 100%; max-width: 420px; height: auto;
  margin: 2.2rem auto 0; border-radius: 16px;
  border: 1px solid var(--card-border);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(124, 92, 255, 0.12);
}
.app-card-figure { text-align: center; margin: 0; padding: 2.4rem 1.5rem 0; }
.app-card-figure figcaption { color: var(--text-dim); font-size: 0.92rem; margin-top: 1rem; }
/* A feature-row visual panel holding a real screenshot instead of an icon. */
.feature-row__visual--shot { overflow: hidden; padding: 0; }
.feature-row__visual--shot img {
  display: block; width: 100%; height: auto;
  border-radius: inherit;
}
/* Screenshot figure inside a comparison article (.post) — same chrome, tighter margins. */
.post .ui-shot { margin: 2rem auto; }
.post .ui-shot + p.shot-caption,
.shot-caption { text-align: center; color: var(--text-dim); font-size: 0.9rem; margin: 0.8rem 0 0; }

/* "Sign up with …" OAuth buttons on the signup page. */
.oauth-buttons {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  margin: 0 0 1.1rem;
}
.oauth-buttons .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  width: 100%;
}
.oauth-buttons svg { flex: none; }
.oauth-divider {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin: 0 0 1.1rem;
  font-size: .9rem;
  opacity: .75;
}
.oauth-divider::before,
.oauth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: .3;
}
/* display:flex above overrides the UA [hidden] rule — keep the buttons and
   divider actually hidden when signup.js switches to OAuth-ticket mode. */
.oauth-buttons[hidden],
.oauth-divider[hidden] { display: none; }
