/*
 * Ori Creamery — "Daylight Cosmos: Carousel" (variant D16).
 *
 * Round 6: Drift (d13) × Float (d14), spin-led. The layout ratios — lockup
 * size, headline, lede, labels, pill, top anchoring — are copied from d13
 * verbatim per owner feedback ("I like the drift's logo to text ratio").
 * The constellation craft (smooth bezier hairlines + anchor stars, shimmer,
 * no captions) comes from d14. The ice creams are the brand ENGRAVINGS (cone /
 * coupe / tub, recoloured via CSS mask) and they DRIFT ACROSS the sky: each travels a horizontal lane
 * (50–140s per crossing/sweep, negative delays = mid-journey at load,
 * fully drawn from the first frame) while slowly TURNING (84–170s full
 * revolutions, mixed directions, like paper ornaments twirling on threads)
 * and bobbing. A quiet 4-burst halo orbits the lockup on invisible
 * ellipses — no rings, no lines.
 *
 * Transform discipline: traversal (.d16-trav), bob (.d16-bob) and rotation
 * (.d16-rot) each own ONE wrapper, so transforms never fight. Dashed
 * constellation lines never carry vector-effect (Chromium pitfall).
 *
 * Base CSS = settled state: figures parked ON-SCREEN at scattered points
 * with slight pleasant tilts, halo bursts parked on their loops,
 * constellation A drawn. Every continuous animation lives inside
 * prefers-reduced-motion: no-preference. Colors come from tokens.css only.
 */

* { box-sizing: border-box; margin: 0; }

html, body { height: 100%; }

body { overflow: clip; }

.d16-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ---- Page frame ---- */

.d16-page {
  position: relative;
  min-height: 100svh;
  overflow: clip; /* wide halo legs crossing edges never cause h-scroll */
  background: var(--color-surface);
}

/* ---- Decorative parallax layers ---- */

.d16-layer {
  position: absolute;
  inset: -28px; /* overscan so parallax never reveals an edge */
  pointer-events: none;
  will-change: transform;
}

.d16-sky-far {
  z-index: 0;
  opacity: 0.55;
}

.d16-constel { z-index: 0; inset: 0; }
.d16-sky-mid { z-index: 1; }

/* Scoped decorative palette (approved colors only; waffle = accent).
   Round-6: the sky-blue burst-stars read "too blue" in the twinkle field,
   lockup halo and constellations — the sky class now inks galaxy so the
   bursts lose their conspicuous light-blue note. Periwinkle stays dominant,
   waffle stays a warm accent, sky survives only as a thin meteorite-tail
   gradient stop. */
.d16-c-peri   { color: var(--color-periwinkle); }
.d16-c-sky    { color: var(--color-galaxy); }
.d16-c-galaxy { color: var(--color-galaxy); }
.d16-c-waffle { color: var(--color-waffle); }

/* ---- Twinkle stars (round-4 scale: 12–48px + two heroes) ---- */

.d16-star {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s, 16px);
  height: var(--s, 16px);
  margin: calc(var(--s, 16px) / -2) 0 0 calc(var(--s, 16px) / -2);
}

.d16-tw {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0.65;
}

/* Far-plane central + large bursts ride the same twinkle, dimmed via the
   innermost <svg> so they read as quiet depth — the central ones never
   compete with the hero text, the large ones never overpower the lockup.
   Set on .d16-tw svg (not .d16-star) because the JS entrance animates the
   .d16-star opacity to 1; this inner multiplier survives that and stacks
   with the twinkle opacity. */
.d16-far--soft    .d16-tw svg { opacity: 0.34; }
.d16-far--whisper .d16-tw svg { opacity: 0.16; }

.d16-tw svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* hero bursts: positions tuned per breakpoint, so they live in classes */
.d16-hero1 { --x: 88%; --y: 66%; --s: 60px; --td: 5.2s; --tl: .5s; --sd: 120s; }
.d16-hero2 { --x: 4%;  --y: 30%; --s: 56px; --td: 6.1s; --tl: 1.7s; --sd: 150s; }

.d16-star--spin .d16-tw { opacity: 0.8; }

/* ---- Constellations: Float craft — smooth bezier hairlines + anchor
   stars, shimmer in/out, no captions. They share the lower-right sky
   pocket on phones (never lit at once); desktop splits them apart. ---- */

.d16-fig {
  position: absolute;
  overflow: visible;
}

/* figure A: scoop-and-cone, periwinkle ink. Settled state = drawn. */
.d16-fig--a {
  right: 3vw;
  bottom: 2svh;
  width: clamp(120px, 32vmin, 250px);
  aspect-ratio: 100 / 150;
}

/* figure B: sundae cup, waffle ink. Rests hidden; shimmer reveals it. */
.d16-fig--b {
  right: 4vw;
  bottom: 4svh;
  width: clamp(110px, 27vmin, 215px);
  aspect-ratio: 100 / 116;
  opacity: 0;
}

.d16-fig-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* pathLength-dashed hairlines: NO vector-effect (Chromium pitfall) */
.d16-line {
  fill: none;
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.d16-fig--a .d16-line {
  stroke: var(--color-periwinkle);
  stroke-opacity: 0.32; /* ink never exceeds 0.35 */
}

.d16-fig--b .d16-line {
  stroke: var(--color-waffle);
  stroke-opacity: 0.3;
}

.d16-cstar {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s, 11px);
  height: var(--s, 11px);
  margin: calc(var(--s, 11px) / -2) 0 0 calc(var(--s, 11px) / -2);
  opacity: 0.55;
}

