/* ============================================================================
   HERO — a centered statement inside a quiet gradient atmosphere, weighted
   toward the work below so the first two sections read as one opening beat.
   ========================================================================== */

.hero {
  position: relative;
  min-height: clamp(560px, 78svh, 680px);
  background: var(--paper);
}

.hero__pin {
  position: relative;
  min-height: inherit;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: calc(var(--hd-h) + var(--s6) + var(--s3)) var(--s6);
  background:
    radial-gradient(ellipse at 50% -18%, var(--sky-glow) 0, transparent 58%),
    linear-gradient(180deg, var(--sky-top) 0%, var(--sky-mid) 62%, var(--paper) 100%);
  isolation: isolate;
  overflow: hidden;
}

.hero__in {
  position: relative;
  z-index: var(--z-content);
  display: grid;
  place-items: center;
  gap: var(--s5);
}

.hero__say {
  display: grid;
  justify-items: center;
  max-width: 1180px;
  text-align: center;
}

/* ------------------------------------------------------------------ kicker */
.hero__kicker {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s4);
}

/* The one place the hero spends a marker colour on a fill rather than a
   stroke — the same licence base.css takes for the availability dot. The
   radius is deliberately lopsided: a true circle here reads as a status LED,
   and this is meant to read as a blot of ink left by the same pen that draws
   the scribbles. */
.hero__kicker i {
  flex: none;
  width: 8px;
  height: 8px;
  background: var(--marker-1);
  border-radius: 62% 38% 55% 45% / 52% 62% 38% 48%;
}

/* -------------------------------------------------------------- the headline */
.hero__h {
  /* The <br>s in the markup are art direction, not content. `balance`
     (inherited from the h1 rule in base.css) would try to even out the lines
     around them and quietly undo the shape. */
  text-wrap: wrap;
  margin-bottom: var(--s5);
  max-width: 100%;
  font-size: clamp(60px, 5.4vw, 78px);
}

/* The artwork stage is parked, so the headline keeps the hero-only type rung
   at every width instead of stepping down to the section-title scale. */

/* ---------------------------------------------------------------- the lead */
/* The 34ch measure comes from .lead in base.css. Restating it here would give
   the site two opinions about the width of the same paragraph. */
.hero__lead { margin-bottom: var(--s5); }

.hero__acts {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--s3);
}

@media (max-width: 700px) {
  .hero {
    min-height: clamp(520px, 82svh, 620px);
  }
  .hero__pin {
    position: relative;
    min-height: inherit;
    padding-block: calc(var(--hd-h) + var(--s4)) var(--s5);
  }
}

@media (min-width: 561px) and (max-width: 1024px) {
  .hero__h { font-size: clamp(50px, 6.6vw, 62px); }
}

@media (max-width: 560px) {
  .hero__h {
    max-width: 11.5ch;
    font-size: clamp(42px, 11.5vw, 48px);
    line-height: .96;
    text-wrap: balance;
  }
  .hero__h br { display: none; }
}

/* ----------------------------------------------------------------- the stage
   The reserved area, and the entire point of this layout. It is drawn as a
   surface — recessed paper, a radius, a hairline you can actually see — because
   an area that is not visibly declared is not reserved, it is just where things
   happen to appear.

   The aspect ratio is fixed rather than derived from its contents so the box is
   exactly the same shape before the module boots, after it fails, and while the
   pool is mid-flight. Nothing inside it ever changes its size, so the hero
   cannot reflow once it has painted. */
.hero__stage {
  position: relative;
  /* Landscape while it is stacked under the type, because the height it takes
     here is height the visitor has to scroll past. 3/2 was the first try and
     came out visibly denser than the desktop square — the same seven pieces in
     two thirds of the area. 4/3 is the shortest this can be and still read as
     a panel with room in it rather than a strip. */
  aspect-ratio: 4 / 3;
  background: var(--paper-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-3);
  /* stickers.js clamps every piece to sit half its own box in from each edge,
     so this should never have anything to do. It stays because the guarantee
     that art cannot escape the frame belongs to the frame, not to arithmetic
     in another file. */
  overflow: hidden;
}

