/* ==========================================================================
   WHAT WE DO — "THE BLADES"   (.bld, index.html, homepage 01)

   Replaces the call (css/call.css — unlinked, kept on disk), which
   replaced the ground, the shift, the relay and the rack.

   WHY THE CALL CAME OUT. It was one photograph annotated like a plate
   out of an engineering monograph: three hairline leaders running off
   the picture to three labels in the margin. The form was correct and
   the restraint was right, but it failed the one thing this section is
   for. A LEADER LINE IS A FOOTNOTE. It asks the reader to follow a
   1px rule to a 13px target and then work out which of the two things
   under that target is being named — that is reading, done twice, and
   the brief here has always been "understand it without reading". A
   buyer looking at it for two seconds saw a photograph with thin lines
   on it and no argument at all.

   AND ONE PICTURE CANNOT CARRY THREE CLAIMS. The three claims are not
   three facets of a single moment; they are THREE DIFFERENT ROOMS —
   the room where the team is trained, the ground it is sent out on,
   and the desk where the week is read back. Pinning all three to one
   frame of a car park meant two of them were being asserted over a
   picture of something else.

   THE READING: THREE ROOMS, ONE PLATE, IN THE ORDER THEY HAPPEN.
   Left to right the section shows a briefing under a lamp, three
   consultants walking out into an open lot, and two people reading a
   printed report. That is hire -> work -> report, and it lands in a
   second with nothing read. The copy underneath confirms it; it does
   not have to deliver it.

   THE ONE DEVICE: A DATUM AND THREE PLUMB LINES. A single hairline
   runs the full width of the container — the rail. From it three
   plumb lines drop, each one longer than the last, and EACH PLUMB LINE
   OPENS INTO ITS PHOTOGRAPH. That is the whole animation and the whole
   argument at once: one standard, dropped into three places, and the
   work is what comes out of it. It is also why the pictures are
   staggered rather than ranked — three panes on one baseline is a card
   grid, and a card grid says "three separate things, pick one". Three
   drops off one rail says "one system, three depths", which is the
   actual claim.

   WHY IT IS NOT ANOTHER STAGED DEPTH. § 02 (the fold) hinges six
   panels open, § 03 (the forge) turns a solid, § 04 (the reach) dollies
   two rooms through Z. A fourth camera move through space would leave
   the top of this page with no change of beat at all. So the depth here
   RESOLVES: every pane arrives ~150px back and slightly turned, and
   every pane ends dead flat and coplanar. At rest this is a printed
   plate, not a scene being held open by script.

   THE CLOSED STATE IS A COMPOSITION IN ITS OWN RIGHT. Before the scrub
   runs there are no pictures — one rule and three gold hairlines
   standing in an otherwise empty band. That emptiness is the section's
   negative space and it is deliberate: the plate is mostly air, the
   pictures are what fills it, and the reader watches that happen.

   THREE PHASES OFF ONE NUMBER. --blo is scrubbed over the section's
   entry and every other value below is arithmetic on it plus the
   column's own index --bli:
     --blr  0.00-0.16  the rail runs out from the left
     --blp  0.14-0.34  the plumb lines drop, staggered by 0.07
     --blb  0.38-0.70  each blade splits open from its own hairline
     --blc  0.54-0.80  the numeral, its rule and the claim resolve
   The stagger is what makes it read as a sequence rather than a
   simultaneous reveal, and it is one line of clamp() per phase.

   THE HAND BRINGS A ROOM FORWARD. Pointing at a column drives --blk to
   1 on it and to -1 on the other two: the named pane comes 24px
   nearer, warms and saturates; the other two fall back, cool and drop
   their type to 60%. Registered as a <number> with a transition, so it
   eases rather than snapping, and it is pure :has() — the interaction
   survives with no script at all. The soft light inside the pane
   follows the pointer (--blmx/--blmy) and is the only thing js touches
   per frame.

   NO BAR OF ANY KIND. The rail is the frame of a plate: it runs out
   once on entry and then stands. Nothing fills, counts, cycles or
   measures, and there is no clock anywhere in this sheet — the only
   motion after the reader arrives is the pointer's.

   3D DISCIPLINE, AND IT IS WHY EACH COLUMN CARRIES ITS OWN
   PERSPECTIVE. This site has lost two builds to preserve-3d being
   silently flattened by an overflow, a filter or a .reveal somewhere up
   the tree. So there is no shared 3D context here at all: `perspective`
   sits on .bld__col, its one 3D child is .bld__pane, and the caption is
   a flat sibling that never enters the context. perspective-origin is
   stepped per column — 155% / 50% / -55% — which puts all three
   vanishing points at the rank's centre and buys the shared-camera look
   with none of the fragility. No .reveal inside the rank.

   NAME CHECK, BECAUSE THIS FILE HAS BEEN BITTEN. `.sht` is the contact
   sheet (css/editorial-3.css) and `.opn` is the careers set
   (css/editorial-2.css); both were the obvious names and both are
   taken. Every class here is .bld*, every custom property --bl*, and
   js/motion.js declares initBlades, which was free.

   REST STATE IS THE FINISHED PLATE. --blo rests at 1, --blz at 0.5 and
   --blk at 0: with no script, a blocked CDN or reduced motion the rail
   is drawn, all three plumb lines hang, all three photographs are open,
   square and coplanar, and every line of type is at full contrast.
   ========================================================================== */