.d16-fig--a .d16-cstar { color: var(--color-periwinkle); }
.d16-fig--b .d16-cstar { color: var(--color-waffle); }

.d16-cstar svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- The carousel: brand-engraving figures drifting ACROSS the sky ----
   Wrapper chain (one transform each, so they never fight):
   .d16-float   lane anchor (top = lane center, left = 0) + vertical center
   .d16-trav    the traversal: translateX from --tx0 to --tx1 (vw-based;
                full crossings run linear/normal and wrap off-screen,
                side sweeps run sine/alternate from an off-screen edge to
                an on-screen apex and back out)
   .d16-bob     gentle vertical bob (±8px)
   .d16-rot     the slow full revolution (or parked tilt --ra)            */

.d16-floats {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  will-change: transform;
  /* Easter egg: the drifting figures are OFF by default — not rendered at
     all (no compositor layers, no physics loop), so the page stays light for
     everyone. Tapping the lockup 7× adds .d16-revealed and fades them in. */
  display: none;
  opacity: 0;
  transition: opacity 0.7s ease;
  /* ≈ sine: sweeps linger at the apex and ease out past the edge */
  --tease-sweep: cubic-bezier(0.37, 0, 0.63, 1);
}

.d16-floats.d16-revealed { display: block; }

.d16-float {
  position: absolute;
  left: 0;
  top: var(--y);
  transform: translateY(-50%);
  /* off-screen endpoints: a full figure height + 48px past either edge
     covers the rotation diagonal + bob + the ±14px parallax budget */
  --goL: calc(-1 * var(--s, 100px) - 48px);
  --goR: calc(100vw + 48px);
}

/* inks: galaxy or waffle line work only (figures never lead with peri) */
.d16-ink-galaxy { color: var(--color-galaxy); }
.d16-ink-waffle { color: var(--color-waffle); --d16-spark: var(--color-periwinkle); }

.d16-trav,
.d16-bob,
.d16-drag,
.d16-rot {
  display: block;
}

/* drag wrapper: ambient lane-cross/bob/rotation live on the OTHER wrappers,
   so the drag offset is purely additive here and never fights them. Base =
   translate(0,0); JS sets a flung offset and springs it back to 0,0 so the
   figure re-merges with its drifting + rotating home. Hit target only —
   pointer-events flip on in JS (and never under reduced motion). */
.d16-drag {
  transform: translate(0px, 0px);
  width: max-content;
}

.d16-floats.d16-grabready .d16-drag {
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
}

.d16-floats.d16-grabready .d16-drag.d16-grabbing { cursor: grabbing; }

/* parked pose (reduced motion / base): scattered ON-SCREEN along the lane */
.d16-trav { transform: translateX(var(--tpark, 40vw)); }

/* parked tilt: decoratives may rest a few degrees off — never the lockup */
.d16-rot { transform: rotate(var(--ra, 0deg)); }

/* Brand engraving figures: the real Ori ice-cream engravings (cone / coupe /
   tub), shipped as external cached SVGs and recolored to each figure's ink
   via an alpha mask (background:currentColor shows through the filled shape,
   transparent gaps become the daylight sky). The shape's intrinsic ratio is
   supplied per-figure so height:var(--s) drives the width. Replaces the old
   inline carousel/meteor d16-ic-* symbols (the corner constellation figures at
   .d16-fig--a/--b are a SEPARATE, still-inline line-art set — see ADR 0009). */
.d16-figmask {
  display: block;
  background: currentColor;
  -webkit-mask: var(--fig) center / contain no-repeat;
          mask: var(--fig) center / contain no-repeat;
  opacity: 0.9;
}
/* drifters size by HEIGHT (--s); meteors by WIDTH (--ms). Both apply a
   per-shape scale (--figscale: bowls smaller than cones) and drifters add a
   per-figure jitter (--figvar) so the cast reads at varied sizes. */
.d16-rot .d16-figmask {
  --fh: calc(var(--s, 100px) * var(--figscale, 1) * var(--figvar, 1));
  height: var(--fh);
  width: calc(var(--fh) * var(--figr, 1));
}
.d16-meteor-fig .d16-figmask {
  --fw: calc(var(--ms, 48px) * var(--figscale, 1));
  width: var(--fw);
  height: calc(var(--fw) / var(--figr, 1));
  opacity: 0.92;
}
/* per-shape: external file, intrinsic width:height ratio, and size scale.
   Owner: bowls (coupe/tub) ~30% smaller, cone ~15% smaller. */
.d16-fig--cone  { --fig: url("figures/cone.svg");  --figr: 0.5312; --figscale: 0.85; }
.d16-fig--coupe { --fig: url("figures/coupe.svg"); --figr: 0.9153; --figscale: 0.70; }
.d16-fig--tub   { --fig: url("figures/tub.svg");   --figr: 1.0070; --figscale: 0.70; }
.d16-fig--flip  { transform: scaleX(-1); }

