:root {
  --bg: #03040a;
  --ink: #f3f5ff;
  --ink-soft: #b9c0dc;
  --blue: #4d68ff;
  --blue-soft: #8ea0ff;
  --glass: rgba(12, 15, 32, 0.62);
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  /* The stage keeps the reference's 1424 x 520 proportions and shrinks on
     short screens so the whole brain fits under the button. */
  --stage-w: clamp(min(880px, 100vw - 32px), (100svh - 510px) * 2.74, min(1400px, 100vw - 32px));
  color-scheme: dark;
}

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

html,
body {
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

img,
svg {
  display: block;
}

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

.skip {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 50;
  padding: 10px 16px;
  border-radius: 999px;
  background: #fff;
  color: #000;
  transform: translateY(-160%);
}

.skip:focus-visible {
  transform: none;
}

/* ---------- Intro choreography ----------
   Nothing is on screen at first. main.js adds .is-intro after a short empty
   beat and each piece blurs in on its own delay. */

[data-intro] {
  opacity: 0;
  filter: blur(12px);
  transform: translateY(14px);
  transition:
    opacity 1000ms ease,
    filter 1100ms ease,
    transform 1200ms var(--ease);
}

.is-intro [data-intro] {
  opacity: 1;
  filter: none;
  transform: none;
}

.is-intro [data-intro="nav"] { transition-delay: 150ms; }
.is-intro [data-intro="frame"] { transition-delay: 150ms; }
.is-intro [data-intro="l1"] { transition-delay: 300ms; }
.is-intro [data-intro="l2"] { transition-delay: 460ms; }
.is-intro [data-intro="lead"] { transition-delay: 760ms; }
.is-intro [data-intro="cta"] { transition-delay: 980ms; }

/* ---------- Nav ---------- */

.nav {
  position: absolute;
  inset: 0 0 auto;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 22px clamp(16px, 2vw, 28px);
}

.nav__brand {
  display: flex;
  align-items: center;
}

.nav__brand img {
  width: 58px;
  height: auto;
  filter: drop-shadow(0 0 10px rgba(110, 140, 255, 0.35));
}

.nav__pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 5px 5px 14px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

.nav__links {
  display: flex;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__links a {
  display: block;
  padding: 9px 14px;
  border-radius: 999px;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  transition: color 160ms ease, background-color 160ms ease;
}

.nav__links a:hover {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.05);
}

/* "Features" carries the same white-to-blue fade as the title. */
.nav__links a[aria-current] {
  background: linear-gradient(90deg, #fff 0 45%, #8c9dff);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.nav__cta {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  height: 44px;
  padding: 0 6px 0 18px;
  border-radius: 999px;
  background: linear-gradient(180deg, #2a40d8, #18259a);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 18px rgba(50, 80, 255, 0.35);
  font-size: 0.9375rem;
  font-weight: 500;
  transition: box-shadow 220ms ease;
}

.nav__cta:hover {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 0 26px rgba(70, 100, 255, 0.55);
}

.nav__cta-icon,
.btn-glow__icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

.nav__cta-icon svg,
.btn-glow__icon svg {
  transition: transform 260ms var(--ease);
}

.nav__cta:hover .nav__cta-icon svg,
.btn-glow:hover .btn-glow__icon svg {
  transform: translate(2px, -2px);
}

.nav__burger {
  display: none;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  cursor: pointer;
}

.nav__burger span,
.nav__burger span::before,
.nav__burger span::after {
  display: block;
  width: 16px;
  height: 1.5px;
  margin: auto;
  background: var(--ink);
  transition: transform 200ms ease, background-color 200ms ease;
}

.nav__burger span::before,
.nav__burger span::after {
  content: "";
  position: relative;
}

.nav__burger span::before { top: -5px; }
.nav__burger span::after { top: 3.5px; }

.nav--open .nav__burger span { background: transparent; }
.nav--open .nav__burger span::before { transform: translateY(5px) rotate(45deg); }
.nav--open .nav__burger span::after { transform: translateY(-5px) rotate(-45deg); }

/* ---------- Top frame line ---------- */

.frame {
  position: absolute;
  inset: 0 0 auto;
  z-index: 2;
  width: 100%;
  height: 200px;
  pointer-events: none;
  fill: none;
  stroke: url(#frame-fade);
  stroke-width: 1;
}

.frame path {
  vector-effect: non-scaling-stroke;
}

/* Head and trail share one timeline: the trail's offset stays 0.18 ahead,
   so its far end always sits on the head. The long gap keeps the dash from
   repeating, and most of each cycle the light is parked off the line. */
.frame__head {
  fill: none;
  stroke: #c7d2ff;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-dasharray: 0.022 3;
  stroke-dashoffset: 0.022;
  opacity: 0;
}

.is-intro .frame__head {
  opacity: 0.85;
  animation: frame-head 10s cubic-bezier(0.45, 0, 0.55, 1) 3.2s infinite;
}

.is-intro .frame__head--r { animation-delay: 8.2s; }

@keyframes frame-head {
  0% { stroke-dashoffset: 0.022; }
  34%, 100% { stroke-dashoffset: -1.02; }
}

/* The nav border catches the light when the right pulse reaches it. */
.nav__pill::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  box-shadow: 0 0 0 1px rgba(150, 170, 255, 0.32), 0 0 16px rgba(70, 100, 255, 0.2);
  opacity: 0;
  pointer-events: none;
}

.is-intro .nav__pill::before {
  animation: nav-catch 10s ease-out 8.2s infinite;
}

@keyframes nav-catch {
  0%, 30% { opacity: 0; }
  35% { opacity: 1; }
  52%, 100% { opacity: 0; }
}

/* ---------- Particles canvas ---------- */
/* Fixed to the screen, but inside .pin so it shares the hero's stacking
   context (see Scene below). */

.particles {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* No z-index here on purpose: main must not form a stacking context, so the
   glow and haze (z-index -1) sit behind the particle canvas while the text and
   pills stay in front of it. */
main {
  position: relative;
}

/* ---------- Scene: the pinned hero and its scroll runway ----------
   .pin sticks while the runway scrolls past. Sticky makes .pin a stacking
   context, which is why the canvas lives inside it: the light layers at
   z-index -1 stay behind the particles. main.js sets --pin-top when the hero
   is taller than the screen, so its bottom shows before it pins, and drives
   --m (morph progress, 0 to 1). One scroll triggers the whole morph, so the
   runway only needs to be long enough to scroll at all. */
.pin {
  position: sticky;
  top: var(--pin-top, 0px);
  --m: 0;
}

.scene__runway {
  height: 35svh;
}

/* No WebGL or no logo: nothing to morph, so no runway. */
.no-morph .scene__runway,
.no-morph .scroll-cue {
  display: none;
}

.scroll-cue {
  position: absolute;
  bottom: 18px;
  left: 50%;
  z-index: 3;
  width: 1px;
  height: 38px;
  overflow: hidden;
  background: linear-gradient(rgba(140, 160, 255, 0), rgba(140, 160, 255, 0.35));
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 1200ms ease;
  pointer-events: none;
}

.scroll-cue span {
  display: block;
  width: 1px;
  height: 12px;
  background: linear-gradient(rgba(220, 228, 255, 0), #dfe5ff);
  animation: cue 2.6s cubic-bezier(0.6, 0, 0.4, 1) infinite;
}

.is-wired .scroll-cue {
  opacity: calc(1 - var(--m) * 3);
  transition-delay: 2.4s;
}

@keyframes cue {
  0% { transform: translateY(-12px); }
  70%, 100% { transform: translateY(38px); }
}

/* ---------- Hero ---------- */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100svh;
  padding: clamp(112px, 15svh, 150px) 16px 24px;
  text-align: center;
  /* The orbit rings reach past the bottom edge; clip them without making
     the hero a scroll container. */
  overflow: clip;
}

.hero__title {
  margin: 0;
  filter: drop-shadow(0 0 24px rgba(110, 140, 255, 0.3));
  font-size: clamp(2.5rem, 1rem + 5.2vw, 5.25rem);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.018em;
}

.hero__line {
  display: block;
  padding-bottom: 0.06em;
  background: linear-gradient(90deg, #ffffff 0%, #e8ebff 34%, #a3b2ff 62%, #5d72ff 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__lead {
  max-width: 35rem;
  margin: 16px auto 0;
  color: var(--ink-soft);
  font-size: clamp(1rem, 0.94rem + 0.2vw, 1.0625rem);
  line-height: 1.3;
  text-wrap: balance;
}

.hero__lead strong {
  color: var(--ink);
  font-weight: 400;
}

/* ---------- Glow button ----------
   Solid electric blue so it never reads as one of the tool pills. The halo
   lives on ::before so it can breathe through opacity alone; a light sweep
   crosses the face now and then. */

.btn-glow {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  height: 54px;
  margin-top: 30px;
  padding: 0 7px 0 26px;
  border: 0;
  border-radius: 999px;
  background:
    linear-gradient(105deg, transparent 42%, rgba(255, 255, 255, 0.34) 50%, transparent 58%) no-repeat,
    linear-gradient(180deg, #5873ff 0%, #2f4bf2 52%, #1f35d4 100%);
  background-size: 260% 100%, 100% 100%;
  background-position: 160% 0, 0 0;
  box-shadow:
    inset 0 0 0 1px rgba(170, 186, 255, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.4),
    inset 0 -3px 8px rgba(8, 18, 110, 0.45);
  color: #fff;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-shadow: 0 1px 2px rgba(8, 18, 110, 0.45);
  cursor: pointer;
  isolation: isolate;
  transition: transform 260ms var(--ease), box-shadow 260ms ease;
}

.btn-glow::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow:
    0 0 16px 2px rgba(74, 104, 255, 0.8),
    0 0 48px 10px rgba(52, 82, 255, 0.42),
    0 16px 60px 8px rgba(40, 70, 255, 0.4);
  opacity: 0;
  transition: opacity 1600ms ease;
}

.is-intro .btn-glow {
  animation: btn-sweep 7s ease-in-out 3.6s infinite;
}

.is-intro .btn-glow::before {
  opacity: 0.85;
  transition-delay: 1500ms;
  animation: breathe 4.8s ease-in-out 3.2s infinite;
}

.btn-glow .btn-glow__icon {
  width: 40px;
  height: 40px;
  background: #fff;
  box-shadow: 0 2px 10px rgba(8, 18, 110, 0.4);
  color: #2340e8;
}

.btn-glow:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 0 0 1px rgba(200, 212, 255, 0.8),
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    inset 0 -3px 8px rgba(8, 18, 110, 0.35);
}

.btn-glow:hover::before {
  opacity: 1;
  animation-play-state: paused;
  transition-delay: 0s;
  transition-duration: 300ms;
}

.btn-glow:active {
  transform: translateY(0) scale(0.985);
}

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

@keyframes btn-sweep {
  0%, 72% { background-position: 160% 0, 0 0; }
  100% { background-position: -60% 0, 0 0; }
}

/* ---------- Stage: brain, wires and tools ----------
   Pills are placed in the reference's 1424 x 520 units (--y); main.js then
   draws each wire from its pill to its point on the brain. */

.stage {
  position: relative;
  width: var(--stage-w);
  aspect-ratio: 1424 / 520;
  margin-top: 22px;
}

/* Soft bloom between the button and the brain. Many stops instead of a
   blur filter, so the falloff stays smooth and cheap. */
.hero__glow {
  position: absolute;
  top: 22%;
  left: 50%;
  z-index: -1;
  width: 84%;
  height: 130%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(40, 72, 255, 0.4),
    rgba(40, 72, 255, 0.3) 22%,
    rgba(38, 68, 255, 0.17) 44%,
    rgba(36, 64, 255, 0.07) 66%,
    rgba(36, 64, 255, 0.02) 84%,
    transparent);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.8, 0.5);
  transition: opacity 2400ms ease 1600ms, transform 2800ms var(--ease) 1600ms;
}

.is-intro .hero__glow {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1, 0.62);
}

/* ---------- Atmosphere ----------
   A stage light just above the frame: a thin light bar on the top edge, one
   clean cone with a brighter core, and a soft lobe that drifts through the
   cone. The cones never move; they only breathe through opacity, which the
   compositor handles for free. Fog and a vignette frame the brain. */

.atmos {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 2600ms ease 700ms;
}

.is-intro .atmos {
  opacity: 1;
}

.atmos > * {
  position: absolute;
  left: 50%;
}

.atmos__source {
  top: -300px;
  width: min(1000px, 130vw);
  height: 560px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(110, 140, 255, 0.3),
    rgba(70, 100, 255, 0.15) 40%,
    rgba(50, 80, 255, 0.045) 72%,
    transparent);
  transform: translateX(-50%);
}