/* the entry, scrubbed by scroll. 1 = the plate is finished. */
@property --blo { syntax: "<number>"; inherits: true; initial-value: 1; }
/* the passage of the section through the viewport, 0 below .. 1 above.
   0.5 is the rest plane. Drives the drift inside each crop only. */
@property --blz { syntax: "<number>"; inherits: true; initial-value: 0.5; }
/* the hand: 1 on the column being pointed at, -1 on the other two,
   0 when nothing is. Registered so it EASES between those states —
   unregistered it would snap, and the whole rack would flinch. */
@property --blk { syntax: "<number>"; inherits: true; initial-value: 0; }
/* where the pointer is standing inside a pane, for the soft light. */
@property --blmx { syntax: "<percentage>"; inherits: true; initial-value: 50%; }
@property --blmy { syntax: "<percentage>"; inherits: true; initial-value: 42%; }

.bld {
  position: relative;
  padding-block: clamp(2.6rem, 1.8rem + 3.4vw, 5.5rem);
  /* the panes arrive turned and set back; clip the horizontal only so
     nothing can ever push the document sideways. */
  overflow-x: clip;
  overflow-y: visible;
  overflow-clip-margin: 2rem;

  /* the three phases, all read off --blo */
  --blr: clamp(0, calc(var(--blo) / 0.16), 1);
}

/* --------------------------------------------------------------------------
   THE HEAD — the claim left, the lede and the one way out right, on one
   baseline. Two objects, full width, nothing between them.
   -------------------------------------------------------------------------- */
.bld__head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  column-gap: clamp(2rem, 1.2rem + 4vw, 5.5rem);
  align-items: end;
  margin-bottom: clamp(2.75rem, 2rem + 3.6vw, 5.5rem);
}

.bld__h {
  margin: 0;
  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;
}

.bld__h em {
  color: var(--gold-light);
  font-style: italic;
  font-weight: 500;
}

.bld__aside {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1rem, 0.8rem + 0.7vw, 1.5rem);
  padding-bottom: 0.35rem;
}

.bld__lede {
  margin: 0;
  max-width: 34ch;
  font-size: var(--text-base);
  line-height: 1.68;
  color: var(--vein);
  text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   THE RANK — the rail, and three columns hanging off it at three depths.
   `align-items: start` is what lets each column keep its own margin-top;
   the grid row takes the height of the deepest one and the bottom of the
   composition is ragged on purpose.
   -------------------------------------------------------------------------- */
.bld__rank {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: clamp(1.15rem, 0.6rem + 2.4vw, 2.6rem);
  align-items: start;

  /* how far below the rail the first column hangs, and how much further
     each one after it. Written once, here. */
  --blbase: clamp(2.25rem, 1.5rem + 2.6vw, 4.25rem);
  --blstep: clamp(1.35rem, 0.85rem + 2.1vw, 3.15rem);
}

/* the datum. A rule, not a measure: it runs out once and then stands. */
.bld__rail {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg,
    var(--gold-shadow) 0%,
    var(--border-gold-strong) 38%,
    var(--border-neutral) 100%);
  transform: scaleX(var(--blr));
  transform-origin: left center;
}

/* --------------------------------------------------------------------------
   A COLUMN. Owns its own perspective (see the header), its own drop off
   the rail, and its own share of every phase.
   -------------------------------------------------------------------------- */
.bld__col {
  position: relative;
  margin-top: var(--bldrop);

  --bldrop: calc(var(--blbase) + var(--bli) * var(--blstep));
  --blp: clamp(0, calc((var(--blo) - 0.14 - var(--bli) * 0.07) / 0.20), 1);
  --blb: clamp(0, calc((var(--blo) - 0.38 - var(--bli) * 0.11) / 0.32), 1);
  --blc: clamp(0, calc((var(--blo) - 0.54 - var(--bli) * 0.10) / 0.26), 1);

  /* all three vanishing points land on the rank's centre */
  perspective: 1500px;
  perspective-origin: calc(50% + (1 - var(--bli)) * 105%) 42%;

  transition: --blk 0.55s var(--ease-out);
}