/* The brand cone is drawn LEANING — its tip sits bottom-RIGHT of the box, not
   on the vertical scoop->tip axis the meteor system assumes. Straighten + recentre
   it ONLY in the meteor head (translate/rotate are % + deg, so they hold at any
   --ms and any --ang) so the tail streams dead-straight off the tip. Tuned in the
   align-cone alignment tool; floats/coupe/tub untouched. */
.d16-meteor-fig .d16-fig--cone {
  transform: translate(-11%, 0%) rotate(17.5deg);
  transform-origin: 50% 50%;
}

/* ---- The lanes (mobile-first) ----
   Round-6 narrow distribution: spread the seven figures across the TOP,
   MIDDLE and BOTTOM thirds so the sky never bundles at the bottom. The
   centered text/lockup column is near-full-width on phones, so figures at
   text heights run the desktop side-sweep choreography scaled down: they
   enter from an edge, stop CLEAR of the centered safe column, and retreat.
   Heights with no text — the sky strip above the lockup, and the bottom
   band below the pill — keep full edge-to-edge crossings.

   Collision budget (we own this): a figure of height --s rotating in place
   sweeps a bounding circle of radius ≈ 0.6·--s (diagonal of a ~100:150
   box). A side sweep's apex --tx1 is the figure box's LEFT edge; its
   rotation circle reaches inward to apex + 0.5·width + 0.6·s. Add bob ±8px
   + parallax ±14px. The safe column on phones is the centered ~76vw band
   (≈12vw gutters), so left sweeps apex near 0–2vw and right sweeps apex so
   the box right edge + envelope still clears ~88vw. Apexes below keep the
   inner rotation envelope out of that 12–88vw band at text heights. */

/* TOP third */
.d16-float--f1 { --y: 5.5%;  --s: 44px; --tx0: var(--goL); --tx1: var(--goR); --tdur: 78s; --tph: -21s; --tpark: 18vw; }   /* full cross: above lockup */
.d16-float--f7 { display: inline; --y: 19%; --s: 38px; --tx0: var(--goR); --tx1: 88vw; --tdur: 58s; --tph: -30s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 90vw; } /* right sweep at lockup height */
/* MIDDLE third */
.d16-float--f2 { --y: 38%; --s: 46px; --tx0: var(--goL); --tx1: -2vw;  --tdur: 64s; --tph: -18s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 1vw; }   /* left sweep at lede height */
.d16-float--f3 { --y: 52%; --s: 44px; --tx0: var(--goR); --tx1: 82vw;  --tdur: 70s; --tph: -40s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 86vw; }  /* right sweep at labels height */
.d16-float--f6 { display: inline; --y: 64%; --s: 42px; --tx0: var(--goL); --tx1: -3vw; --tdur: 60s; --tph: -8s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 2vw; } /* left sweep below pill, above bottom band */
/* BOTTOM third */
.d16-float--f4 { --y: 84%; --s: 50px; --tx0: var(--goR); --tx1: var(--goL); --tdur: 102s; --tph: -52s; --tpark: 60vw; }  /* full cross: bottom band */
.d16-float--f5 { --y: 94%; --s: 46px; --tx0: var(--goL); --tx1: var(--goR); --tdur: 88s;  --tph: -70s; --tpark: 30vw; }  /* full cross: bottom band */

/* ---- Meteorites: whole-comet ice creams that rotate to point along their
   own travel (mirrors daylight-15). The layer is hidden by default and only
   revealed inside the no-preference media query, so reduced-motion never
   shows them. Wrapper chain (one transform each):
   .d16-meteor       lane centre — translate(--lx, --ly) to the point the
                     streak passes through mid-flight.
   .d16-meteor-rot   the WHOLE comet: rotated by --ang so the figure's
                     scoop->tip axis lies on the travel line (scoop LEADS),
                     and it TRAVELS along its own local -Y axis (the animation
                     drives translateY). Because both the figure and the tail
                     live in this rotated frame, the motion is guaranteed
                     collinear with the figure and the tail is always EXACTLY
                     opposite travel, at any viewport. transform-origin 0 0 =
                     the figure centre (the figure is itself translate(-50%,
                     -50%) about this point).
   .d16-meteor-fig   the engraving ice cream, drawn scoop-up, centred on the
                     pivot. It inherits --ang, so the scoop points the way
                     it's going and the cone tip trails.
   .d16-meteor-tail  just below the pivot (+local-y, --tail-gap behind the
                     cone tip) so — once rotated by --ang — it streams
                     straight back, opposite travel, never over the figure. */

.d16-meteors {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none; /* never draggable, never blocks the hero */
  display: none;        /* revealed only under no-preference */
  will-change: transform;
  --d16-spark: var(--color-periwinkle);
}

/* the meteor sits at its lane centre; the rotating wrapper inside carries
   both the spin and the run. */
.d16-meteor {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform: translate(var(--lx, 50vw), var(--ly, 50vh));
}

/* the whole comet: rotated by --ang so the figure's scoop->tip axis lies on
   the travel line (scoop LEADS), and it TRAVELS along its own local -Y axis
   (the animation drives translateY). transform-origin 0 0 = the figure
   centre. Parked at the run's start (130vh down the local axis) when
   unanimated, so reduced motion shows nothing on screen. */
.d16-meteor-rot {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform: rotate(var(--ang, 0deg)) translateY(180vh);
  transform-origin: 0 0;
}

/* head: centred on the rotation pivot. It inherits --ang, so the ice cream
   points the way it's going (scoop forward), leaning into the streak. */
