/* clients.css — 07 / Clients
 *
 * This section scales on the normal viewport (clamp), not on the hero's contain
 * scale — it is a document-flow section, not a fixed frame.
 *
 * The red field is the same #E6002A the hero's bar grows into, so the sections meet
 * with no seam. Keep them in sync if the accent ever changes.
 */

/* The track exists to give the section room to FREEZE at its end: once the last
   row and the section's own bottom padding are on screen, it stops moving and
   holds there while the red is clipped away. Keep the hold below in step with
   CURTAIN_VH in sequence.js and with the .ach-track negative margin that slides
   the grey panel in behind it. */
.clients-track {
  position: relative;

  /* Docks Clients at the moment the hero unpins, rather than a full viewport
     later. Without this the hero (100vh tall) has to scroll entirely off before
     Clients arrives, and because the hero's own content has already faded by then
     that whole screen is blank red — one wasted screenful between "the red fills
     the frame" and anything happening.

     The hero is raised to z-index 10 so it still covers Clients completely while
     it is pinned; Clients is simply revealed beneath it as it leaves. Both are the
     same red, so the handover is invisible. */
  margin-top: -100vh;
}

/* The stick distance. Must be real content — a sticky box is confined to its
   parent's CONTENT box, so padding here would give it nowhere to hold. */
.clients-track__hold { height: 100vh; }

.clients {
  --pad: clamp(24px, 4vw, 64px);

  /* WHERE THE MENU'S `Brands` LINK LANDS.
     #clients is the top of a pinned sequence — sequence.js's track is
     380vh = 100vh curtain + 80vh hold + 200vh flight — so the element's own
     offset is progress ZERO: the field fully red, nothing entered. Anchoring
     there dropped the visitor on a blank screen and read as a broken link.

     A NEGATIVE scroll-margin scrolls FURTHER than the element's top. 68vh puts
     the arrival at the frame where the heading is still in view AND the first
     brand rows have played in — measured h2 at +294px with three rows on screen.
     The window is roughly 55-100vh; below it the curtain is still shut, above it
     the heading has scrolled off.

     MEASURE .clients__h2, NOT .clients__head. The head is a grid CONTAINER whose
     left cell is 104vh tall with the heading sticky at its bottom, so the
     container's top sits about a viewport above the visible headline. Reading
     the container's rect says the heading is off-screen while it is plainly in
     frame — that error cost a long detour into sequence.js on 2026-08-17 for a
     problem that was only ever this one number.

     vh, not px: the runway is written in vh and the two have to move together.
     If CURTAIN_VH or REST_FRAC change in sequence.js, check this with them. */
  scroll-margin-top: -68vh;

  /* Freezes the section's LAST screenful, so the bottom row stops rather than
     sliding up off the top.

     NOT `bottom: 0`. Bottom-sticky pulls a box UP into view and holds it there;
     it cannot push one DOWN to freeze its tail, and with no room above it inside
     the track it cannot move at all. The technique that works is a NEGATIVE top
     of (viewport - own height): the box then sticks exactly when its bottom edge
     would rise above the viewport bottom. Its own height is content-dependent,
     so sequence.js measures it and writes `top` — see syncClientsPin(). */
  position: sticky;
  top: 0;
  z-index: 6;
  /* background: declared once in main.css, with the display-p3 uplift beside
     it. Repainting it here would win on source order and kill the uplift. */
  color: #fff;
  padding: clamp(56px, 7vw, 104px) var(--pad) clamp(48px, 6vw, 88px);
}

/* The 62% rule runs the full height of the section and fades in only once the
   third row starts revealing — main.js gates it on [data-cl="2"]. */
.clients__divider {
  position: absolute;
  left: calc(var(--pad) + (100% - 2 * var(--pad)) * 0.62);
  top: 0; bottom: 0;
  width: 1px;
  background: var(--hair-on-red);
  pointer-events: none;
  opacity: 0;
  transition: opacity 1400ms var(--ease-out);
}
.clients__divider[data-shown] { opacity: 1; }

.clients__inner { position: relative; max-width: 1880px; margin: 0 auto; }


/* --- header ------------------------------------------------------------- */

.clients__head {
  display: grid;
  grid-template-columns: calc(62% - 22px) 1fr;
  align-items: end;
}

/* The tall left cell is what makes the heading sit low and then stick as the rows
   scroll past it. */
.clients__headcol {
  height: 104vh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-right: clamp(24px, 3vw, 44px);
}
.clients__sticky { position: sticky; bottom: 16vh; }

.clients__eyebrow {
  font-family: var(--hud); font-weight: 400; font-size: 11px; line-height: 1;
  letter-spacing: .16em; text-transform: uppercase;
}
.clients__h2 {
  margin: 24px 0 0;
  font-family: var(--display); font-weight: 900;
  font-size: clamp(2.2rem, 4.6vw, 4rem);
  line-height: 1.04;
  letter-spacing: -.03em;
}

