/* ==========================================================================
   THE STANDARD — "THE ASSAY"   (.asy, index.html § 04)

   Replaces the bench (css/bench.css — unlinked, kept on disk), which
   replaced the standard (css/standard.css — also unlinked, also kept).

   TWO PIECES OF CLIENT DIRECTION BUILT THIS FILE, AND THEY PULL AGAINST
   EACH OTHER, SO BOTH ARE WRITTEN DOWN.

   1. "REMOVE THE LINES FROM THE SECTION, IT MAKES THE DESIGN MORE UGLY."
      The bench WAS its lines — five hairlines at one pitch running the
      full width and struck on through the photograph. At 1280 that put
      eleven horizontal rules on screen at once, which reads as a table,
      or as a page still loading. There is no rule anywhere in this file:
      structure comes from marks, tone, radius and space.

   2. "DON'T MAKE JUST TEXT AND IMAGE DESIGN", with flowstatecreative.org
      given as the reference. The pass before this one set the five
      criteria as one continuous run-in passage with the photograph
      floated into it — typographically correct, and the verdict was that
      the text organisation looked wrong. A run-in paragraph asks the
      reader to work out where one criterion ends and the next begins;
      five discrete entries do not. THE FIVE ARE FIVE ENTRIES AGAIN,
      ranged left on one axis with a numbered mark each.

   THE VOCABULARY IS READ OFF THE REFERENCE AND TRANSLATED, NOT COPIED.
   flowstatecreative.org is a dark navy site with a teal/blue accent, and
   its premium register comes from a specific, repeatable set of moves —
   measured off the live page rather than guessed at:

     - soft radial accent orbs, 350–600px, 6–14% alpha, behind everything
     - a rounded image wrap (22px) inside an OFFSET DASHED RING (28px,
       2px dashed accent at 12%)
     - a wide soft shadow under the image, 0 30px 80px at 30% black
     - small floating panels overlapping the image: 14px radius,
       translucent ground, 1px accent border
     - gradient-filled marks and gradient-clipped headline words
     - a 32x2 accent bar before every section label

   Every one of those is here in Marble's own palette — antique gold on
   warm stone instead of teal on navy — and the 32x2 bar was already in
   the system as `.eyebrow::before`, which is why the label needs no extra
   markup for it.

   THE RADIUS IS THE ONE PLACE THIS SECTION LEAVES THE SITE'S SCALE.
   --radius-md is 3px and that is right for a ruled editorial page; it is
   not what the reference's register is made of. The rounding here is
   local, declared once as --asyr on .asy, and it does not leak — no other
   sheet reads it.

   THE SECTION IS ALIVE AT REST, WHICH IS THE OTHER HALF OF THE BRIEF.
   Five independent clocks, all long, all out of phase, not one of them
   travelling toward a full state:

     --asyb   24s   the plate breathes, a printed photograph alive at rest
     orb A    26s   drifts and swells behind the list
     orb B    34s   the same, out of phase, behind the plate
     ring     20s   the dashed register mark round the plate swells 3%
     aura     18s   the glow the plate sits in rises and falls

   AND THE SECTION IS BUILT ON SCROLL, WHICH IS THE HALF THAT WAS MISSING.
   js/motion.js guarded initAssay on `.asy__c`, a class this section has
   never carried, so the module returned before its first tween and the
   standard was the one block on the home page with no entrance at all —
   every ramp below was written, registered and never driven. It now runs
   TWO numbers, and they are different KINDS of number:

     --asyp  the ENTRANCE. 0 to 1 over the section's own run-up, and it
             ARRIVES. It stages the five criteria one after another,
             settles the plate the last 26px and 4.5% of its size, closes
             the dashed ring onto it, brings the aura up under it and
             lands the chip last, at 0.66.

     --asys  the PASSAGE. -1 to +1 across the whole time the section is
             on screen, and it never arrives anywhere — it is only ever
             passing through 0. Four things read it at four rates and in
             both directions: the near orb up, the far orb down, the
             stage up, the crop INSIDE the plate down, the chip up
             against the stage it sits on. That counter-motion is the
             depth; a single element sliding is not parallax.

   THE ONE BRIGHT EVENT IS THE STRIKE. Each entry derives --asyf, a
   triangle on its OWN transit — 0, 1 at the halfway point, 0 again — and
   a gold halo flares off that criterion's mark as it lands and is gone
   by the time it is up. Five separate strikes going down the column.
   Read the difference carefully, because it is the whole constraint:
   five things each flaring once as they arrive is an event per item; one
   light moving through five things is a sweep, and this page has twice
   been told to stop drawing those.

   NOTHING SWEEPS, FILLS, COUNTS OR RUNS OUT — including the metallic word
   in the heading, whose gradient is static with the breath on brightness
   instead. A gradient that slides its own background-position is a
   highlight travelling across a word, and travelling highlights are
   exactly what this page has twice been told to stop doing.

   REST IS THE FINISHED SECTION. --asyp initial-value 1 and --asys 0, so
   with no JS, a dead CDN or reduced motion every entry is at full key,
   the plate is settled and the crop is centred.

   NAME CHECK: `.asy`, `--asy*`, `data-assay` and `initAssay` were grepped
   across css/, js/ and every *.html before a line was written.
   ========================================================================== */