.d16-meteor-fig {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
}


/* tail: inside the rotated wrapper, its near (bright/top) end starts a little
   down local +Y (--tail-gap) so — once the wrapper is rotated to --ang — the
   tail's near end sits JUST BEHIND the figure (opposite travel) with a small
   clear gap, never drawing over the figure body. Width tracks the head. */
.d16-meteor-tail {
  position: absolute;
  left: 0;
  top: var(--tail-gap, 24px);
  transform: translate(-50%, 0);
}

.d16-meteor-tail svg {
  display: block;
  width: calc(var(--ms, 48px) * var(--tail-scale, 1.3));
  height: auto;
  overflow: visible;
}

/* tail strands: the no-preference block animates a per-strand flicker + a
   gentle length pulse; base state here is the fully drawn soft cluster. */
.d16-tstrand { will-change: opacity; }

/* Style B spark-trail dots: base = drawn; the no-preference block streams
   the dash phase so the sparks flow back from the tip and fade. NEVER carry
   vector-effect on these pathLength-dashed lines (Chromium pitfall). */
.d16-tspark { will-change: stroke-dashoffset, opacity; }

/* --ang = the comet's heading: the rotation applied to the whole figure+tail,
   and the axis it travels along. The figure is drawn scoop-up, so rotating by
   --ang and moving toward local -Y makes the SCOOP LEAD in the screen
   direction (sin(ang), -cos(ang)) — with the cone tip + tail trailing exactly
   opposite. Round-6 carousel: a VARIED fleet of headings — a steep up-left, a
   near-horizontal rightward "across", a near-horizontal leftward "across", and
   ONE shallow down-right diagonal. Every |ang| stays <= ~120deg from straight-
   up, so the scoop always LEADS and the icon never tips inverted (no near-180
   straight-down). The travel run is widened (translateY ±180vh / ±160vh bright)
   so the near-horizontal streaks fully cross the viewport WIDTH, not just its
   height — no meteor fades mid-screen.
   --lx/--ly = lane centre (where the streak crosses mid-flight).
   --tail-gap pushes the tail's near end just past the cone tip (no overlap).
   Negative delays + varied durations scatter the passes with long gaps. */
.d16-meteor--m1 {                  /* cone, galaxy — steep up-left (the larger cone) */
  --ms: 44px;
  --ang: -58deg;
  --lx: 24vw;  --ly: 44vh;
  --mdur: 26s; --mdel: -5s;
  --tail-gap: 34px;
  --mtpulse: 3.1s; --mtpdel: -0.4s;
}

.d16-meteor--m2 {                  /* sundae cup, galaxy — near-horizontal, rightward */
  --ms: 33px;
  --tail-scale: 1.45;
  --ang: 84deg;
  --lx: 50vw;  --ly: 26vh;
  --mdur: 35s; --mdel: -22s;
  --tail-gap: 15px;
  --mtpulse: 3.7s; --mtpdel: -1.5s;
}

.d16-meteor--m3 {                  /* popsicle, galaxy — near-horizontal, leftward */
  --ms: 30px;
  --ang: -93deg;
  --lx: 55vw;  --ly: 70vh;
  --mdur: 22s; --mdel: -13s;
  --tail-gap: 13px;
  --mtpulse: 2.6s; --mtpdel: -0.9s;
}

.d16-meteor--m4 {                  /* cone, galaxy — shallow down-right (the smaller cone) */
  --ms: 32px;
  --tail-scale: 1.45;
  --ang: 116deg;
  --lx: 80vw;  --ly: 40vh;
  --mdur: 31s; --mdel: -12s;
  --tail-gap: 34px;
  --mtpulse: 3.4s; --mtpdel: -2.1s;
}

/* ---- Content: Drift's ratios — lockup HIGH, stack top-anchored ---- */

.d16-hero {
  position: relative;
  z-index: 3;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start; /* extra height falls below, into the sky */
  text-align: center;
  /* figures live behind the hero (lower z-index) and stay there, so a
     thrown figure can never cover text. The hero lets pointers fall
     through to the figures; the only interactive bits opt back in. */
  pointer-events: none;
  /* Lockwrap clearspace padding eats into this, putting the lockup's top
     edge at ~8svh. Bottom padding reserves the carousel's sky band. */
  padding: max(calc(8svh - var(--space-6)), 8px) var(--container-pad-mobile) 160px;
}

/* Lockup wrapper: padding = clearspace (≥ ½ burst height on every side). */
.d16-lockwrap {
  position: relative;
  padding: var(--space-6);
}

.d16-lockup {
  position: relative;
  z-index: 1;
  width: clamp(204px, 48vw, 330px);
  /* the hero is pointer-events:none; opt the lockup back in so the hidden
     "tap 7× to summon the floats" easter egg can count taps (cursor left as
     default so the egg stays unannounced) */
  pointer-events: auto;
  /* Optical centering: the official stacked signature is geometrically centered,
     but its mass is the ORI/CREAMERY wordmark (left) while the burst (light)
     fills the top-right. The wordmark centre sits ~10% left of the viewBox
     centre, so nudge the whole mark right by that much to centre the wordmark.
     Pure positioning — the mark itself is never recoloured/rotated/stretched. */
  transform: translateX(10%);
}

.d16-lockup svg {
  display: block;
  width: 100%;
  height: auto;
}