/* the registration mark where the plumb line meets the rail */
.bld__col::before {
  content: "";
  position: absolute;
  top: calc(-1 * var(--bldrop));
  left: 50%;
  width: 11px;
  height: 1px;
  margin-left: -5.5px;
  background: var(--gold);
  opacity: calc(var(--blp) * 0.85);
}

/* the plumb line itself, dropped from the rail to the head of the pane */
.bld__plumb {
  position: absolute;
  top: calc(-1 * var(--bldrop));
  left: 50%;
  width: 1px;
  height: var(--bldrop);
  margin-left: -0.5px;
  background: linear-gradient(180deg, var(--gold-shadow), var(--border-gold-strong));
  transform: scaleY(var(--blp));
  transform-origin: top center;
}

/* --------------------------------------------------------------------------
   THE PANE — the one element in the 3D context. It arrives 150px back and
   nine degrees off square and ends dead flat; the hand adds 24px either
   way on top of that.
   -------------------------------------------------------------------------- */
.bld__pane {
  position: relative;
  aspect-ratio: 4 / 5;
  transform:
    translateZ(calc((var(--blb) - 1) * 150px + var(--blk) * 24px))
    rotateY(calc((1 - var(--blb)) * -9deg + var(--blk) * 1.8deg))
    rotateX(calc((1 - var(--blb)) * 2.2deg));
  transform-origin: 50% 34%;
}

/* the blade. clip-path opens it from a 1px hairline at its own centre
   line — at --blb 0 exactly one pixel of it is visible, which is where
   the plumb line above it lands. The grouping property flattens this
   element's own subtree, which is correct: nothing inside is 3D. */
.bld__blade {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(50% * (1 - var(--blb)) - 0.5px * (1 - var(--blb))));
  filter:
    brightness(calc(1 + var(--blk) * 0.15))
    saturate(calc(1 + var(--blk) * 0.2))
    contrast(calc(1 + var(--blk) * 0.04));
  background: var(--surface);
}

.bld__crop {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.bld__crop img {
  position: absolute;
  inset: -3% 0;
  width: 100%;
  height: 106%;
  object-fit: cover;
  display: block;
  /* the settle, and a whisper of differential drift across the passage
     so the three pictures are not one flat sheet at rest */
  transform:
    scale(calc(1.13 - var(--blb) * 0.13 + var(--blk) * 0.03))
    translateY(calc((var(--blz) - 0.5) * (1 + var(--bli) * 0.35) * -2.4%));
  transform-origin: 50% 45%;
}

/* the print. A low scrim so the pane reads as ink on stock rather than a
   bright photo dropped on a dark page, plus the light under the hand. */
.bld__wash {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(62% 44% at var(--blmx) var(--blmy),
      rgba(192, 156, 96, calc(0.02 + max(var(--blk), 0) * 0.16)),
      transparent 72%),
    linear-gradient(180deg,
      rgb(var(--scrim-rgb) / 0.34) 0%,
      rgb(var(--scrim-rgb) / 0.04) 34%,
      rgb(var(--scrim-rgb) / 0.52) 100%);
}

/* the parting line. It IS the closed state — the picture grows out of
   it — so it holds full strength at --blb 0 and is gone by 0.38. */
.bld__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  margin-left: -0.5px;
  background: linear-gradient(180deg,
    transparent 0%, var(--gold) 14%, var(--gold-light) 50%, var(--gold) 86%, transparent 100%);
  opacity: clamp(0, calc(1 - var(--blb) * 2.6), 1);
}

/* the hairline frame, drawn only once the blade is open enough to have
   edges worth ruling */
.bld__pane::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--border-gold);
  opacity: clamp(0, calc((var(--blb) - 0.45) / 0.4), 1);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   THE CAPTION — flat, outside the perspective, never over a photograph.
   -------------------------------------------------------------------------- */
.bld__cap {
  padding-top: clamp(1.1rem, 0.8rem + 1.2vw, 1.85rem);
  opacity: clamp(0, calc(var(--blc) * (1 + var(--blk) * 0.4)), 1);
  transform: translateY(calc((1 - var(--blc)) * 16px));
}

/* THE MARK — the numeral and its rule on one line, and the numeral is at
   the size every other numbered item on this site uses (.trp__n 0.6875rem,
   .fld__n and .rsv__n 0.7rem, .rch__no 0.7-0.86rem). An earlier pass set it
   at 2.35rem, which both invented a second numeral system for one section
   and put a display-sized "01" three inches under the eyebrow's own "/ 01".
   The graphic weight belongs to the rule, which is free to be long. */
.bld__mark {
  display: flex;
  align-items: center;
  gap: clamp(0.65rem, 0.5rem + 0.5vw, 0.95rem);
  margin: 0 0 clamp(0.8rem, 0.6rem + 0.6vw, 1.2rem);
}

.bld__no {
  flex: none;
  font-size: 0.7rem;
  font-weight: 700;
  font-style: normal;
  line-height: 1;
  letter-spacing: 0.2em;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  transition: color 0.5s var(--ease-out);
}

