/* ============================================================
   Arche Advisory — the six stages as a scrolling image sequence
   v9.2

   WHAT CHANGED, AND WHY IT IS SMALLER THAN IT LOOKS

   v4.3 through v9.1 ran these six as a rotating 3D carousel:
   cards absolutely positioned in a perspective deck, five seconds
   each, driven by js/stages.js, with a pause control because
   WCAG 2.2.2 requires one for anything that auto-advances for
   more than five seconds.

   v9.2 replaces all of that with six stacked bands. No timer, no
   transforms, no JavaScript. Three consequences worth naming:

     1. Nothing auto-advances, so 2.2.2 no longer applies and the
        pause control is gone rather than broken.
     2. The carousel showed one card's body at a time. Every word
        of all six is now on the page at once, which is why the
        copy did not have to be cut to fit.
     3. js/stages.js is dead. It is left in the folder rather than
        deleted, but nothing references it, so build/publish.py
        will not package it.

   THE LAYOUT, IN THREE PASSES

   Pass one put the image beside the copy in its own column. Pass
   two made the photograph the full-bleed background of the band
   and put the copy in a panel on top of it. Pass three — this one
   — removed the panel. The type now sits directly on the image.

   The panel existed because of a real measurement: the reference
   layout (Perplexity Computer's "How it works") carries twelve to
   eighteen words per step and these stages carry forty-eight to
   seventy-five, and forty-eight words over a photograph at display
   size does not work. What makes it work now is that the two jobs
   were separated. The STAGE NAME is display type — 40px to 104px,
   set against the viewport, and it is the thing meant to be seen
   from across the room. The BODY is capped at a 34em measure and
   set at a reading size. One element got much louder; the other
   did not get bigger at all. That is why the copy still fits.

   Nothing was cut in any of the three passes.

   FULL BLEED

   .stages breaks out of .wrap to the full viewport with the
   standard

       width: 100vw; margin-inline: calc(50% - 50vw);

   and 100vw counts the scrollbar on Windows, which would give the
   page a horizontal scroll of about seventeen pixels. `.method`
   carries `overflow-x: clip` to absorb that. `clip` and not
   `hidden`: `hidden` would make the section a scroll container and
   break `position: sticky` for anything inside it.

   ALTERNATION, AND WHY THE PANELS HUG THE CENTRE

   Odd stages put the copy panel in the left column, even stages in
   the right, so the open half of the photograph alternates down
   the page. Both sit in grid row 1 and swap columns. The DOM order
   never changes, so a screen reader always hears the image alt
   then the copy.

   The panel is then pushed toward the middle of its own column —
   `justify-self: end` on the left, `start` on the right — so the
   edge nearest the centre line sits about half the grid gap away
   from it. Without that, a capped-width panel falls to the start
   of its column, which on a 21:9 display parks stage 1 against the
   far left and stage 2 against the far right. The eye then has to
   travel most of the screen between one stage and the next, and
   the reading line moves so far that the section stops reading as
   one sequence. Hugging the centre keeps every panel inside the
   same narrow band of the screen; only which side of the centre
   line it sits on changes.

   The photograph still runs the full width underneath, so nothing
   is lost by moving the panel inward — the open space simply
   redistributes to both edges instead of one.

   LEGIBILITY

   With the panel gone, the scrim is the only thing between the
   copy and the photograph, so it carries the whole load: two
   stacked gradients, a directional wash weighted over the text
   half and a soft vertical lift at the top and bottom edges. The
   copy adds a wide, low-opacity text-shadow on top of that. Text
   never sits on unmodified photograph at any viewport.
   ============================================================ */

/* ------------------------------------------------------------
   The strip
------------------------------------------------------------ */

.stages {
  margin-top: 64px;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  border-top: var(--bw) solid var(--line);
}

/* ------------------------------------------------------------
   One band

   `isolation: isolate` gives the band its own stacking context,
   so the absolutely positioned background cannot escape it and
   z-index inside a band never has to reason about the page.
------------------------------------------------------------ */

.stage {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(20px, 2vw, 40px);
  min-height: clamp(520px, 52vw, 700px);
  padding-block: clamp(64px, 6vw, 104px);
  padding-inline: clamp(20px, 3vw, 56px);
  background: var(--ink);
  border-bottom: var(--bw) solid var(--line);
}