.d16-lockup-fill { fill: var(--color-periwinkle); }

/* ---- The lockup halo: 4 bursts on invisible enclosing ellipses ----
   Geometry: the orbit point sits at the lockwrap's center-x and a raised
   center-y (--hcy); two nested 100%-size divs sway in lockwrap-relative
   percentages a quarter period apart, tracing an ellipse (--hrx/--hry)
   sized so it clears the lockup+clearspace rectangle on every leg and
   threads the gap above the headline at the bottom. Subtle by design:
   small bursts, 54–88s periods, opacity .55–.7, no rings, no lines. */

.d16-halo {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  --hcy: 40%;
}

.d16-ho {
  position: absolute;
  inset: 0;
}

.d16-hx,
.d16-hy {
  width: 100%;
  height: 100%;
}

/* park pose (reduced motion / base): scattered points on the loops */
.d16-hx { transform: translateX(calc(var(--hrx) * var(--hpkx, 0))); }
.d16-hy { transform: translateY(calc(var(--hry) * var(--hpky, 0))); }

.d16-ho1 { --hrx: 116%; --hry: 66.7%; --hdur: 62s; --hph: -7s;  --hlead: -0.5; --hpkx: 0.95;  --hpky: 0.31; }
.d16-ho2 { --hrx: 132%; --hry: 65%;   --hdur: 78s; --hph: -31s; --hlead: -1.5; --hpkx: -0.9;  --hpky: -0.44; }
.d16-ho3 { --hrx: 120%; --hry: 66%;   --hdur: 88s; --hph: -54s; --hlead: -0.5; --hpkx: 0.31;  --hpky: -0.95; }
.d16-ho4 { --hrx: 146%; --hry: 64%;   --hdur: 54s; --hph: -18s; --hlead: -1.5; --hpkx: -0.6;  --hpky: 0.8; }

.d16-hstar {
  position: absolute;
  left: 50%;
  top: var(--hcy, 40%);
  width: var(--s, 12px);
  height: var(--s, 12px);
  margin: calc(var(--s, 12px) / -2) 0 0 calc(var(--s, 12px) / -2);
}

.d16-hstar svg {
  display: block;
  width: 100%;
  height: 100%;
  opacity: var(--o, 0.6);
}

/* ---- Entrance sparkles at the clearspace boundary ---- */

.d16-spark {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s);
  height: var(--s);
  margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2);
  color: var(--color-periwinkle);
}

.d16-spark .d16-tw { opacity: 0.7; }

/* ---- Type stack: d13's exact relationships ---- */

.d16-headline {
  margin-top: clamp(12px, 2.2svh, 28px);
  font-family: var(--font-heading);
  font-weight: var(--font-weight-semibold);
  /* Mobile: the floor + vw term were oversized on phones; desktop still
     reaches the 104px cap (just at a slightly wider viewport). */
  font-size: clamp(34px, 9.2vw, 104px);
  line-height: 1.05;
  color: var(--color-text);
  max-width: 16ch;
  text-wrap: balance;
}

.d16-lede {
  margin-top: var(--space-3);
  font-family: var(--font-body);
  font-size: 16.5px;
  line-height: 25px;
  color: var(--color-text-muted);
  max-width: 32ch;
  text-wrap: pretty;
}

.d16-labels {
  margin-top: clamp(14px, 2svh, 24px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  font-family: var(--font-subhead);
  font-weight: var(--font-weight-regular);
  letter-spacing: 0.14em;
  color: var(--color-text);
}
/* Lead line ("COMING SOON 2026") sits a touch larger, above the location. */
.d16-labels-lead {
  font-size: 15px;
  line-height: 19px;
  letter-spacing: 0.18em;
}
.d16-labels-place {
  font-size: 11.5px;
  line-height: 16px;
  color: var(--color-text-muted);
}

/* Mobile breaks at the middle dot, never mid-phrase */
.d16-nowrap { white-space: nowrap; }

/* ---- CTA pill: platinum on galaxy (AA), periwinkle burst inside ---- */

.d16-pill {
  margin-top: clamp(20px, 3svh, 30px);
  pointer-events: auto; /* the hero is pointer-events:none for figure drag */
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 56px;
  min-width: 56px;
  padding: var(--space-3) clamp(32px, 4vw, 44px);
  background: var(--color-galaxy);
  color: var(--color-text-inverse);
  border-radius: var(--radius-pill);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: var(--lh-xs);
  letter-spacing: 0.04em;
  box-shadow: var(--shadow-1);
}

.d16-pill:hover,
.d16-pill:focus-visible {
  background: color-mix(in srgb, var(--color-galaxy) 84%, var(--color-periwinkle));
}

.d16-pill:focus-visible {
  outline: 2px solid var(--color-galaxy);
  outline-offset: 3px;
}

.d16-pill-burst {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--color-periwinkle);
}

/* ---- Tilt affordance (iOS only, revealed by JS) ---- */

.d16-tilt {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 4;
  min-height: 48px;
  min-width: 48px;
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface-alt);
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-subhead);
  font-size: var(--text-2xs);
  letter-spacing: 0.12em;
  color: var(--color-galaxy);
  box-shadow: var(--shadow-1);
  cursor: pointer;
}

.d16-tilt:focus-visible {
  outline: 2px solid var(--color-galaxy);
  outline-offset: 2px;
}

