/* ═══════════════════════════════════════════════════════════════════════════
   The opening chapter's back layers, and the stages that stand in front.

   A fixed canvas used to live here holding one particle field for the whole
   document, and it drew one formation: the studio's mark. The mark is now an
   image on the landing screen (`.hero-mark` below, lit by styles/hero.css), so
   the field drew a second copy of a shape the page already had, in the same
   pocket of the same frame. The canvas is gone and `scripts/main.js` never
   builds the field; `scripts/swarm/` stays, because the wave draws through its
   point shader and the field itself is one call from returning.

   What is left in this file is the vignette that closes the frame, the mark's
   place in the stack, and the stages. On compositing: everything here is laid
   over the page with plain source-over. `mix-blend-mode: plus-lighter` is
   deliberately NOT used: the wave is already additive inside its own canvas
   (`gl.blendFunc(ONE, ONE)`, see scripts/swarm/renderer.js) and that canvas is
   genuinely transparent between its points, so source-over lays the result over
   the page exactly as drawn. plus-lighter would re-blend an already-additive
   image against an un-isolated backdrop, which is a second set of rules to
   reason about for no visible gain.
   ═════════════════════════════════════════════════════════════════════════ */

/* A soft darkening at the edges of the frame. The formations are widest at
   their dust shell, and without this the cloud ends at the window rather than
   receding past it. */
.swarm-vignette {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 128% 96% at 50% 46%,
      transparent 56%, rgba(2, 6, 11, 0.26) 82%, rgba(2, 6, 11, 0.64) 100%);
}

/* ── the mark ─────────────────────────────────────────────────────────────
   The studio's own mark, standing at the back of the opening chapter, where a
   Spline robot used to stand. It is the same artifact the STUDIO section
   discovers by light, on the same terms and from the same file, and it is
   served from this origin, which the robot was not.

   Layering: it carries the canvas's own z-index and comes before it in the
   document, so paint order (not a magic number) puts it behind the cloud.
   Nothing here may raise it above; the whole effect is that the swarm passes
   in FRONT of it. One thing stands behind it since the depth layers came: the
   atmosphere (`.hero-atmos`, styles/depth.css), the fog the mark is seen
   through, which is the first positioned element in the document now.

   Placement: 64% across, which is exactly where the opening chapter frames its
   formation (`camera.frameX` in scripts/swarm/chapters.js). The particles draw
   this same mark, so the image is the solid the field is a sketch of, and they
   share one pocket on the right while the copy keeps the left. Centred was
   tried and is a trap: the hero's scrim is a left-hand wash, so half of it
   arrives at 8% brightness.

   The reveal itself: rest opacity, the cold pool and the travelling window,
   is in styles/hero.css beside the rest of the hero's furniture. What lives
   here is only where it sits in the stack. */
.hero-mark {
  position: fixed;
  left: 64%;
  top: 50%;
  z-index: 0;
  width: min(54vw, 720px);
  aspect-ratio: 1 / 1;
  pointer-events: none;
  opacity: 0;
  /* Two transforms, and they are different kinds of thing. The first is the
     boot choreography: the mark rises 26px into place once. The second is
     the JOURNEY: `--mdx`/`--mdy` carry the V from this resting box toward the
     upper right of the process section, `--ms` grows it, and `--mshift` is
     how far it rides up with that section once its stage lets go, so the
     composition leaves the screen as one thing rather than the V staying
     behind. All four are written by scripts/process/journey.js from the
     scroll, and all four are identity until the reader leaves the hero, so
     the resting mark is exactly the resting mark. They are on THIS rule as
     well as the lit one below so that the moment the chapter releases the
     mark, it fades where it is instead of snapping back to the centre. */
  transform: translate(-50%, calc(-50% + 26px))
             translate(var(--mdx, 0px), calc(var(--mdy, 0px) + var(--mshift, 0px)))
             scale(var(--ms, 1));
  transition: opacity 900ms var(--ease-soft) 260ms,
              transform 1100ms var(--ease-out) 260ms;
}

/* It belongs to the opening chapter: `data-chapter` is set on the root by
   main.js for whichever chapter owns the viewport, so it comes up when the
   reader is in the hero: and, since the process section, to the journey
   after it: `html.journey` is set by scripts/process/journey.js for as long
   as the reader is anywhere below the hero, and it is what keeps the V lit
   past its own chapter while it travels into the next section's background. */