/* ------------------------------------------------------------
   The photograph — now the ground of the whole band
------------------------------------------------------------ */

.stage-media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--ink-750);
}

/* The band is roughly 2:1 on a wide desktop and portrait-leaning on a
   phone, so a 16:9 source is always being cropped by `cover`. Biasing
   object-position toward the open side keeps the subject — who is
   composed into that side — inside the crop instead of sliding under
   the panel. Mobile resets to centre, because there is no open side. */
.stage-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 68% 42%;
  display: block;
}

/* v9.2 — these six carry a scrubbed parallax in js/motion.js, so they
   transform on every scroll frame. Promoting them is earned rather
   than speculative; the GSAP performance skill is explicit that
   will-change should go on elements that actually animate and nowhere
   else. Scoped to .gsap-motion so it costs nothing when the vendor
   files did not load and the parallax is not running. */
.gsap-motion .stage-media img,
.gsap-motion .band--bleed .band-video {
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .gsap-motion .stage-media img,
  .gsap-motion .band--bleed .band-video {
    will-change: auto;
  }
}

.stage:nth-child(even) .stage-media img { object-position: 32% 42%; }

/* The directional scrim.

   v9.2 removed the copy panel — the text now sits directly on the
   photograph, with nothing between it and the image. The scrim is
   therefore the ONLY thing keeping the copy legible, so it does more
   work than it did and is weighted more heavily over the text half.

   Two layers, and both are needed. A single linear gradient across
   the frame cannot both hold 0.90 under a paragraph and stay light
   enough at the far edge for the photograph to read as a photograph.
   So: a directional wash that opens toward the picture side, plus a
   soft vertical lift that keeps the top and bottom edges of the band
   from going flat. */
.stage-media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(
      to bottom,
      rgba(14, 15, 13, 0.34) 0%,
      rgba(14, 15, 13, 0.10) 30%,
      rgba(14, 15, 13, 0.10) 66%,
      rgba(14, 15, 13, 0.42) 100%
    ),
    linear-gradient(
      to right,
      rgba(14, 15, 13, 0.72) 0%,
      rgba(14, 15, 13, 0.90) 20%,
      rgba(14, 15, 13, 0.90) 48%,
      rgba(14, 15, 13, 0.50) 70%,
      rgba(14, 15, 13, 0.26) 100%
    );
}

.stage:nth-child(even) .stage-media::before {
  background:
    linear-gradient(
      to bottom,
      rgba(14, 15, 13, 0.34) 0%,
      rgba(14, 15, 13, 0.10) 30%,
      rgba(14, 15, 13, 0.10) 66%,
      rgba(14, 15, 13, 0.42) 100%
    ),
    linear-gradient(
      to left,
      rgba(14, 15, 13, 0.72) 0%,
      rgba(14, 15, 13, 0.90) 20%,
      rgba(14, 15, 13, 0.90) 48%,
      rgba(14, 15, 13, 0.50) 70%,
      rgba(14, 15, 13, 0.26) 100%
    );
}

/* The disclosure asterisk belongs on the part of the photograph a
   reader can actually see, which is the side the panel is not on.
   v9.2 pushed it into the corner and dropped the disc, so the glyph
   also has to flip which way it is packed inside its 24px target. */
.stage:nth-child(even) .ai-mark {
  right: auto;
  left: 0;
  justify-content: flex-start;
  padding: 2px 0 0 5px;
}

/* ------------------------------------------------------------
   The copy — now directly on the photograph

   v9.2 removed the panel: no background, no border, no radius, no
   backdrop blur. The type sits on the image. Two consequences that
   drive everything below.

   1. THE SCRIM IS THE ONLY THING KEEPING IT LEGIBLE, so the copy
      also carries a wide, low-opacity text-shadow. It is set soft
      and large on purpose — a tight dark shadow reads as an outline
      and looks cheap; a 28px blur at 55% reads as depth and is
      almost invisible until you remove it.

   2. WITHOUT A PANEL THERE IS NO BOX TO SIZE AGAINST, so every
      measurement here is viewport-relative rather than fixed. The
      stage name in particular is set in vw with clamp() bounds, so
      it scales continuously with the full-bleed image behind it
      instead of stepping at breakpoints. Resize the window and the
      name grows and shrinks with the photograph.
------------------------------------------------------------ */