.bld__rule {
  flex: 1 1 auto;
  height: 1px;
  background: linear-gradient(90deg, var(--border-gold-strong), var(--border-neutral));
  transform: scaleX(var(--blc));
  transform-origin: left center;
}

.bld__claim {
  margin: 0;
  font-size: clamp(1.02rem, 0.94rem + 0.34vw, 1.24rem);
  font-weight: 600;
  line-height: 1.32;
  letter-spacing: -0.022em;
  color: var(--ink);
  text-wrap: balance;
}

.bld__sub {
  margin: 0.55rem 0 0;
  font-size: var(--text-sm);
  line-height: 1.62;
  color: var(--vein);
  max-width: 30ch;
  text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   THE HAND. Pure :has() — it works with the script dead. The :hover rule
   scores (0,5,0) against the sibling rule's (0,4,0), so the column being
   pointed at wins its own value back.
   -------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .bld__rank:has(.bld__col:hover) .bld__col { --blk: -1; }
  /* `perspective` already makes every column a stacking context, so the
     one coming forward has to be lifted out of DOM order or its right
     edge paints under its neighbour. */
  .bld__rank:has(.bld__col:hover) .bld__col:hover { --blk: 1; z-index: 2; }
  .bld__rank:has(.bld__col:hover) .bld__col:hover .bld__no { color: var(--gold-light); }
  .bld__col { cursor: default; }
}

/* --------------------------------------------------------------------------
   NARROWER — the rank holds its three columns down to 860px on a tighter
   drop and a tighter measure.
   -------------------------------------------------------------------------- */
@media (max-width: 1080px) {
  .bld__rank {
    --blbase: 1.9rem;
    --blstep: 1.2rem;
  }
  .bld__sub { font-size: 0.8125rem; }
}

/* --------------------------------------------------------------------------
   861px AND DOWN — the rank turns on its side. THREE ROWS, PICTURE LEFT AND
   ITS CLAIM BESIDE IT, because the alternative is worse in both directions:
   three columns at this width give a 200px measure that the sub-line cannot
   live in, and one full-width picture per row runs the section past 3,300px
   — measured, not guessed. At 46% the picture is still the largest thing in
   the row and the copy still gets a 30-character measure.

   The plumb line moves with it. It hangs at 23% — the centre of a 46% track
   — so it still lands exactly on the blade's parting line, which is the one
   piece of geometry in this sheet that has to stay true at every width.

   Only the first row hangs off the rail. Rows two and three follow it in
   sequence and a plumb line on each would be three datums, not one.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .bld__head {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(1.25rem, 1rem + 1.6vw, 2rem);
    align-items: start;
  }
  .bld__h { max-width: 18ch; }
  .bld__lede { max-width: 46ch; }

  .bld__rank {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(1.75rem, 1.2rem + 3vw, 2.75rem);
    --blbase: 1.75rem;
    --blstep: 0rem;
  }

  .bld__col {
    display: grid;
    grid-template-columns: minmax(0, 46%) minmax(0, 1fr);
    column-gap: clamp(1.15rem, 0.7rem + 2.4vw, 2rem);
    align-items: center;
    /* shallower, and squarely over the picture rather than off at the
       rank's centre — there is no rank to share a camera with now */
    perspective: 900px;
    perspective-origin: 23% 50%;
  }

  .bld__pane {
    transform: translateZ(calc((var(--blb) - 1) * 90px));
  }

  .bld__cap { padding-top: 0; }
  .bld__sub { max-width: none; font-size: var(--text-sm); }

  .bld__plumb,
  .bld__col::before { left: 23%; }

  .bld__col + .bld__col .bld__plumb,
  .bld__col + .bld__col::before { display: none; }
  .bld__col + .bld__col { margin-top: 0; }
}

/* --------------------------------------------------------------------------
   600px AND DOWN — a phone cannot hold two things across, so the picture
   takes the full measure and the claim sits under it. Square, because 4:5 at
   full width is 470px of photograph on a 390px screen and the section
   becomes a scroll in its own right.
   -------------------------------------------------------------------------- */
@media (max-width: 600px) {
  .bld__col {
    display: block;
    perspective: none;
  }
  .bld__pane {
    aspect-ratio: 1 / 1;
    transform: none;
  }
  .bld__cap { padding-top: clamp(0.9rem, 0.7rem + 1.2vw, 1.2rem); }
  .bld__plumb,
  .bld__col::before { left: 50%; }
}

/* --------------------------------------------------------------------------
   REDUCED MOTION — the finished plate, and nothing to watch. --blo,
   --blz and --blk all rest at the values that draw it, so this only has
   to stop the eases.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .bld__col { transition: none; }
  .bld__no { transition: none; }
  .bld__pane { transform: none; }
}