html[data-chapter='signal'] .hero-mark,
html.journey .hero-mark {
  opacity: 1;
  transform: translate(-50%, -50%)
             translate(var(--mdx, 0px), calc(var(--mdy, 0px) + var(--mshift, 0px)))
             scale(var(--ms, 1));
}

/* On the journey the V recedes as it travels, a fifth of its light, on the
   same number that moves it: and nothing about it is transitioned: every
   value is rewritten on the frame it changes, and a transition on a value
   that changes every frame is a layer lagging the page. When the journey
   ends off the top of the screen the chapter rule above has already let go,
   and the mark's own fade below the fold is the base rule's 900ms. */
html.journey .hero-mark {
  opacity: calc(1 - 0.2 * var(--mark-p, 0));
  transition: none;
}

/* ── the hero yields ───────────────────────────────────────────────────────
   As the reader scrolls, the opening copy is already moving up and out with
   the page. `--yield`, written on the section by scripts/process/journey.js
   over the first quarter of the travel, takes its WEIGHT away before its
   position has gone: less light, a short drift ahead of the scroll, and a
   softness that says the eye has moved on to the next plane. The controls
   and the small labels go with it. Restrained on purpose: it is one number
   on one block, and at rest it is the identity. */
.section--hero .hero__inner .chapter-copy {
  opacity: calc(1 - var(--yield, 0));
  /* The copy is the nearest plane in the frame, and the camera is moving
     forward: what is nearest leaves fastest and grows a little as it passes
     the lens. Three per cent, from its own left edge, so it leans out of the
     frame rather than swelling in it. */
  transform: translate3d(0, calc(var(--yield, 0) * -28px), 0)
             scale(calc(1 + var(--yield, 0) * 0.06));
  transform-origin: 0% 50%;
  filter: blur(calc(var(--yield, 0) * 3px));
}
.section--hero .chapter-signals,
.section--hero .chapter-scroll { opacity: calc(1 - var(--yield, 0)); }

/* Forced colours asks for a page made of system colours; an additive cloud of
   arbitrary blues is the opposite of that. main.js never builds the swarm
   there: this makes sure nothing of it is left on screen either way. The
   mark goes with it: it is a lit artifact discovered by a pointer, which is
   the same answer. */
@media (forced-colors: active) {
  .swarm-vignette,
  .hero-mark { display: none; }
}

/* ── the chapter stages ───────────────────────────────────────────────────
   A stage is a screen of scroll with its copy held to one side. The swarm is
   behind it and the copy is over it, so the only thing a stage draws is the
   gradient that keeps type legible against whatever the cloud is doing. */

.chapter-stage {
  position: relative;
  z-index: 2;
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.chapter-stage .section__pin {
  position: relative;
  width: 100%;
  min-height: 100svh;
  display: flex;
  align-items: center;
}

/* The scrim is a one-sided wash, not a full-frame dimmer: the cloud stays
   fully visible on the empty side of the layout, which is the side the
   composition is actually on. */
.chapter-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
}

/* The hero's copy sits left, so its wash is a left one like every other
   chapter's. A centred pool was tried and is a trap: the formation is centred
   too, so the pool lands squarely on the composition and darkens the one part
   of the frame the reader is meant to be looking at. Measured, it was taking
   about 70% of the light out of the brightest cells of the picture. */
.section--hero .chapter-scrim {
  background: linear-gradient(100deg,
    rgba(2, 6, 11, 0.92) 0%, rgba(2, 6, 11, 0.74) 22%,
    rgba(2, 6, 11, 0.22) 46%, transparent 66%);
}

/* `.section__inner` is `position:absolute; inset:0` (experience.css), and the
   chapter stages put a `.col` inside it, which positions itself. The hero has
   no column (its copy is the only thing in the frame) so the inner does the
   centring there instead. */
.hero__inner {
  display: flex;
  align-items: center;
  padding: 0 var(--gutter);
}

/* On a chapter stage the copy IS the `.col`, and `.col` positions itself
   absolutely (experience.css). This stylesheet loads later, so a bare
   `position`/`width` here would win on equal specificity and drop the column
   back into flow: which reads as merely "high up" on a left-hand chapter and
   as copy clipped off the window edge on a right-hand one. So nothing here
   touches layout for a `.col`; it only sets the measure. */
.chapter-copy:not(.col) {
  position: relative;
  max-width: 44ch;
}

.hero__inner .chapter-copy { width: min(52ch, 56vw); }

