/* DOM stack for the star field (spec 3). Back to front: backdrop (canvas, ambient glow, vignette),
   labels, page content, drag surfaces. The engine writes the backdrop's opacity / visibility /
   transform, the canvas opacity and mask, and --astra-copy-opacity on <html>. */

.astra-backdrop {
  position: fixed;
  inset: 0;
  height: 100lvh;
  background: #000;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.astra-backdrop canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.astra-ambient {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse farthest-corner at center, transparent 0%,
    color-mix(in srgb, #23435f 6.25%, transparent) 25%,
    color-mix(in srgb, #23435f 25%, transparent) 50%,
    color-mix(in srgb, #23435f 56.25%, transparent) 75%, #23435f 100%);
  mix-blend-mode: plus-lighter;
  opacity: 0;
  animation: astra-ambient-in 5.5s ease-out both;
}
@keyframes astra-ambient-in { to { opacity: 0.55; } }
/* a page entered from another (<html data-enter>, set by its head script before the first paint): the
   light is already up, as on the page before (boot.js holds it so once the page drops the attribute) */
html[data-enter] .astra-ambient { animation-delay: -5.5s; }
.astra-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(circle, transparent 47%, rgba(0, 0, 0, 0.18) 72%, rgba(0, 0, 0, 0.78) 100%);
}
/* the night sky: while the field is empty (between two pages, as the stars leave and until they are
   in; lighter in the fly-through's rush) the view is never a black hole in the ring of the ambient
   light. An even glow fills the middle to the ring's level (the ambient's own ramp, turned inside
   out) and a faint dust of points lies over it, each dust layer a single point on a tile of its own
   size, the sizes prime to each other so the points never line up. page.js writes --astra-sky (0 to
   1) on the backdrop; a page entered from another starts with it up (<html data-enter>, set by its
   head script before the first paint), the engine taking it from there. */
.astra-backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--astra-sky, 0);
  mix-blend-mode: plus-lighter;
  background:
    radial-gradient(1.3px 1.3px at 23px 41px, rgba(214, 226, 255, 0.5), transparent) 0 0 / 173px 149px,
    radial-gradient(1px 1px at 97px 13px, rgba(255, 238, 222, 0.42), transparent) 0 0 / 211px 197px,
    radial-gradient(1.5px 1.5px at 151px 88px, rgba(200, 218, 255, 0.34), transparent) 0 0 / 293px 239px,
    radial-gradient(1px 1px at 61px 177px, rgba(255, 255, 255, 0.4), transparent) 0 0 / 331px 283px,
    radial-gradient(1.1px 1.1px at 250px 30px, rgba(226, 234, 255, 0.3), transparent) 0 0 / 389px 353px,
    radial-gradient(ellipse farthest-corner at center, rgba(35, 67, 95, 0.27) 0%, rgba(35, 67, 95, 0.235) 25%,
      rgba(35, 67, 95, 0.13) 50%, rgba(35, 67, 95, 0.03) 64%, rgba(35, 67, 95, 0) 74%);
}
html[data-enter] .astra-backdrop { --astra-sky: 1; }

/* the fly-through's light ([data-astra-flythrough]; page.js makes both layers and writes the star's
   place, --astra-fx / --astra-fy, on the backdrop). On the way in the camera homes in on one star off
   the middle of the view (.astra-focus): page.js writes its core's radius (--r, px), its opacity and
   how far its halo has grown in (--g, 0..1). Far off it is a star like its neighbours: a small soft
   point, no halo; the halo grows in only as the camera comes close. The core is the white of the
   section and its glow grows with it, so the view turns white because the star has filled it. No
   rays, no cross. The light holds while the block passes; the way out
   is the way in run backwards, the light shrinking back into the same star. The plain white ground
   (.astra-flood, --astra-ebb on <html>) serves reduced motion: no star; the same light, one plain
   crossfade each way. Both lie over the night sky (the backdrop's ::after, added onto what is under
   it): its tint turned the white blue in the middle of the view, a different white from the
   section's and from the header's band over it; the light is #f7f7f4 everywhere. */
.astra-flood {
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;
  opacity: var(--astra-ebb, 0);
  background: #f7f7f4;
}
.astra-focus {
  --r: 0;
  --g: 1;
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle at var(--astra-fx, 50%) var(--astra-fy, 50%), #f7f7f4 calc(var(--r) * 1px), rgba(247, 247, 244, 0.8) calc(var(--r) * 1.45px), rgba(228, 235, 247, calc(0.36 * var(--g))) calc(var(--r) * 2.8px), rgba(206, 219, 243, calc(0.14 * var(--g))) calc(var(--r) * 6px), rgba(206, 219, 243, calc(0.045 * var(--g))) calc(var(--r) * 11px), rgba(206, 219, 243, 0) calc(var(--r) * 19px));
}