/* the section's scrub clock. 1 = every entry at full key, plate settled. */
@property --asyp { syntax: "<number>"; inherits: true; initial-value: 1; }
/* --asyx IS GONE, AND IT COULD NEVER HAVE WORKED. It was the pan — -2 on
   criterion 01 to +2 on criterion 05 — spent on
   `object-position: calc(50% + var(--asyx) * 3.4%)`.

   AN object-position PERCENTAGE RESOLVES AGAINST THE DIFFERENCE BETWEEN
   THE BOX AND THE PICTURE INSIDE IT. The plate is 4/5 and the source is
   4/5, so `cover` scales the picture to fill it exactly and that
   difference is ZERO. Every value of --asyx resolved to the same
   position; measured, the picture's left edge sat at the same offset at
   -2, 0 and +2. The one thing in this section that answered the hand had
   no travel to move within, and the arithmetic that made the plate's
   proportion exactly right — see THE PLATE — is what took it away.

   The walk is five literal `translate` values on the picture instead, one
   per :has() rule at the foot of this file, moving within the 5% of
   overscan the 1.1 scale leaves. */
/* the plate's breath */
@property --asyb { syntax: "<number>"; inherits: true; initial-value: 0; }
/* THE PASSAGE. -1 with the section's top still at the foot of the window,
   0 as it crosses the middle, +1 as it leaves over the top. Everything
   that hangs in depth reads it — the two orbs, the whole stage, the crop
   INSIDE the plate and the chip — and every one of them takes it at a
   different rate and in one of two directions, which is what makes the
   section a field with depth in it rather than a block sliding past.

   IT IS NOT AN ENTRANCE AND IT NEVER ARRIVES ANYWHERE. --asyp is the
   entrance and it finishes; this one runs the full passage and is only
   ever passing through 0. Registered at 0 so a dead script leaves the
   depth field exactly at its settled position. */
@property --asys { syntax: "<number>"; inherits: true; initial-value: 0; }

.asy {
  position: relative;
  padding-block: clamp(3rem, 2rem + 4vw, 6rem);
  /* the orbs are 17–38rem across and hang off both edges; nothing may push
     the document sideways. Vertical is left alone so the plate's shadow
     can fall into the section's own padding. */
  overflow-x: clip;

  /* THE LOCAL ROUNDING, declared once. --radius-md (3px) is the site's
     editorial scale and is deliberately not used in this section. */
  --asyr: clamp(0.9rem, 0.6rem + 0.9vw, 1.4rem);

  /* HOW FAR THE DEPTH FIELD TRAVELS. One multiplier on every parallax in
     the file so the whole field can be pulled back in one place. Cut on
     narrow screens, where the stage sits ABOVE the list instead of
     beside it: the same offsets that read as depth in two columns read
     as a gap opening and closing in one. */
  --asyd: 1;

}

/* --------------------------------------------------------------------------
   THE GROUND. Two gold orbs well under the ink, on long clocks out of
   phase with each other. This is the section's life at rest: nothing
   moves against the reader, the ground simply is never twice the same.
   -------------------------------------------------------------------------- */
