/*
 * Full-page space background + scroll-driven parallax (landing page only).
 *
 * The ESO Milky Way panorama (eso0932a, ESO/S. Brunier, CC BY 4.0) is the
 * background of the ENTIRE landing page, not just the hero. Three stacked
 * position:fixed layers sit behind the page content (negative z-index, so the
 * normal-flow sections paint on top):
 *
 *   .space-bg     the photo itself (object-fit: cover), panned + zoomed on the
 *                 <img> as you scroll (the galactic band travels down the page).
 *                 NOT background-attachment: fixed (broken on iOS); a real fixed
 *                 element transformed on the compositor instead.
 *   .space-scrim  a readability veil (darker at top/bottom, lighter through the
 *                 middle so the bright band stays visible) — keeps every dark
 *                 glass panel + light body text well above contrast minimums.
 *   .space-stars  one sparse foreground star layer at a FASTER parallax rate,
 *                 for depth. Star pixels live on ::before/::after.
 *
 * Motion is transform-only, scroll-driven, and happens ONLY while scrolling —
 * there is no continuous/idle animation. Perf constraint learned the hard way
 * (see the git history / knowledge-base note): full-viewport fixed layers that
 * animate every frame are cheap on Blink/WebKit but expensive on Gecko, and any
 * continuous animation under the backdrop-blurred glass panels forced a per-frame
 * re-blur. So: (1) no ambient drift/twinkle, (2) the glass panels carry no
 * backdrop-filter (near-opaque fills instead), (3) the scroll pan runs via CSS
 * scroll-driven animations (animation-timeline: scroll()) where supported, with
 * a rAF transform fallback (assets/space-scroll.js) for older WebKit, and is
 * disabled entirely on Gecko (static background there — it can't pan a full-bleed
 * layer at 60fps). prefers-reduced-motion freezes everything — static photo,
 * no parallax.
 */

/* Dark base so nothing ever flashes white while the photo decodes. */
html {
  background-color: #05060d;
}

.space-bg,
.space-scrim,
.space-stars {
  position: fixed;
  inset: 0;
  pointer-events: none;
  contain: strict;
}

.space-bg {
  z-index: -3;
  overflow: hidden;
  background-color: #05060d;
}

.space-bg picture,
.space-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  /* Base scale leaves headroom so the scroll pan never exposes an edge. */
  transform: scale(1.16) translate3d(0, 0, 0);
  transform-origin: center 38%;
}

/* Compositing hint reserved ONLY for the element that actually moves on scroll
   (the photo), and only where the pan runs — dropped on Gecko below, where the
   background is static. Deliberately not on the container/scrim (never move). */
.space-bg img {
  will-change: transform;
}

/* Readability veil. Slightly stronger top (behind the transparent nav + hero
   headline) and bottom (footer), lighter through the middle third so the bright
   galactic core still reads as a photograph rather than a flat wash. A faint
   radial vignette pulls focus to center. The SVG fractal-noise film kills
   gradient banding on the large dark ramps (near-invisible, 0.025 alpha). */
.space-scrim {
  z-index: -2;
  background:
    radial-gradient(ellipse 120% 80% at 50% 38%, rgba(5, 6, 13, 0) 40%, rgba(5, 6, 13, 0.55) 100%),
    linear-gradient(
      180deg,
      rgba(5, 6, 13, 0.68) 0%,
      rgba(5, 6, 13, 0.42) 16%,
      rgba(5, 6, 13, 0.40) 48%,
      rgba(5, 6, 13, 0.52) 78%,
      rgba(5, 6, 13, 0.72) 100%
    );
}

.space-scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.025;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Sparse foreground stars. The transform layer (pan) is this element; the star
   pixels themselves sit on two pseudo-elements so their twinkle (opacity) stays
   fully independent of the scroll pan (transform) that rides on the parent. */
.space-stars {
  z-index: -1;
  will-change: transform;
}

.space-stars::before,
.space-stars::after {
  content: "";
  position: absolute;
  inset: 0;
  background-repeat: repeat;
  background-size: 520px 520px;
}

/* Primary field. */
.space-stars::before {
  opacity: 0.7;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='520' height='520'%3E%3Cg fill='%23ffffff'%3E%3Ccircle cx='40' cy='70' r='0.9' opacity='0.9'/%3E%3Ccircle cx='120' cy='30' r='0.6' opacity='0.5'/%3E%3Ccircle cx='210' cy='110' r='1' opacity='0.85'/%3E%3Ccircle cx='300' cy='60' r='0.5' opacity='0.45'/%3E%3Ccircle cx='380' cy='140' r='0.8' opacity='0.75'/%3E%3Ccircle cx='470' cy='40' r='0.6' opacity='0.55'/%3E%3Ccircle cx='70' cy='200' r='0.7' opacity='0.7'/%3E%3Ccircle cx='170' cy='250' r='0.5' opacity='0.4'/%3E%3Ccircle cx='260' cy='210' r='0.9' opacity='0.8'/%3E%3Ccircle cx='350' cy='280' r='0.6' opacity='0.5'/%3E%3Ccircle cx='440' cy='230' r='1' opacity='0.9'/%3E%3Ccircle cx='30' cy='330' r='0.6' opacity='0.55'/%3E%3Ccircle cx='130' cy='390' r='0.8' opacity='0.7'/%3E%3Ccircle cx='230' cy='350' r='0.5' opacity='0.4'/%3E%3Ccircle cx='320' cy='420' r='0.7' opacity='0.65'/%3E%3Ccircle cx='410' cy='380' r='0.6' opacity='0.5'/%3E%3Ccircle cx='490' cy='450' r='0.9' opacity='0.8'/%3E%3Ccircle cx='90' cy='470' r='0.6' opacity='0.5'/%3E%3Ccircle cx='200' cy='490' r='0.7' opacity='0.6'/%3E%3Ccircle cx='360' cy='500' r='0.5' opacity='0.4'/%3E%3C/g%3E%3C/svg%3E");
}