/* ── the wordmark ─────────────────────────────────────────────────────────
   The studio's name, crowning the opening chapter. It is set on `.display`
   (foundation.css) like every other piece of display type on the page, in the
   `--cyan` variant, and everything below is composition rather than colour.

   Tracking: this is the one place Bebas is set OPEN. Section headings on this
   site are tracked tight and synthetically condensed, because they are reading
   matter set at a distance; a logotype is not read as a word at all, it is read
   as a shape, and the letters need air between them to hold one. There is no
   compensating text-indent here, unlike `.whisper` in experience.css: letter
   spacing is trailing space, so it only ever moves a CENTRED line off centre.
   This one is ranged left, and an indent would push it off the gutter that the
   kicker above it and the headline below it both start on.

   The halo is what makes the face read as cyan-lit rather than merely cool. It
   is L2, not L3: structural light, wide and low, the kind you notice only when
   it is taken away. The second shadow is not decoration, the wordmark is
   painted through `background-clip: text`, so it has no fill of its own to sit
   on, and against a bright band of the cloud its top edge would otherwise
   disappear. */
.hero-wordmark {
  font-size: clamp(3rem, 6.4vw, 6.4rem);
  line-height: 0.88;
  letter-spacing: 0.085em;
  margin: 0 0 0.16em;
  filter: drop-shadow(0 0 34px var(--cyan-l2))
          drop-shadow(0 2px 3px rgba(0, 0, 0, 0.72));
}

.chapter-body {
  font-size: clamp(0.95rem, 1.15vw, 1.06rem);
  line-height: 1.62;
  color: var(--text-2);
  margin: 0 0 1.5em;
  max-width: 46ch;
}

.chapter-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.1rem 1.6rem;
}

/* ── the hero's furniture ─────────────────────────────────────────────── */

.chapter-signals {
  position: absolute;
  left: var(--gutter);
  bottom: 3.6rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-3);
}