.asy__orb {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform, opacity;
}

.asy__orb--a {
  width: clamp(20rem, 34vw, 38rem);
  aspect-ratio: 1;
  top: -6%;
  left: -12%;
  background: radial-gradient(circle, rgb(184 145 88 / 0.13) 0%, rgb(184 145 88 / 0) 68%);
  animation: asy-drift-a 26s ease-in-out infinite alternate;
  /* the passage, on `translate` rather than `transform`, because the
     drift clock owns `transform` and the individual transform properties
     COMPOSE with it instead of overwriting it. Both orbs would otherwise
     have to give up their own life to gain the parallax. */
  translate: 0 calc(var(--asys) * var(--asyd) * -2.6rem);
}

.asy__orb--b {
  width: clamp(17rem, 28vw, 31rem);
  aspect-ratio: 1;
  right: -10%;
  bottom: -14%;
  background: radial-gradient(circle, rgb(192 156 96 / 0.1) 0%, rgb(192 156 96 / 0) 70%);
  animation: asy-drift-b 34s ease-in-out infinite alternate;
  /* the far orb goes the OTHER way and further. Two grounds separating
     as the section crosses the screen is the depth. */
  translate: 0 calc(var(--asys) * var(--asyd) * 3.4rem);
}

@keyframes asy-drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); opacity: 0.75; }
  to   { transform: translate3d(3.5%, 5%, 0) scale(1.1); opacity: 1; }
}
@keyframes asy-drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.08); opacity: 1; }
  to   { transform: translate3d(-4%, -4.5%, 0) scale(1); opacity: 0.7; }
}

/* --------------------------------------------------------------------------
   THE GRID. The list and the stage, side by side, both reaching a
   container edge. 42% to the stage is what holds the photograph at 500px
   at 1280 — see the note on the plate.
   -------------------------------------------------------------------------- */
.asy__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(19rem, 42%, 32rem);
  gap: clamp(2.2rem, 1.2rem + 3.2vw, 4.6rem);
  align-items: center;
}

/* --------------------------------------------------------------------------
   THE HEAD
   -------------------------------------------------------------------------- */
.asy__h {
  margin: 0 0 clamp(1.4rem, 1rem + 1.5vw, 2.4rem);
  font-size: clamp(1.85rem, 1.35rem + 2.15vw, 3.05rem);
  font-weight: 600;
  letter-spacing: -0.036em;
  line-height: 1.05;
  text-wrap: balance;
  max-width: 15ch;
}

/* THE METALLIC WORD. Gold-light into gold into gold-shadow across the
   phrase, clipped to the glyphs. Static gradient, breath on brightness —
   see the header for why it may not slide. */
.asy__h em {
  font-style: italic;
  font-weight: 500;
  background: linear-gradient(104deg,
    var(--gold-light) 0%,
    var(--gold) 46%,
    var(--gold-shadow) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: asy-sheen 7s ease-in-out infinite alternate;
}

@keyframes asy-sheen {
  from { filter: brightness(0.94); }
  to   { filter: brightness(1.12); }
}

/* --------------------------------------------------------------------------
   THE FIVE. Five entries on one axis: a ringed numeral, the name, the
   line. No box, no border and no rule — the marks carry the structure and
   the wash under the hand carries the state.
   -------------------------------------------------------------------------- */
.asy__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(0.15rem, 0.05rem + 0.3vw, 0.4rem);
}