/* the dark stars on the white (ink.js): a canvas the size of the view that follows the view through
   the fly-through block and its white runways, under the block's content (the letters are drawn over
   the stars; near a line of text a star is held to what keeps that text's contrast), and a second one
   over the content that draws only inside the block's cards, where the stars are seen faintly, as
   through paper; neither takes pointer events, and the block is its own stacking context */
[data-astra-flythrough] { isolation: isolate; }
.astra-ink {
  position: absolute;
  top: 0;
  z-index: -1;
  display: block;
  max-width: none;
  margin: 0;
  pointer-events: none;
  opacity: 0;
}
.astra-ink-front { z-index: 1; }

/* the header's mark drawn from live stars (mark.js): a canvas laid over the mark element, reaching
   40% of its side beyond it on every side (the script sets its size and a negative margin of that
   reach). It is placed before the element, out of flow, so it sits where the element sits, the first
   thing in the lockup; it takes no pointer events. */
.astra-logo {
  position: absolute;
  display: block;
  max-width: none;
  pointer-events: none;
}

/* still of the field (tier 0, no WebGL2, context loss, errors, no script) */
.astra-poster {
  position: absolute;
  left: 50%;
  top: 50%;
  height: 100%;
  width: auto;
  max-width: none;
  transform: translate(-50%, -50%);
  opacity: var(--astra-poster-opacity, 1);
}
@media (max-aspect-ratio: 18/25) { .astra-poster { height: 85.8268%; } }

/* the two big labels either side of the field */
[data-astra-label] {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 20;
  margin: 0;
  color: #fafafa;
  text-shadow: 0 2px 6px #000000a6, 0 0 1.25rem #00000073;
  user-select: none;
  pointer-events: none;
  opacity: var(--astra-copy-opacity, 1);
}
[data-astra-label="left"] { left: clamp(1.25rem, 4vw, 3.5rem); }
[data-astra-label="right"] { right: clamp(1.25rem, 4vw, 3.5rem); }
@media not (min-width: 768px) {
  [data-astra-label="left"] { left: 1rem; }
  [data-astra-label="right"] { right: 1rem; }
}
.astra-letter {
  display: inline-block;
  opacity: 0;
  transform: translateX(var(--astra-shift, 0));
  animation: astra-letter-in 1s cubic-bezier(0.22, 1, 0.36, 1) var(--astra-delay, 0.85s) forwards;
}
@keyframes astra-letter-in { to { opacity: 1; transform: none; } }
.astra-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* hero and cue sections */
[data-astra-hero] {
  position: relative;
  min-height: var(--astra-viewport-height, 100svh);
}
[data-astra-cue] {
  position: relative;
  width: 100%;
  max-width: 36rem;
  height: 80vh;
  margin-inline: auto;
  touch-action: pan-y pinch-zoom;
}
[data-astra-cue] > svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
}
/* where the stars cannot form the picture (still poster, reduced motion) its outline shows instead */
[data-astra-static="true"] [data-astra-cue] > svg { color: #fafafa4d; opacity: 1; }

/* the lead heading drifts against the scroll (the engine writes the offset) */
[data-astra-parallax] { translate: 0 var(--astra-title-parallax-y, 0px); }

/* drag surface: a real button over the hero or cue */
.astra-drag {
  position: absolute;
  inset: 0;
  z-index: 1;
  padding: 0;
  border: 0;
  background: none;
  cursor: grab;
  touch-action: pan-y pinch-zoom;
}
.astra-drag[data-dragging="true"] { cursor: grabbing; }
.astra-drag:focus-visible { outline: 0.125rem solid #ffffffb3; outline-offset: -0.25rem; }
[data-astra-static="true"] .astra-drag { cursor: auto; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .astra-backdrop::after { display: none; }
  .astra-ambient { opacity: 0.55; animation: none; }
  .astra-flood { opacity: var(--astra-ebb, 0); background: #f7f7f4; }
  .astra-focus { display: none; }
  [data-astra-cue] > svg { color: #fafafa4d; opacity: 1; }
  [data-astra-parallax] { translate: none; }
  .astra-letter { opacity: 1; transform: none; animation: none; }
  .astra-drag { cursor: auto; pointer-events: none; }
}

/* private lab switches (?lab=1&ambient=0 / &labels=0) */
.astra-lab-noambient .astra-ambient,
.astra-lab-noambient .astra-vignette { display: none; }
.astra-lab-nolabels [data-astra-label] { display: none; }