.stage-copy {
  position: relative;
  z-index: 1;
  grid-row: 1;
  grid-column: 1;
  /* Still hugging the centre line — the reason from v9.2 stands:
     without it, a 21:9 display parks stage 1 against the far left
     and stage 2 against the far right, and the six stop reading as
     one sequence. */
  justify-self: end;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: clamp(520px, 52vw, 900px);
  padding-block: clamp(16px, 2vw, 32px);
  text-shadow: 0 1px 28px rgba(14, 15, 13, 0.55);
}

.stage:nth-child(even) .stage-copy {
  grid-column: 2;
  justify-self: start;
}

/* ------------------------------------------------------------
   The stage number — the quiet half of the pairing
------------------------------------------------------------ */

.stage-no {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(12px, 0.95vw, 17px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sage-bright);
  margin-bottom: clamp(10px, 1vw, 18px);
  /* A short sage rule under the number, so the eye has somewhere to
     land before it hits the name. Replaces the motif that used to
     sit here without reintroducing a drawing. */
  padding-bottom: clamp(8px, 0.8vw, 14px);
  border-bottom: 2px solid var(--sage-bright);
  width: fit-content;
  padding-inline-end: clamp(24px, 3vw, 56px);
}

/* ------------------------------------------------------------
   The stage name — the loud half

   This is the element the brief is about: "bold and pop out,
   grabbing the user's attention". It runs from 40px on a phone to
   112px on a wide desktop, on a continuous 7vw curve, with the line
   height pulled under 1 so the two-word names stack as a block
   rather than as a list.

   overflow: hidden on the h3 plus a block-level inner span is what
   lets js/motion.js reveal it from behind its own edge. That mask
   is the single boldest gesture in the sequence and it costs one
   wrapper element.
------------------------------------------------------------ */

.stage h3 {
  font-family: var(--font-display);
  font-weight: var(--w-regular);
  /* THE NUMBER THAT STOPS WORDS BREAKING.

     "architecture" is twelve characters and appears in three of the
     six names — stages 3, 4 and 5, which is exactly where the breaks
     were reported. Two things were wrong at 6.2vw / 112px: the copy
     block was capped by its grid column rather than by its own
     max-width across most desktop widths, and the estimate of
     Nevera's average character width was too generous.

     Fixed at both ends. The column got wider (the band's gap dropped
     from 64px to 40px and its side padding from 88px to 56px, and
     .stage-copy's max-width went 780 -> 900), and the type curve came
     down from 6.2vw/112 to 5.6vw/104. Every one of the six now clears
     its block at every width from 390px up, with the tightest margin
     at 810px.

     `overflow-wrap: normal` and `hyphens: none` below are the belt to
     that braces: if a future name is longer than any of these six,
     it will overflow visibly rather than break silently — and an
     overflow is a bug someone notices, where a mid-word break just
     looks like a design decision. */
  font-size: clamp(40px, 5.6vw, 104px);
  line-height: 0.98;
  letter-spacing: -0.028em;
  color: var(--fg-1);
  margin: 0;
  overflow: hidden;
  overflow-wrap: normal;
  hyphens: none;
}

/* THE DESCENDER TRAP, WRITTEN DOWN BECAUSE IT IS NOT OBVIOUS.

   A line-height under 1 means glyphs overflow their line box, so the
   'g' in "architecture" and the 'y' in "Technology" hang below the
   h3's content box and get clipped by overflow: hidden.

   The instinctive fix — padding-bottom on the h3 — is WRONG here, and
   quietly so. `overflow: hidden` clips at the PADDING box, not the
   content box, so bottom padding opens a window exactly as tall as
   the padding. The span, translated down by 100% of its own height,
   would sit partly inside that window and show a sliver of the name
   before the reveal starts.

   Putting the room on the span instead fixes both at once: the
   descenders live inside the span's own box, and the span's height
   grows to include the padding, so yPercent: 100 still moves it
   completely clear of the h3's edge. */
.stage-h-in {
  display: block;
  padding-bottom: 0.14em;
  will-change: transform;
}

.stage-body {
  margin-top: clamp(20px, 2.2vw, 36px);
  padding-top: clamp(18px, 1.8vw, 28px);
  border-top: var(--bw) solid rgba(246, 244, 239, 0.22);
}