/* ---- Continuous motion (entirely skipped under reduced motion) ---- */

@media (prefers-reduced-motion: no-preference) {

  /* meteorites: revealed only here, so reduced motion never shows them.
     One shared keyframe drives travel along each comet's own local axis;
     each meteor varies --ang (heading), --mdur (speed) and --mdel (scatter).
     A short bright pass inside a long cycle = streak, then a long dark gap. */
  .d16-meteors { display: block; }

  .d16-meteor-rot {
    animation: d16-meteor var(--mdur, 28s) linear var(--mdel, 0s) infinite;
  }

  /* tail life: a gentle length pulse (scaleY from the head/tip) layered with a
     soft opacity flicker. Origin is the tip (top) so it stays put and the
     strands breathe out behind it. Mild + slow — a soft glow. */
  .d16-meteor-tail svg {
    transform-origin: 50% 0;
    animation: d16-tailpulse var(--mtpulse, 3.1s) ease-in-out
      var(--mtpdel, 0s) infinite alternate;
  }

  /* Style A — feathered strands: TWO layered motions per strand for a richer
     trail. (1) an opacity shimmer WAVE: a single shared period with a
     per-strand delay cascade (-0 → -0.9s) so the brightening ripples DOWN the
     cluster rather than blinking as one. (2) a subtle per-strand sway + length
     wiggle (skewX + scaleY about the tip) so the feathers drift and breathe
     slightly out of phase with the shimmer. transform-origin pins the bright
     near end (top) so the strands only flex behind it. */
  .d16-tstrand {
    transform-origin: 50% 0;
    animation:
      d16-tailflick var(--mtflick, 2s) ease-in-out var(--mtfdel, 0s) infinite alternate,
      d16-strandsway var(--mtsway, 4.6s) ease-in-out var(--mtswdel, 0s) infinite alternate;
  }
  /* shimmer wave: same period across the cluster, staggered delay = ripple.
     sway: slightly different period per strand so the drift never locks up. */
  .d16-tstrand--core            { --mtflick: 2s; --mtfdel: -0s;   --mtsway: 5.2s; --mtswdel: -0.3s; }
  .d16-tstrand:nth-of-type(2)   { --mtflick: 2s; --mtfdel: -0.3s; --mtsway: 4.3s; --mtswdel: -1.2s; }
  .d16-tstrand:nth-of-type(3)   { --mtflick: 2s; --mtfdel: -0.45s; --mtsway: 4.9s; --mtswdel: -2.1s; }
  .d16-tstrand:nth-of-type(4)   { --mtflick: 2s; --mtfdel: -0.7s; --mtsway: 4.1s; --mtswdel: -0.8s; }
  .d16-tstrand:nth-of-type(5)   { --mtflick: 2s; --mtfdel: -0.9s; --mtsway: 4.7s; --mtswdel: -1.7s; }

  /* Style B — distinct from A: a soft wide plume that SHIMMERS (opacity glow
     layered with a gentle length breathe about the tip), under a dotted
     spark-trail that STREAMS back from the tip and TWINKLES. Each spark strand
     gets its own stream speed, fade period AND delay so the embers sparkle
     independently — a scattered twinkle, never a marching dotted line. */
  .d16-tstrand--plume {
    animation:
      d16-plumebreathe var(--mtflick, 2.6s) ease-in-out var(--mtfdel, -0.3s) infinite alternate,
      d16-plumelen var(--mtplen, 4.2s) ease-in-out var(--mtpldel, -0.8s) infinite alternate;
  }
  .d16-tspark {
    animation:
      d16-sparkstream var(--mtstream, 1.6s) linear var(--mtsdel, 0s) infinite,
      d16-sparkfade var(--mtsfade, 2s) ease-in-out var(--mtsfdel, 0s) infinite alternate;
  }
  .d16-tspark--alt { --mtstream: 2.3s; --mtsdel: -0.7s; --mtsfade: 2.9s; --mtsfdel: -1.3s; }

  /* the traversal: crossings run linear/normal (both endpoints are
     off-screen, so the wrap is invisible); side sweeps override to
     sine/alternate. Negative --tph delays start everyone mid-journey. */
  .d16-trav {
    animation-name: d16-cross;
    animation-duration: var(--tdur, 90s);
    animation-timing-function: var(--tease, linear);
    animation-delay: var(--tph, 0s);
    animation-iteration-count: infinite;
    animation-direction: var(--tdir, normal);
  }

  /* the gentle bob */
  .d16-bob {
    animation: d16-bobk var(--bdur, 13s) ease-in-out var(--bdel, 0s) infinite;
  }

  /* the slow turn: full revolutions, mixed directions via --rturn sign.
     Negative --rdel delays load each figure at its own mid-turn angle. */
  .d16-rot {
    animation: d16-turn var(--rdur, 120s) linear var(--rdel, 0s) infinite;
  }

  /* lockup halo: same sway-pair mechanics in lockwrap-relative % */
  .d16-hx {
    animation: d16-halo-x var(--hdur, 70s) cubic-bezier(0.37, 0, 0.63, 1)
      var(--hph, 0s) infinite alternate;
  }

  .d16-hy {
    animation: d16-halo-y var(--hdur, 70s) cubic-bezier(0.37, 0, 0.63, 1)
      calc(var(--hph, 0s) + var(--hdur, 70s) * var(--hlead, -0.5)) infinite alternate;
  }

  /* twinkles everywhere */
  .d16-tw {
    animation: d16-twinkle var(--td, 4.5s) ease-in-out var(--tl, 0s) infinite alternate;
  }

  /* hero bursts rotate very slowly (sanctioned for decoratives) */
  .d16-star--spin .d16-tw svg {
    animation: d16-self var(--sd, 120s) linear infinite;
  }

  /* jelly squash-and-stretch on the CTA */
  .d16-pill {
    transition: transform 0.47s cubic-bezier(0.34, 1.8, 0.4, 1);
  }

  .d16-pill:hover  { transform: scale(1.06, 0.94); }
  .d16-pill:active { transform: scale(0.9, 1.08); }
}

