/* hero-trail.css — the hero's pointer/touch image trail. Spec: 132 revision 5.

   Its own file rather than an append to hero.css, following hero-sky.css's
   precedent for a hero sub-feature: hero.css is at 599 lines against 003 §7's
   ~700 ceiling, and 062 §4.1 / 063 §6.1 record that file being mis-estimated by
   50 lines once already.

   Three boxes, and the middle one exists for touch-action alone:

     .hero__trail        inset: 0   touch-action: pan-y   (items live here)
       └ .hero__trail-band   middle ?trailband= fraction   touch-action: none

   ⚠️ THE BAND IS A REAL ELEMENT AND HAS TO BE (133 §13.2, R5). An earlier draft
   said "gutters left at pan-y", which would not have happened: a full-bleed
   .hero__trail is the hit element in the gutters too, so they resolve to
   whatever it declares. The browser intersects touch-action from the HIT
   ELEMENT up to the nearest scrollable ancestor — projects.css:131-143 records
   the same rule and the same PM error in the opposite direction — so the split
   has to be by element, not by declaration. */

.hero__trail {
  position: absolute;
  inset: 0;

  /* ⚠️ Z-INDEX 2 — hero.css's LAYER STACK, slot 2, and NOT ONLY A NUMBER.
     This declaration is a stacking context, so a trail item's own z-index —
     any value, including the 999 C2a forces — is confined below .bar at z 71
     and .badge at z 70, which are fixed siblings of <main> (index.html:93,
     :109) and outside this stack entirely. That is Xin Lu's requirement that
     the effect never covers the chrome (132 §1 item 6).

     ⚠️ TWO OTHER STACKING CONTEXTS DO THE SAME JOB TODAY AND NEITHER IS A
     GUARANTEE. 133 §5.2 measured all twelve combinations: ScrollTrigger's pin
     on #hero (absent under prefers-reduced-motion, because scroll.js:30-36's
     MEDIA has no reduce entry and no trigger is ever created), and
     .section__inner's `will-change: opacity` at layout.css:24 — which is a
     LIVE REMOVAL CANDIDATE, since intro.css:81-98 already removed it from one
     .section__inner and `grep -rn "section__inner" js` returns zero.

     So: do not renumber this, do not remove it, and do not "protect" it by
     pinning layout.css:24 in place — that would make a shared stylesheet
     load-bearing for the hero's z-order, which is the situation this comment
     exists to end. C2b asserts at least one context and reports which. */
  z-index: 2;

  /* The gutters. touch-action: pan-y here and none on the band below, so a drag
     starting in a gutter scrolls the page across the whole hero and a touch
     starting in the middle draws for that gesture. THE ZONE IS DECIDED WHERE
     THE FINGER LANDS, NOT WHERE IT TRAVELS — the same rule that makes the
     project cards work. */
  touch-action: pan-y;

  /* Items are pointer-events: none (below); this box and the band are hit
     targets, because touch-action is only consulted on an element the hit test
     actually reaches. 133 §5.6 measured an item with pointer-events: none
     vanishing from elementsFromPoint entirely. */
  pointer-events: auto;
}

/* ⚠️ IN REAL PIXELS AT THE THREE VIEWPORTS THIS PROJECT TESTS, the default 0.75
   band leaves gutters of 48.8 px at 390 wide, 53.8 px at 430 and 40.0 px at
   320 — the last is under the usual 44 px minimum, which is why ?trailband=
   exists rather than being a constant. */
.hero__trail-band {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(100% * var(--trail-band, 0.75));
  left: calc(50% - 100% * var(--trail-band, 0.75) / 2);
  touch-action: none;
  pointer-events: auto;
}

.hero__trail-item {
  position: absolute;

  /* ⚠️ THE MULTIPLIER APPLIES TO THE CLAMP'S RESULT, NOT ITS MIDDLE TERM, and
     the difference is the whole usefulness of the flag (133 §7). ?bodysize=
     multiplies --body-ratio inside clamp(15px, …, 20px) (body.js:73,
     hero.css:308), so on a width-bound viewport the floor governs and the flag
     correctly appears dead — body.js:65-71 documents that honestly and 130 §5
     records someone being confused by it anyway.

     Written this way instead, ?trailsize= is monotone and always live. The
     middle-term form would be dead below 2.26x at 390 px and 2.76x at 320 px,
     which is the device Xin Lu types flags on. C6 prints which term binds. */
  width: calc(clamp(150px, 17vw, 300px) * var(--trail-size, 1));

  /* ⚠️ NATIVE ASPECT, NO CROP (132 §1 item 2). 1126x736 = 1.5299:1, which Xin
     Lu chose after being shown that the images are not the 16:9 he assumed.
     The box's ratio equals the file's exactly, so `100% 100%` neither crops
     nor letterboxes — and unlike `cover` it cannot start cropping if a future
     re-cut changes the source ratio without this line being revisited. */
  aspect-ratio: 900 / 588;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: center;

  /* ⚠️ NOT A HIT TARGET. Items spawn under the pointer, so hit-testable items
     would take every hit from the band beneath them and the touch split would
     stop working on the one gesture that matters. It also keeps them out of
     elementsFromPoint, which is why every probe in 132 §7 asserts an item's
     PRESENCE in the returned array before asserting its order. */
  pointer-events: none;

  /* Composited: six of these scale, rotate and fade at once over a live WebGL
     canvas. 132 §8 records frame cost as deliberately unmeasured, with a stated
     trigger — this hint is the one cheap thing that is not a measurement. */
  will-change: transform, opacity;

  /* Set per item by trail.js: --trail-r is the item's own rotation. The
     translate(-50%,-50%) centres the box on the pointer; scale is animated. */
  transform: translate(-50%, -50%) rotate(var(--trail-r, 0deg));
}

/* ⚠️ NO REDUCED-MOTION RULE HERE, DELIBERATELY, AND IT IS NOT AN OMISSION.
   Under `prefers-reduced-motion: reduce` trail.js does not spawn at all, so
   there is nothing for CSS to still. The site's pattern elsewhere is STILL, NOT
   GONE — sky/index.js:653 freezes one aurora frame, type.js:134 shows the first
   phrase complete, badge.css:83 arrives settled — and a trail is the one effect
   with no still state, because its content exists only as a consequence of
   movement. "Stop the movement" and "show nothing" are the same instruction.
   ?trail=1 forces it on for testing; C11 emulates the setting at browser level,
   because sky/index.js:657-668 records what a reduced branch nobody exercised
   cost this project. */