.asy__item {
  --asyq: clamp(0, calc((var(--asyp) - 0.04 - var(--asyi) * 0.12) / 0.34), 1);

  /* THE STRIKE. A triangle riding the entry's OWN transit: 0 before the
     criterion moves, 1 at the halfway point, 0 again once it is up and read
     — quiet at both ends, so it is an EVENT and not a state. It is
     what makes each mark flare as its criterion lands, five separate
     strikes going down the column rather than one light travelling
     through them, which is the thing this page has twice been told to
     stop drawing.

     Written as the smaller of a rise and a fall because abs() is younger
     than the browsers this site still has to look right in. */
  --asyf: min(clamp(0, calc(var(--asyq) / 0.5), 1),
              clamp(0, calc((1 - var(--asyq)) / 0.5), 1));

  position: relative;
  z-index: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: clamp(0.9rem, 0.7rem + 0.7vw, 1.35rem);
  padding: clamp(1rem, 0.75rem + 0.9vw, 1.5rem) clamp(0.8rem, 0.6rem + 0.7vw, 1.2rem);
  border-radius: var(--asyr);
  /* THE ARRIVAL: a rise and a short lateral together, staggered down the
     five. The lateral is what turns a stack of lines coming up off the
     floor into five things being SET DOWN on an axis — it lands each
     entry against the left edge the marks already establish. 10px, which
     is under the mark's own diameter: any further and the column visibly
     reflows on a slow scrub. */
  transform: translate3d(calc((1 - var(--asyq)) * -10px), calc((1 - var(--asyq)) * 16px), 0);
  opacity: calc(0.12 + var(--asyq) * 0.88);

  /* ONE OWNER PER PROPERTY. `transform` above is the SCRUB'S and carries
     no transition; `translate` is THE HAND'S, carries a literal value at
     both ends, and does.

     Sharing `transform` broke it two ways. The hand's rule was a whole-
     value override, so hovering an entry while the section was still
     entering discarded its arrival and snapped it into place. And a
     transition over a scrub-fed transform answers every frame's new value
     by starting a fresh ease toward it, so the entry can only trail the
     scroll. On separate properties the two compose, and either can run
     part-way through the other. */
  transition: translate 0.45s var(--ease-out);
  translate: 0 0;
}

/* THE WASH UNDER THE HAND. A soft gold field with no edge of its own,
   raked across the entry so it reads as light falling on it rather than
   as a panel switching on. */
.asy__item::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(100deg,
    rgb(184 145 88 / 0.13) 0%,
    rgb(184 145 88 / 0.05) 46%,
    rgb(184 145 88 / 0) 82%);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}

/* THE MARK. A gold ring with the criterion's figure in it. Under the hand
   the ring FILLS and the figure reverses to the ground colour — the one
   high-contrast event in the section, and the reason nothing else here
   needs a border to say "this one". */
.asy__mark {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(2.4rem, 1.9rem + 1.1vw, 3rem);
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgb(184 145 88 / 0.32);
  background: rgb(184 145 88 / 0.08);
  /* the flare swells the mark a few percent at the moment of the strike.
     On `scale`, not `transform`, so it stays clear of anything the hand
     does. */
  scale: calc(1 + var(--asyf) * 0.07);
  transition: border-color 0.45s var(--ease-out), background 0.45s var(--ease-out),
              box-shadow 0.45s var(--ease-out);
}

/* THE FLARE ITSELF. A gold halo struck off the ring as the criterion
   lands and gone by the time it is at full key — the section's one
   bright event, and the reason the five read as five.

   NO TRANSITION ON IT, ON PURPOSE. --asyf is scrubbed, and a transition
   on a scrubbed value is not a smoothing, it is a lag: every frame
   restarts a 0.45s ease that never finishes, and the halo trails the
   scroll by half a second. */
.asy__mark::after {
  content: "";
  position: absolute;
  inset: -32%;
  /* BEHIND THE MARK, not over it. Painted in front, a 0.42 gold field
     washes the numeral at the peak of the strike and the figure is
     briefly unreadable — the one moment the eye is on it. .asy__item is
     already a stacking context (position + z-index: 0), so -1 puts the
     halo behind the ring and its own translucent ground while leaving it
     above the wash under the entry. */
  z-index: -1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle,
    rgb(214 178 112 / 0.42) 0%,
    rgb(184 145 88 / 0.14) 44%,
    rgb(184 145 88 / 0) 72%);
  opacity: var(--asyf);
  scale: calc(0.68 + var(--asyf) * 0.5);
}

.asy__digit {
  font-size: clamp(0.7rem, 0.62rem + 0.24vw, 0.82rem);
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--gold);
  transition: color 0.45s var(--ease-out);
}

/* the left column and the entry's text cell. Both need min-width: 0 and
   nothing else: inside a grid track an auto-sized child refuses to shrink
   below its longest unbreakable word, and "Coachable and open to feedback"
   is long enough to push the track past its share at 1120. */
.asy__col,
.asy__body { min-width: 0; }