@keyframes d16-cross {
  from { transform: translateX(var(--tx0, -20vw)); }
  to   { transform: translateX(var(--tx1, 120vw)); }
}

/* Meteorite streak: one shared keyframe for all four. The comet travels along
   its OWN local axis — translateY runs from +130vh (started behind, down the
   local axis) to -130vh (exited ahead, toward the scoop) — while rotate(--ang)
   holds its heading. Because both the figure and the tail live in this rotated
   frame, the motion is exactly collinear with the figure and the tail trails
   precisely opposite travel, at every angle/viewport. Visible the WHOLE way
   across (full edge-to-edge), opacity ramps only at the very entrance/exit,
   then held off-screen the long remainder = the gap. */
@keyframes d16-meteor {
  0%     { transform: rotate(var(--ang)) translateY(180vh);  opacity: 0; }
  4%     { transform: rotate(var(--ang)) translateY(160vh);  opacity: 0.92; }
  22%    { transform: rotate(var(--ang)) translateY(-160vh); opacity: 0.92; }
  25%    { transform: rotate(var(--ang)) translateY(-180vh); opacity: 0; }
  25.01% { transform: rotate(var(--ang)) translateY(180vh);  opacity: 0; }
  100%   { transform: rotate(var(--ang)) translateY(180vh);  opacity: 0; }
}

/* gentle comet-tail length pulse: strands breathe out from the tip */
@keyframes d16-tailpulse {
  from { transform: scaleY(0.9); }
  to   { transform: scaleY(1.08); }
}

/* soft per-strand opacity flicker — a shimmering glow, never a hard blink.
   Staggered delays across the strands turn this into a wave rippling down the
   feathered cluster. */
@keyframes d16-tailflick {
  from { opacity: 0.5; }
  to   { opacity: 1; }
}

/* Style A per-strand sway + length wiggle: a gentle skew + scaleY about the
   bright tip (transform-origin 50% 0) so the feathers drift and breathe a
   touch, out of phase with the shimmer. Tiny amplitudes — a soft live trail. */
@keyframes d16-strandsway {
  from { transform: skewX(-2.2deg) scaleY(0.96); }
  to   { transform: skewX(2.2deg)  scaleY(1.05); }
}

/* Style B plume: a slow soft opacity breathe — glows in and out. */
@keyframes d16-plumebreathe {
  from { opacity: 0.5; }
  to   { opacity: 1; }
}

/* Style B plume length breathe: the plume stretches/relaxes from the tip,
   layered under the opacity breathe for a soft pulsing column of haze. */
@keyframes d16-plumelen {
  from { transform: scaleY(0.94); }
  to   { transform: scaleY(1.07); }
}

/* Style B sparks: stream the dash phase one full gap so the little sparks
   flow steadily back from the tip (pathLength=100, gap≈13 → shift 13.5). */
@keyframes d16-sparkstream {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: 13.5; }
}

/* and they twinkle in and out as they stream — each spark strand runs its own
   period + delay so the embers sparkle independently, a scattered shimmer. */
@keyframes d16-sparkfade {
  from { opacity: 0.22; }
  to   { opacity: 0.95; }
}

@keyframes d16-bobk {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(var(--by, -8px)); }
}

@keyframes d16-turn {
  from { transform: rotate(var(--ra, 0deg)); }
  to   { transform: rotate(calc(var(--ra, 0deg) + var(--rturn, 360deg))); }
}

@keyframes d16-halo-x {
  from { transform: translateX(calc(-1 * var(--hrx))); }
  to   { transform: translateX(var(--hrx)); }
}

@keyframes d16-halo-y {
  from { transform: translateY(calc(-1 * var(--hry))); }
  to   { transform: translateY(var(--hry)); }
}

@keyframes d16-twinkle {
  from { opacity: 0.35; transform: scale(0.88); }
  to   { opacity: 0.8;  transform: scale(1.08); }
}

@keyframes d16-self {
  from { transform: rotate(0turn); }
  to   { transform: rotate(1turn); }
}

/* ---- Desktop (≥1180px): the centered text column runs nearly the full
   height at 1440×900, so no clear full-width band exists — all seven
   figures run partial traversals instead: in from a side edge, a long
   sweep through the side sky column (deepest in the top corners, which
   are open all the way to the lockup clearspace), back out. Sweeps are
   sine/alternate, 50–75s per leg; apexes keep every rotation envelope
   clear of the lockup rect and the headline/lede/pill column. Both
   constellations split to opposite corners, halo ellipses re-tune for
   the wider clearance rectangle. ---- */