.clients__intro { padding-left: 26px; text-align: right; }
.clients__roster {
  font-family: var(--hud); font-weight: 400; font-size: 11px; line-height: 1;
  letter-spacing: .16em; text-transform: uppercase;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--hair-on-red);
}
.clients__para {
  margin: 24px 0 22px auto;
  max-width: 34ch;
  font-family: var(--body); font-weight: 400; font-size: 13.5px; line-height: 1.65;
  color: rgba(255, 255, 255, .92);
}
.clients__meta {
  font-family: var(--hud); font-weight: 400; font-size: 10.5px; line-height: 1;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}


/* --- brand rows ---------------------------------------------------------- */

.rows {
  margin-top: clamp(40px, 5vw, 72px);
  border-top: 1px solid var(--hair-on-red);
}

.row {
  position: relative;
  display: grid;
  grid-template-columns: calc(62% - 22px) 44px minmax(0, 1fr);
  align-items: center;
  min-height: clamp(112px, 9vw, 150px);
  padding: 22px 0;
  border-bottom: 1px solid var(--hair-on-red);
}

/* Hairlines grow from the centre on the active row. They stay in the DOM at
   scaleX(0) so hovering only has to add a class — no node churn. */
.row__hair {
  position: absolute; left: 0; right: 0;
  height: 1px;
  background: #fff;
  pointer-events: none;
  transform: scaleX(0);
  transform-origin: 50% 50%;
}
.row__hair--top    { top: -1px; }
.row__hair--bottom { bottom: -1px; }
.row.is-active .row__hair { animation: omGrow 420ms var(--ease-io) both; }

.row__lead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px;
  padding-right: 18px;
}
.row__bracket {
  font-family: var(--hud); font-weight: 500; font-size: 10px; line-height: 1.8;
  letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255, 255, 255, .74);
  white-space: nowrap;
  opacity: 0;
}
.row.is-active .row__bracket { animation: omFade 260ms ease both; }

.row__name {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2rem, 3.7vw, 3.3rem); line-height: 1;
  letter-spacing: -.015em;
  white-space: nowrap;
  color: rgba(255, 255, 255, .52);
}
.row.is-active .row__name { color: #fff; animation: omLift 240ms var(--ease-io) both; }

.row__chip {
  justify-self: center;
  display: inline-block;
  font-family: var(--hud); font-weight: 700; font-size: 10px; line-height: 1;
  padding: 5px 8px;
  border: 1px solid rgba(255, 255, 255, .5);
  background: var(--red);
  color: rgba(255, 255, 255, .72);
}
.row.is-active .row__chip {
  background: #fff; color: var(--red);
  animation: omChip 340ms var(--ease-io) both;
}

.row__tail { padding-left: 26px; }
.row__sector {
  font-family: var(--hud); font-weight: 500; font-size: 10.5px; line-height: 1;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .6);
}
.row.is-active .row__sector { color: #fff; }

.row__line {
  font-family: var(--body); font-weight: 400; font-size: 13.5px; line-height: 1.45;
  margin-top: 9px;
  opacity: 0;
}
.row.is-active .row__line { animation: omFade 260ms ease both; }


/* --- scroll reveal -------------------------------------------------------
   Driven off the same rAF pass as the hero choreography, not IntersectionObserver.
   main.js stamps data-shown on each element once, on a 130ms x order stagger. */

/* --- the brand rows: fade and slide in from the left --------------------
   850ms, not the handoff's 1150ms. The last brand was still fading in as the red
   began to recede; a shorter transition keeps the wait for data-reveal-done
   brief. REVEAL_DURATION in main.js must match. */
.row[data-cl] {
  opacity: 0;
  transform: translateX(-34px);
  transition: opacity 850ms var(--ease-out), transform 850ms var(--ease-out);
}
.row[data-cl][data-shown] { opacity: 1; transform: none; }

/* --- the two header blocks: BUMP UP FROM THEIR BASE ----------------------
   Each piece sits below a clip edge and slides into place — the same treatment
   the About copy uses. The blocks themselves are never faded: the mask does the
   whole reveal, and fading as well would dim the text mid-slide. Only the small
   mono labels above them fade, since a one-line label has nothing to slide from. */
.bump-clip { display: block; overflow: hidden; }
.bump {
  /* flow-root, not block. The masked piece is offset by 105% of ITS OWN height,
     so that height has to include the child's margins — with plain `block` they
     collapse straight through and the box measures short, leaving the last line
     of the paragraph poking out above the clip edge. */
  display: flow-root;
  transform: translateY(105%);
  transition: transform 900ms var(--ease-out);
}
[data-cl][data-shown] .bump { transform: none; }

/* Each block's second masked piece trails the first. */
.clients__h2 .bump-clip:nth-child(2) .bump,
.clients__intro .bump-clip:nth-child(3) .bump { transition-delay: 110ms; }
.bump > p, .bump > .clients__meta { margin-top: 0; }

.clients__eyebrow, .clients__roster {
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
[data-cl][data-shown] .clients__eyebrow,
[data-cl][data-shown] .clients__roster { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  [data-cl], [data-cl][data-shown], .clients__divider { transition: none !important; }
  .bump { transform: none !important; transition: none !important; }
  [data-cl="0"] .clients__eyebrow, [data-cl="1"] .clients__roster { opacity: 1 !important; }
}