.chapter-signals li { display: inline-flex; align-items: center; }
.chapter-signals li + li::before {
  content: '';
  width: 3px; height: 3px;
  margin-right: 1.4rem;
  border-radius: 50%;
  background: var(--cyan-2, #22e6ff);
  opacity: 0.55;
}

.chapter-scroll {
  position: absolute;
  right: var(--gutter);
  bottom: 3.6rem;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
  text-decoration: none;
  transition: color 240ms ease;
}

.chapter-scroll:hover,
.chapter-scroll:focus-visible { color: var(--cyan-2, #22e6ff); }

.chapter-scroll i {
  width: 1px;
  height: 34px;
  background: linear-gradient(to bottom, var(--cyan-2, #22e6ff), transparent);
  animation: chapter-scroll-pulse 2.4s ease-in-out infinite;
}

@keyframes chapter-scroll-pulse {
  0%, 100% { opacity: 0.30; transform: scaleY(0.7); transform-origin: top; }
  50%      { opacity: 0.95; transform: scaleY(1);   transform-origin: top; }
}

/* ── narrow and short windows ─────────────────────────────────────────────
   This condition is copied exactly from experience.css, where the same query
   turns `.section__inner` from an absolute overlay into an in-flow block and
   `.col` stops positioning itself. The two must agree: if this one is narrower,
   there is a band of window sizes where the copy is in normal flow but the
   hero furniture is still absolutely positioned over it. */
@media (max-width: 900px), (max-height: 639px), (max-aspect-ratio: 1/1) {
  /* experience.css dissolves the pin here (`display: contents`) so the section
     itself becomes the flex column. This stylesheet loads last and its
     `.chapter-stage .section__pin` rule is the more specific one, so it has to
     hand the layout back explicitly rather than leave the pin as a flex row. */
  .chapter-stage .section__pin { display: contents; }

  /* The composition turns ninety degrees on one column. There is no empty
     side to leave the drawing in, so it is given the empty TOP instead: the
     copy is anchored to the foot of the screen and the field frames its
     formation a third of the way down (`viewFrameY` in scripts/swarm/swarm.js),
     so the picture and the sentence share the screen rather than the sentence
     standing on the picture. Centred, the copy sat squarely over the one part
     of the frame the reader was meant to be looking at, under a wash that took
     three quarters of its light away. */
  .chapter-stage {
    flex-direction: column;
    justify-content: flex-end;
    align-items: stretch;
    min-height: 92svh;
  }

  /* The wash follows the copy down: clear over the drawing, and only closing
     in where the type actually is. */
  .section--hero .chapter-scrim {
    background: linear-gradient(180deg,
      transparent 0%, rgba(2, 6, 11, 0.16) 28%, rgba(2, 6, 11, 0.70) 48%,
      rgba(2, 6, 11, 0.88) 66%, rgba(2, 6, 11, 0.92) 100%);
  }

  /* On one column the mark stands in the upper part of the first screen,
     the room the copy leaves when it is anchored to the foot, and the room
     the wash above keeps clear on purpose. It used to be `display: none`
     here, on the grounds that there was nowhere for it to stand that was not
     behind the copy; that was true while the particle field drew the same
     mark a third of the way down, and with the field gone a phone opened on
     a black screen with the studio's one object missing from it.

     It is still fixed, still the one element, still lit by the chapter. The
     journey does not run on this frame, so its variables are the identity
     and one number replaces them: `--exit`, written by
     scripts/process/journey.js as the hero leaves, the mark dims, rises a
     little and steps back on it, which is the phone's own `--yield`. Sized
     by the shorter of the two so a landscape strip gets a mark that fits
     under its header rather than one that fills it. */
  .hero-mark,
  html[data-chapter='signal'] .hero-mark,
  html.journey .hero-mark {
    left: 50%;
    top: calc(var(--header-h) - 2svh);
    width: min(72vw, 46svh);
    transform: translate(-50%, calc(var(--exit, 0) * -12svh)) scale(calc(1 - var(--exit, 0) * 0.1));
    transition: opacity 900ms var(--ease-soft) 260ms;
  }
  html[data-chapter='signal'] .hero-mark { opacity: calc(1 - var(--exit, 0)); }

  .hero__inner { padding: 0; }
  .hero__inner .chapter-copy { width: 100%; }
  .chapter-copy:not(.col) { max-width: none; }

  /* Set smaller and tighter on one column, where the copy runs full width and
     6.4vw would put the wordmark alone on the first screen with the sentence it
     is meant to be crowning pushed under the fold.

     Fitted by the one relationship that has to survive the breakpoint: the
     wordmark is twice the height of the headline under it. Measured, Bebas at
     this tracking sets the string 3.54× its font size, so the ceiling is what
     keeps that ratio at 834px (an 80px wordmark over a 40px headline) rather
     than what keeps it inside the gutter, the column is 742px there and the
     string is 283px, so overflow is not the binding constraint at any width in
     this range. The floor is: at 320px the vw term is what the clamp discards,
     and 2.6rem is where the string still reads as a logotype rather than as a
     line of small caps. */
  .hero-wordmark {
    font-size: clamp(2.6rem, 13vw, 5rem);
    letter-spacing: 0.06em;
  }

  /* With the pin dissolved these become direct flex children of the section,
     in DOM order: and experience.css gives `.section__inner` `order: 1`, which
     puts everything with the default order 0 AHEAD of the copy. The hero's
     furniture belongs under its headline, so it is ordered explicitly rather
     than left to inherit a source order that no longer means what it did. */
  /* The scroll hint leaves with the pointer it was written for: a thumb does
     not need to be told the page scrolls, and the sixty pixels it took at
     the foot of the screen are the room between the mark and the kicker. */
  .chapter-scroll { display: none; }
  /* The signals restate the kicker three lines above them, the same three
     capabilities, in the same mono, and on one column that repeat is a
     screen's worth of furniture standing between the headline and the mark.
     The kicker keeps the words; the mark keeps the room. */
  .chapter-signals { display: none; }
  .chapter-scrim { order: 0; }
  .chapter-scroll i { display: none; }
  /* The opening chapter is one full screen, copy at its foot, the mark above. */
  .section--hero.chapter-stage { min-height: 100svh; }
}

@media (prefers-reduced-motion: reduce) {
  .chapter-scroll i { animation: none; opacity: 0.6; }
  /* Reduced motion keeps the mark, and stops it moving: the attention field
     does not run there at all (scripts/studio/attention.js returns early), so
     what is left is a still artifact at its resting brightness rather than one
     that rises, settles and is read across by a travelling window. */
  .hero-mark { transition: none !important; }
  /* ...and on one column it does not travel on the scroll either: it dims as
     the hero leaves, and stays where it stood. */
  @media (max-width: 900px), (max-height: 639px), (max-aspect-ratio: 1/1) {
    .hero-mark,
    html[data-chapter='signal'] .hero-mark { transform: translate(-50%, 0); }
  }
}