.asy__nm {
  margin: 0;
  /* 22.6px at 1280. At 20.8px the name sat closer to its own 15.2px
     description than to the 48.8px heading above it, and the column read
     as one undifferentiated grey. The step is now 48.8 / 22.6 / 15.2. */
  font-size: clamp(1.05rem, 0.85rem + 0.7vw, 1.45rem);
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.25;
  color: var(--ink);
  text-wrap: balance;
  transition: color 0.45s var(--ease-out);
}

.asy__nt {
  margin: clamp(0.28rem, 0.22rem + 0.2vw, 0.42rem) 0 0;
  font-size: clamp(0.875rem, 0.84rem + 0.14vw, 0.95rem);
  line-height: 1.55;
  color: var(--vein);
  text-wrap: pretty;
  transition: color 0.45s var(--ease-out);
}

/* --------------------------------------------------------------------------
   THE STAGE. Three layers under the photograph and one over it — ring,
   aura, plate, chip. Not one of them has a straight edge of its own,
   which is the whole point of the register.
   -------------------------------------------------------------------------- */
.asy__stage {
  position: relative;
  align-self: center;
  /* THE STAGE RISES SLOWER THAN THE PAGE, and the near orb behind it
     rises faster — three rates across one section is the whole of the
     depth. 2.2rem over a full passage: the plate is never anywhere the
     static composition did not already put it, it just does not arrive
     there at the same moment the type does. */
  translate: 0 calc(var(--asys) * var(--asyd) * -2.2rem);
  will-change: translate;
}

/* THE RING. An offset dashed gold outline standing behind the plate — the
   reference's move, where it is a static decoration.

   IT DOES NOT ROTATE, AND THAT WAS A DELIBERATE REVERSAL. The first draft
   turned it once every 72 seconds on the theory that a revolution that
   slow is felt rather than seen. It is not: 360deg over 72s is 5deg per
   second, which is 50deg in the ten seconds somebody spends reading the
   five criteria — plainly visible, and A ROTATING DASHED RING IS THE
   EXACT SHAPE OF A LOADING SPINNER. This page has been told twice to stop
   putting loading indicators in front of the reader, and drawing one by
   accident is no better than drawing one on purpose. It breathes instead:
   a 3% scale and a little opacity on a 20s clock, which is life without
   direction. */
.asy__ring {
  position: absolute;
  inset: clamp(-1.6rem, -1.1rem - 0.9vw, -0.9rem);
  z-index: 0;
  border: 1px dashed rgb(184 145 88 / 0.22);
  border-radius: calc(var(--asyr) * 2.1);
  pointer-events: none;
  animation: asy-ringbreath 20s ease-in-out infinite alternate;
  /* the register mark CLOSES onto the plate as the section settles,
     from 8% wide down to true. It still may not turn — see above. On
     `scale` so the 20s breath keeps its own `transform`. */
  scale: calc(1.08 - var(--asyp) * 0.08);
}

@keyframes asy-ringbreath {
  from { transform: scale(0.985); opacity: 0.66; }
  to   { transform: scale(1.015); opacity: 1; }
}

/* THE AURA. The glow the plate sits in, rising and falling on its own
   clock. Behind the plate, so it never washes the faces. */
.asy__aura {
  position: absolute;
  inset: -18%;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(62% 52% at 32% 34%, rgb(184 145 88 / 0.3), rgb(184 145 88 / 0) 70%),
    radial-gradient(58% 58% at 74% 76%, rgb(143 109 56 / 0.24), rgb(143 109 56 / 0) 72%);
  animation: asy-glow 18s ease-in-out infinite alternate;
  /* the glow comes up UNDER the plate as it lands, and lags the stage on
     the passage — a light source that is not stuck to the thing it is
     lighting. */
  scale: calc(0.86 + var(--asyp) * 0.14);
  translate: 0 calc(var(--asys) * var(--asyd) * 1.5rem);
}

@keyframes asy-glow {
  from { opacity: 0.55; transform: scale(0.96); }
  to   { opacity: 1;    transform: scale(1.05); }
}