/* The light bar: a hairline of white-blue light with a flat glow under it,
   like an anamorphic streak from a source just off the top of the frame. */
.atmos__bar {
  top: 0;
  width: min(1180px, 92vw);
  height: 150px;
  background:
    linear-gradient(90deg, transparent, rgba(150, 170, 255, 0.4) 26%, rgba(236, 240, 255, 0.95) 50%, rgba(150, 170, 255, 0.4) 74%, transparent) 50% 0 / 100% 1.5px no-repeat,
    radial-gradient(50% 9% at 50% 0, rgba(200, 212, 255, 0.4), transparent),
    radial-gradient(48% 100% at 50% 0, rgba(110, 140, 255, 0.2), rgba(80, 110, 255, 0.06) 45%, transparent 80%);
  transform: translateX(-50%);
}

/* A cone from the top centre: conic stops give straight, clean edges that
   fade softly; the mask fades it out on the way down. */
/* The apex sits 240px above the page, so the cone enters the frame already
   wide instead of narrowing to a visible line at the top. */
.atmos__beam {
  top: -240px;
  width: min(1700px, 170vw);
  height: calc(116% + 240px);
  background: conic-gradient(from 154deg at 50% 0,
    transparent 0deg,
    rgba(70, 100, 255, 0.05) 9deg,
    rgba(84, 114, 255, 0.1) 26deg,
    rgba(70, 100, 255, 0.05) 43deg,
    transparent 52deg);
  -webkit-mask-image: radial-gradient(58% 96% at 50% 0, #000 26%, rgba(0, 0, 0, 0.5) 56%, transparent 90%);
  mask-image: radial-gradient(58% 96% at 50% 0, #000 26%, rgba(0, 0, 0, 0.5) 56%, transparent 90%);
  transform: translateX(-50%);
  animation: beam-breathe 9s ease-in-out infinite alternate;
}

.atmos__beam--core {
  background: conic-gradient(from 169deg at 50% 0,
    transparent 0deg,
    rgba(120, 145, 255, 0.06) 6deg,
    rgba(150, 170, 255, 0.11) 11deg,
    rgba(120, 145, 255, 0.06) 16deg,
    transparent 22deg);
  animation-duration: 13s;
  animation-direction: alternate-reverse;
}

/* A soft lobe of light that drifts slowly across the cone. */
.atmos__sweep {
  top: 6%;
  width: min(560px, 60vw);
  height: 62%;
  margin-left: calc(min(560px, 60vw) / -2);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(90, 120, 255, 0.075), rgba(90, 120, 255, 0.025) 60%, transparent);
  animation: sweep 21s ease-in-out infinite alternate;
}

.atmos__fog {
  top: 30%;
  width: min(1500px, 150vw);
  height: 70%;
  margin-left: calc(min(1500px, 150vw) / -2);
  background:
    radial-gradient(38% 46% at 30% 55%, rgba(40, 70, 255, 0.1), transparent 70%),
    radial-gradient(34% 40% at 72% 48%, rgba(60, 90, 255, 0.09), transparent 70%),
    radial-gradient(50% 50% at 50% 70%, rgba(30, 55, 220, 0.11), transparent 75%);
  animation: fog-drift 26s ease-in-out infinite alternate;
}

.atmos__vignette {
  inset: 0;
  left: 0;
  background:
    radial-gradient(120% 90% at 50% 36%, transparent 48%, rgba(1, 2, 6, 0.82) 100%),
    linear-gradient(transparent 82%, rgba(1, 2, 6, 0.45));
}

@keyframes beam-breathe {
  from { opacity: 0.7; }
  to { opacity: 1; }
}

@keyframes sweep {
  from { transform: translateX(-34%); opacity: 0.6; }
  50% { opacity: 1; }
  to { transform: translateX(34%); opacity: 0.6; }
}

@keyframes fog-drift {
  from { transform: translate(-4%, 0) scale(1); }
  to { transform: translate(4%, -3%) scale(1.06); }
}

/* Grain sits above the bloom and the light but behind the particles. */
.hero__grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .55 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
  opacity: 0.07;
  animation: grain 0.9s steps(5) infinite;
}

