/* ═══════════════════════════════════════════════════════════════════════════
   fx-ambient — the intake.
   Content falls into the open skull. A sparse cone of 1-bit glyphs (inline
   <symbol>s in the markup, stroked in --ink at the sheet's hairline weight)
   drifts down from above the head, sways, and drops into the cavity,
   shrinking and fading exactly at the rim. Underneath it the figure breathes.
   All motion is transform/opacity on the compositor; fx-ambient.js only
   supplies random numbers and pauses it.
   ═══════════════════════════════════════════════════════════════════════════ */

.intake {
  position: absolute; inset: 0;
  /* Above the drawing whatever its DOM position — the icons must cross the
     forehead in front of it and vanish at the rim, not behind the skin. */
  z-index: 1;
  pointer-events: none;
  /* Everything is placed in cqw/cqh of this box, so the field follows the
     plate through every breakpoint without a measurement in JS. */
  container-type: size;

  /* Where the hole is, in % of the plate. Measured off hero-520.png: the
     cavity mouth spans x 21–39%, its rim sits at y 4–9%. */
  --intake-x: 30;          /* cqw, centre of the mouth */
  --intake-y: 6.5;         /* cqh, the rim — where an icon vanishes */
  --intake-mouth: 6;       /* ± cqw the landings scatter across */
  --intake-spread: 34;     /* ± cqw the spawns scatter across: the cone's base */
  --intake-headroom: 34;   /* cqh above the plate a spawn may start */
  --intake-room: 9999px;   /* set by JS: distance to the section's clip edge */
  --intake-count: 14;
}
/* Below the two-column hero the figure sits under the CTA, so there is no
   headroom: the cone flattens and the glyphs are drawn in from the sides. */
@media (max-width: 899px) {
  .intake { --intake-count: 7; --intake-headroom: 5; --intake-spread: 30; }
}

/* The <symbol> sheet. Zero-sized, kept in the document so <use> resolves. */
.intake__defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.intake__g,
.intake__still {
  display: block;
  width: var(--s, 18px); height: var(--s, 18px);
  fill: none;
  stroke: var(--ink);
  stroke-width: 2.2;         /* ≈ --rule-thin once scaled to 18px */
  stroke-linecap: round; stroke-linejoin: round;
  transform-origin: 50% 50%;
}

/* A falling icon: the outer span carries the fall (translate/scale/opacity),
   the svg inside carries the sway and the wobble. Two elements because sway
   and fall both want `translate`. Its box is centred on (0,0) so the
   keyframes can speak in centre coordinates. */
.intake__i {
  position: absolute;
  left: calc(var(--s, 18px) * -.5);
  top:  calc(var(--s, 18px) * -.5);
  opacity: 0;
}

/* The still scatter: what the idea looks like when nothing may move
   (reduced motion, JS off). Three glyphs hanging above the crown. */
.intake__still { position: absolute; opacity: .55; }
.intake__still--a { --s: 17px; left: 27%; top: -8%;  rotate: -12deg; }
.intake__still--b { --s: 15px; left: 42%; top: -14%; rotate: 8deg; }
.intake__still--c { --s: 19px; left: 58%; top: -6%;  rotate: -5deg; }
@media (max-width: 899px) {
  .intake__still--a { left: 18%; top: -3%; }
  .intake__still--b { left: 38%; top: -5%; }
  .intake__still--c { left: 60%; top: -2%; }
}
.intake.is-live .intake__still { display: none; }

@media (prefers-reduced-motion: no-preference) {
  .intake__i {
    animation: intake-fall var(--dur, 10s) linear var(--delay, 0s) infinite;
  }
  .intake__g {
    /* Sway and wobble run on their own clocks, out of phase with the fall
       and with each other, so no two icons ever move in step. */
    animation:
      intake-sway var(--swt, 3.4s) ease-in-out calc(var(--delay, 0s) * .37) infinite alternate,
      intake-spin var(--rt, 4.2s)  ease-in-out calc(var(--delay, 0s) * .61) infinite alternate;
  }
  .intake.is-paused .intake__i,
  .intake.is-paused .intake__g { animation-play-state: paused; }

  /* The breathe. Gated on .is-live so it only ever runs where the JS can
     pause it; origin low on the shoulders so it's the head that lifts. It
     goes on the whole registered stack (fx-hero's layers must move as one)
     and on the bare img where the hero is still a single image. */
  .hero__plate:has(> .intake.is-live) > .hero__stack,
  .hero__plate:has(> .intake.is-live) > img {
    transform-origin: 50% 88%;
    animation: intake-breathe 6.4s ease-in-out infinite;
  }
  .hero__plate:has(> .intake.is-paused) > .hero__stack,
  .hero__plate:has(> .intake.is-paused) > img { animation-play-state: paused; }
}

/* Timing functions live in the keyframes: each applies from that keyframe
   to the next one naming the same property, so the fall can gather speed
   over its whole length while opacity and scale keep their own shape at
   the rim. */
@keyframes intake-fall {
  0% {
    translate:
      calc((var(--intake-x) + var(--intake-spread) * var(--rx0, 0)) * 1cqw)
      calc(-1 * var(--ry0, .7) * min(var(--intake-room), var(--intake-headroom) * 1cqh));
    scale: 1;
    opacity: 0;
    animation-timing-function: cubic-bezier(.35, .1, .65, .5);
  }
  14%  { opacity: var(--o, .6); }
  72%  {
    opacity: var(--o, .6); scale: .82;
    animation-timing-function: cubic-bezier(.55, 0, .9, .55);
  }
  100% {
    translate:
      calc((var(--intake-x) + var(--intake-mouth) * var(--rx1, 0)) * 1cqw)
      calc((var(--intake-y) + 1.5 * var(--ry1, 0)) * 1cqh);
    scale: .3;
    opacity: 0;
  }
}
@keyframes intake-sway {
  from { translate: calc(var(--sw, 6px) * -1) 0; }
  to   { translate: var(--sw, 6px) 0; }
}
@keyframes intake-spin {
  from { rotate: calc(var(--r, 10deg) * -1); }
  to   { rotate: var(--r, 10deg); }
}
@keyframes intake-breathe {
  50% { scale: 1.012; }
}