/* THE PLATE.

   500px WIDE AT 1280 AND THAT IS A CEILING, NOT A CHOICE. The source is
   800x1000. At 42% of an 1190 container the plate renders 500px, a 0.63
   downscale, sharp on a 2x screen. A full-bleed band was drawn and thrown
   out on the same arithmetic: 1190px of frame from an 800px file is a
   1.49x upscale, and the softness shows on faces.

   8/9 (0.889) AGAINST A 0.800 SOURCE, so `cover` crops 11% off the
   HEIGHT, never the width. The three people stand across the frame; the
   crop must not reach them from the sides. */
.asy__plate {
  position: relative;
  z-index: 1;
  margin: 0;
  /* 4/5 IS THE SOURCE'S OWN RATIO (800x1000), so `cover` crops NOTHING.
     8/9 was drawn first and took 11% off the height for no reason: the
     column beside it is 630px whatever the plate does, so the plate is
     free to take the proportion the file was made at. */
  aspect-ratio: 4 / 5;
  border-radius: var(--asyr);
  overflow: hidden;
  background: var(--surface);
  /* the wide soft drop the reference uses, closed by a gold hairline and
     lit along its top edge — this site takes its light from edges */
  box-shadow:
    0 30px 80px -24px rgb(0 0 0 / 0.62),
    0 0 0 1px rgb(184 145 88 / 0.14),
    inset 0 1px 0 rgb(255 255 255 / 0.05);
  transform: translate3d(0, calc((1 - var(--asyp)) * 26px), 0);
  /* and opens the last 4.5% of its size as it settles. A photograph that
     grows into its frame reads as a print being laid down; one that only
     slides up reads as a page still loading its images. Kept small
     enough that the gold hairline never visibly changes weight. */
  scale: calc(0.955 + var(--asyp) * 0.045);
}

.asy__plate img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  /* 34% holds the subject line; the x is what the hand moves */
  /* STATIC, AND THE CROP WALK IS NOT HERE ANY MORE. It was
     `calc(50% + var(--asyx) * 3.4%)`, and it had no travel: an
     object-position percentage resolves against the difference between
     the box and the picture inside it, the plate is 4/5 and the source is
     4/5, so `cover` leaves that difference at exactly ZERO. The walk now
     rides `translate`, where a percentage is of the element and the scale
     below leaves 5% of overscan each side to move within. */
  object-position: 50% 34%;
  filter: brightness(0.86) saturate(0.95);

  /* THE CROP MOVES INSIDE THE FRAME, AGAINST THE FRAME. The plate goes
     up on the passage and the picture in it comes down, which is the one
     move that makes a photograph read as seen THROUGH a window rather
     than printed on the page.

     IT IS ON transform, NOT object-position. The x of object-position is
     the hand's, and it carries a 0.6s transition for it; scrubbing the y
     through the same property would restart that ease every frame and
     the crop would trail the scroll.

     THE BASE SCALE IS 1.10 AND IT IS NOT A TASTE DECISION. 4/5 is the
     source's own ratio, so `cover` crops nothing and the picture exactly
     fills the plate — the instant it is offset 2.2% the plate's own
     ground shows at the edge.

     THE TOP EDGE IS THE TIGHT ONE AND IT IS THE ONE TO CHECK. The scale
     is taken about a point 38% down, so a 10% overscan is NOT 5% a side:
     it is 3.8% above and 6.2% below. Against a 2.2% travel that leaves
     1.6% of the plate's height in hand at the end of the passage, about
     10px at 1280 — measured at both extremes, not reasoned about. Raise
     the travel without raising the scale and the plate's own ground
     appears along the top edge at the bottom of the passage only, which
     is the kind of fault that never shows up in a still.

     The extra scale on entry (0.06) closes with --asyp, so the settled
     state is the sharp one. */
  transform:
    translate3d(0, calc(var(--asys) * var(--asyd) * 2.2% + (1 - var(--asyp)) * -1.6%), 0)
    scale(calc(1.1 + var(--asyb) * 0.03 + (1 - var(--asyp)) * 0.06));
  transform-origin: 50% 38%;
  will-change: transform;
  /* only `filter` is transitioned, and only because both its ends are
     literal. The transform is scrub-fed and must stay out of the list —
     see the note on .asy__item. The crop's easing is the `translate`
     transition below, over five literal stops. */
  animation: asy-breathe 24s ease-in-out infinite alternate;
  /* the crop walk rides `translate` — literal values set by the five
     :has() rules, so it is a transition that actually runs. `transform`
     is the scrub's and stays out of the list. The 5% of horizontal
     overscan the 1.1 scale leaves is what the 1.8% travel moves within. */
  translate: 0 0;
  transition: filter 0.55s var(--ease-out), translate 0.6s var(--ease-out);
}