.stage-body > p {
  font-size: clamp(16px, 1.15vw, 20px);
  line-height: 1.62;
  color: var(--fg-1);
  opacity: 0.94;
  /* The name may run the full width of the block; the paragraph
     must not. Measure is capped independently. */
  max-width: 34em;
}

.stage-out {
  margin-top: clamp(20px, 2vw, 32px);
  padding-top: 18px;
  border-top: var(--bw) solid rgba(246, 244, 239, 0.18);
  max-width: 34em;
}
.stage-out .label {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(10px, 0.7vw, 13px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sage-bright);
  margin-bottom: 8px;
}
.stage-out .text {
  font-size: clamp(15px, 1.05vw, 19px);
  line-height: 1.55;
  color: var(--fg-1);
}

/* ------------------------------------------------------------
   Breakpoints
------------------------------------------------------------ */

@media (max-width: 1199.98px) {
  .stage { min-height: clamp(460px, 58vw, 600px); }
  /* Between 810 and 1200 the block is at its narrowest relative to the
     type, so the curve eases off here rather than everywhere. */
  .stage h3 { font-size: clamp(40px, 5.2vw, 72px); }
}

/* Below 810px two columns cannot both hold their measure. The band
   becomes one column: the photograph still fills it edge to edge,
   and the panel sits on top with a margin so the image reads as a
   frame around it rather than disappearing behind it. The scrim
   turns vertical, because there is no longer an open side. */
@media (max-width: 809.98px) {
  .stages { margin-top: 44px; }

  .stage {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    padding-block: clamp(40px, 9vw, 64px);
    padding-inline: var(--pad-x);
  }

  .stage-media::before,
  .stage:nth-child(even) .stage-media::before {
    background: linear-gradient(
      to bottom,
      rgba(14, 15, 13, 0.42) 0%,
      rgba(14, 15, 13, 0.78) 26%,
      rgba(14, 15, 13, 0.88) 100%
    );
  }

  .stage-media img,
  .stage:nth-child(even) .stage-media img {
    object-position: center 38%;
  }

  /* One column, so there is no centre line to hug. Reset the
     alignment or the panel would sit at one edge of a full-width
     track and leave dead space beside it. */
  .stage-copy,
  .stage:nth-child(even) .stage-copy {
    grid-row: 1;
    grid-column: 1;
    justify-self: stretch;
    max-width: none;
    padding-block: clamp(8px, 3vw, 20px);
  }

  .ai-mark,
  .stage:nth-child(even) .ai-mark {
    top: 0;
    right: 0;
    left: auto;
    justify-content: flex-end;
    padding: 2px 5px 0 0;
  }

  /* On one column the name has the full screen width, so it can be
     set against the viewport rather than against half of it. 11vw
     keeps it as the loudest thing on a phone screen — which is the
     whole point of the treatment — without wrapping to four lines. */
  .stage h3 { font-size: clamp(38px, 10vw, 60px); }
  .stage-no { font-size: 12px; letter-spacing: 0.2em; }
  .stage-body { margin-top: 20px; padding-top: 18px; }
  .stage-body > p { font-size: 17px; max-width: none; }
  .stage-out { max-width: none; }
  .stage-out .text { font-size: 16px; }
}

/* ------------------------------------------------------------
   Motion — the panel's blur is the only expensive thing here and
   it is static, so there is nothing to disable. Left as a marker
   for whoever adds a transition later.
------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .stage-copy { transition: none; }
}

/* ------------------------------------------------------------
   Print — the images are decorative in print and cost a page each
------------------------------------------------------------ */

@media print {
  .stages { width: auto; margin-inline: 0; }
  .stage {
    grid-template-columns: 1fr;
    min-height: 0;
    padding: 0;
    background: none;
    border-color: #ccc;
    break-inside: avoid;
  }
  .stage-media { display: none; }
  .stage-copy {
    grid-column: 1 !important;
    max-width: none;
    padding: 16px 0;
    text-shadow: none;
  }
  /* 112px display type is right on a lit screen and absurd on A4. */
  .stage h3 { font-size: 24pt; overflow: visible; }
  .stage-h-in { will-change: auto; }
}