/* Secondary field: a different sparse set, offset, twinkling on the opposite
   phase so individual stars fade in and out rather than the whole sky dimming. */
.space-stars::after {
  opacity: 0.55;
  background-position: 210px 130px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='520' height='520'%3E%3Cg fill='%23ffffff'%3E%3Ccircle cx='95' cy='55' r='0.7' opacity='0.8'/%3E%3Ccircle cx='250' cy='90' r='0.5' opacity='0.5'/%3E%3Ccircle cx='400' cy='45' r='0.8' opacity='0.7'/%3E%3Ccircle cx='150' cy='160' r='0.6' opacity='0.6'/%3E%3Ccircle cx='330' cy='185' r='0.9' opacity='0.85'/%3E%3Ccircle cx='60' cy='260' r='0.6' opacity='0.55'/%3E%3Ccircle cx='210' cy='300' r='0.7' opacity='0.7'/%3E%3Ccircle cx='450' cy='290' r='0.5' opacity='0.5'/%3E%3Ccircle cx='120' cy='420' r='0.8' opacity='0.75'/%3E%3Ccircle cx='290' cy='460' r='0.6' opacity='0.55'/%3E%3Ccircle cx='390' cy='430' r='0.7' opacity='0.65'/%3E%3Ccircle cx='480' cy='150' r='0.6' opacity='0.5'/%3E%3Ccircle cx='30' cy='120' r='0.5' opacity='0.45'/%3E%3Ccircle cx='175' cy='380' r='0.6' opacity='0.6'/%3E%3C/g%3E%3C/svg%3E");
}

/* ── Scroll-driven parallax ───────────────────────────────────────────────────
   The ONLY motion on the background: it pans as you scroll (progress 0 at page
   top → 1 at bottom); the photo drifts up + zooms in, the stars drift up ~3x
   faster for depth. There is deliberately NO continuous/idle animation. A slow
   time-based ambient drift + star twinkle used to live here; it was removed
   because on Gecko (Firefox/Zen) it re-composited the full-viewport fixed layers
   every frame even at rest (~46fps idle, constant jank) and, under the glass
   panels, forced a per-frame re-blur. Motion now happens only while scrolling.

   CSS scroll-timeline path (Blink + modern WebKit), guarded by @supports.
   Engines without a working scroll timeline get the rAF fallback in
   space-scroll.js (same transforms, inline). Gecko is opted out entirely (see
   .space-no-parallax below): it composites these full-bleed layers too slowly to
   pan per frame (~35fps scroll on BOTH the CSS and rAF paths), so its background
   stays static — the photo + stars still read premium, they just don't pan. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .space-bg img {
      animation: space-pan linear both;
      animation-timeline: scroll(root block);
    }
    .space-stars {
      animation: space-stars-pan linear both;
      animation-timeline: scroll(root block);
    }
  }
}

@keyframes space-pan {
  from {
    transform: scale(1.16) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.28) translate3d(0, -8%, 0);
  }
}

@keyframes space-stars-pan {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(0, -22%, 0);
  }
}

/* The rAF fallback (space-scroll.js) adds this class to opt the pan layers out
   of the CSS pan animation while it drives those transforms inline itself
   (belt-and-braces; the @supports guard already excludes non-timeline engines). */
.space-js-parallax .space-bg img,
.space-js-parallax .space-stars {
  animation: none;
}

/* Gecko (Firefox/Zen): keep the background fully static. space-scroll.js adds
   this class on Gecko, cancelling the CSS-timeline pan and any inherited pan
   transform, and dropping the compositing hint since nothing moves. Full-bleed
   fixed layers are too costly to pan per frame there (measured ~35fps vs ~118fps
   static); a static photo keeps the premium look with zero scroll cost. */
.space-no-parallax .space-bg img,
.space-no-parallax .space-stars {
  animation: none !important;
  will-change: auto;
}
.space-no-parallax .space-bg img {
  transform: scale(1.16) translate3d(0, 0, 0);
}
.space-no-parallax .space-stars {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .space-bg img,
  .space-bg picture,
  .space-stars,
  .space-stars::before,
  .space-stars::after {
    animation: none !important;
    transform: scale(1.16) translate3d(0, 0, 0);
  }
  .space-stars,
  .space-stars::before,
  .space-stars::after {
    transform: none;
  }
}