@keyframes asy-breathe {
  from { --asyb: 0; }
  to   { --asyb: 1; }
}

/* the veil: lit from the top right, sinking toward the list at the lower
   left, so the plate sits INTO the page rather than on it. Written as a
   floor plus the remainder so it cannot exceed 1 — opacity CLAMPS above
   1, so a ramp that leaves its own legal range at one end flattens out
   and fails silently. */
.asy__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(118deg,
      rgb(var(--scrim-rgb) / 0.46) 0%,
      rgb(var(--scrim-rgb) / 0.1) 40%,
      rgb(var(--scrim-rgb) / 0) 64%),
    linear-gradient(0deg,
      rgb(var(--scrim-rgb) / 0.5) 0%,
      rgb(var(--scrim-rgb) / 0) 44%);
  /* THE SCRUB OWNS IT, so it carries no transition: a half-second ease
     restarted on every scrubbed frame can only lag the scroll. */
  opacity: calc(0.82 + (1 - var(--asyp)) * 0.18);
}

/* THE CHIP. One floating panel overlapping the plate's lower left — the
   reference's move. It carries the count and nothing else: this section
   is one of the places on the page where an invented figure would be a
   claim, and there are exactly five criteria. */
.asy__chip {
  /* THE CHIP IS THE LAST THING IN. Its own window opens at 0.66 of the
     entrance and closes at 0.94, so it lands after the plate has settled
     and after the fifth criterion has been struck — the count is a
     statement about the five, and it may not arrive before them. */
  --asyc: clamp(0, calc((var(--asyp) - 0.66) / 0.28), 1);

  position: absolute;
  z-index: 2;
  left: clamp(-1.1rem, -0.7rem - 0.7vw, -0.5rem);
  bottom: clamp(1.1rem, 0.8rem + 1vw, 1.9rem);
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.62rem 0.95rem;
  border-radius: calc(var(--asyr) * 0.72);
  border: 1px solid rgb(184 145 88 / 0.26);
  background: rgb(23 21 15 / 0.86);
  box-shadow: 0 14px 40px -12px rgb(0 0 0 / 0.6);
  backdrop-filter: blur(6px);
  pointer-events: none;
  opacity: var(--asyc);
  /* in from the plate's edge, and then floating against the stage for
     the rest of the passage — the chip travels the other way to the
     thing it overlaps, which is what keeps it reading as detached. */
  translate:
    calc((1 - var(--asyc)) * 16px)
    calc((1 - var(--asyc)) * 10px + var(--asys) * var(--asyd) * -1.7rem);
}