/* Still stacked, but wide enough that 4/3 turns into a 600px monolith the
   visitor has to scroll the whole of. The ratio widens instead of the box
   growing — same frame, less of the screen. */
@media (min-width: 620px) {
  .hero__stage { aspect-ratio: 16 / 9; }
}

@media (min-width: 900px) {
  /* Square once it has a column to itself: a canvas, not a banner. At the
     widths this applies to it lands within ~30px of the type block's height,
     which is what makes the two columns read as a pair. */
  .hero__stage { aspect-ratio: 1 / 1; }
}

/* ------------------------------------------------------------------- the cue
   Bottom-left, inside the frame, in the metadata register — it labels the box
   it sits in rather than floating in the hero at large, which is how the
   previous version managed to point at nothing in particular. */
.hero__cue {
  position: absolute;
  left: var(--s4);
  bottom: var(--s4);
  z-index: var(--z-content);
  color: var(--faint);
  /* Decorative and inert: it must never intercept a pointer event meant for the
     stage it is standing on, and a drag across the stage must not select it. */
  pointer-events: none;
  user-select: none;
  transition: opacity var(--t-move) var(--ease-out);
}

/* Set by stickers.js on the first spawn. Once you have understood the
   instruction, the instruction is clutter — and it fades rather than moving,
   because something sliding away in the corner of a box you are drawing in is
   a second thing to look at. */
.hero__cue.is-gone { opacity: 0; }

/* ----------------------------------------------------------- the sticker pool
   Inset over the stage, under the cue, deaf to the pointer — the stage itself
   is what listens. */
.hero__field {
  position: absolute;
  inset: 0;
  z-index: var(--z-art);
  pointer-events: none;
  /* Two dozen transforming children. Containment keeps their work out of the
     document's layout and paint trees rather than merely clipping the result of
     it, and paint containment does the clipping too. */
  contain: layout paint;

  /* The one number hero.css and stickers.js have to agree on, so it is
     declared once here and read back out of the cascade there.

     Every sticker node is a fixed square of this size and every size variation
     is a scale() on top of it. That is what lets the pool animate without ever
     writing a width, a height or an aspect-ratio after init — and it means a
     piece that finishes loading late cannot reflow anything, because its box
     was already the right size before the bytes arrived.

     120px is the largest the stage ever draws a piece; the source art is
     ~150px square, so even the biggest is a downscale. It went up from 104
     when the field became a 554px box: a piece that read as a passing detail
     across a full-width hero reads as a speck inside a frame. */
  --hero-sticker-box: 120px;
}

.hero__sticker {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--hero-sticker-box);
  height: var(--hero-sticker-box);
  /* The art is not uniformly square (105–160px in either axis). A fixed square
     box plus `contain` letterboxes each piece without anyone having to measure
     it or store its ratio — and the transparent margin that leaves is what
     absorbs the ±14° tilt at the frame's edge. */
  object-fit: contain;
  opacity: 0;
  /* Parked well outside the stage until the pool is first used, so a node that
     has never been assigned a piece cannot flash at the origin. */
  transform: translate3d(-200%, -200%, 0) scale(0);
}

/* The resting composition: placed once at load and thereafter only ever
   changes opacity, so it is the one thing on this stage that can be eased by
   the cascade instead of by the loop. The trail cannot — its opacity is
   rewritten every frame, and a transition would spend the whole frame chasing
   a value that has already moved. */
.hero__sticker.is-rest {
  transition: opacity var(--t-move) var(--ease-out);
}

/* Compositor promotion is only worth its memory when something is actually
   going to move. The resting pieces are excluded: a six-node opacity
   transition twice a visit does not need a permanent layer, and under reduced
   motion .is-live is never set at all. */
.hero__field.is-live .hero__sticker:not(.is-rest) {
  will-change: transform, opacity;
}
