/* ===== Lane A — hero cosmos (fx-hero.css) =====
   All hero motion styles. Everything is additive: without JS (no html.fx-on /
   html.fx-open) or with reduced motion the page renders exactly like the
   base page. Monochrome only — white/silver light on black. */

/* --- A1: dot-matrix field (canvas sits behind the orbit artwork) --- */
.dotfield {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* --- A2: light on the orbit — overlay locked to .orbit-img geometry.
   Every geometry rule below mirrors .orbit-img in styles.css. --- */
.orbit-fx {
  position: absolute;
  top: 50%;
  right: 0;
  width: min(88vw, 1200px);
  max-width: 100%;
  height: auto;
  aspect-ratio: 1200 / 1000;
  transform: translateY(-52%);
  overflow: visible;
  pointer-events: none;
  display: none; /* revealed only by html.fx-on (JS, motion allowed) */
}
html.fx-on .orbit-fx {
  display: block;
}

@media (max-width: 700px) {
  .orbit-fx {
    top: -94px;
    left: 50%;
    right: auto;
    width: 640px;
    max-width: none;
    transform: translateX(-65%);
  }
}

.orbit-fx .kfx-ping,
.orbit-fx .kfx-star {
  transform-box: fill-box;
  transform-origin: center;
}

/* Comet — one lap: 6.5 s travel + 2.5 s darkness = 9 s period.
   dashoffset 90 → -1000 moves the 90-unit bright segment from the dark
   lower-left end of the arc to the upper-right end and out. */
@keyframes kfx-comet {
  0% { stroke-dashoffset: 90; animation-timing-function: cubic-bezier(.45, .05, .55, .95); }
  72.22% { stroke-dashoffset: -1000; animation-timing-function: linear; }
  100% { stroke-dashoffset: -1000; }
}

/* Satellite ping — whisper */
@keyframes kfx-ping {
  0% { transform: scale(1); opacity: .6; }
  100% { transform: scale(6); opacity: 0; }
}

/* Star glint — whisper */
@keyframes kfx-star {
  0% { opacity: .15; transform: scale(.85); }
  100% { opacity: .6; transform: scale(1.1); }
}

html.fx-on .visual .kfx-comet,
html.fx-on .visual .kfx-comet-glow {
  animation: kfx-comet 9s linear .35s backwards infinite;
  animation-play-state: paused;
}
html.fx-on .visual .kfx-ping {
  animation: kfx-ping 3.6s cubic-bezier(.2, .6, .3, 1) backwards infinite;
  animation-play-state: paused;
}
html.fx-on .visual .kfx-ping-b {
  animation-delay: 1.8s;
}
html.fx-on .visual .kfx-star {
  animation: kfx-star 5s ease-in-out alternate infinite;
  animation-play-state: paused;
}
/* Run only while .visual is on screen and the document is visible
   (.fx-run is toggled by fx-hero.js via IntersectionObserver +
   visibilitychange). Paused CSS animations draw no frames. */
html.fx-on .visual.fx-run .kfx-comet,
html.fx-on .visual.fx-run .kfx-comet-glow,
html.fx-on .visual.fx-run .kfx-ping,
html.fx-on .visual.fx-run .kfx-star {
  animation-play-state: running;
}

/* --- A3: mouse parallax (fine pointer + desktop only).
   The transition is the smoothing — no JS animation loop. --px/--py are
   set on .visual by fx-hero.js and inherit to these children. Gated under
   .visual.fx-run (hero on screen) so no transition can linger off-screen. --- */
@media (hover: hover) and (pointer: fine) and (min-width: 901px) {
  html.fx-on .visual.fx-run .orbit-img,
  html.fx-on .visual.fx-run .orbit-fx {
    translate: calc(var(--px, 0) * -16px) calc(var(--py, 0) * -12px);
    transition: translate 1.4s cubic-bezier(.22, 1, .36, 1);
  }
  html.fx-on .visual.fx-run .dotfield {
    translate: calc(var(--px, 0) * -5px) calc(var(--py, 0) * -4px);
    transition: translate 1.4s cubic-bezier(.22, 1, .36, 1);
  }
}

/* --- A4: opening sequence — the scene focuses in (html.fx-open is added
   by fx-boot.js only on desktop / fine pointer / no-preference / no hash,
   and removed after 2800 ms no matter what). --- */
@keyframes orbit-focus {
  from { opacity: 0; filter: blur(20px); scale: 1.06; }
}
@keyframes line-focus {
  from { opacity: 0; filter: blur(14px); translate: 0 .18em; }
}
@keyframes eyebrow-focus {
  from { opacity: 0; letter-spacing: .9em; }
}
@keyframes dotfield-in {
  from { opacity: 0; }
}
html.fx-open .orbit-img,
html.fx-open .orbit-fx {
  animation: orbit-focus 2s cubic-bezier(.22, 1, .36, 1) .1s backwards;
}
html.fx-open .dotfield {
  animation: dotfield-in 1.4s ease .4s backwards;
}
html.fx-open .headline .line {
  animation: line-focus 1.2s cubic-bezier(.22, 1, .36, 1) backwards;
}
html.fx-open .headline .line:nth-child(1) { animation-delay: .25s; }
html.fx-open .headline .line:nth-child(2) { animation-delay: .42s; }
html.fx-open .eyebrow {
  animation: eyebrow-focus 1.4s cubic-bezier(.22, 1, .36, 1) .15s backwards;
}

/* --- A5: scroll depth of field (.hero-dof + --sp set by fx-hero.js).
   transition:none only while hero-dof is on, so the base entrance
   transitions don't fight the per-scroll updates. At the top of the page
   (no hero-dof) nothing here applies. --- */
.hero.hero-dof .content {
  translate: 0 calc(var(--sp) * 48px);
  opacity: calc(1 - var(--sp) * .7);
  filter: blur(calc(var(--sp) * 6px));
  transition: none;
}
.hero.hero-dof .visual {
  translate: 0 calc(var(--sp) * -36px);
  opacity: calc(1 - var(--sp) * .85);
  transition: none;
}

/* --- Reduced motion: fx-hero.js never adds fx-on/fx-open under reduce and
   removes them on a runtime flip; these guards close any residual race.
   After load there are zero running animations — static states only. --- */
@media (prefers-reduced-motion: reduce) {
  html.fx-on .orbit-fx {
    display: none;
  }
  html.fx-open .orbit-img,
  html.fx-open .orbit-fx,
  html.fx-open .dotfield,
  html.fx-open .headline .line,
  html.fx-open .eyebrow {
    animation: none;
  }
}