.asy__chip b {
  font-size: clamp(1.05rem, 0.95rem + 0.4vw, 1.3rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums lining-nums;
  background: linear-gradient(135deg, var(--gold-light), var(--gold-shadow));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.asy__chip i {
  font-style: normal;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--vein);
}

/* --------------------------------------------------------------------------
   THE HAND. Pure :has(), no script, so it keeps working with the script
   dead. The entry under the pointer lights and the crop walks to that
   point in the group; the other four are left exactly as they were,
   because copy that greys out as the mouse moves punishes reading.
   -------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  /* the same 6px it always was, moved off `transform` — which the scrub
     owns — and onto `translate`, which composes with it. Hovering an
     entry mid-entrance used to discard its arrival entirely. */
  .asy__item:hover { translate: 6px 0; }
  .asy__item:hover::before { opacity: 1; }

  .asy__item:hover .asy__mark {
    border-color: rgb(192 156 96 / 0.9);
    background: linear-gradient(135deg, var(--gold-light), var(--gold-shadow));
    box-shadow: 0 0 22px -4px rgb(184 145 88 / 0.55);
  }
  .asy__item:hover .asy__digit { color: var(--obsidian); }
  .asy__item:hover .asy__nm { color: var(--white); }
  .asy__item:hover .asy__nt { color: var(--ink); }

  /* THE CROP WALKS ACROSS THE GROUP. Criterion 01 pushes the picture
     right, which brings the left of the group into the middle of the
     plate; 05 pushes it left. Written straight onto the picture as five
     literal values rather than through a number on the section, because
     a var()-fed declaration cannot be transitioned and this one has to
     ease — a crop that cuts between positions reads as a glitch. */
  .asy:has(.asy__item[data-c="1"]:hover) .asy__plate img { translate: 1.8% 0; }
  .asy:has(.asy__item[data-c="2"]:hover) .asy__plate img { translate: 0.9% 0; }
  .asy:has(.asy__item[data-c="3"]:hover) .asy__plate img { translate: 0 0; }
  .asy:has(.asy__item[data-c="4"]:hover) .asy__plate img { translate: -0.9% 0; }
  .asy:has(.asy__item[data-c="5"]:hover) .asy__plate img { translate: -1.8% 0; }

  /* THE PICTURE ANSWERS THE HAND, THE VEIL NO LONGER DOES. The veil's
     opacity is the scrub's and carries no transition, so lifting it on
     hover would be a hard cut. brightness carries the whole gesture
     instead — two literal values, so it eases — and one lift was always
     enough: raising the exposure AND pulling the scrim was double-
     counting the same beat. */
  .asy:has(.asy__item:hover) .asy__plate img { filter: brightness(1.06) saturate(1.02); }
}

/* --------------------------------------------------------------------------
   NARROW. The stage goes over the list at 900 — below that the column
   left for the five entries is narrower than the descriptions need and
   the names start wrapping to three lines.
   -------------------------------------------------------------------------- */
@media (max-width: 1120px) {
  .asy__grid { grid-template-columns: minmax(0, 1fr) 38%; }
}

@media (max-width: 900px) {
  /* THE DEPTH FIELD IS PULLED BACK ONCE THE STAGE SITS ABOVE THE LIST.
     Side by side, the offsets read as three planes at three rates. In
     one column they are all on the same axis as the scroll itself, and
     the same numbers read as a gap opening and closing between the
     picture and the type. Halved, not killed: on a phone the picture
     drifting a little against its frame is most of what makes the
     section feel made. */
  .asy { --asyd: 0.5; }

  .asy__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.8rem, 1.2rem + 2.4vw, 2.8rem);
  }

  /* head, then picture, then the five. The head and the list are one
     column child so they stay together when the stage moves up. */
  .asy__stage { order: -1; }

  .asy__plate { aspect-ratio: 3 / 2; }
  .asy__ring { inset: clamp(-1.1rem, -0.8rem - 0.5vw, -0.7rem); }
  .asy__aura { inset: -12%; }
  .asy__h { max-width: 20ch; }
}

@media (max-width: 560px) {
  .asy { --asyd: 0.38; }
  .asy__plate { aspect-ratio: 4 / 3; }
  .asy__item { padding-inline: 0.6rem; }
  .asy__chip { left: 0.6rem; padding: 0.5rem 0.8rem; }
  .asy__orb--a { width: 22rem; }
}

/* --------------------------------------------------------------------------
   REDUCED MOTION. Every clock stops. The section is a finished
   composition without any of them.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* --asyp rests at 1 and --asys at 0, so the settle, the strike and the
     whole depth field are already at their finished values here and
     nothing needs unwinding. What is written out is the five standing
     clocks and anything whose REST value is not its natural one. */
  .asy { --asyd: 0; }
  .asy__orb,
  .asy__ring,
  .asy__aura,
  .asy__h em { animation: none; }
  .asy__orb,
  .asy__aura,
  .asy__chip,
  .asy__stage { translate: none; }
  .asy__mark::after { opacity: 0; }
  .asy__plate { transform: none; }
  .asy__plate img {
    animation: none;
    transform: scale(1.1);
    transition: none;
  }
  .asy__item,
  .asy__item::before,
  .asy__mark,
  .asy__digit,
  .asy__nm,
  .asy__nt { transition: none; }
  .asy__item { translate: none; }
}