@media (min-width: 1180px) {

  .d16-float--f1 { --y: 9%;  --s: 100px; --tx0: var(--goL); --tx1: 26vw; --tdur: 58s; --tph: -38s;  --tease: var(--tease-sweep); --tdir: alternate; --tpark: 14vw; }
  .d16-float--f2 { --y: 30%; --s: 116px; --tx0: var(--goL); --tx1: 26vw; --tdur: 70s; --tph: -30s;  --tease: var(--tease-sweep); --tdir: alternate; --tpark: 7vw; }
  .d16-float--f3 { --y: 52%; --s: 118px; --tx0: var(--goL); --tx1: 9vw;  --tdur: 52s; --tph: -4s;   --tease: var(--tease-sweep); --tdir: alternate; --tpark: 2vw; }
  .d16-float--f4 { --y: 78%; --s: 108px; --tx0: var(--goL); --tx1: 10vw; --tdur: 75s; --tph: -100s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 4vw; }
  .d16-float--f5 { --y: 50%; --s: 112px; --tx0: var(--goR); --tx1: 84vw; --tdur: 60s; --tph: -45s;  --tease: var(--tease-sweep); --tdir: alternate; --tpark: 87vw; }
  .d16-float--f6 { display: inline; --y: 79%; --s: 100px; --tx0: var(--goR); --tx1: 84vw; --tdur: 50s; --tph: -12s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 90vw; }
  .d16-float--f7 { display: inline; --y: 8%;  --s: 88px;  --tx0: var(--goR); --tx1: 67vw; --tdur: 66s; --tph: -45s; --tease: var(--tease-sweep); --tdir: alternate; --tpark: 72vw; }

  .d16-fig--a {
    right: 3vw;
    top: 6svh;
    bottom: auto;
    width: 26vmin;
  }

  .d16-fig--b {
    left: 3vw;
    right: auto;
    bottom: 5svh;
    width: 23vmin;
  }

  .d16-hero1 { --x: 62%; --y: 95%; }
  .d16-hero2 { --x: 6%;  --y: 64%; --s: 72px; }

  /* halo: raised center, wider legs (the clearspace rectangle is the
     same fraction of the wrap, but the headline gap below is tighter) */
  .d16-halo { --hcy: 38%; }

  .d16-ho1 { --hrx: 148%; --hry: 66%; }
  .d16-ho2 { --hrx: 170%; --hry: 65%; }
  .d16-ho3 { --hrx: 155%; --hry: 65.8%; }
  .d16-ho4 { --hrx: 200%; --hry: 64%; }

  .d16-hero {
    padding-left: var(--container-pad-desktop);
    padding-right: var(--container-pad-desktop);
    padding-bottom: var(--space-12);
  }

  .d16-lede {
    font-size: var(--text-lg);
    line-height: var(--lh-lg);
    max-width: 46ch;
  }

  .d16-labels-lead {
    font-size: 17px;
    line-height: 22px;
  }
  .d16-labels-place {
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
  }
}

/* Mobile: thin the decorative burst field to cut compositor layers. Each
   twinkling star is its own promoted layer; the full desktop field (~17MB of
   layer backing-store) risks stutter/eviction on low-end mobile GPUs — the IG
   audience. We drop the large peripheral "whisper" bursts, the central band
   behind the hero text, and half the mid twinkle field. The headline figures,
   meteors, lockup halo and remaining stars keep the sky alive. */
@media (max-width: 700px) {
  .d16-far--whisper { display: none; }
  .d16-sky-far .d16-far--soft { display: none; }
  .d16-sky-mid .d16-star:nth-of-type(even) { display: none; }

  /* Phone fit: smaller logo + hero type, and lift the whole stack up while
     leaving the IG pill where it is (its larger top margin reclaims the lift),
     so the text gets more breathing room below instead of feeling cramped. */
  .d16-hero { padding-top: max(calc(5svh - var(--space-6)), 8px); }
  .d16-lockup { width: clamp(165px, 48.4vw, 216px); }
  .d16-headline { font-size: clamp(31px, 8.6vw, 66px); margin-top: clamp(10px, 1.8svh, 24px); }
  .d16-lede { font-size: 17px; line-height: 25px; }
  .d16-labels-lead { font-size: 15.5px; line-height: 20px; }
  .d16-labels-place { font-size: 12px; line-height: 16.5px; }
  .d16-pill { margin-top: clamp(40px, 6svh, 60px); }

  /* Drifters ~20% larger on phones — the mobile-first base read a touch small
     once revealed; bump HEIGHT (--s) only, positions/lanes unchanged. */
  .d16-float--f1 { --s: 53px; }
  .d16-float--f2 { --s: 55px; }
  .d16-float--f3 { --s: 53px; }
  .d16-float--f4 { --s: 60px; }
  .d16-float--f5 { --s: 55px; }
  .d16-float--f6 { --s: 50px; }
  .d16-float--f7 { --s: 46px; }

  /* Meteorites another ~25% smaller on phones (+ proportional tail gap). */
  .d16-meteor--m1 { --ms: 34px; --tail-gap: 26px; }
  .d16-meteor--m2 { --ms: 25px; --tail-gap: 12px; }
  .d16-meteor--m3 { --ms: 22px; --tail-gap: 10px; }
  .d16-meteor--m4 { --ms: 24px; --tail-gap: 24px; }
}