@keyframes grain {
  0% { background-position: 0 0; }
  20% { background-position: -17px 9px; }
  40% { background-position: 12px -21px; }
  60% { background-position: -8px 18px; }
  80% { background-position: 21px 6px; }
  100% { background-position: 0 0; }
}

.stage__wires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* Fibres run from the tile to the brain, so starting the dash at -1 and
   easing to 0 grows each one out of the brain towards its tile. The stroke
   is a gradient set by main.js: faint at the tile, bright at the brain. */
.wire {
  fill: none;
  stroke: rgba(196, 206, 255, 0.4);
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-dasharray: 1 1;
  stroke-dashoffset: -1;
  transition: stroke-dashoffset 1100ms var(--ease);
  transition-delay: calc(var(--i) * 140ms);
}

/* A soft blurred copy under each fibre, so it reads as light, not a line. */
.wires-glow {
  filter: url(#fibre-blur);
  opacity: 0.55;
}

.wire--glow {
  stroke-width: 3.2;
}

/* The finer strands grow from where they split, a beat after the main
   fibre; the filaments at the entries come last. */
.wire--fibre {
  stroke: rgba(200, 212, 255, 0.42);
  stroke-width: 0.75;
  stroke-dashoffset: 1;
  transition-delay: calc(var(--i) * 140ms + 300ms);
}

/* The filaments reach into the brain, so they fade as it becomes the logo. */
.wire--tip,
.node--tip {
  opacity: calc(1 - var(--m, 0));
}

.wire--tip {
  stroke: rgba(220, 228, 255, 0.55);
  stroke-width: 0.6;
  transition-duration: 600ms;
  transition-delay: calc(var(--i) * 140ms + 900ms);
}

.is-wired .wire {
  stroke-dashoffset: 0;
}

.pulse {
  fill: none;
  stroke: #d5ddff;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 0.03 1.5;
  stroke-dashoffset: 0.03;
  opacity: 0;
}

.is-pulsing .pulse {
  opacity: 1;
  animation: pulse-in 3.8s cubic-bezier(0.45, 0, 0.55, 1) var(--d) infinite;
}

.is-pulsing .pulse--out {
  animation-name: pulse-out;
}

/* "in" travels pill to brain, "out" brain to pill. */
@keyframes pulse-in {
  from { stroke-dashoffset: 0.03; }
  to { stroke-dashoffset: -1.03; }
}

@keyframes pulse-out {
  from { stroke-dashoffset: -1.03; }
  to { stroke-dashoffset: 0.03; }
}

/* Synapse dots: one where each fibre leaves its tile, one where it enters
   the brain and one at the tip of each branch. */
.nodes circle {
  fill: #e8edff;
  opacity: 0;
  transition: opacity 500ms ease;
  transition-delay: calc(var(--i) * 140ms + 500ms);
}

.nodes {
  filter: url(#pulse-glow);
}

.is-wired .nodes circle:not(.node--tip) {
  opacity: 1;
}

.is-wired .nodes .node--tip {
  opacity: calc(1 - var(--m, 0));
}

/* Box the particle brain fills (main.js reads it every frame). */
.stage__brain {
  position: absolute;
  top: 2%;
  left: 28.4%;
  width: 42%;
  aspect-ratio: 1.2;
  pointer-events: none;
}

/* Soft blue haze around the formed brain. */
.stage__haze {
  position: absolute;
  z-index: -1;
  inset: -8% -10%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(40, 70, 255, 0.3), rgba(40, 70, 255, 0.1) 60%, transparent);
  opacity: 0;
  transition: opacity 2000ms ease;
}

.is-formed .stage__haze {
  opacity: 1;
}

/* As the mark forms: a brighter core and a thin anamorphic flare through
   the middle. Opacity follows the scroll, so no transition. */
.stage__reveal {
  position: absolute;
  z-index: -1;
  inset: 0 -40%;
  background: radial-gradient(22% 46% at 50% 50%, rgba(60, 90, 255, 0.3), transparent 75%);
  opacity: calc(var(--m) * var(--m) * 0.9);
}

/* ---------- Orbit: grid, rings and ripples behind the brain ----------
   Faded out towards the top so nothing crosses the button or the copy. */

.orbit {
  position: absolute;
  z-index: -1;
  top: 50%;
  left: 50%;
  width: 172%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: 0;
  transition: opacity 2200ms ease 400ms;
}

.is-formed .orbit {
  opacity: 1;
}

.orbit__grid {
  position: absolute;
  inset: 14%;
  background:
    linear-gradient(rgba(110, 140, 255, 0.09) 1px, transparent 1px) 50% 50% / 44px 44px,
    linear-gradient(90deg, rgba(110, 140, 255, 0.09) 1px, transparent 1px) 50% 50% / 44px 44px;
  -webkit-mask-image: radial-gradient(closest-side at 50% 58%, #000 20%, transparent 100%);
  mask-image: radial-gradient(closest-side at 50% 58%, #000 20%, transparent 100%);
}

.orbit__rings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.orbit__ring {
  fill: none;
  stroke: url(#orbit-fade);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.orbit__ring.orbit__dash {
  stroke-dasharray: 2 6;
  animation: orbit-turn 90s linear infinite;
  transform-origin: 0 0;
}

.orbit__dot {
  fill: #dfe5ff;
}

.orbit__halo {
  fill: rgba(110, 140, 255, 0.28);
}

/* Each dot starts on the right (0deg) and goes clockwise; it hides while it
   passes over the top (270deg, 75% of the turn), where the copy sits. */
.orbit__spin {
  transform-origin: 0 0;
  animation: orbit-dot 26s linear infinite;
}

.orbit__spin--b { animation-duration: 38s; animation-delay: -15s; }
.orbit__spin--c { animation-duration: 54s; animation-delay: -30s; }

@keyframes orbit-dot {
  0% { transform: rotate(0deg); opacity: 1; }
  58% { opacity: 1; }
  64% { opacity: 0; }
  88% { opacity: 0; }
  94% { opacity: 1; }
  100% { transform: rotate(360deg); opacity: 1; }
}

@keyframes orbit-turn {
  to { transform: rotate(360deg); }
}

/* A ring leaves the brain every 2.4 s, grows and fades. */
.orbit__ripple {
  fill: none;
  stroke: url(#orbit-fade);
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
  transform-origin: 0 0;
  opacity: 0;
}

.is-formed .orbit__ripple {
  animation: ripple 7.2s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
}

.is-formed .orbit__ripple:nth-of-type(2) { animation-delay: 2.4s; }
.is-formed .orbit__ripple:nth-of-type(3) { animation-delay: 4.8s; }

@keyframes ripple {
  0% { opacity: 0; transform: scale(0.55); }
  12% { opacity: 0.9; }
  100% { opacity: 0; transform: scale(1.6); }
}

.tools {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Each tool: a small glass tile holding a hairline icon, its name below.
   --y places the icon's centre; the name hangs under it. */
.tool {
  position: absolute;
  top: calc(var(--y) / 520 * 100% - 23px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: #d9ddf2;
  font-size: 0.875rem;
  letter-spacing: -0.005em;
  line-height: 1.2;
  white-space: nowrap;
  opacity: 0;
  filter: blur(8px);
  transition: opacity 700ms ease, filter 800ms ease, transform 900ms var(--ease);
  transition-delay: calc(var(--i) * 140ms + 650ms);
}

.tool__icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.07), transparent 60%),
    linear-gradient(180deg, #121420, #0a0b12);
  /* Glass edge and a barely-there neutral halo: the tiles should read as
     lit by the scene, not glow blue on their own. */
  box-shadow:
    inset 0 0 0 1px rgba(214, 220, 236, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    0 0 12px 0 rgba(200, 208, 230, 0.06);
}

/* The rail carries on past the tile and fades out towards the edge. */
.tool__icon::before {
  content: "";
  position: absolute;
  top: 50%;
  width: 260px;
  height: 1px;
  pointer-events: none;
}

.tool--l .tool__icon::before {
  right: 100%;
  background: linear-gradient(270deg, rgba(196, 206, 255, 0.18), transparent);
}

.tool--r .tool__icon::before {
  left: 100%;
  background: linear-gradient(90deg, rgba(196, 206, 255, 0.18), transparent);
}

.tool svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: #f2f4ff;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tool--l {
  left: 5.6%;
  transform: translateX(-12px);
}

.tool--r {
  right: 5.6%;
  transform: translateX(12px);
}

/* The middle tiles step out past the others, so the six read as an arc
   around the brain. They may leave the stage, but stop 12px short of the
   screen edge (desktop only; on narrow screens the tools sit in a grid). */
@media (min-width: 960px) {
  .tool--l.tool--mid { left: max(-4%, (var(--stage-w) - 100vw) / 2 + 12px); }
  .tool--r.tool--mid { right: max(-4%, (var(--stage-w) - 100vw) / 2 + 12px); }
}

.is-wired .tool {
  opacity: 1;
  filter: blur(0);
  transform: none;
}

/* "Data in" under the left group, "Work out" under the right. */
.stage__flow {
  position: absolute;
  top: calc(430 / 520 * 100%);
  inset-inline: 0;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(185, 192, 220, 0.62);
}

.stage__flow span {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 900ms ease 1200ms;
}

.stage__flow span::before {
  content: "";
  width: 5px;
  height: 5px;
  border: 1px solid currentColor;
  border-radius: 50%;
}

.stage__flow-in { left: 21%; }
.stage__flow-out { left: 79%; }

.is-wired .stage__flow span {
  opacity: 1;
}

/* Four-point sparks: two thin rays crossed, twinkling slowly. */
.stage__sparks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.stage__sparks i {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: var(--s);
  height: var(--s);
  background: rgba(226, 232, 255, 0.85);
  clip-path: polygon(50% 0, 56% 44%, 100% 50%, 56% 56%, 50% 100%, 44% 56%, 0 50%, 44% 44%);
  opacity: 0;
}

.is-wired .stage__sparks i {
  animation: spark 6s ease-in-out var(--d) infinite;
}

@keyframes spark {
  0%, 100% { opacity: 0; transform: scale(0.6) rotate(0deg); }
  45% { opacity: 0.8; transform: scale(1) rotate(45deg); }
  60% { opacity: 0.55; }
}

/* ---------- Narrow screens ----------
   The wires need the side-by-side layout; below it the tools sit in a grid
   under the brain and appear in turn once it has formed. */

@media (max-width: 959px) {
  .stage {
    width: min(560px, 100%);
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .stage__wires {
    display: none;
  }

  .stage__brain {
    position: relative;
    top: auto;
    left: auto;
    width: min(94%, 460px);
  }

  .hero__glow {
    top: 18%;
    width: 130%;
    height: 80%;
  }

  .tools {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 22px 8px;
    width: 100%;
    margin-top: 22px;
  }

  .tool,
  .tool--l,
  .tool--r {
    position: relative;
    top: auto;
    left: auto;
    right: auto;
    gap: 10px;
    font-size: 0.8125rem;
    white-space: normal;
    text-align: center;
    transform: translateY(10px);
    transition-delay: calc(var(--i) * 120ms + 200ms);
  }

  .tool__icon::before,
  .stage__flow,
  .stage__sparks {
    display: none;
  }

  .is-wired .tool {
    transform: none;
  }
}

@media (max-width: 760px) {
  .frame {
    display: none;
  }

  .nav__burger {
    display: block;
  }

  .nav__links {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    display: grid;
    min-width: 200px;
    padding: 8px;
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 18px;
    background: #0a0c1a;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity 180ms ease, transform 180ms ease, visibility 0s 180ms;
  }

  .nav--open .nav__links {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }

  .nav__links a {
    padding: 12px 14px;
  }

  .nav__cta {
    padding-left: 14px;
  }
}

@media (max-width: 400px) {
  .nav__brand img {
    width: 48px;
  }

  .tool__icon {
    width: 42px;
    height: 42px;
    border-radius: 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-intro],
  .tool,
  .wire,
  .nodes circle,
  .stage__flow span,
  .hero__glow,
  .atmos,
  .stage__haze {
    transition: none !important;
  }

  .btn-glow,
  .btn-glow::before,
  .pulse,
  .orbit__ripple,
  .orbit__spin,
  .orbit__dash,
  .stage__sparks i,
  .atmos__beam,
  .atmos__sweep,
  .atmos__fog,
  .scroll-cue span,
  .hero__grain,
  .nav__pill::before {
    animation: none !important;
  }

  .frame__head,
  .orbit__ripple,
  .orbit__spin,
  .scene__runway,
  .scroll-cue {
    display: none;
  }
}
