/* ==========================================================================
   editorial-2.css, part 2 of 2  (continues into editorial-3.css)

   SPLIT FOR A REASON, DO NOT MERGE BACK. The dev server silently stops
   sending this file at 261,120 bytes (255 KiB) - not every time, roughly
   three requests in five. Once editorial-2.css passed that size the whole
   .skyhero block (byte 279,014) fell past the cut, so on most loads the
   homepage hero rendered with no CSS at all. editorial.css was split for
   the same reason earlier.

   The split is at a brace-depth-zero boundary and the two files
   concatenate back to the original byte for byte, so the cascade is
   unchanged. Keep both under ~200KB; if one approaches 255KB, split again.
   ========================================================================== */

.aim__mission .aim__stmt {
  font-size: clamp(1.6rem, 1.2rem + 1.85vw, 2.85rem);
}
.aim__mission .aim__stmt b {
  font-weight: inherit;
  font-style: italic;
  color: var(--gold-light);
}

/* the vision sits below the slot: it is the thing the opening reveals */
.aim__vision {
  position: absolute;
  right: 0;
  top: 71%;
  max-width: 33rem;
  padding-top: clamp(1.1rem, 0.9rem + 0.9vw, 1.6rem);
  border-top: 1px solid rgba(192, 156, 96, 0.4);
}
/* --ink, NOT --vein, and that is the recurring trap avoided by construction
   rather than measured afterwards: --vein is a mid grey that clears AA against
   the flat page but not against a photograph with lit roofs in it. Copy laid
   over a picture gets the full ink value; the emphasis carries the hierarchy
   instead of a dimmer base. */
.aim__vision .aim__stmt {
  font-size: clamp(1.15rem, 0.98rem + 0.8vw, 1.62rem);
  line-height: 1.34;
  color: var(--ink);
}
.aim__vision .aim__stmt b {
  font-weight: inherit;
  color: var(--gold-light);
}

/* --------------------------------------------------------------------------
   Narrower
   -------------------------------------------------------------------------- */

@media (max-width: 1100px) {
  .aim__mission { max-width: 34rem; top: 46%; }
  .aim__vision { top: 68%; max-width: 27rem; }
}

@media (max-width: 900px) {
  /* ABSOLUTE PLACEMENT IS DROPPED, NOT RETUNED. Two text blocks pinned to
     percentages of a fixed-height band will collide the moment the type wraps,
     and at this width both of them wrap. In flow they can never overlap. */
  .aim__band {
    height: auto;
    min-height: min(92svh, 40rem);
  }
  .aim__inner {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(2rem, 1.5rem + 2.6vw, 3.25rem);
    padding-block: clamp(2.61rem, 1.74rem + 5.22vw, 4.06rem);
  }
  .aim__mission,
  .aim__vision {
    position: static;
    translate: none;
    max-width: 40rem;
  }
  /* the matte can no longer hide the vision on a flowing band, so it only
     frames the picture */
  .aim__matte { height: calc((1 - var(--open)) * 22%); }
  .aim__veil {
    background:
      linear-gradient(180deg, rgba(6, 6, 8, 0.7) 0%, rgba(6, 6, 8, 0.52) 45%, rgba(6, 6, 8, 0.86) 100%),
      radial-gradient(40rem 24rem at 12% 4%, rgba(156, 120, 72, 0.1), transparent 66%);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* --open is already at its finished value; the frame is simply open */
  .aim__shot img { transform: none; }
}

/* ==========================================================================
   THE FOUNDER — "the focus pull"   (about.html, section 03)

   It replaced an `.ed` split: a blank marble panel captioned "Portrait
   pending" (its image used `data-src`, so the clean pass hid it) beside three
   paragraphs. That was `.ed`'s LAST USER SITE-WIDE, so `.ed` / `.ed__media` /
   `.ed__copy` are now dead and are a safe follow-up deletion — alongside
   `.ed--flip`, `.ed--overlap` and `.kicker-lg`, freed by the two rebuilds
   above. None of them were sliced out in this pass, per the near-miss that
   silently deleted `.argue` along with `.omit`.

   THE GRAMMAR IS A RACK FOCUS, and it was the last optical move still free.
   This site travels X, pushes Z, climbs Y, holds the camera still while
   planes separate, turns an object, removes material, opens a letterbox and
   pulls back, swings doors inward, develops a print, runs a clock. Nothing
   moves the FOCAL PLANE. It is also the argument rather than an effect laid
   on top of one: the camera never moves and the composition never changes —
   what changes is what is allowed to be sharp. The week you had falls away,
   the runner resolves, and the number is the last thing to come clear.

   ONE NUMBER. `--focus` runs 0..1 and each plate computes its own sharpness
   from its distance to it, then sharpness drives blur, grade, bloom AND the
   frame hairline together — so a four-part move is one scrubbed value and CSS
   owns every filter (the clock's compose-in-CSS pattern). It is UNREGISTERED
   because GSAP writes it every frame and it never has to interpolate, and it
   reaches the plates for free without the site-wide recalc cost of a
   registered inherited property. Its CSS value is 1, THE FINISHED STATE, so
   no-JS, no-CDN and reduced-motion all render a fully resolved composition.

   `--fpeek` is the SEPARATE hover number, registered `inherits: false` and
   declared on the two elements that actually read it. Two numbers rather than
   one is the point: a scrub and a transition can then never fight over a
   property. Same split as the corridor's `--lamp` / `--lampx`.

   DELIBERATELY NOT A BAND. `.aim` directly above is a full-bleed photograph
   with the type laid over it, and a second one here would be the
   adjacent-section uniformity that `.wall` and `.worlds` had to be pulled
   apart for on index. This is three separate plates at three depths with the
   type in the negative space BETWEEN them — which also means no copy sits
   over a photograph anywhere in this section and it needs no scrim at all.

   NO LIKENESS, PERMANENT. The runner plate is uncaptioned on purpose, the
   same call `.lane` made on team: a name under a silhouette is the claim the
   rules forbid, so "Derrick / Founder" sits with the type. That plate is a
   drop-in slot for real photography the day it exists.

   Photography is an explicit exception to the `.media img` clean pass via the
   `.fdr__*` classes: car-dawn (the week you had), track (the standard) and
   whiteboard (the number). None is used as a real photograph anywhere else on
   about.html.
   ========================================================================== */

.fdr {
  position: relative;
  /* the far plate runs off the right edge; clip so it can never become a
     scrollbar. The corridor above bleeds LEFT — the alternation is what stops
     two bleeding sections on one page reading as one layout. */
  overflow-x: clip;
  --bleed: max(var(--gutter), calc((100vw - var(--container)) / 2));
  --focus: 1;
  /* THE BLUR CEILING IS A VARIABLE BECAUSE IT IS SIZE-DEPENDENT. A radius
     that reads as depth of field on a 700px plate is an unidentifiable
     smear on a 340px one — the same empty-screen failure the corridor's
     detail plate and the results tally both had to be graded out of. It
     drops with the layout at the breakpoint below. */
  --blur-max: 8px;
}

.fdr__ground {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(54rem 30rem at 80% 10%, rgba(156, 120, 72, 0.075), transparent 66%),
    radial-gradient(38rem 26rem at 6% 82%, rgba(128, 96, 48, 0.055), transparent 70%);
}

.fdr > .container { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   The broken grid
   -------------------------------------------------------------------------- */

.fdr__stage {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(1rem, 0.5rem + 1.4vw, 2.25rem);
  row-gap: 0;
}

/* EYEBROW HIGH, HEADLINE LOW, AIR BETWEEN THEM. The head stretches to the
   height of the plate beside it and the h2 is pushed to the foot of that
   column, so the left half has content at the top AND the bottom instead of a
   short block floating against a 440px photograph — the empty-quarter failure
   this site has now paid for four times. */
.fdr__head {
  grid-column: 1 / 7;
  grid-row: 1;
  z-index: 4;
  display: flex;
  flex-direction: column;
  padding-bottom: clamp(0.5rem, 0.3rem + 0.8vw, 1.25rem);
}

.fdr__h {
  margin-top: auto;
  padding-top: clamp(1.75rem, 1.25rem + 3vw, 4.25rem);
  text-wrap: balance;
}
.fdr__h em {
  font-style: italic;
  color: var(--gold-light);
}

/* --------------------------------------------------------------------------
   The three plates

   Each carries `.reveal`, so NEITHER its rotation NOR anything else here may
   use `transform`: `.reveal.is-visible` sets `transform: none` and would wipe
   it the instant the observer fired. The independent `rotate` property is
   applied separately from `transform`, which is the same fix the letterbox
   needed for its centring. GSAP owns the inner image's transform alone.
   -------------------------------------------------------------------------- */

.fdr__plate {
  position: relative;
  margin: 0;
  overflow: hidden;
  /* the bloom blends with `screen`; without an explicit stacking context that
     blend resolves against whatever is painted below the plate in the section
     rather than against the photograph (the corridor's lesson) */
  isolation: isolate;
  box-shadow:
    inset 0 3px 14px -6px rgba(0, 0, 0, 0.9),
    0 24px 56px -32px rgba(0, 0, 0, 0.95);
  /* abs() is too new to rely on here, so the distance is max(a - b, b - a) */
  --dist: max(calc(var(--focus) - var(--f)), calc(var(--f) - var(--focus)));
  --sharp: clamp(0, calc(var(--min) + clamp(0, calc(1 - var(--dist) / var(--w)), 1) * (1 - var(--min))), 1);
}

/* THE FOCAL POSITIONS. `--f` is where along the pull this plane is sharp,
   `--w` how forgiving its depth of field is, and `--min` the sharpness it can
   never fall below. At the resting value (`--focus: 1`) the composition is a
   real shallow-depth photograph rather than a flat one: the number razor
   sharp in front, the runner a whisper soft, the week you had softened behind.
   That resting state is what no-JS renders, so it has to be the best-looking
   frame in the whole pull, not the start of it.

   EVERY `--min` IS SET BY WHAT THE PICTURE STILL HAS TO SAY, NOT BY TASTE.
   The far plate first ran a floor of 0.14 and settled at nearly 8px of blur,
   which turned a road at first light into a brown smear — a 700px rectangle
   carrying no information, which is the failure this site has now paid for
   five times. Softened is the brief; unreadable is not. The runner is the
   subject of the section and is never allowed to be a blur at all. Only the
   number, nearest the reader, is permitted to arrive from nothing. */
.fdr__plate--far  { --f: 0;    --w: 0.75; --min: 0.5;  }
.fdr__plate--mid  { --f: 0.62; --w: 0.72; --min: 0.62; }
.fdr__plate--near { --f: 1;    --w: 0.6;  --min: 0.1;  }

/* far · the week you had. Wide, bleeding off the right edge of the page, and
   riding up through the section's top padding so the composition breaks its
   own boundary rather than sitting inside it. */
.fdr__plate--far {
  grid-column: 7 / -1;
  grid-row: 1;
  align-self: start;
  z-index: 1;
  margin-top: clamp(-4.5rem, -1.5rem - 2.5vw, -1.5rem);
  margin-right: calc(-1 * var(--bleed));
  /* WIDE AND SHALLOW, and the ratio is doing layout work rather than framing
     work: this plate sets the height of the whole top row, and at 16/10 it
     opened a 190px void between the eyebrow and the headline beside it. */
  aspect-ratio: 2.1 / 1;
}

/* mid · the standard. NO aspect-ratio: it is `height: 100%` across two rows,
   so it is exactly as tall as the copy and the near plate beside it. With a
   fixed aspect it either fell short of them or forced the rows open. Same
   call the seat made on team. */
.fdr__plate--mid {
  grid-column: 1 / 6;
  grid-row: 2 / 4;
  z-index: 3;
  height: 100%;
  min-height: clamp(22rem, 15rem + 22vw, 36rem);
  rotate: 0.5deg;
}

/* near · the number. Small, nearest, tilted hardest, and reaching back over
   BOTH the copy above it and the runner beside it. THE NEGATIVE MARGIN HAS TO
   BEAT THE COLUMN GAP BEFORE ANY OVERLAP EXISTS AT ALL — the arithmetic the
   corridor and the careers bar both paid for. */
.fdr__plate--near {
  grid-column: 6 / 11;
  grid-row: 3;
  z-index: 4;
  aspect-ratio: 3 / 2;
  margin-top: clamp(-5.5rem, -2rem - 3vw, -2rem);
  margin-left: clamp(-7rem, -2.5rem - 3.5vw, -2.5rem);
  rotate: -1.4deg;
}

/* THE SHOT IS LARGER THAN ITS PLATE, and that is what makes the blur usable:
   a `filter: blur()` on an element flush with its clip box feathers its own
   edge into transparency. Held 3rem proud on every side, the soft edge falls
   outside the plate and the plate keeps a hard cut. The overscan also gives
   the parallax somewhere to travel. */
.fdr__shot {
  position: absolute;
  inset: -3rem;
  z-index: 0;
}

.fdr__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.fdr__plate--far  .fdr__shot img { object-position: 50% 54%; }
.fdr__plate--mid  .fdr__shot img { object-position: 50% 38%; }
.fdr__plate--near .fdr__shot img { object-position: 52% 46%; }

/* --fpeek IS REGISTERED AND --focus IS NOT, AND THE SPLIT IS DELIBERATE.
   GSAP writes --focus every frame of the scrub, so it never has to
   interpolate and can stay unregistered and inherit for free. --fpeek is a
   hover nudge CSS has to TRANSITION, and an unregistered property cannot
   interpolate — it would step. It is registered `inherits: false` and
   declared on each element that reads it, because a registered INHERITED
   property is computed on every element in every document that loads this
   stylesheet, and editorial.css is on all nine pages (the cost the clock's
   specular pair paid for).

   Note the transition is on --fpeek, NEVER on `filter`. A transition on the
   filter itself would smooth every frame of the scrub as well, which is the
   "a tween and a transition must not share a property" bug in a new coat. */
@property --fpeek { syntax: "<number>"; inherits: false; initial-value: 0; }

.fdr__shot {
  --fpeek: 0;
  --s: clamp(0, calc(var(--sharp) + var(--fpeek)), 1);
  transition: --fpeek 0.7s var(--ease-out);
  filter:
    blur(calc((1 - var(--s)) * var(--blur-max)))
    brightness(calc(0.76 + var(--s) * 0.4))
    saturate(calc(0.72 + var(--s) * 0.26))
    contrast(calc(0.98 + var(--s) * 0.1));
}

/* OUT OF FOCUS, HIGHLIGHTS BLOOM. This is the detail that makes the pull read
   as a lens rather than as a blur filter: the softer a plane is, the more its
   warm sources spread. Three pools sharing no axis, because anything on a
   regular period reads as a pattern laid over a photograph. */
.fdr__bloom {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  mix-blend-mode: screen;
  --fpeek: 0;
  --s: clamp(0, calc(var(--sharp) + var(--fpeek)), 1);
  transition: --fpeek 0.7s var(--ease-out);
  opacity: calc((1 - var(--s)) * 0.72);
  background:
    radial-gradient(9rem 9rem at 24% 30%, rgba(255, 226, 176, 0.3), transparent 68%),
    radial-gradient(13rem 13rem at 71% 22%, rgba(192, 156, 96, 0.24), transparent 70%),
    radial-gradient(7rem 7rem at 58% 78%, rgba(255, 238, 206, 0.2), transparent 66%);
}

/* THE HAIRLINE IS PART OF THE FOCUS, not a frame drawn around a picture. The
   plate that is sharp is also the one that is ringed, so the eye is told
   where to look by the site's own line language instead of by an arrow. */
.fdr__plate::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(156, 120, 72, calc(0.1 + var(--sharp) * 0.42));
  background: linear-gradient(
    180deg,
    rgba(6, 6, 8, 0.34) 0%,
    transparent 30%,
    rgba(6, 6, 8, calc(0.52 - var(--sharp) * 0.2)) 100%);
}

/* --------------------------------------------------------------------------
   The reading column
   -------------------------------------------------------------------------- */

.fdr__read {
  grid-column: 7 / 12;
  grid-row: 2;
  z-index: 2;
  min-width: 0;
  padding-top: clamp(1.5rem, 1rem + 2.4vw, 3.25rem);
  /* the near plate rides up over this block, so the column has to give it
     ground to land on or it covers the attribution */
  padding-bottom: clamp(2.24rem, 1.28rem + 3.2vw, 4.48rem);
}

.fdr__lede {
  font-size: var(--text-lg);
  line-height: 1.7;
  color: var(--ink);
  text-wrap: pretty;
}

.fdr__body {
  margin-top: clamp(0.9rem, 0.75rem + 0.6vw, 1.25rem);
  color: var(--vein);
  text-wrap: pretty;
}

/* THE NAME SITS WITH THE TYPE, NEVER UNDER A PHOTOGRAPH. */
.fdr__who {
  margin-top: clamp(1.4rem, 1.1rem + 1.2vw, 2rem);
  padding-top: clamp(0.8rem, 0.65rem + 0.6vw, 1.1rem);
  border-top: 1px solid var(--border-gold);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--vein);
}
.fdr__who b {
  font-weight: 600;
  color: var(--gold-light);
}

/* --------------------------------------------------------------------------
   The close

   NO RULE ABOVE IT, deliberately: the corridor two sections up already closes
   on a hairline plus one display statement, and repeating that device would
   make the page's two rebuilt sections end the same way. Here the hierarchy
   does it instead — the concession is set small and the payoff is set large.
   -------------------------------------------------------------------------- */

.fdr__close {
  grid-column: 2 / 11;
  grid-row: 4;
  margin-top: clamp(1.8rem, 1.296rem + 2.52vw, 3.96rem);
}

.fdr__stmt {
  max-width: 44rem;
  font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.3rem);
  line-height: 1.6;
  color: var(--vein);
  text-wrap: pretty;
}

.fdr__payoff {
  margin-top: clamp(0.85rem, 0.7rem + 0.9vw, 1.45rem);
  max-width: 52rem;
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 1.2rem + 2.2vw, 3rem);
  line-height: 1.16;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}
/* additive only, so it is simply absent where --focus never moves */
.fdr__payoff em {
  font-style: italic;
  color: var(--gold-light);
  text-shadow: 0 0 calc(var(--focus) * 24px) rgba(192, 156, 96, calc(var(--focus) * 0.26));
}

/* --------------------------------------------------------------------------
   Narrower

   Below 901px the absolute relationships are DROPPED ENTIRELY rather than
   retuned — three overlapping plates pinned to grid lines collide the moment
   the type wraps, and at this width it wraps. The stage flows in source
   order, which is also the argument in order: the headline, the week you had,
   the runner, the copy, the number, the close. `initFocus` is gated to the
   same breakpoint, so the plates simply hold their resting depth of field.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
  /* a smaller plate needs a smaller circle of confusion, or the background
     stops being a background and becomes a stain */
  .fdr { --blur-max: 5px; }
  .fdr__stage {
    display: flex;
    flex-direction: column;
    gap: clamp(1.5rem, 1.1rem + 1.8vw, 2.5rem);
  }
  .fdr__head { padding-bottom: 0; }
  .fdr__h { padding-top: clamp(0.9rem, 0.75rem + 0.6vw, 1.3rem); }

  /* THE PLATES ARE CAPPED, NOT RUN FULL WIDTH, and this was a measured fault
     rather than a preference. Stacked at the top of this range the three
     photographs took the full 828px column, which made the portrait 1035px
     tall on its own and took the section from 1500px at 960 to 2795px at 900
     — the layout nearly doubling across one breakpoint. Capping the width
     and flattening the crops holds it near 1900px AND keeps the three apart:
     on one column the SIZE and the CROP are the only things left doing that
     (the floor's lesson on team). */
  .fdr__plate { rotate: none; margin: 0; height: auto; min-height: 0; }
  .fdr__plate--far {
    width: calc(100% + var(--bleed));
    margin-right: calc(-1 * var(--bleed));
    aspect-ratio: 2.2 / 1;
  }
  /* the cap is sized so the two staggered plates still OVERLAP in x — at
     30rem the portrait ended at 480 and the right-aligned plate below began
     at 476 on an 828 column, which read as two isolated blocks with a void
     beside each. Overlapping their horizontal ranges is what makes a single
     column zigzag instead of stack. */
  .fdr__plate--mid {
    aspect-ratio: 4 / 5;
    width: min(100%, 33rem);
  }
  .fdr__plate--near {
    aspect-ratio: 3 / 2;
    width: min(72%, 22rem);
    margin-left: auto;
  }
  .fdr__shot { inset: -1.75rem; }

  .fdr__read { padding-top: 0; padding-bottom: 0; max-width: 42rem; }
  .fdr__close { margin-top: clamp(1.5rem, 1.2rem + 1.4vw, 2.5rem); }
}

/* on a phone the caps stop helping and start starving the pictures. The
   portrait needs no rule here: `min(100%, 30rem)` already goes full width
   once the column is narrower than the cap, and forcing `width: 100%` at this
   breakpoint made the section TALLER at 620 (2181px) than at 700 (2061px) —
   a layout that grows as the viewport shrinks. */
@media (max-width: 620px) {
  .fdr__plate--far { aspect-ratio: 16 / 9; }
  .fdr__plate--near { width: min(84%, 20rem); }
}

/* you look at it, and it comes into focus. CSS owns this because it is
   presentational and stateless, it survives GSAP being absent, and
   `pointerenter` is not reliably dispatched through CDP — so a JS version
   could not be measured in the headless sweep either (the corridor's find). */
@media (hover: hover) {
  .fdr__plate:hover .fdr__shot,
  .fdr__plate:hover .fdr__bloom { --fpeek: 0.24; }
}

@media (prefers-reduced-motion: reduce) {
  /* --focus is already at its finished value; only the hover lift stops */
  .fdr__shot, .fdr__bloom { transition: none; }
  .fdr__plate:hover .fdr__shot,
  .fdr__plate:hover .fdr__bloom { --fpeek: 0; }
}

/* ==========================================================================
   VALUES — "no adjectives"   (about.html, section 04)

   It replaced `.values` / `.value`: a four-across grid of identical boxes,
   each an h3 over one sentence. Those two classes had NO other user site-wide
   and are now dead — a safe follow-up deletion alongside `.ed`, `.ed--flip`,
   `.ed--overlap` and `.kicker-lg`. None were sliced out here, per the
   near-miss that silently removed `.argue` along with `.omit`.

   TWO FAULTS, AND THE SECOND ONE IS THE INTERESTING ONE. (1) Four equal boxes
   saying four equal things is the shape team's `.seat` was rebuilt out of.
   (2) It sat DIRECTLY ABOVE the `.statbar`, which is also four things across
   — two four-up rows in succession, which reads as one template applied
   twice. Always look at the section below as well as the section above.

   **UNEQUAL SIZING DOES NOT BREAK A REPEATED COMPONENT, A DIFFERENT STRUCTURE
   DOES** — the lesson `.seat` paid for, and the trap this section sets,
   because four boxes at four different sizes still read as four boxes. So
   each value holds its sentence in a different RELATIONSHIP: 01 below the
   name, 02 beside it on one baseline, 03 above it, 04 hanging in the margin
   beside a name that runs the full width. The sizes vary too (74 / 50 / 67 /
   87px at 1440, deliberately not monotonic), but that is the second-order
   difference, not the one doing the work.

   AND THE HIERARCHY IS NOT FAKED. A mission and a vision are unequal and
   `.aim` was right to argue it; these four are not. The KB is explicit —
   "Four. Not five, not six. Each gets one line and no adjectives." Nothing
   here promotes one over another. What varies is the setting.

   THE GRAMMAR IS THE WORDS TIGHTENING, and it was the last free move because
   it is not a movement at all. Everything on this site moves a camera, an
   object, light or focus; nothing changes TYPE'S OWN METRICS. Each name
   arrives open, wide-tracked and haloed — decorative — and closes into a
   tight, hard, struck noun. The headline made mechanical: the adjectives come
   off and the word is what is left.

   **NOTHING THAT AFFECTS LEGIBILITY IS DRIVEN FROM `--set`.** The names hold
   full opacity at every value of it and the entrance is the robust `.reveal`
   observer; only tracking and an ADDITIVE halo move. That is the recurring
   "what element does the opacity actually land on" trap answered by making
   the answer "nothing" — a scrub that never fires cannot leave a heading at a
   fraction here, because no opacity is on the scrub in the first place.

   THE HALO IS `filter: drop-shadow`, NOT `text-shadow`, and that is forced:
   these names are `background-clip: text` with a transparent fill, and a
   text-shadow paints from the glyph geometry independently of the clipped
   background, so it muddies the metal instead of haloing it. drop-shadow
   operates on the composited result and does the right thing.

   EACH SENTENCE'S ENFORCING CLAUSE IS THE GOLD ONE, and that is the argument:
   none of the four is asserted, each is backed by something you could point
   at. It also does real work on a copy problem found before building — "We
   sell what we would buy" is also on index (`.wall`) and "Contracts,
   coverage, install dates ... We do not drop things" is also on team
   (`.seat`), so the emphasis is put on the clauses unique to this page. The
   KB wording itself is unchanged.

   Photography is an explicit exception to the `.media img` clean pass via
   `.four__*`: signature (the checkable artefact) running off the LEFT edge,
   and retail (a market someone runs like they own it) small and tilted.
   Neither is used as a real photograph elsewhere on about.html, and the left
   bleed alternates against `.fdr` above, which bleeds right. Both are graded
   DOWN — in `.fdr` the photographs are the composition, here the type is, and
   two adjacent sections cannot both be led by pictures.
   ========================================================================== */

.four {
  position: relative;
  /* the wide plate runs off the left edge, and an open-tracked name is wider
     than its own column by design — clip so neither can become a scrollbar */
  overflow-x: clip;
  --bleed: max(var(--gutter), calc((100vw - var(--container)) / 2));
  /* how far the letters stand apart before the word closes. In em, so it
     scales with each name's own size. */
  --track-open: 0.105em;
}

.four__ground {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(46rem 28rem at 12% 22%, rgba(156, 120, 72, 0.07), transparent 68%),
    radial-gradient(40rem 26rem at 90% 84%, rgba(128, 96, 48, 0.055), transparent 70%);
}

.four > .container { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   The descent
   -------------------------------------------------------------------------- */

.four__stage {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(1rem, 0.5rem + 1.4vw, 2.25rem);
  row-gap: 0;
}

/* the wrapper exists only to group the four in the markup; it must not
   become a grid item or the whole descent collapses into one cell. Safe on a
   plain div — there are no list semantics here to lose. */
.four__set { display: contents; }

.four__head {
  grid-column: 1 / 5;
  grid-row: 1;
  align-self: end;
  padding-bottom: clamp(0.4rem, 0.3rem + 0.5vw, 0.9rem);
}

.four__h {
  margin-top: clamp(0.8rem, 0.65rem + 0.6vw, 1.2rem);
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   A name

   `--set` runs 0 (open, haloed, decorative) to 1 (closed, hard, struck) and
   is declared on each value so the four can settle in turn. CSS value 1 is
   the FINISHED state, so no-JS / no-CDN / reduced-motion render four solid
   words and GSAP only ever takes them backwards to animate them forwards.
   -------------------------------------------------------------------------- */

.four__v { --set: 1; min-width: 0; }

.four__n {
  display: block;
  /* the tightening is only legible if the word never rewraps mid-close */
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: calc(var(--track-open) * (1 - var(--set)) - var(--set) * 0.015em);
  background: linear-gradient(172deg, #fdf8ef 0%, #f0e4cd 30%, #cdb287 64%, #9a7c4c 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 calc((1 - var(--set)) * 20px) rgba(192, 156, 96, calc((1 - var(--set)) * 0.4)));
}

.four__p {
  color: var(--vein);
  line-height: 1.65;
  text-wrap: pretty;
}

/* THE CLAUSE THAT ENFORCES THE VALUE. Full `--gold-light` at every value of
   --set, never dimmed — only the glow is on the number, so this can not
   become the contrast bug this codebase has now shipped three times. */
.four__p b {
  font-weight: 600;
  color: var(--gold-light);
  text-shadow: 0 0 calc(var(--set) * 15px) rgba(192, 156, 96, calc(var(--set) * 0.26));
}

/* ---- 01 · the sentence sits BELOW the name ------------------------------ */

.four__v--1 {
  grid-column: 5 / -1;
  grid-row: 1;
}
.four__v--1 .four__n { font-size: clamp(2.4rem, 1.5rem + 3.6vw, 4.6rem); }
.four__v--1 .four__p {
  margin-top: clamp(0.9rem, 0.7rem + 0.8vw, 1.45rem);
  max-width: 38rem;
}

/* ---- 02 · the sentence sits BESIDE the name, on its baseline ------------- */

.four__v--2 {
  grid-column: 7 / -1;
  grid-row: 2;
  /* ALIGNED TO THE PLATE'S BOTTOM, NOT ITS MIDDLE. Centred, this block sat
     in the vertical middle of a 440px photograph with ~170px of black above
     and below it, which reads as a hole rather than as air. Sharing a
     baseline with the plate's lower edge makes the same space deliberate. */
  align-self: end;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: clamp(0.9rem, 0.7rem + 1.2vw, 1.9rem);
  margin-top: clamp(1.62rem, 1.08rem + 2.304vw, 3.6rem);
}
.four__v--2 .four__n {
  flex: 0 0 auto;
  font-size: clamp(1.8rem, 1.3rem + 2.2vw, 3.1rem);
}
.four__v--2 .four__p { flex: 1 1 13rem; }

/* ---- 03 · the sentence sits ABOVE the name ------------------------------- */

.four__v--3 {
  grid-column: 2 / 9;
  grid-row: 3;
  align-self: end;
  /* the NAME is what aligns to the right edge; the sentence is pushed there
     but stays ragged-right, because right-aligned body copy is a slower read
     and this section already asks the eye to move around */
  text-align: right;
  margin-top: clamp(1.62rem, 1.08rem + 2.304vw, 3.6rem);
  padding-bottom: clamp(0.5rem, 0.4rem + 0.6vw, 1rem);
}
.four__v--3 .four__p {
  margin: 0 0 clamp(0.9rem, 0.7rem + 0.8vw, 1.45rem) auto;
  max-width: 34rem;
  text-align: left;
}
.four__v--3 .four__n { font-size: clamp(2.2rem, 1.4rem + 3.2vw, 4.2rem); }

/* ---- 04 · the sentence hangs in the margin beside a full-width name ------ */

.four__v--4 {
  grid-column: 1 / -1;
  grid-row: 4;
  display: grid;
  grid-template-columns: minmax(0, 0.3fr) minmax(0, 1fr);
  column-gap: clamp(1.25rem, 0.9rem + 1.8vw, 2.75rem);
  align-items: end;
  margin-top: clamp(1.8rem, 1.224rem + 2.592vw, 3.96rem);
}
.four__v--4 .four__p {
  grid-column: 1;
  margin-bottom: clamp(0.35rem, 0.3rem + 0.4vw, 0.75rem);
}
.four__v--4 .four__n {
  grid-column: 2;
  font-size: clamp(2.6rem, 1.6rem + 4.2vw, 5.4rem);
}

/* --------------------------------------------------------------------------
   The two plates

   Both carry `.reveal`, so the tilt uses the independent `rotate` property —
   `.reveal.is-visible` sets `transform: none` and would wipe a rotation the
   instant the observer fired. GSAP owns the inner image's transform alone.
   -------------------------------------------------------------------------- */

.four__plate {
  position: relative;
  margin: 0;
  overflow: hidden;
  box-shadow:
    inset 0 0 0 1px rgba(156, 120, 72, 0.26),
    inset 0 3px 14px -6px rgba(0, 0, 0, 0.9),
    0 22px 52px -32px rgba(0, 0, 0, 0.95);
}

.four__shot { position: absolute; inset: 0; overflow: hidden; }

/* GRADED DOWN ON PURPOSE. `.fdr` directly above is led by its photographs;
   this section is led by its type, and two adjacent sections cannot both be
   led by pictures.
   Laid out taller than its opening so the scroll drift never exposes an edge.
   Layout, not a base transform — GSAP owns this element's transform alone. */
.four__shot img {
  position: relative;
  top: -6%;
  width: 100%;
  height: 112%;
  object-fit: cover;
  filter: brightness(0.82) saturate(0.78) contrast(1.04);
}

.four__plate::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(6, 6, 8, 0.36) 0%, transparent 34%, rgba(6, 6, 8, 0.56) 100%);
}

.four__plate--wide {
  grid-column: 1 / 7;
  grid-row: 2;
  align-self: start;
  /* SHALLOW ON PURPOSE. This plate sets the height of its whole row and at
     16/10 it stood 440px tall beside a two-line sentence. The row is now
     sized by a photograph the copy can actually stand level with. */
  margin-top: clamp(1.62rem, 1.08rem + 2.304vw, 3.6rem);
  margin-left: calc(-1 * var(--bleed));
  aspect-ratio: 2.25 / 1;
}
.four__plate--wide .four__shot img { object-position: 54% 50%; }

.four__plate--small {
  grid-column: 9 / -1;
  grid-row: 3;
  align-self: end;
  /* A SMALLER TOP MARGIN THAN ITS ROW PARTNER IS THE INTERLOCK. Both items
     hang from the same row line, so the difference between their margins is
     exactly how far this plate rises above the copy beside it. */
  margin-top: clamp(0.5rem, 0.2rem + 1vw, 1.5rem);
  aspect-ratio: 5 / 4;
  rotate: -1.7deg;
}
.four__plate--small .four__shot img { object-position: 50% 42%; }

/* --------------------------------------------------------------------------
   Narrower

   One column, in source order — the head, then the four in turn with the two
   photographs where they already sit between them. The names stay `nowrap`
   (the tightening is the section), so the open tracking has to come down or
   "Performance" outruns a phone column before it closes.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
  .four { --track-open: 0.06em; }

  .four__stage {
    display: flex;
    flex-direction: column;
    gap: clamp(2rem, 1.4rem + 2.6vw, 3.5rem);
  }
  /* **`align-self` FLIPS AXIS BETWEEN GRID AND FLEX, AND IT MUST BE RESET
     HERE.** In the grid above it is VERTICAL - `end` means "sit on the row's
     bottom edge". In this flex COLUMN the cross axis is horizontal, so the
     very same declarations silently became "align right", and the headline,
     Precision, Ownership and the small plate all jumped to the right margin.
     Nothing overflowed, nothing collided and nothing was hidden, so the
     numeric sweep passed it clean - it was only visible in a screenshot.
     Any grid that collapses to flex has to neutralise its own align-self. */
  .four__head,
  .four__v,
  .four__plate { align-self: stretch; margin-top: 0; }

  .four__head { padding-bottom: 0; }

  .four__v--1 .four__n { font-size: clamp(2rem, 1.5rem + 2.4vw, 3.2rem); }
  .four__v--2 .four__n { font-size: clamp(1.6rem, 1.3rem + 1.4vw, 2.4rem); }
  .four__v--3 .four__n { font-size: clamp(1.9rem, 1.45rem + 2.1vw, 3rem); }
  .four__v--4 .four__n { font-size: clamp(2.1rem, 1.55rem + 2.7vw, 3.5rem); }

  /* right alignment needs a wide column to read as alignment rather than as
     a mistake, and the margin-hung sentence needs a margin to hang in */
  .four__v--3 { text-align: left; padding-bottom: 0; }
  .four__v--3 .four__p { margin-left: 0; }
  /* ONE COLUMN IS WHERE THE FOUR STRUCTURES CONVERGE, and 03 and 04 are the
     pair that collide: both end up with the sentence above the name. 04 keeps
     its "hanging in the margin" character by staying OFFSET while 03 sits
     flush, so the two adjacent beats still differ in kind rather than only in
     size (the whole point this section is built on). */
  .four__v--4 { display: block; }
  .four__v--4 .four__p {
    margin-bottom: clamp(0.7rem, 0.6rem + 0.5vw, 1.1rem);
    margin-left: auto;
    max-width: 86%;
  }

  .four__p { max-width: 42rem; }

  .four__plate--wide {
    width: calc(100% + var(--bleed));
    margin-left: calc(-1 * var(--bleed));
    aspect-ratio: 2.1 / 1;
  }
  /* KEEPS THE LANDSCAPE CROP IT HAS ON THE GRID. Left at 4/5 it stood 418px
     tall against nothing on its left, which is a hole rather than a margin;
     the same crop at 5/4 is 269px and the air beside it reads as air. */
  .four__plate--small {
    rotate: none;
    width: min(72%, 23rem);
    margin-left: auto;
    aspect-ratio: 5 / 4;
  }
}

@media (max-width: 620px) {
  .four__plate--wide { aspect-ratio: 16 / 9; }
  .four__plate--small { width: min(82%, 19rem); }
}

/* ============================================================
   CULTURE — "THE ROOM"   (.rm, about.html section 04)

   THE CAMERA TURNS, IN PLACE. Everything else on this site travels
   X (.wall), pushes Z (.worlds), climbs Y (.rise), holds still (.lp)
   or turns an OBJECT (.forge). Rotating the VIEW was the one camera
   move still free, and the section's headline is literally about
   standing inside something.

   ONE NUMBER: --pan, the bearing in degrees, written on .rm by the
   module every frame. CSS computes the scene rotation, each frame's
   grade and shadow, the caption handover, the drifting light pool
   and the bearing strip from it. --pan is UNREGISTERED on purpose:
   it never needs to interpolate and it inherits to the HUD (which
   sits outside the 3D context) for free.

   The BASE below is a finished hand-set gallery. .is-live is added
   only at >=901px with GSAP present, so no-JS / no-CDN / reduced
   motion / phones all get a complete section, never a blank stage.
   ============================================================ */

/* the pointer nudge is a SEPARATE number from the scrub, so a tween
   and a transition can never fight over the same value. inherits:false
   keeps it off every element on all nine pages. */
@property --sway { syntax: "<number>"; inherits: false; initial-value: 0; }

.rm {
  --pan: 0;
  position: relative;
  isolation: isolate;
  overflow-x: clip;
  /* no ground of its own: the site has ONE, and it is fixed. See .ground. */
}

.rm__head { max-width: 54rem; margin-bottom: clamp(2.25rem, 1.75rem + 2.6vw, 4rem); }
.rm__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.9rem, 1.1rem + 3.3vw, 4rem);
  line-height: 1.05;
  letter-spacing: -0.024em;
  color: var(--ink);
  margin-block: 0.75rem 1rem;
}
.rm__lead {
  font-size: var(--text-base);
  line-height: 1.62;
  color: var(--vein);
  max-width: 34rem;
}

/* ---- BASE: a hand-set gallery, uneven on purpose ------------- */

.rm__track,
.rm__stage,
.rm__space { position: relative; }

.rm__scene {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(1rem, 0.7rem + 1.4vw, 2.25rem);
  align-items: start;
  padding-inline: var(--gutter);
  max-width: calc(var(--container) + 2 * var(--gutter));
  margin-inline: auto;
}

.rmp { position: relative; margin: 0; }
.rmp--1 { grid-column: 1 / 10; grid-row: 1; }
.rmp--3 { grid-column: 1 / 5;  grid-row: 2; }
.rmp--2 { grid-column: 5 / 13; grid-row: 2; margin-top: clamp(1.28rem, 4.48vw, 5.12rem); }
.rmp--4 { grid-column: 4 / 13; grid-row: 3; }

.rmp__shot {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 3px;
  background: var(--obsidian-raised);
  aspect-ratio: var(--w) / var(--h);
}
.rmp__shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(0.92) saturate(0.86) contrast(1.03);
}
.rmp__edge {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border: 1px solid var(--border-gold);
  border-radius: 3px;
}

.rmp__cap { display: block; padding-top: 0.9rem; }
.rmp__k {
  display: block;
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-light);
  margin-bottom: 0.4rem;
}
.rmp__t {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.05rem, 0.9rem + 0.6vw, 1.4rem);
  line-height: 1.28;
  color: var(--ink);
}

/* everything that only exists inside the room */
.rm__win,
.rm__wall,
.rm__floor,
.rm__lamp,
.rm__vig,
.rm__grain,
.rm__hud { display: none; }

.rm__stmt {
  margin-top: clamp(1.8rem, 1.44rem + 2.16vw, 3.24rem);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.5rem, 1.1rem + 1.7vw, 2.4rem);
  line-height: 1.2;
  letter-spacing: -0.018em;
  color: var(--ink);
  max-width: 47rem;
  text-wrap: balance;
}
.rm__stmt b { color: var(--gold-light); font-weight: 500; }

@media (max-width: 700px) {
  .rm__scene { grid-template-columns: minmax(0, 1fr); }
  .rmp--1, .rmp--2, .rmp--3, .rmp--4 {
    grid-column: 1;
    grid-row: auto;
    margin-top: 0;
  }
  /* the crop is the only thing keeping four frames distinct in one
     column, so the aspects stay unequal rather than normalising */
  .rmp--1 .rmp__shot { aspect-ratio: 16 / 9; }
  .rmp--3 .rmp__shot { aspect-ratio: 4 / 5; width: min(100%, 21rem); }
  .rmp--4 .rmp__shot { aspect-ratio: 5 / 4; }
}

/* ============================================================
   LIVE — the room, and the turn
   ============================================================ */

.rm.is-live { --u: 1px; }
@media (min-width: 901px) and (max-width: 1319px) { .rm.is-live { --u: 0.86px; } }
@media (min-width: 901px) and (max-width: 1099px) { .rm.is-live { --u: 0.74px; } }

.rm.is-live .rm__track { height: 200svh; }

.rm.is-live .rm__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
}

.rm.is-live .rm__space {
  position: absolute;
  inset: 0;
  perspective: calc(1400 * var(--u));
  perspective-origin: 50% 50%;
}

/* the whole room turns as one. No GSAP transform here: CSS owns it,
   composed from the scrubbed --pan and the transitioned --sway. */
.rm.is-live .rm__scene {
  display: block;
  position: absolute;
  inset: 0;
  padding: 0;
  margin: 0;
  max-width: none;
  transform-style: preserve-3d;
  transform: rotateY(calc((var(--pan) + var(--sway)) * -1deg));
  transition: --sway 0.75s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}

/* Every station sits on the same cylinder: rotate to its bearing, push
   out to its own depth, then lift. --y is applied AFTER translateZ so it
   moves in world Y, and the box is centred by inset+margin rather than a
   percentage translate (a % translate resolves against the element's own
   size, which is the trap this site has paid for three times).

   BOTH SIGNS ARE NEGATIVE AND BOTH MATTER. translateZ is NEGATIVE because
   positive Z is TOWARD the viewer: the first cut pushed every frame in
   front of the camera and they rendered 1592px wide instead of 515. And
   the bearing is negated so that on-screen bearing works out to the plain
   sum (--a + --pan), which is what every reaction in this section reads. */
.rm.is-live .rmp,
.rm.is-live .rm__win,
.rm.is-live .rm__wall {
  position: absolute;
  inset: 0;
  margin: auto;
  width: calc(var(--w) * var(--u));
  height: calc(var(--h) * var(--u));
  transform:
    rotateY(calc(var(--a) * -1deg))
    translateZ(calc(var(--d) * var(--u) * -1))
    translateY(calc(var(--y) * var(--u)));
}

/* --e is the station's bearing RELATIVE to where you are looking, and
   --face is how square-on it is. Squared falloff, computed with plain
   number multiplication so no JS touches any of it.
   --sway is deliberately left out: it is registered inherits:false, and
   an unregistered property's expression resolves such a reference to the
   registered INITIAL value on every child. */
.rm.is-live .rmp {
  --e: calc(var(--a) + var(--pan));
  --face: clamp(0, calc(1 - var(--e) * var(--e) / 1700), 1);
  opacity: 0;
  transition: opacity 1s cubic-bezier(0.22, 0.61, 0.36, 1);
  transition-delay: calc(var(--i) * 0.14s);
}
.rm.is-live.is-in .rmp { opacity: 1; }

.rm.is-live .rmp__shot {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  border-radius: 2px;
  /* STATIC, and that is a performance decision. A box-shadow whose BLUR
     RADIUS is driven from --face repaints the shadow on all four frames
     every frame of the scrub - it was the second biggest cost in the
     section. The lift is carried by the scrim and the hairline instead,
     both of which are compositor work. */
  box-shadow: 0 calc(42 * var(--u)) calc(92 * var(--u)) calc(-28 * var(--u)) rgba(0, 0, 0, 0.88);
}

/* THE GRADE IS A SCRIM, NOT A FILTER, FOR THE SAME REASON. Re-filtering
   four large bitmaps every frame is the single most expensive thing this
   section could do; fading a flat rect over them is compositor work and
   looks the same. The static filter below still sets the house grade. */
.rm.is-live .rmp__shot::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--obsidian);
  opacity: calc((1 - var(--face)) * 0.64);
}

/* the picture shifts against its own frame, so each photograph keeps a
   little interior depth of its own as the room turns past it. The scale
   is headroom for that shift and nothing else. */
.rm.is-live .rmp__shot img {
  --es: clamp(-40, var(--e), 40);
  transform: translateX(calc(var(--es) * -0.5px)) scale(1.13);
  filter: brightness(1.06) saturate(0.92) contrast(1.07);
}

.rm.is-live .rmp__edge {
  border-color: var(--gold-shadow);
  /* z-index keeps it above ::after, which is later in paint order */
  z-index: 2;
  opacity: calc(0.3 + var(--face) * 0.7);
  box-shadow: inset 0 0 0 1px rgba(192, 156, 96, 0.11);
}

/* the real caption stays in the document for assistive tech; the visible
   one is drawn flat, above the 3D, out of the accessibility tree */
.rm.is-live .rmp__cap {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- the room itself ---------------------------------------- */

.rm.is-live .rm__floor {
  display: block;
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(2600 * var(--u));
  height: calc(1700 * var(--u));
  margin-left: calc(-1300 * var(--u));
  margin-top: calc(-850 * var(--u));
  /* pushed to -1500 so the whole plane stays BEHIND the camera plane. A
     floor centred at z:0 has half its depth at positive z, i.e. behind
     the viewer's eye, where the perspective divide blows up. */
  transform: translate3d(0, calc(400 * var(--u)), calc(-1180 * var(--u))) rotateX(75deg);
  background:
    radial-gradient(28% 24% at 33% 26%, rgba(214, 172, 102, 0.58), transparent 74%),
    radial-gradient(22% 20% at 71% 30%, rgba(198, 156, 92, 0.4), transparent 76%),
    radial-gradient(46% 40% at 46% 40%, rgba(150, 114, 62, 0.22), transparent 78%),
    radial-gradient(54% 50% at 50% 34%, #14131a 0%, rgba(12, 12, 17, 0.6) 58%, transparent 100%);
}

/* WHERE THE LIGHT COMES FROM. No photograph and no copy: it is
   architecture, which is also what stops the set reading as a four-up.

   ON AN OBSIDIAN BRAND THE INSTINCT IS TO KEEP GOING DARKER, and the
   first cut of this was a flat tan rectangle with a hard edge: a panel,
   not a source. What fixes it is what fixed the doorways on what-we-do -
   a genuinely blown-out core, a large bloom AROUND it, and no hard edge
   anywhere (the mask feathers the glass into its own spill). */
.rm.is-live .rm__win {
  display: block;
  background:
    radial-gradient(64% 52% at 46% 36%, rgba(255, 250, 240, 1), rgba(240, 210, 156, 0.94) 38%, rgba(178, 136, 76, 0.62) 70%, rgba(104, 78, 44, 0.1) 100%);
  box-shadow:
    0 0 calc(560 * var(--u)) calc(210 * var(--u)) rgba(178, 134, 70, 0.34),
    0 0 calc(190 * var(--u)) calc(44 * var(--u)) rgba(236, 198, 130, 0.4);
  -webkit-mask-image: radial-gradient(66% 60% at 48% 40%, #000 30%, rgba(0, 0, 0, 0.6) 68%, transparent 100%);
  mask-image: radial-gradient(66% 60% at 48% 40%, #000 30%, rgba(0, 0, 0, 0.6) 68%, transparent 100%);
}
.rm.is-live .rm__win-glass {
  position: absolute;
  inset: 0;
  background: linear-gradient(118deg, rgba(255, 255, 250, 0.5), transparent 42%);
}
/* the frame is read as a SILHOUETTE against the light, so the bars are
   soft-edged and never fully opaque - a crisp black cross on the glass
   turned the whole thing back into a graphic */
.rm.is-live .rm__win-bar {
  position: absolute;
  background: rgba(10, 9, 12, 0.55);
  filter: blur(calc(4 * var(--u)));
}
.rm.is-live .rm__win-bar--v { top: 10%; bottom: 12%; left: 47%; width: calc(13 * var(--u)); }
.rm.is-live .rm__win-bar--h { left: 10%; right: 10%; top: 26%; height: calc(11 * var(--u)); }

/* the far surfaces. Dark, but never black: they are what the frames hang
   on, and they carry the window's spill across the room. */
.rm.is-live .rm__wall {
  display: block;
  background:
    radial-gradient(50% 54% at 50% 42%, rgba(30, 28, 35, 0.97) 0%, rgba(14, 13, 18, 0.62) 56%, transparent 100%);
}
/* only the segment the window is standing against catches its spill */
.rm.is-live .rm__wall:first-of-type {
  background:
    radial-gradient(40% 36% at 64% 38%, rgba(150, 114, 64, 0.32), transparent 72%),
    radial-gradient(50% 54% at 50% 42%, rgba(34, 31, 39, 0.97) 0%, rgba(15, 14, 19, 0.62) 56%, transparent 100%);
}

/* ---- atmosphere, flat on the stage --------------------------- */

/* The pool of light is fixed in the ROOM, so as you turn it crosses your
   view. Its centre is tied to the window's own bearing.

   IT IS TRANSLATED, NEVER REDRAWN. The first cut put --pan inside the
   gradient's own `at` position, which repaints a full-viewport gradient
   on every frame of the scrub - measured as the single biggest cost in
   the section. A fixed-size element with a static gradient, moved by a
   transform, is compositor work and looks identical. Same lesson as the
   .cover gleam: move the light, do not recompute it. */
.rm.is-live .rm__lamp {
  display: block;
  position: absolute;
  top: 42%;
  left: 50%;
  width: calc(1500 * var(--u));
  height: calc(1150 * var(--u));
  margin: calc(-575 * var(--u)) 0 0 calc(-750 * var(--u));
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(150, 114, 62, 0.19), rgba(150, 114, 62, 0.07) 54%, transparent 78%);
  transform: translate3d(calc((var(--pan) - 52) * 14px), 0, 0);
  will-change: transform;
}

.rm.is-live .rm__vig {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    radial-gradient(120% 88% at 50% 44%, transparent 38%, rgba(4, 4, 6, 0.82) 100%),
    linear-gradient(0deg, rgba(4, 4, 6, 0.92) 0%, rgba(4, 4, 6, 0.66) 14%, transparent 34%);
}

.rm.is-live .rm__grain {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: 0.36;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

/* ---- the flat layer ------------------------------------------ */

.rm.is-live .rm__hud {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}

.rm.is-live .rm__caps {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  bottom: clamp(6.5rem, 12vh, 10rem);
  width: min(52rem, 76%);
  height: 4.6rem;
}
/* A PLATEAU AND THEN A CLIFF, not a smooth curve. A bare parabola holds
   the caption around 0.15 for a long stretch of the handover, which reads
   as a rendering fault rather than as one statement giving way to the
   next. Multiplying by 4.5 clamps it to full within about 10deg and takes
   it to zero over the next 1.5, so exactly one line is ever on screen and
   there is a short blackout between stations (the .wall handover). */
.rm.is-live .rm__cap {
  --e: calc(var(--a) + var(--pan));
  --sh: clamp(0, calc((1 - var(--e) * var(--e) / 144) * 4.5), 1);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  opacity: var(--sh);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.25rem, 1rem + 0.9vw, 1.75rem);
  line-height: 1.24;
  letter-spacing: -0.012em;
  color: var(--ink);
  text-shadow: 0 2px 22px rgba(4, 4, 6, 0.9);
}
.rm.is-live .rm__cap::before { content: attr(data-line); }

.rm.is-live .rm__bear {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  bottom: clamp(2.6rem, 5vh, 4.5rem);
  width: min(42rem, 70%);
  height: 3rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
}
.rm.is-live .rm__bear-rule {
  position: absolute;
  left: 0;
  right: 0;
  top: 0.6rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-shadow) 26%, var(--gold) 50%, var(--gold-shadow) 74%, transparent);
  opacity: 0.6;
}
.rm.is-live .rm__bear-mark {
  position: absolute;
  left: 50%;
  top: 0.1rem;
  width: 0.42rem;
  height: 0.42rem;
  margin-left: -0.21rem;
  border-left: 1px solid var(--gold-light);
  border-top: 1px solid var(--gold-light);
  rotate: 45deg;
}
/* the labels slide, the mark does not, so where you are looking needs
   no explaining. Position and travel are the same 9px per degree, which
   is what puts the facing station exactly on the mark. */
.rm.is-live .rm__bear-strip {
  position: absolute;
  left: 0;
  right: 0;
  top: 1.25rem;
  height: 1.4rem;
  transform: translateX(calc(var(--pan) * 9px));
}
.rm.is-live .rmb {
  --e: calc(var(--a) + var(--pan));
  --face: clamp(0, calc(1 - var(--e) * var(--e) / 260), 1);
  position: absolute;
  left: calc(50% + var(--a) * 9px);
  top: 0;
  translate: -50% 0;
  white-space: nowrap;
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
/* two stacked pseudo copies rather than colour arithmetic, and no text
   node either way, so nothing here is announced or dimmed below AA */
.rm.is-live .rmb::before,
.rm.is-live .rmb::after {
  content: attr(data-label);
  display: block;
}
.rm.is-live .rmb::before { color: var(--ink); opacity: 0.6; }
.rm.is-live .rmb::after {
  position: absolute;
  inset: 0;
  color: var(--gold-light);
  opacity: var(--face);
}

@media (prefers-reduced-motion: reduce) {
  .rm.is-live .rm__scene { transition: none; }
  .rm.is-live .rmp { transition: none; opacity: 1; }
}

/* ============================================================
   WHAT-WE-DO HERO — "ONE ENCOUNTER, TWO MOMENTS"  (.prop)

   It layers over `.hero--cover`, which keeps the type block, the
   h1 scale and the specification exactly as the client already
   approved them (they asked for smaller type and NO RULES on this
   hero, and both of those hold here). What changes is the right
   half: the monogram struck as an object is replaced by real
   photography, because what-we-do.html had NO photograph anywhere
   on it and the headline is literally about PEOPLE.

   NO RULES, NO REGISTRATION MARKS, NO PLINTH. The client removed
   all of that from this hero once already. Nothing here draws a
   line.

   TWO PLATES, ONE ENCOUNTER: the team on the way in, then a hand
   at the counter. Deliberately UNALIKE - one wide and bleeding off
   the page, one portrait and tucked back across the column seam -
   so two photographs in one object never resolve into a set.

   THE ENTRANCE IS THE SITE'S OWN SIGNATURE, EXTENDED. runIntro()
   still line-masks the h1; the plates are uncovered by the same
   gesture on the following beats, each from a DIFFERENT edge, so
   the near one finishes as the second sentence finishes. It is
   pure CSS keyed off the preloader's `is-loading`, so the from-
   state only exists while the curtain is up and no-JS renders the
   finished hero.

   `.cover__object` / `.cover__mark` / `.cover__halo` / `.cover__gleam`
   / `.cover__echo` are now UNUSED on this page. Not deleted, per the
   .argue near-miss: removing `.prop` from the section and restoring
   that markup is all a revert takes.
   ============================================================ */

.prop { overflow-x: clip; }

/* the left column must stay wide enough that "We have the people who
   can sell it." holds ONE line at the capped size - widen the column
   before ever enlarging the type (the note this hero already carries) */
.prop .cover__body {
  grid-template-columns: 1.42fr 0.58fr;
  align-items: center;
}

/* a warm pool the plates stand in, so they lift off the ground without
   needing a frame drawn round them */
.prop__wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(30rem 26rem at 82% 54%, rgba(158, 122, 70, 0.14), transparent 68%),
    radial-gradient(44rem 20rem at 60% 112%, rgba(5, 5, 7, 0.85), transparent 66%);
}

/* ---- the plates -------------------------------------------- */

.prop__plates {
  --pbleed: max(var(--gutter), calc((100vw - var(--container)) / 2));
  position: relative;
  width: calc(100% + var(--pbleed));
  margin-right: calc(var(--pbleed) * -1);
  height: clamp(19rem, 34svh + 7rem, 30rem);
  align-self: center;
}

.prop__plate {
  position: absolute;
  margin: 0;
  /* the reveal rides on clip-path, NEVER on transform: GSAP owns the
     transform of the image inside, and by now a tween and a CSS
     transition sharing one property has cost this project four bugs */
  clip-path: inset(0 0 0 0);
  transition: clip-path 1.15s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.prop__shot {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 3px;
  background: var(--obsidian-raised);
  aspect-ratio: var(--ar);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.72);
}
.prop__shot::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border: 1px solid var(--border-gold);
  border-radius: 3px;
}
.prop__shot img {
  position: absolute;
  inset: -6%;
  width: 112%;
  max-width: none;
  height: 112%;
  object-fit: cover;
  /* the overscan is the parallax travel: an image flush with its own
     clip box has nowhere to move without exposing an edge */
}

.prop__plate--far {
  --ar: 16 / 10;
  right: 0;
  top: 0;
  width: 100%;
}
.prop__plate--far .prop__shot img { filter: brightness(0.82) saturate(0.8) contrast(1.04); }

/* pulled back ACROSS THE COLUMN SEAM so the two halves interlock. It
   stops well clear of the headline and of the specification - measured,
   not guessed (see the width sweep). */
.prop__plate--near {
  --ar: 3 / 4;
  left: -2.25rem;
  bottom: 0;
  width: 55%;
}
.prop__plate--near .prop__shot img { filter: brightness(1.04) saturate(0.9) contrast(1.06); }

.prop__label {
  position: relative;
  margin-top: 0.7rem;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-light);
  transition: opacity 0.85s ease;
}
.prop__plate--far .prop__label {
  text-align: right;
  padding-right: calc(var(--pbleed) + 0.5rem);
}

/* ---- entrance: the curtain is the only from-state -------------- */

.is-loading .prop__plate--far { clip-path: inset(0 0 100% 0); }
.is-loading .prop__plate--near { clip-path: inset(100% 0 0 0); }
.is-loading .prop__label { opacity: 0; }

/* the far plate opens downward as the first sentence lands, the near one
   upward so it completes with the second - two different edges, because
   two plates opening the same way would read as one component twice */
.prop__plate--far { transition-delay: 0.34s; }
.prop__plate--near { transition-delay: 0.66s; }
.prop__plate--far .prop__label { transition-delay: 1.05s; }
.prop__plate--near .prop__label { transition-delay: 1.3s; }

/* ---- narrow --------------------------------------------------- */

@media (max-width: 900px) {
  .prop .cover__body {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.75rem, 1.25rem + 2.5vw, 2.75rem);
  }
  /* SIDE BY SIDE, NOT STACKED. Flowing the near plate under the wide one
     took the phone hero to 1272px; sitting them next to each other at
     unequal widths, unequal crops and unequal baselines keeps the block
     about a third of that and the pair still does not read as a set. */
  .prop__plates {
    display: grid;
    grid-template-columns: 1.25fr 0.75fr;
    gap: 0.85rem;
    align-items: start;
    width: 100%;
    margin-right: 0;
    height: auto;
    max-width: 34rem;
  }
  .prop__plate { position: static; width: auto; }
  .prop__plate--far { --ar: 4 / 3; }
  .prop__plate--near { --ar: 3 / 4; margin-top: 2.2rem; }
  .prop__plate--near .prop__label { text-align: left; }
}

@media (max-width: 480px) {
  .prop__plates { grid-template-columns: 1.15fr 0.85fr; gap: 0.6rem; }
  .prop__plate--near { margin-top: 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .prop__plate, .prop__label { transition: none; }
}

/* ==========================================================================
   THE FOOTER — "the plate"   (all nine pages)

   The old footer was the shape every footer on the web already has: a
   four-column grid of headed link lists with a brand block on the left and
   a rule above a copyright bar. It was also the only part of the site
   still built that way.

   THIS SITE IS ABOUT DOORS, so the footer is the plate beside ours. One
   engraved surface carrying the firm's name, the address you could drive
   to, and a way to reach a person. It is the component that appears on
   every page, so it should read as the object that identifies the firm -
   which is what a nameplate is.

   THE ADDRESS IS THE HERO, and that is the argument rather than a layout
   preference. Every claim on this site rests on Marble being a real firm
   that turns up in person; a street address is the least deniable version
   of that. It is set larger than anything else down here.

   RESTRAINT, NOT SKEUOMORPHISM. The plate is implied by how light behaves
   on it - a lit lip along the top edge, a slow sheen, a specular that
   follows the pointer, letters that sit down IN the surface and come up
   when you touch them - never by a literal metal texture. The brushed
   diagonal is at three per cent and the noise is there to stop the
   gradients banding.

   NO PHOTOGRAPH DOWN HERE, and that is a constraint rather than a
   preference: this is ONE component on NINE pages, and index.html already
   uses sixteen of the seventeen frames, so any photograph in the footer
   would duplicate a frame somewhere. Same reasoning that governs the FAQ.

   NO RULES EITHER. The three bands are separated by space and by a soft
   trough in the light, never by a drawn line - `.stop` owns the drawn
   boundary on what-we-do and the client has already asked once for rules
   to come off a composition.

   --isheen / --imx / --imy are written by GSAP every frame, so none of
   them is registered with @property: nothing needs interpolating and an
   inherited registered property would cost a recalc on all nine pages.
   Every one of them has a resting CSS value, so with no JS at all the
   plate is simply lit and still.
   ========================================================================== */

.imp {
  position: relative;
  z-index: 2;
  overflow: hidden;
  padding-top: clamp(2.75rem, 2.2rem + 2.2vw, 4.25rem);
  background:
    /* the pool of light the plate stands in */
    radial-gradient(120% 68% at 50% 118%, rgba(156, 120, 72, 0.14), transparent 68%),
    radial-gradient(80% 50% at 18% -10%, rgba(192, 156, 96, 0.06), transparent 70%),
    /* a transparent deepening, NOT a colour: the plate sits lower than
       the page but on exactly the same ground as everything else */
    linear-gradient(180deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.34) 45%, rgba(0, 0, 0, 0.52) 100%);
}

/* the brushed diagonal, deliberately almost invisible: at any strength you
   can actually see, it stops being a surface and becomes a texture */
.imp::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background: repeating-linear-gradient(104deg, rgba(255, 255, 255, 0.03) 0 1px, transparent 1px 7px);
}

.imp__grain {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0.35;
  pointer-events: none;
  /* no grain of its own: .grain::after covers the whole site */
  background: none;
}

/* THE LIP. The plate's top edge catching the light, brightest where the
   page has just come to rest on it. It does not draw itself - a rule that
   opens from the centre is `.stop`'s gesture and this component sits on
   the same page. Only its intensity moves. */
.imp__lip {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(156, 120, 72, 0.35) 14%, rgba(226, 198, 148, calc(0.5 + 0.5 * var(--ilip, 1))) 50%, rgba(156, 120, 72, 0.35) 86%, transparent);
  box-shadow: 0 0 18px rgba(192, 156, 96, calc(0.16 * var(--ilip, 1)));
}

/* the specular. No descendants, so the backdrop-filter containing-block
   trap cannot apply, and it sits under every piece of type. */
.imp__sheen {
  --imx: 50;
  --imy: 40;
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(30% 46% at calc(var(--imx) * 1%) calc(var(--imy) * 1%), rgba(255, 240, 212, 0.07), transparent 70%);
}

.imp > .container { position: relative; z-index: 4; }

/* ---- the inscription ---- */
/* the middle column is given the room because the address is the largest
   thing on the plate and must not break mid-street */
.imp__top {
  display: grid;
  grid-template-columns: 1.12fr 1.14fr 0.86fr;
  /* row gap is deliberately tighter than the column gap: at one and two
     columns the blocks stack, and the column measure would leave a hole */
  gap: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem) clamp(2rem, 1.25rem + 2.6vw, 4rem);
  align-items: start;
  padding-bottom: clamp(2rem, 1.6rem + 1.6vw, 2.9rem);
}

.imp__wordmark { display: inline-flex; align-items: center; gap: 0.9rem; }
.imp__mono { position: relative; display: block; flex: none; }
.imp .wordmark__mono { width: clamp(3.25rem, 2.8rem + 1.6vw, 4.5rem); height: auto; }

/* THE GLEAM, and this is the trick worth keeping: the element is MASKED to
   the monogram and only its background-position moves. Translating the
   element itself would slide the mask off the mark instead of moving light
   across it. Ported from the what-we-do cover, which no longer uses it. */
.imp__gleam {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 38%, rgba(255, 246, 224, 0.85) 50%, transparent 62%);
  background-size: 320% 100%;
  background-position: calc(var(--igl, 130) * 1%) 0;
  /* stylesheet-relative, not document-relative - a 404 here does not fail
     quietly, it drops the mask and paints the gradient as a rectangle */
  -webkit-mask: url("../assets/mark-240.png") center / contain no-repeat;
          mask: url("../assets/mark-240.png") center / contain no-repeat;
  opacity: 0.75;
}

/* the name carries the plate, so it is set larger here than in the nav */
.imp .wordmark__name { font-size: clamp(1.15rem, 1rem + 0.55vw, 1.52rem); }
.imp .wordmark__sub { letter-spacing: 0.28em; }

.imp__line {
  margin-top: clamp(1.1rem, 0.9rem + 0.7vw, 1.6rem);
  max-width: 24rem;
  color: var(--vein);
  font-size: var(--text-sm);
  line-height: 1.75;
}

.imp__label {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
}

/* the address, set larger than anything else on the plate */
.imp__addr {
  margin-top: clamp(0.9rem, 0.75rem + 0.5vw, 1.25rem);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.18rem, 1.02rem + 0.62vw, 1.5rem);
  line-height: 1.3;
  letter-spacing: -0.008em;
  color: var(--white);
  /* the street must never break mid-line; if it has to wrap, the two
     halves should be even rather than one long line and one orphan */
  text-wrap: balance;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7);
}

.imp__reach {
  margin-top: clamp(0.9rem, 0.75rem + 0.5vw, 1.25rem);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: var(--text-sm);
}
.imp__reach a {
  color: var(--vein);
  overflow-wrap: anywhere;
  transition: color 0.35s ease;
}
.imp__reach a:hover { color: var(--gold-light); }

/* ---- the field ----
   A hairline the type sits on, never a boxed input: a bordered rectangle
   with a filled button beside it is the one piece of default UI this site
   had left. */
.imp__news form { margin-top: clamp(0.9rem, 0.75rem + 0.5vw, 1.25rem); }
.imp__field {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  max-width: 22rem;
  padding-bottom: 0.65rem;
}
.imp__field::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, rgba(156, 120, 72, 0.55), rgba(156, 120, 72, 0.16));
  transform: scaleX(1);
  transform-origin: 0 50%;
  transition: background 0.45s ease, box-shadow 0.45s ease;
}
.imp__field:focus-within::after {
  background: linear-gradient(90deg, var(--gold-light), rgba(192, 156, 96, 0.4));
  box-shadow: 0 0 14px rgba(192, 156, 96, 0.35);
}
.imp__field input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0;
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
}
.imp__field input::placeholder { color: rgba(160, 160, 158, 0.9); }
.imp__field input:focus { outline: none; }
.imp__field button {
  flex: none;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--gold-light);
  cursor: pointer;
  transition: transform 0.4s var(--ease-out), color 0.35s ease;
}
.imp__field button svg { width: 1rem; height: 1rem; }
.imp__field button:hover { transform: translateX(3px); color: var(--white); }

.footer__newsnote { margin-top: 0.7rem; font-size: var(--text-xs); color: var(--vein); letter-spacing: 0.02em; }
.footer__newsnote.is-ok { color: var(--gold-light); }

/* ---- the run ----
   Every page name cut along the base as ONE inscription. Three headed
   columns of links is the shape this footer was rebuilt to get away from,
   and it is also the shape that made the old one read as a template.

   The channel is a soft trough in the light rather than a pair of rules:
   no hard edge anywhere, because nothing down here is allowed to draw a
   line across the page. */
.imp__run {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 0.6rem + 1.6vw, 2.5rem);
  padding-block: clamp(1.5rem, 1.2rem + 1.2vw, 2.25rem);
  background: radial-gradient(130% 130% at 50% 50%, rgba(0, 0, 0, 0.45), transparent 76%);
}
.imp__run a {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(190, 190, 190, 0.92);
  /* sunk into the plate, and it comes up when you touch it */
  text-shadow: 0 1px 0 rgba(226, 200, 150, 0.07);
  transition: color 0.35s ease, text-shadow 0.4s ease;
}
.imp__run a:hover {
  color: var(--gold-light);
  text-shadow: 0 0 18px rgba(192, 156, 96, 0.5);
}

/* ---- the base ---- */
.imp__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-block: clamp(1.35rem, 1.1rem + 1vw, 2rem);
  font-size: var(--text-sm);
  color: var(--vein);
}
.imp__top-link {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(190, 190, 190, 0.92);
  text-shadow: 0 1px 0 rgba(226, 200, 150, 0.07);
  transition: color 0.35s ease, text-shadow 0.4s ease;
}
.imp__top-link svg { width: 0.9rem; height: 0.9rem; transition: transform 0.45s var(--ease-out); }
.imp__top-link:hover { color: var(--gold-light); text-shadow: 0 0 18px rgba(192, 156, 96, 0.5); }
.imp__top-link:hover svg { transform: translateY(-3px); }

/* ---- the inscription ----
   The name cut into the base of the plate and running off the bottom of
   the page. This is the giant-wordmark footer the client asked for, made
   Marble's rather than generic in three ways:

   1. IT IS SET IN THE SITE'S OWN DISPLAY FACE, not a grotesque. A didone
      at 290px is a different object from the heavy sans this device
      usually uses - the hairlines are the point.
   2. IT IS FILLED WITH THE SITE'S METALLIC CLIP-TEXT RAMP, the same
      device the engraved numerals use. Largest possible instance of a
      device the site already speaks, rather than a new one.
   3. IT IS LIT FROM BELOW, because the plate's pool of light is at its
      foot. The letters are brightest where they meet it and dissolve
      upward into the surface. Same rule as everywhere else on this site:
      one light source, honestly obeyed.

   Note it is FILLED. `.proofx__ghost` on index is outlined with
   -webkit-text-stroke and drifts behind the evidence panels; two giant
   words on one page had to read as different objects.

   Sized in container query units so the word spans the container measure
   exactly at every width, with a vw clamp behind it that errs small. */
.imp__inscription {
  container-type: inline-size;
  overflow: hidden;
  margin-top: clamp(0.25rem, 0.1rem + 0.8vw, 1rem);
}
.imp__name {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.85rem, 20vw, 18rem);
  line-height: 0.78;
  letter-spacing: -0.045em;
  text-align: center;
  white-space: nowrap;
  /* the base of the letters runs off the page rather than resting on it */
  margin-bottom: -0.135em;
  background-image:
    linear-gradient(104deg, transparent 36%, rgba(255, 248, 232, 0.5) 50%, transparent 64%),
    linear-gradient(180deg,
      rgba(226, 226, 224, calc(0.025 * var(--nlit, 1))) 0%,
      rgba(226, 226, 224, calc(0.075 * var(--nlit, 1))) 30%,
      rgba(216, 202, 178, calc(0.22 * var(--nlit, 1))) 56%,
      rgba(240, 231, 212, calc(0.46 * var(--nlit, 1))) 80%,
      rgba(255, 249, 236, calc(0.62 * var(--nlit, 1))) 100%);
  background-size: 300% 100%, 100% 100%;
  background-position: calc(var(--nx, 130) * 1%) 0, 0 0;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
@supports (font-size: 1cqw) {
  /* tuned by measuring the ink width against the container measure: this
     lands the word at 0.995 of the measure at every width */
  .imp__name { font-size: 24cqw; }
}
/* if the text is not clippable the word must still be a word, not a hole */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .imp__name { background-image: none; color: rgba(232, 232, 230, 0.2); }
}

@media (min-width: 1081px) {
  .imp__run { justify-content: space-between; }
}

@media (max-width: 1080px) {
  .imp__top { grid-template-columns: 1fr 1fr; }
  .imp__id { grid-column: 1 / -1; }
  .imp__line { max-width: 34rem; }
}

@media (max-width: 720px) {
  .imp__top { grid-template-columns: 1fr; }
  .imp__field { max-width: none; }
  .imp__base { justify-content: flex-start; gap: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .imp__field::after,
  .imp__field button,
  .imp__run a,
  .imp__top-link,
  .imp__top-link svg,
  .imp__reach a { transition: none; }
}

/* ==========================================================================
   CONTACT HERO — "THE ANSWER"   (.ans, contact.html)

   It replaced the shared `.hero--page` (eyebrow, headline, one sub line, a 5%
   mark watermark). `.hero--page` is UNTOUCHED and still runs privacy-policy
   and terms.

   THE GRAMMAR IS A BEAT, and it was the one movement a contact page should
   own. Everything else on this site moves because the READER moves: `.wall`
   travels X, `.worlds` pushes Z, `.rise` climbs Y, `.lp` holds the camera
   still, `.forge` turns, `.hire` strikes type through, `.cuts` register,
   `.latent` develops, `.lane` slides hairlines in, `.tbl` places, `.addr`
   turns in place, `.run` cuts open on a diagonal, `.fld` redivides, `.otn`
   overtakes, `.scp` widens, `.xch` converges, `.blk` carves. Not one of them
   is TIMED. This hero arrives, holds for a moment with nothing happening at
   all, and then answers. The silence is the design, and it is the difference
   between a page that performs at you and a page that responds to you.

   THE COPY WAS ALREADY THE COMPOSITION, which is now the most reliable move
   on this project (`.tbl` came out of "your side of the table", `.addr` out
   of "whose door it is", `.fld` out of "four functions, one field"). The sub
   line read "One call. A straight answer." So the page is set as one
   exchange: your side carries the claim and the way in, our side is the desk
   where it is answered, and the reply does not exist until the beat passes.

   THE LIGHT HAS A SOURCE YOU CAN POINT AT. `training.jpg` contains a lit
   brass lamp, so every warm value in this composition is thrown by an object
   inside the photograph - the pool on the ground under the reply, the graze
   on the near plate's top right corner, the bloom on the frame. That is the
   `.tbl` rule (one light source, honestly obeyed) applied to a hero, and it
   is the only reason two photographs and a block of type read as one room
   rather than as three elements.

   IT NEVER STARTS BLACK. The unanswered desk sits at brightness 0.62 with
   contrast PULLED DOWN to 0.88, which lifts the blacks: a dim room, not an
   empty screen. This site has produced the empty-screen failure five times by
   grading a from-state into the dark, and a hero is the worst possible place
   to do it again.

   TWO NUMBERS, NEVER ONE. `--ans` (0 unanswered, 1 answered) is written by
   GSAP every frame; `--brt` is the slow breath the room takes once it is lit.
   Both are UNREGISTERED - they are written per frame so nothing needs
   interpolating, and a registered inherited property costs a style recalc on
   all nine pages that load this stylesheet. `--ans`'s declared value is 1,
   THE FINISHED STATE, so no-JS / no-CDN / reduced motion all render a lit
   desk with the reply in place.

   NOTHING SHARES A PROPERTY WITH A TWEEN, the rule this project has now paid
   for five times. GSAP owns `--ans`, `--brt`, `--amx/--amy` and the two
   images' `transform`. CSS owns the plates' entrance transform (on the PLATE,
   never on the image inside it), the reply's arrival, the number's hover
   colour, and the specular's opacity. The number's hairline is computed from
   `--ans` and therefore carries NO transition at all - the hover brightening
   is a second pseudo-element on plain opacity.

   Photography is an explicit exception to the `.media img` clean pass via its
   own `.ans__*` classes: training (the desk, and the lamp) and phone-win (the
   call). contact.html carried no photograph at all before this.
   ========================================================================== */

.hero--answer {
  min-height: min(86svh, 47rem);
  padding-top: clamp(3.625rem, 3.045rem + 1.392vw, 4.495rem);
  padding-bottom: clamp(2rem, 1.5rem + 1.6vw, 3.25rem);
  justify-content: stretch;
}

.ans {
  /* 0 unanswered, 1 answered. GSAP owns it; 1 IS the finished state. */
  --ans: 1;
  /* the breath the room takes once it is lit. A SECOND number, so the
     ambient loop and the answer can never fight over one value. */
  --brt: 1;
}

.ans__inner {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.ans__body {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: clamp(2rem, 1.1rem + 3.6vw, 4.75rem);
  align-items: center;
}

/* THE LIGHT REACHES THE WHOLE ROOM, and this is the `.gates` finding again:
   a composition lit only where the photography is leaves the other half
   reading as flat black rather than as the same space, and on this brand the
   instinct to keep going darker is what produced the dead gap there.

   It is a lamp belonging to the composition, not a section ground: every
   alpha is under 0.09, both pools are centred on the objects that throw them,
   and the lower one is pulled up so it has faded to nothing well above the
   hero's bottom edge. A section may light its own CONTENT; what it must never
   do is paint a ground, a vignette or a texture that steps at its boundary. */
.ans__floor {
  position: absolute;
  inset: -8% -12%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(52% 46% at 78% 32%, rgba(240, 200, 130, calc(0.008 + var(--ans) * 0.125)), transparent 72%),
    radial-gradient(80% 42% at 44% 60%, rgba(180, 136, 78, calc(0.005 + var(--ans) * 0.072)), transparent 76%);
  transition: opacity 1.2s var(--ease-out) 0.3s;
}

/* ---- your side --------------------------------------------------------- */

.ans__claim { max-width: 34rem; }

.ans h1 {
  font-size: clamp(2.1rem, 1.55rem + 2.3vw, 3.25rem);
  line-height: 1.06;
  letter-spacing: -0.026em;
  margin-bottom: clamp(1rem, 0.85rem + 0.7vw, 1.5rem);
}
.ans h1 em { font-style: italic; color: var(--gold-light); }

.ans .hero__sub { max-width: 29rem; margin-bottom: 0; }
.ans .hero__actions { margin-top: clamp(1.35rem, 1.05rem + 1.2vw, 2.05rem); }

/* ---- our side ---------------------------------------------------------- */

.ans__stage {
  /* the desk runs off the right edge of the page. The bleed is CAPPED, or a
     1728 viewport hands the stage 224px of extra width and this hero grows
     140px taller than every other hero on the site. */
  --abl: min(max(var(--gutter), (100vw - var(--container)) / 2), 5rem);
  position: relative;
  width: calc(100% + var(--abl));
  margin-right: calc(var(--abl) * -1);
}

/* THE LAMP. Its first pool sits where the brass lamp sits inside the
   photograph; its second is the light that lands on the ground the reply
   stands on. Both are bounded to the stage - a section may light its own
   content, never its own ground. */
.ans__lamp {
  position: absolute;
  inset: -12% -8% -14% -6%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(42% 40% at 66% 22%, rgba(240, 202, 132, calc(0.015 + var(--ans) * 0.26)), transparent 72%),
    radial-gradient(62% 42% at 72% 78%, rgba(186, 144, 84, calc(0.008 + var(--ans) * 0.16)), transparent 74%);
  transition: opacity 1.2s var(--ease-out) 0.3s;
}

.ans__plates {
  position: relative;
  aspect-ratio: 1 / 0.85;
}

.ans__plate {
  position: absolute;
  margin: 0;
  /* the entrance rides the PLATE's transform; GSAP owns the transform of the
     image inside it, so no property ever has two owners */
  /* THE PLATES MUST BE FULLY DOWN BEFORE THE BEAT STARTS, or the held
     moment is not held: it is covered by their own entrance and the lamp
     comes on over a composition that is still assembling. Entrance ends
     1.26s after the curtain; initAnswer waits 2.3s. */
  transition: opacity 1s var(--ease-out), transform 1s var(--ease-out);
}
.ans__plate--in { transition-delay: 0.1s; }
.ans__plate--out { transition-delay: 0.26s; }

.ans__shot {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 3px;
  background: var(--obsidian-raised);
  aspect-ratio: var(--ar);
}
.ans__shot::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  border: 1px solid rgba(156, 120, 72, calc(0.14 + var(--ans) * 0.3));
  border-radius: 3px;
}
.ans__shot img {
  position: absolute;
  inset: -6%;
  width: 112%;
  /* main.css sets a global img { max-width: 100% } which silently clamps any
     deliberately oversized image. Assume this every time. */
  max-width: none;
  height: 112%;
  object-fit: cover;
}

/* the desk. This is the frame that lights. */
.ans__plate--in {
  --ar: 3 / 2;
  top: 0;
  right: 0;
  width: 75%;
  z-index: 1;
}
.ans__plate--in .ans__shot {
  box-shadow:
    0 34px 80px -34px rgba(0, 0, 0, 0.78),
    0 0 calc(2rem + var(--ans) * 4.5rem) rgba(186, 142, 80, calc(0.02 + var(--ans) * 0.17));
}
.ans__plate--in .ans__shot img {
  /* THE FLOOR IS DELIBERATELY HIGH and contrast is pulled DOWN in the
     unanswered state, which lifts the blacks: a dim room reads as a room, a
     crushed one reads as an empty screen, and this site has produced that
     failure five times by grading a from-state into the dark. What sells the
     lamp coming on is the pools going from nothing to full and the colour
     returning, NOT how black the photograph started. */
  filter:
    brightness(calc((0.7 + var(--ans) * 0.38) * var(--brt)))
    contrast(calc(0.9 + var(--ans) * 0.18))
    saturate(calc(0.5 + var(--ans) * 0.56));
}

/* the call. It never lights itself - it only takes what spills off the desk. */
.ans__plate--out {
  --ar: 3 / 4;
  left: 0;
  bottom: 6%;
  width: 41%;
  z-index: 2;
}
.ans__plate--out .ans__shot {
  box-shadow: 0 30px 66px -28px rgba(0, 0, 0, 0.82);
}
.ans__plate--out .ans__shot img {
  filter:
    brightness(calc((0.78 + var(--ans) * 0.11) * var(--brt)))
    contrast(1.02)
    saturate(calc(0.74 + var(--ans) * 0.18));
}

/* the graze along the corner that faces the lamp. One light source, obeyed. */
.ans__rim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: 3px;
  background: radial-gradient(62% 46% at 103% 4%, rgba(255, 226, 170, calc(0.015 + var(--ans) * 0.2)), transparent 66%);
}

/* the pointer specular on the near plate's glass. Its own layer on its own
   opacity, so it can never smear the scrub. */
.ans__glass {
  --amx: 50;
  --amy: 50;
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  border-radius: 3px;
  background: radial-gradient(30% 24% at calc(var(--amx) * 1%) calc(var(--amy) * 1%), rgba(255, 240, 214, 0.16), transparent 70%);
  opacity: 0;
  transition: opacity 0.55s var(--ease-out);
}
.ans__plate--out.is-spec .ans__glass { opacity: 1; }

/* ---- the reply ---------------------------------------------------------
   It stands on lit ground, not on a photograph, so it carries NO scrim: on
   plain obsidian a scrim only paints a slightly different black rectangle
   with visible edges (the `.otn` finding). */

/* BOUND TO THE GEOMETRY, NOT TO A WIDTH THAT HAPPENS TO WORK AT 1440.
   `left` is set past the near plate's own width, so the reply can never
   reach into the photograph however the column resolves - the `.chan`
   lesson, which cost a breakpoint's worth of tuning there. */
.ans__reply {
  position: absolute;
  left: 45%;
  /* THE TYPE STOPS AT THE CONTAINER EDGE, NOT AT THE BLEED. The photograph
     is allowed to run off the page; a sentence is not, and it overran by
     65px before this (the same call `.lane` made about its hairlines). */
  right: var(--abl);
  bottom: 0;
  z-index: 3;
}

.ans__line {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.02rem + 1.15vw, 1.95rem);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
  transition: opacity 0.95s var(--ease-out), transform 0.95s var(--ease-out);
}
.ans__line em { font-style: italic; color: var(--gold-light); }

.ans__num {
  position: relative;
  display: inline-block;
  margin-top: clamp(1rem, 0.8rem + 0.8vw, 1.5rem);
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 0.94rem + 0.72vw, 1.55rem);
  letter-spacing: -0.015em;
  color: var(--ink);
  text-decoration: none;
  transition:
    opacity 0.95s var(--ease-out) 0.14s,
    transform 0.95s var(--ease-out) 0.14s,
    color 0.35s var(--ease-out);
}
/* computed from --ans, which GSAP writes every frame, so it carries no
   transition of its own - that is the smear this project has paid for five
   times. The hover brightening is the ::before, on plain opacity. */
.ans__num::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.34rem;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(192, 156, 96, calc(0.22 + var(--ans) * 0.58)),
    rgba(192, 156, 96, calc(0.04 + var(--ans) * 0.14)));
}
.ans__num::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.34rem;
  height: 1px;
  background: linear-gradient(90deg, rgba(238, 208, 152, 0.95), rgba(238, 208, 152, 0.16));
  box-shadow: 0 0 10px rgba(192, 156, 96, 0.45);
  opacity: 0;
  transition: opacity 0.35s var(--ease-out);
}
.ans__num:hover,
.ans__num:focus-visible { color: var(--gold-light); }
.ans__num:hover::before,
.ans__num:focus-visible::before { opacity: 1; }

.ans__now {
  margin-top: 0.7rem;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--vein);
  transition: opacity 0.95s var(--ease-out) 0.26s, transform 0.95s var(--ease-out) 0.26s;
}
.ans__now b { color: var(--gold-light); font-weight: 600; }

/* ---- the beat ----------------------------------------------------------
   `is-waiting` is added by initAnswer only when it is going to animate, so
   the reply's from-state cannot exist on any path that will not clear it. */

.ans.is-waiting .ans__line,
.ans.is-waiting .ans__num,
.ans.is-waiting .ans__now {
  opacity: 0;
  transform: translateY(16px);
}

/* ---- entrance ----------------------------------------------------------
   Keyed off the preloader's `is-loading`, so the from-state only exists
   while the curtain is up and no-JS renders the finished hero. The two
   plates arrive on different vectors, or they read as one object sliding. */

.is-loading .ans__plate--in {
  opacity: 0;
  transform: translate3d(2.4rem, 1.2rem, 0) scale(1.035);
}
.is-loading .ans__plate--out {
  opacity: 0;
  transform: translate3d(-2rem, 2.2rem, 0) scale(1.045);
}
.is-loading .ans__lamp,
.is-loading .ans__floor { opacity: 0; }

/* ---- responsive --------------------------------------------------------
   Below 981 the exchange stacks and the reply leaves the composition for
   normal flow. The two frames keep overlapping all the way down: two objects
   can hold a phone screen where four could not (the `.floor` limit). */

@media (max-width: 1180px) {
  /* the desk takes a little more of the row and the call gives up a little
     width, so the reply keeps a readable measure as the column narrows. Its
     left edge follows the call down. */
  .ans__body { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }
  .ans__plate--out { width: 39%; }
  .ans__reply { left: 43%; }
}

/* THE REPLY WRAPS TO MORE LINES AS ITS COLUMN NARROWS, so the ground it
   stands on has to grow with it or its top edge climbs into the desk. Two
   gentle steps rather than one, or the hero's height visibly snaps at the
   breakpoint (the `--fh` convention `.tbl` and `.run` both use). */
@media (max-width: 1120px) {
  .ans__plates { aspect-ratio: 1 / 0.89; }
}
@media (max-width: 1060px) {
  .ans__plates { aspect-ratio: 1 / 0.93; }
}

@media (max-width: 980px) {
  .hero--answer {
    min-height: 0;
    padding-bottom: clamp(2.5rem, 2rem + 2vw, 4rem);
  }
  .ans__body {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(2rem, 1.5rem + 2.2vw, 3.25rem);
  }
  .ans__claim { max-width: 40rem; }
  .ans .hero__sub { max-width: 36rem; }
  .ans__plates { aspect-ratio: 1 / 0.72; }
  .ans__plate--in { width: 82%; }
  .ans__plate--out { width: 44%; bottom: 0; }
  .ans__reply {
    position: static;
    width: 100%;
    max-width: 34rem;
    margin-top: clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem);
  }
  .ans__lamp { inset: -8% -6% -12% -6%; }
}

@media (max-width: 560px) {
  .ans__plates { aspect-ratio: 1 / 0.86; }
  .ans__plate--in { width: 88%; }
  .ans__plate--out { width: 52%; }
}

@media (prefers-reduced-motion: reduce) {
  .ans__plate,
  .ans__lamp,
  .ans__floor,
  .ans__line,
  .ans__num,
  .ans__now,
  .ans__glass { transition: none; }
}

/* ==========================================================================
   REACH US — "THE SHEET"   (.sht, contact.html section 02)

   It replaced a `.grid` col-5 / col-7: a bulleted contact list on the left,
   a two-column boxed form on the right. That is the shape EVERY contact page
   on the web has, and it was also the last piece of template markup on this
   page. `.contact-list` and every `.form*` selector are DELETED from main.css
   (contact.html was their only user site-wide).

   THE GRAMMAR IS COMPLETION, DRIVEN BY THE READER'S INPUT, and that was free
   because nothing else on this site is driven by input at all. Every other
   section moves because the reader SCROLLS - travel X, push Z, climb Y, hold
   still, turn, register, develop, slide, place, redivide, overtake, widen,
   converge, carve - and the hero directly above moves on a TIMED BEAT. This
   one moves because you type. Each field's serial is engraved into the ground
   and takes its metal as the field is satisfied, so the page is finished by
   the reader rather than performed at them. It is also the only section where
   the reader is the author, which is the honest reason it should work this
   way.

   NOTHING IS HIDDEN AND NOTHING IS DISABLED. A multi-step wizard was the
   obvious "premium form" move and it was rejected for the same reason the
   careers ladder rejected one: on a page whose whole job is to get a message
   sent, an interaction that hides four of five fields is a cost, not a
   feature. All five are on the page at all times, every label is a real
   <label for>, and the submit button is never disabled - it is UNLIT, and the
   existing handler still validates and reports.

   NO BOXES, NO CARD, NO SIDEBAR. The fields are hairlines on the ground,
   which is this brand's actual material (1px gold on obsidian), and the
   section runs the full measure of the page. The `.omit` lesson still holds:
   wrapping a section in a container is almost always wrong here.

   FOUR TIGHT, ONE VAST - the compression `.handoff` uses. The four
   particulars are ruled across the head of the sheet at hand-set unequal
   widths (1.15 / 0.95 / 1.25 / 0.8, and the widest is email because that is
   the one we answer to), and the fifth gets the whole measure, a serial twice
   the size and REAL RULED LINES.

   THE RULED LINES ARE THE TEXTAREA'S OWN BACKGROUND WITH
   `background-attachment: local`, which is the only way they scroll WITH what
   you write instead of sliding out of register the moment the field
   overflows. They are also why the field has no padding-top: `background-
   origin` is the padding box, so any top padding would offset every rule from
   its own line box.

   NO PHOTOGRAPH AND NO LIGHT IN THIS SECTION, DELIBERATELY. The hero directly
   above is two photographs under a lamp, and two adjacent sections sharing a
   medium is exactly the uniformity that reads as generated. This one is
   colder and purely structural, the same temperature drop `.jobx` uses
   against its neighbours.

   `--rdy` (0 to 1, how much of the required message exists) is REGISTERED
   with `inherits: false` and set on `.sht__send` ITSELF, never on an
   ancestor - a registered non-inheriting property read from a descendant can
   only ever resolve to its initial value, which is the latent `.tbl` bug.
   Because it is registered, CSS transitions it and GSAP never touches it, so
   the ring can ease without a tween and a transition sharing a value.
   ========================================================================== */

@property --rdy {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

.sht {
  overflow-x: clip;
}

/* ---- the masthead ------------------------------------------------------ */

.sht__head {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
  gap: clamp(1rem, 0.6rem + 1.6vw, 2.5rem);
  align-items: end;
}

.sht h2 {
  font-size: clamp(1.75rem, 1.35rem + 1.7vw, 2.6rem);
  line-height: 1.12;
  letter-spacing: -0.02em;
}

.sht__lead {
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--vein);
  max-width: 22rem;
  justify-self: end;
  text-align: right;
  text-wrap: pretty;
}

/* static: it is structure, not motion. Nothing on this site draws a rule
   except `.rules`, and that pen belongs to results.html. */
.sht__rule {
  display: block;
  height: 1px;
  margin-top: clamp(1.75rem, 1.35rem + 1.8vw, 3rem);
  background: linear-gradient(90deg,
    rgba(156, 120, 72, 0.5) 0%,
    rgba(156, 120, 72, 0.24) 58%,
    rgba(156, 120, 72, 0.05) 100%);
}

/* ---- the sheet --------------------------------------------------------- */

.sht__form {
  margin-top: clamp(2.5rem, 2rem + 2.4vw, 4rem);
  display: grid;
  /* hand-set unequal, so four fields on one row never resolve into the even
     columns of a default form grid */
  grid-template-columns:
    minmax(0, 1.15fr) minmax(0, 0.95fr) minmax(0, 1.25fr) minmax(0, 0.8fr);
  /* tight enough that the four hairlines read as ONE rule carrying four
     particulars, broken only by a hairline gap. That is what a printed form
     actually looks like, and it is what stops four fields reading as four
     separate widgets. */
  column-gap: clamp(0.85rem, 0.65rem + 0.8vw, 1.35rem);
  row-gap: clamp(1.9rem, 1.5rem + 1.6vw, 2.75rem);
}

.fld--e,
.sht__foot { grid-column: 1 / -1; }

.fld__label {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(0.3rem, 0.25rem + 0.25vw, 0.45rem);
  margin-bottom: clamp(0.45rem, 0.38rem + 0.35vw, 0.7rem);
  cursor: pointer;
}

/* the serial is ENGRAVED and takes its metal when the field is satisfied.
   One transition on background-position moves the ramp through the glyph, so
   the numeral fills rather than merely changing colour. The dark end stops at
   a bronze rather than at black: an unlit numeral still has to be visible. */
.fld__serial {
  font-family: var(--font-display);
  font-style: normal;
  font-size: clamp(1.35rem, 1.05rem + 1vw, 1.95rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.015em;
  /* the unlit end stops at a bronze, never at a near-black. An engraved
     numeral still has to be a numeral you can read - the first cut ended at
     #46381f and the row of serials effectively disappeared. */
  background-image: linear-gradient(180deg,
    #fff4dd 0%, #eccb93 16%, #d0aa6d 34%, #ab8952 56%, #8a6f41 78%, #745c36 100%);
  background-size: 100% 300%;
  background-position: 0 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.75));
  transition: background-position 0.85s var(--ease-out);
}
.fld.is-done .fld__serial { background-position: 0 0; }

.fld__name {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--gold);
  transition: color 0.35s var(--ease-out);
}
.fld.is-focus .fld__name { color: var(--gold-light); }
.fld__name em {
  font-style: normal;
  color: var(--gold-light);
  margin-left: 0.15em;
}

.fld__well {
  position: relative;
  display: block;
}

.fld__in {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0 0 0.65rem;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  line-height: 1.5;
  outline: none;
}
/* THE PLACEHOLDERS ARE HINTS, NOT LABELS, which is the only accessible way
   to use one: every field keeps its own visible <label for>, so the
   placeholder is free to say what a good answer looks like rather than
   repeating the field name. They also do real compositional work - a form of
   empty hairlines on obsidian reads as a void, and this is what makes the
   sheet look written on before anybody writes on it. */
.fld__in::placeholder {
  color: #7c7c82;
  opacity: 1;
}
.fld__in--msg::placeholder {
  font-style: italic;
  color: #83838a;
}

/* the browser's autofill paints its own background over a field that is meant
   to be a hairline on obsidian. This is the only way to keep it. */
.fld__in:-webkit-autofill,
.fld__in:-webkit-autofill:hover,
.fld__in:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 1000px var(--obsidian) inset;
  caret-color: var(--ink);
  transition: background-color 9999s ease-out;
}

/* three states on one hairline: unwritten, being written, written. The lit
   layer is a separate pseudo on its own opacity, so the base rule's colour is
   never something two rules fight over. */
.fld__line {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  /* FLAT, not a ramp: four adjacent fields with a fading gradient each
     would read as four rules restarting, which is the thing this row is
     built to avoid */
  background: rgba(156, 120, 72, 0.32);
  pointer-events: none;
}
.fld__line::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(232, 198, 138, 0.95), rgba(232, 198, 138, 0.28));
  box-shadow: 0 0 14px rgba(192, 156, 96, 0.5);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}
.fld.is-done .fld__line::after { opacity: 0.4; }
/* after is-done on purpose: the field you are in is always the brightest */
.fld.is-focus .fld__line::after { opacity: 1; }

/* ---- the fifth field ---------------------------------------------------
   THE MESSAGE IS SET IN THE DISPLAY FACE, and that is the section's whole
   signature. The four particulars are DATA and stay in the sans; what you
   actually have to say is PROSE, so you write it in the same Bodoni the site
   sets its arguments in. Nothing else asks you to type into a didone, and it
   makes the act of writing feel like the thing it is rather than like filling
   a field in. */

.fld--e .fld__serial { font-size: clamp(2rem, 1.35rem + 2.6vw, 3.6rem); }
.fld--e .fld__name { font-size: 0.78rem; }
/* the boundary of the sheet has to be legible AGAINST its own ruled lines */
.fld--e .fld__line { background: rgba(156, 120, 72, 0.5); }

.fld__in--msg {
  /* NO padding-top: background-origin is the padding box, so any would push
     every rule off its own line */
  padding: 0;
  margin-bottom: 1rem;
  min-height: 14rem;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.45rem);
  line-height: 2.2rem;
  resize: vertical;
  /* the rules scroll WITH the writing */
  background-attachment: local;
  background-image: repeating-linear-gradient(180deg,
    transparent 0,
    transparent calc(2.2rem - 1px),
    rgba(156, 120, 72, 0.2) calc(2.2rem - 1px),
    rgba(156, 120, 72, 0.2) 2.2rem);
}
.fld--e.is-focus .fld__in--msg {
  background-image: repeating-linear-gradient(180deg,
    transparent 0,
    transparent calc(2.2rem - 1px),
    rgba(156, 120, 72, 0.34) calc(2.2rem - 1px),
    rgba(156, 120, 72, 0.34) 2.2rem);
}

/* ---- the foot ---------------------------------------------------------- */

.sht__foot {
  margin-top: clamp(0.1rem, 0.05rem + 0.3vw, 0.5rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.9rem, 0.6rem + 1.2vw, 1.9rem);
}

.sht__alt {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--vein);
}
.sht__alt a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(156, 120, 72, 0.42);
  transition: color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}
.sht__alt a:hover,
.sht__alt a:focus-visible {
  color: var(--gold-light);
  border-color: var(--gold-light);
}

.sht__status {
  flex: 1 1 12rem;
  min-width: 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--success);
}
.sht__status.is-error { color: var(--error); }

/* ---- the send ----------------------------------------------------------
   It is never disabled, only unlit. `--rdy` drives the ring and the interior
   warmth CONTINUOUSLY, but the fill and the dark label switch in ONE step on
   `is-ready`: a colour interpolating from gold-on-dark to dark-on-gold passes
   through about 1.3:1 in the middle, which would leave the label unreadable
   for the whole time somebody is filling the form in. */

.sht__send {
  --rdy: 0;
  position: relative;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.95rem 1.75rem;
  border: 1px solid rgba(156, 120, 72, calc(0.3 + var(--rdy) * 0.6));
  border-radius: 2px;
  background: transparent;
  color: var(--gold-light);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 0 calc(var(--rdy) * 2.6rem) rgba(186, 142, 80, calc(var(--rdy) * 0.2));
  transition:
    --rdy 0.75s var(--ease-out),
    color 0.5s var(--ease-out),
    filter 0.3s var(--ease-out),
    transform 0.3s var(--ease-out);
}
/* the interior warmth, growing with what has been written */
.sht__send::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(180deg, rgba(156, 120, 72, 0.2), rgba(156, 120, 72, 0.05));
  opacity: var(--rdy);
}
/* the earned fill */
.sht__send::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(135deg, #a8834a 0%, #e2c188 40%, #a67f45 74%, #cfa96a 100%);
  opacity: 0;
  transition: opacity 0.6s var(--ease-out);
}
.sht.is-ready .sht__send::after { opacity: 1; }
.sht.is-ready .sht__send { color: #14100a; }

.sht__sendlabel,
.sht__arrow { position: relative; z-index: 2; }
.sht__arrow {
  width: 15px;
  height: 15px;
  transition: transform 0.35s var(--ease-out);
}
.sht__send:hover { filter: brightness(1.08); transform: translateY(-1px); }
.sht__send:hover .sht__arrow { transform: translateX(4px); }
.sht__send:focus-visible {
  outline: 2px solid var(--gold-light);
  outline-offset: 3px;
}

/* the one earned moment: a specular crossing the pill the first time the
   message can actually be sent. GSAP owns --sx and the opacity here; no CSS
   transition touches either. */
.sht__sheen {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: linear-gradient(102deg,
    transparent 40%, rgba(255, 250, 236, 0.55) 50%, transparent 60%);
  background-size: 260% 100%;
  background-position: calc(var(--sx, 130) * 1%) 0;
  opacity: 0;
}

/* ---- responsive --------------------------------------------------------
   The four particulars fold 4 -> 2 -> 1. The message field keeps the whole
   measure at every width, because it is the field that matters and the point
   of the composition is that it dominates. */

@media (max-width: 1180px) {
  .sht__form { grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); }
}

@media (max-width: 860px) {
  .sht__head { grid-template-columns: minmax(0, 1fr); }
  .sht__lead {
    justify-self: start;
    text-align: left;
    max-width: 30rem;
    margin-top: clamp(0.75rem, 0.6rem + 0.6vw, 1.1rem);
  }
}

@media (max-width: 640px) {
  .sht__form { grid-template-columns: minmax(0, 1fr); }
  .sht__foot { flex-direction: column; align-items: stretch; }
  .sht__send { margin-left: 0; justify-content: center; }
  .sht__status { flex: 0 0 auto; }
}

@media (prefers-reduced-motion: reduce) {
  .fld__serial,
  .fld__line::after,
  .fld__name,
  .sht__send,
  .sht__send::after,
  .sht__arrow,
  .sht__alt a { transition: none; }
}

/* ==========================================================================
   THE OFFICE — "WHERE THE MAP ENDS"   (.ofc, contact.html section 03)

   It replaced a `.media--pano` frame: a 21:8 grey letterbox sitting under a
   `.section-head`, which is the shape every contact page on the web has and
   the last piece of template markup left on this page.

   THE MAP IS THE GROUND, NOT A PICTURE IN A BOX, and that alone is what stops
   the section reading as generated. It runs the full width of the page, the
   head sits above it on clean obsidian, and the composition is laid over it.

   THE MECHANIC IS LIGHT GATHERING TO A POINT, and it was still free. Every
   other section on this site moves in some way already claimed - `.wall`
   travels X, `.worlds` pushes Z, `.rise` climbs Y, `.lp` holds the camera
   still, `.forge` turns, `.hire` strikes type through, `.cuts` register,
   `.latent` develops, `.lane` slides hairlines in, `.floor` spreads, `.clk`
   runs on its own clock, `.board` reorders, `.barx` rakes, `.jobx` cuts,
   `.scp` widens, `.trn` backlights, `.hld` waits, `.opn` closes a brace,
   `.ans` holds a beat, `.sht` is finished by the reader. Nothing CONTRACTS.
   Here the warmth arrives spread thin across the whole territory and pulls in
   until only one point is lit, which is the section's own sentence: out of
   all of it, this is the address.

   THE ADDRESS SITS BESIDE THE POINT, SO THE POINT IS ITS MARKER. That is the
   test this client's rejected devices kept failing (the queue of 18 marks,
   the aperture, the machined plate): the meaning has to survive without a
   caption explaining it. A lit point with an address set next to it needs no
   explaining at all. It is also why there is no tether drawn between them -
   a hairline reaching a terminal is `.stop`'s gesture on what-we-do, and
   adjacency already says it.

   THE PHOTOGRAPH IS THE ARGUMENT, NOT DECORATION. A map gives you the street
   and stops. What it cannot show you is who answers, which is the one thing
   this firm sells, so a hand at a door is laid across the band and the line
   under it names the boundary. It also occludes the band's top edge, which is
   the `.lane` trick: a plate that crosses an edge reads as lying ON the
   surface rather than as a panel beside it.

   THE MAP IS INERT UNTIL ASKED FOR. An embedded map swallows the wheel, so
   this one carries `pointer-events: none` and a real toggle. Live, the grade
   opens up toward a usable map and the decorative layers stand down - the art
   never has to fight the utility, because they are never on at once.

   TWO NUMBERS, NEVER ONE, the pattern this project has paid for repeatedly.
   `--gth` (0 spread, 1 gathered) is written by GSAP every frame and NOTHING
   computed from it carries a transition. `--lit` (0 art, 1 usable) is a class
   toggle CSS transitions on its own. Both are UNREGISTERED: they are written
   per frame or per click so nothing needs interpolating, and a registered
   inherited property costs a style recalc on all nine pages that load this
   stylesheet. `--gth`'s declared value is 1, THE FINISHED STATE, so no-JS /
   no-CDN / reduced motion all render a gathered, lit point.

   GOOGLE'S OWN MARKER IS KEPT AND RINGED rather than covered or cropped
   around. The embed is queried BY ADDRESS, so its pin is geocoded rather than
   asserted from coordinates we would be inventing, and on a page whose whole
   promise is that everything is checkable that is the only defensible way to
   put a pin on a map. `grayscale(1)` first in the filter chain kills every
   colour Google ships - the red pin, blue water, green parks - so the plate
   can be re-tinted to this brand's two colours instead of fighting them.
   Nothing opaque is placed over the bottom of the map, where the embed's own
   attribution sits.
   ========================================================================== */

.ofc {
  /* 0 the territory is evenly, dimly lit; 1 the light has gathered onto the
     one point. GSAP owns it, and 1 IS the finished state. */
  --gth: 1;
  /* 0 the composition; 1 the map the reader asked to use. A SECOND number, so
     a scrubbed value and a toggled one can never fight over one property. */
  --lit: 0;
  overflow-x: clip;
}

/* the measure belongs to the HEADING, never to the container it sits in -
   `.container` carries `margin-inline: auto`, so capping the container's own
   width centres the whole head on the page */
.ofc__head h2 { max-width: 46rem; }

/* ---- the band ---------------------------------------------------------- */

.ofc__band {
  position: relative;
  /* clears the head by more than the door plate rises, so the plate can cross
     the band's top edge without ever reaching the headline above it */
  margin-top: clamp(1.98rem, 1.44rem + 2.448vw, 3.78rem);
  /* the pool blends with `screen`; isolate so it lifts the MAP and not the
     page ground behind the section */
  isolation: isolate;
}

.ofc__map {
  position: relative;
  /* sized to the composition, not to a picture ratio: the door and the
     address both finish around the same height, and a band deeper than that
     leaves a strip of unlit map under everything */
  height: clamp(21rem, 11.5rem + 21vw, 31rem);
  overflow: hidden;
  border-block: 1px solid rgba(156, 120, 72, 0.16);
  /* THE GROUND UNDER THE MAP IS THE SITE'S OWN MARBLE. A WebGL map on a
     third-party basemap is the one thing in this section that can fail
     outright, and a band that fails to black is the empty-screen failure this
     project has produced six times. Under the canvas is the same crafted
     panel the clean pass paints everywhere else, so the worst case is a
     marble slab carrying the composition rather than a hole. */
  background-color: var(--obsidian-raised);
  background-image:
    radial-gradient(140% 120% at 50% 46%, rgba(156, 120, 72, 0.14), transparent 56%),
    linear-gradient(155deg, #15151b 0%, var(--obsidian-raised) 58%, #0d0c11 100%);
}

.ofc__gl {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1s var(--ease-out);
}

/* only once the basemap has actually painted */
.ofc.is-mapped .ofc__gl { opacity: 1; }

/* the reader has asked for the map, so it is handed over whole: open, live,
   labelled, and with the composition standing down in front of it */
.ofc.is-live { --lit: 1; }

/* MapLibre's own furniture, brought onto the brand. The attribution is
   REQUIRED and is never covered by anything opaque - it only ever sits under
   the same grade the rest of the map does. */
.ofc .maplibregl-ctrl-attrib {
  background: rgba(6, 6, 8, 0.62);
  color: rgba(190, 186, 178, 0.86);
  font-family: var(--font-sans);
  font-size: 10px;
  border-radius: 3px 0 0 0;
}

.ofc .maplibregl-ctrl-attrib a { color: rgba(214, 182, 128, 0.92); }

.ofc .maplibregl-ctrl-attrib-button {
  background-color: rgba(192, 156, 96, 0.16);
  border-radius: 50%;
}

.ofc .maplibregl-ctrl-bottom-right { z-index: 6; }

/* ---- the mark (<MapMarker> + <MarkerContent> + <MarkerLabel>) ----------
   It rides the map's own coordinate, so it is over the building at every
   zoom - which is the real gain over the ring the embed version had to hold
   at the centre of the frame and hope. */

.ofcm {
  position: relative;
  width: 0;
  height: 0;
}

/* the rings CONTRACT with the light rather than fading up, so the mark and
   the pool around it are visibly the same event. Nothing here carries a
   transition: every value is computed from the variable GSAP writes each
   frame, and a transition on top of that would only fight it. */
.ofcm__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc((2.9 + (1 - var(--gth, 1)) * 4.2) * 1rem);
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  border: 1px solid rgba(192, 156, 96, calc(0.14 + var(--gth, 1) * 0.52));
  box-shadow:
    0 0 calc((0.4 + var(--gth, 1) * 1.5) * 1rem) rgba(192, 156, 96, calc(var(--gth, 1) * 0.22)),
    inset 0 0 calc((0.5 + var(--gth, 1) * 1.2) * 1rem) rgba(192, 156, 96, calc(var(--gth, 1) * 0.14));
}

/* a second ring still on its way in. Two radii arriving read as one light
   closing; a single ring reads as a UI target. */
.ofcm__ring--out {
  width: calc((4.6 + (1 - var(--gth, 1)) * 8.5) * 1rem);
  border-color: rgba(192, 156, 96, calc(0.05 + var(--gth, 1) * 0.2));
  box-shadow: none;
}

.ofcm__dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 7px;
  height: 7px;
  translate: -50% -50%;
  border-radius: 50%;
  background: var(--gold-light);
  box-shadow: 0 0 10px rgba(214, 176, 112, 0.9);
}

/* SET TO THE LEFT OF THE MARK, NOT CENTRED OVER IT. Centred, the name runs
   symmetrically out of the point and its right half lands under the address
   column, which is the one half of this band that is already occupied. Left
   is the open side, and it clears the outer ring at every value of `--gth`
   because the offset is computed from the same number the ring is. */
.ofcm__label {
  position: absolute;
  right: calc(50% + 1.9rem + (1 - var(--gth, 1)) * 2.4rem);
  top: 50%;
  translate: 0 -50%;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(240, 236, 228, 0.92);
  text-shadow: 0 1px 10px rgba(6, 6, 8, 0.98), 0 0 3px rgba(6, 6, 8, 0.9);
  opacity: var(--gth, 1);
  pointer-events: none;
}

/* ---- the zoom controls (<MapControls>) --------------------------------- */

.ofc__ctrl {
  position: absolute;
  right: clamp(0.9rem, 0.6rem + 1.2vw, 1.6rem);
  bottom: clamp(2.4rem, 2rem + 1.2vw, 3.1rem);
  z-index: 7;
  display: none;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border-gold);
  border-radius: 3px;
  background: rgba(8, 8, 12, 0.78);
}

.ofc.is-live .ofc__ctrl { display: flex; }

.ofc__cbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  background: none;
  color: var(--gold-light);
  cursor: pointer;
  transition: color var(--dur-fast) ease, background var(--dur-fast) ease;
}

.ofc__cbtn + .ofc__cbtn { border-top: 1px solid var(--border-gold); }
.ofc__cbtn:hover,
.ofc__cbtn:focus-visible { color: var(--white); background: rgba(156, 120, 72, 0.16); }
.ofc__cbtn svg { width: 14px; height: 14px; }

/* ---- the light ---------------------------------------------------------

   THE STACK IS THE WHOLE TRICK AND IT IS EASY TO GET BACKWARDS: the grade
   (`__edge`) sits UNDER the light (`__pool`), because a vignette painted over
   a pool simply erases it - the gathered and spread states rendered
   identically until this order was fixed. The shade then sits over BOTH, so
   the light never reaches the type it would otherwise wash out. Grade, then
   light, then protect the copy. */

/* Layer one is the pool that TIGHTENS and brightens as `--gth` rises; layer
   two is the diffuse warmth over the whole territory that gives way as it
   does. The two moving against each other is the gather - one alone would
   only be a light coming up. */
.ofc__pool {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: screen;
  background:
    radial-gradient(circle at 50% 50%,
      rgba(238, 204, 146, calc(0.12 + var(--gth) * 0.42)) 0%,
      rgba(176, 134, 76, calc(0.06 + var(--gth) * 0.2)) calc((6 + (1 - var(--gth)) * 16) * 1%),
      transparent calc((21 + (1 - var(--gth)) * 44) * 1%)),
    radial-gradient(circle at 50% 50%,
      rgba(140, 106, 60, calc((1 - var(--gth)) * 0.22)) 0%,
      transparent 84%);
  opacity: calc(1 - var(--lit) * 0.82);
  transition: opacity 0.8s var(--ease-out);
}

/* the grade, kept OFF the bottom edge on purpose - the embed's attribution
   sits there and nothing opaque may cross it */
.ofc__edge {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* the fall-off has to be STEEP. Blurred map tiles are tonally flat, so a
     gentle vignette leaves an even haze across the band and the point stops
     being the brightest thing in it, which is the whole composition.

     The third gradient buries the embed's own controls, which sit hard
     against its left and right edges. It is a GRADE, not a cover: it stands
     down with everything else the moment the map goes live, so the reader
     using the map gets Google's chrome and its attribution intact. */
  background:
    radial-gradient(112% 74% at 50% 46%,
      transparent 16%, rgba(6, 6, 8, 0.52) 50%, rgba(6, 6, 8, 0.86) 100%),
    linear-gradient(180deg, rgba(6, 6, 8, 0.78), transparent 28%, transparent 84%, rgba(6, 6, 8, 0.42)),
    linear-gradient(90deg, rgba(6, 6, 8, 0.78), transparent 13%, transparent 87%, rgba(6, 6, 8, 0.62));
  opacity: calc(1 - var(--lit) * 0.93);
  transition: opacity 0.8s var(--ease-out);
}

/* the side the address stands in. Masked top and bottom so the tint DISSOLVES
   instead of ending on an edge - the `.wall` lesson, where a scrim that stops
   dead leaves a seam across the screen. */
.ofc__shade {
  position: absolute;
  inset: 0 0 0 auto;
  width: min(60%, 56rem);
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(270deg,
    rgba(6, 6, 8, 0.92) 0%,
    rgba(6, 6, 8, 0.85) 40%,
    rgba(6, 6, 8, 0.46) 74%,
    transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
}

/* ---- the toggle -------------------------------------------------------- */

.ofc__use {
  position: absolute;
  top: clamp(0.9rem, 0.6rem + 1.2vw, 1.6rem);
  right: clamp(0.9rem, 0.6rem + 1.2vw, 1.6rem);
  z-index: 7;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 0.95rem;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-light);
  background: rgba(8, 8, 12, 0.66);
  border: 1px solid var(--border-gold);
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease,
              background var(--dur-fast) ease;
}

.ofc__use:hover,
.ofc__use:focus-visible {
  color: var(--white);
  border-color: var(--border-gold-strong);
  background: rgba(12, 12, 16, 0.86);
}

.ofc__usedot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold-shadow);
  box-shadow: 0 0 0 0 rgba(192, 156, 96, 0);
  transition: background var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
}

.ofc.is-live .ofc__usedot {
  background: var(--gold-light);
  box-shadow: 0 0 0 3px rgba(192, 156, 96, 0.18);
}

/* ---- the composition laid over the band -------------------------------- */

.ofc__over {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: grid;
  /* two halves, so the divide falls on the container's centre line - which is
     the viewport's centre line, which is where the embed puts the pin. The
     address therefore starts a fixed distance right of the point at EVERY
     width, with no per-breakpoint tuning. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(1.5rem, 0.8rem + 2.6vw, 3.75rem);
  align-items: center;
  /* the map underneath stays reachable once it is live */
  pointer-events: none;
  transition: opacity 0.65s var(--ease-out);
}

.ofc__over > * { pointer-events: auto; }

/* live, the composition stands down to the address alone: the reader asked
   for a map, and everything else is in front of it */
.ofc.is-live .ofc__door { opacity: 0; pointer-events: none; }
.ofc__door { transition: opacity 0.55s var(--ease-out); }

/* ---- the door ---------------------------------------------------------- */

.ofc__door {
  grid-column: 1;
  justify-self: start;
  align-self: start;
  /* rises across the band's top edge. The band's own margin-top is larger, so
     it can never reach the headline. */
  margin: clamp(-3.25rem, -2rem - 1.6vw, -2rem) 0 0 clamp(0rem, -1.5rem + 3vw, 2.5rem);
  width: min(100%, 19.5rem);
}

.ofc__plate {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--obsidian);
  border: 1px solid rgba(156, 120, 72, 0.22);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.03) inset,
    0 36px 74px -34px rgba(0, 0, 0, 0.96);
  /* the independent property, so GSAP owns `transform` on the shot inside
     uncontested - the lesson this project has paid for five times */
  rotate: -1.15deg;
}

.ofc__shot {
  position: absolute;
  inset: -5% 0;
  display: block;
}

.ofc__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* the knocker and the hand, not the dark left third of the frame */
  object-position: 62% 46%;
  filter: brightness(1.02) contrast(1.04) saturate(0.96);
}

.ofc__cap {
  margin-top: 1rem;
  margin-left: 0.15rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: var(--text-xl);
  line-height: 1.25;
  color: var(--gold-light);
  text-shadow: 0 2px 14px rgba(6, 6, 8, 0.95);
}

/* ---- the address ------------------------------------------------------- */

.ofc__addr {
  grid-column: 2;
  justify-self: start;
  align-self: center;
  max-width: 25rem;
  padding-left: clamp(0.5rem, 0.1rem + 1.6vw, 2.25rem);
}

.ofc__num {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.9rem, 1.9rem + 4.2vw, 5.6rem);
  line-height: 0.84;
  letter-spacing: -0.035em;
  /* near-vertical ramp on purpose: run it across a wide figure at a shallow
     angle and the outer glyphs fall into its darkest stop */
  background: linear-gradient(174deg,
    rgba(248, 244, 236, 0.96),
    rgba(202, 165, 107, 0.9) 46%,
    rgba(128, 96, 56, 0.62));
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  /* one shadow on the whole figure guarantees it separates from any tone in
     the map it crosses, without retuning a single colour */
  filter: drop-shadow(0 3px 12px rgba(0, 0, 0, 0.92));
}

.ofc__street,
.ofc__city {
  font-size: var(--text-lg);
  line-height: 1.45;
  color: var(--ink);
  text-shadow: 0 2px 12px rgba(6, 6, 8, 0.9);
}

.ofc__street { margin-top: clamp(1rem, 0.8rem + 0.8vw, 1.5rem); }

.ofc__city { color: var(--vein); }

.ofc__rule {
  display: block;
  height: 1px;
  width: 3.5rem;
  margin: clamp(1.1rem, 0.9rem + 0.8vw, 1.6rem) 0;
  background: linear-gradient(90deg, rgba(156, 120, 72, 0.62), rgba(156, 120, 72, 0.08));
}

.ofc__note {
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--vein);
  max-width: 21rem;
  text-shadow: 0 2px 12px rgba(6, 6, 8, 0.9);
}

.ofc__dir {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: clamp(1.1rem, 0.9rem + 0.8vw, 1.6rem);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-light);
}

.ofc__dir svg {
  width: 15px;
  height: 15px;
  transition: transform var(--dur-fast) var(--ease-out);
}

.ofc__dir:hover { color: var(--white); }
.ofc__dir:hover svg { transform: translateX(4px); }

/* ---- narrow ------------------------------------------------------------
   The composition is DROPPED rather than retuned below 940px. Type laid over
   a map needs horizontal room to stay off the roads, and a 19rem photograph
   on a 390px band is a sticker rather than a plate. The band keeps the map
   and its gathered point; the door and the address fall into normal flow
   underneath, in source order, so the shown and spoken sequences match. */

@media (max-width: 940px) {
  .ofc__map { height: clamp(16rem, 10rem + 24vw, 22rem); }

  .ofc__over {
    position: static;
    display: block;
    margin-top: clamp(1.75rem, 1.4rem + 1.6vw, 2.75rem);
  }

  .ofc__shade { display: none; }

  .ofc__door {
    margin: 0;
    width: min(100%, 17rem);
  }

  .ofc__cap { font-size: var(--text-lg); }

  .ofc__addr {
    max-width: none;
    padding-left: 0;
    margin-top: clamp(2rem, 1.6rem + 1.8vw, 3rem);
  }

  .ofc__note { max-width: 26rem; }

  /* nothing is laid over anything at this width, so no copy needs lifting
     off a photograph */
  .ofc__street,
  .ofc__city,
  .ofc__note,
  .ofc__cap { text-shadow: none; }
}

@media (max-width: 560px) {
  .ofc__door { width: 100%; }
  .ofc__plate { aspect-ratio: 3 / 2; rotate: -0.7deg; }
}

/* the mark's name is set beside it, so on a narrow band it runs off the left
   edge before it can be read. The address is directly underneath at this
   width, so the label is dropped rather than shrunk to nothing. */
@media (max-width: 640px) {
  .ofcm__label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .ofc__gl,
  .ofc__pool,
  .ofc__edge,
  .ofc__over,
  .ofc__door,
  .ofc__dir svg { transition: none; }
}

/* ==========================================================================
   Home hero — the cinematic skyline.

   A canvas canyon on a tall sticky track. The camera flies down it as the
   page scrolls and four type beats hand off above it: the lead statement,
   then three numbers. Everything below is layout and type; the canyon itself
   is drawn in js/skyhero.js.
   ========================================================================== */

.skyhero { position: relative; }

.skyhero__track {
  position: relative;
  /* Was 600vh: six screens of scroll for three stat beats, and on its own
     38% of the whole homepage. The track length is only the DISTANCE the
     choreography is spread over - every beat, the typing and the camera are
     driven by progress through it, so shortening it costs no content and no
     motion, it just stops asking for six screens of thumb to read three
     numbers. */
  height: 340vh;
  height: 340svh;
}

.skyhero__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--obsidian);
}

.skyhero__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The four beats share one stage; only ever one of them is legible. */
.skyhero__beat {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: clamp(6.5rem, 13vh, 9rem) var(--gutter) clamp(6rem, 13vh, 8.5rem);
  overflow: hidden;
  text-align: center;
  pointer-events: none;
  will-change: transform, opacity;
}

.skyhero__beat a,
.skyhero__beat button { pointer-events: auto; }

.skyhero__beat--stat { opacity: 0; }

/* ---- the lead statement ---- */

.skyhero__beat--lead .container { width: 100%; }

.skyhero__beat--lead .hero__content {
  /* A hook has to be readable in one glance, which means a narrow measure and
     a short line, not the widest type the viewport allows. At 68rem the two
     statements ran ~1200px each and read as a paragraph set large. */
  max-width: 58rem;
  margin-inline: auto;
}

/* THE DATUM ROW. The canyon's own instrument is the HUD bottom-left: a
   hairline, then a tracked value that swaps live. All four beats are set on
   that same instrument so they read as four frames of one read-out rather
   than a hero followed by three unrelated slides.

   Here the three facts are three READINGS separated by hairline ticks. Set as
   one run-on of middots it measured 730px on one line, and broke to three
   ragged lines of centred micro-caps on a phone. */
.skyhero__beat--lead .eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem clamp(0.7rem, 1.8vw, 1.15rem);
  letter-spacing: 0.26em;
  color: var(--gold-light);
  margin-bottom: clamp(1.35rem, 2.8vh, 2rem);
  text-shadow: 0 1px 18px rgba(6, 6, 8, 0.9);
}
/* Tracking adds its space AFTER the last glyph, which walks centred caps off
   true centre by exactly one track. Give it back. */
.skyhero__beat--lead .eyebrow > span { text-indent: 0.26em; }
.skyhero__beat--lead .eyebrow i {
  width: 1px;
  height: 0.72em;
  background: currentColor;
  opacity: 0.4;
}

.skyhero__beat--lead h1 {
  /* Sized to keep each statement on ONE line inside the 58rem measure. The
     old ceiling (4.75rem) wrapped both of them, which turned a two-line hook
     into a four-line paragraph and buried the offer below the fold. */
  font-size: clamp(1.9rem, 4.4vw, 3.6rem);
  line-height: 1.12;
  letter-spacing: -0.028em;
  font-weight: 600;
  margin-bottom: clamp(1.15rem, 2.4vh, 1.7rem);
  text-shadow: 0 2px 44px rgba(6, 6, 8, 0.85);
}

/* The lead is a STATEMENT — two lines of equal rank, the second carrying the
   whole offer. It deliberately does NOT take the figure's value-and-unit
   relationship below: shrinking "You pay for what we close." would demote
   the one clause the page is selling. */

.skyhero__beat--lead .hero__sub {
  max-width: 36rem;
  margin-inline: auto;
  margin-bottom: clamp(1.9rem, 4vh, 2.85rem);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.1875rem);
  line-height: 1.6;
  color: rgba(232, 232, 230, 0.72);
  text-shadow: 0 1px 26px rgba(6, 6, 8, 0.85);
  text-wrap: pretty;
}
.skyhero__beat--lead .hero__sub strong {
  color: rgba(232, 232, 230, 0.95);
  font-weight: 500;
}

.skyhero__beat--lead .hero__actions { justify-content: center; }

/* ---- the three numbers ---- */

/* the same datum the lead is set on, closed at both ends because a reading
   has a start and a finish */
.skyhero__eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.7rem, 1.6vw, 1.1rem);
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--gold-light);
  text-shadow: 0 1px 18px rgba(6, 6, 8, 0.9);
}
.skyhero__eyebrow > span { text-indent: 0.3em; }
.skyhero__eyebrow i {
  display: block;
  width: clamp(1.25rem, 3.4vw, 2.25rem);
  height: 1px;
  background: currentColor;
  opacity: 0.5;
}

.skyhero__figure {
  position: relative;
  margin: clamp(1.4rem, 3.2vh, 2.3rem) 0 0;
  font-size: clamp(2.1rem, 5.4vw, 4.75rem);
  line-height: 1.12;
  letter-spacing: -0.022em;
  font-weight: 500;
  text-shadow: 0 2px 44px rgba(6, 6, 8, 0.85);
  /* the figures are the substance of these three beats: lining, tabular, so
     38,000 and $3.5M set on the same digit width and neither jitters as the
     typewriter lays it down character by character */
  font-variant-numeric: lining-nums tabular-nums;
}

/* VALUE, THEN UNIT. Both lines used to set at 76px/500 - identical - so the
   reading and the word that qualifies it carried the same rank and the whole
   figure read as one flat block. The gold line is now stamped under the
   reading: smaller, tracked open, still gold. It reads as the conclusion of a
   measurement rather than a second headline.

   Sized in em so it scales with the clamp, and so the caret - which is parked
   off measured character rects - lands on the right glyph at any width. */
.skyhero__figure > .u-gold {
  font-size: 0.82em;
  letter-spacing: 0.012em;
  margin-top: 0.1em;
}

/* THE EXCEPTION, AND IT IS A REAL ONE. Beats one and two are readings —
   "38,000 customers / acquired." is a value and the word that qualifies it,
   so stamping the second line smaller is right. Beat three is not a reading;
   it is a rhetorical pair, and "We reach people." is the payoff the whole
   hero is driving at. Demoting it to 0.82em would sell the punchline short,
   so that beat keeps both lines at parity and separates them on colour. */
.skyhero__beat--pair .skyhero__figure > .u-gold {
  font-size: 1em;
  letter-spacing: inherit;
  margin-top: 0;
}

/* Direct children only — the script wraps every character of a line in its
   own span for the typewriter, and those have to stay inline. */
.skyhero__figure > span { display: block; }
.skyhero__figure > span > span { display: inline; }

/* the typewriter caret, parked by measured character rects. The blink is
   switched on and off through animation-name from the script. */
.skyhero__caret {
  position: absolute;
  width: 2px;
  background: var(--gold-light);
  opacity: 0;
  pointer-events: none;
}

@keyframes skyhero-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

.skyhero__line {
  margin: clamp(1.5rem, 3.4vh, 2.3rem) auto 0;
  max-width: 40rem;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.1875rem);
  line-height: 1.6;
  color: rgba(232, 232, 230, 0.72);
  text-shadow: 0 1px 26px rgba(6, 6, 8, 0.85);
  /* two or three centred lines under a big figure: balance beats ragged, and
     it is what stops "Signed, activated accounts." dropping a lone word */
  text-wrap: balance;
}

/* WHEN THE DATUM ROW STOPS FITTING ON ONE LINE, IT STACKS.

   The three readings plus their ticks measure 736px, and the lead sits inside
   two nested gutters, so the row only holds one line down to about 890px of
   viewport. Below that a wrapping flex row breaks in the worst possible
   place: at 820px it put "B2B and direct to consumer" on a second line and
   left the second TICK dangling off the end of the first - a separator
   separating nothing. Letting the row reflow as inline text fails the same
   way, just less predictably.

   The cut is at 960 rather than the true 890 on purpose. At 910 the row
   cleared by only 18px, and this measures a live font: a different fallback,
   a rounded-up rendering or a slightly wider Inter re-strands the tick. 960
   buys about 70px of slack, so the stack takes over well before the row is
   in danger.

   Below it the readings STACK: a deliberate centred column, ticks off, tight
   leading. Three short lines, no orphans, at any width. */
@media (max-width: 960px) {
  .skyhero__beat--lead .eyebrow {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0.3rem;
    line-height: 1.35;
    letter-spacing: 0.18em;
  }
  .skyhero__beat--lead .eyebrow > span { text-indent: 0.18em; }
  .skyhero__beat--lead .eyebrow i { display: none; }
}

/* SHORT AND NARROW. The lead carries four blocks plus the cue, and on a phone
   its content measured 622px inside a 601px box - over budget against a beat
   that clips. Tightening the tracking and the block gaps here recovers the
   overflow AND drops the datum row from three ragged lines to two. */
@media (max-width: 700px) {
  .skyhero__beat {
    padding: clamp(5.25rem, 11vh, 7rem) var(--gutter) clamp(4.75rem, 10vh, 6.5rem);
  }
  .skyhero__beat--lead .eyebrow { margin-bottom: 1.05rem; }
  .skyhero__beat--lead h1 { margin-bottom: 1.05rem; }
  .skyhero__beat--lead .hero__sub {
    margin-bottom: 1.5rem;
    line-height: 1.55;
  }
  .skyhero__eyebrow { letter-spacing: 0.2em; }
  .skyhero__eyebrow > span { text-indent: 0.2em; }
}

/* THE SHORT PHONE. A 667px-tall viewport is the common floor (iPhone SE/8 and
   a lot of Android), and there the lead carries four blocks into a box that
   CLIPS: content measured 613px against 507px of room, overflowed its padding
   at both ends, and ran the buttons straight through the scroll cue.

   The beat centres its content and pins the cue absolutely, so padding barely
   moves the collision - only the content's own height does. Everything below
   is the type set one notch down so the whole statement fits above the cue. */
@media (max-width: 700px) and (max-height: 740px) {
  /* The bottom padding is doing a second job here: the content is CENTRED in
     the padding box, so trimming its height only buys back half a pixel of
     cue clearance per pixel cut. Weighting the padding to the bottom lifts the
     whole stack off the cue directly. */
  .skyhero__beat {
    padding-block: clamp(3.5rem, 8vh, 4.75rem) clamp(4rem, 9vh, 5rem);
  }
  .skyhero__beat--lead .eyebrow {
    gap: 0.2rem;
    line-height: 1.25;
    margin-bottom: 0.6rem;
  }
  .skyhero__beat--lead h1 {
    font-size: clamp(1.5rem, 6.6vw, 2.05rem);
    line-height: 1.1;
    margin-bottom: 0.6rem;
  }
  .skyhero__beat--lead .hero__sub {
    font-size: 0.875rem;
    line-height: 1.5;
    margin-bottom: 0.85rem;
  }
  .skyhero__beat--lead .hero__actions { gap: 0.6rem; }
  .skyhero__beat--lead .hero__actions .btn { padding-block: 0.7rem; }
  .skyhero__figure { line-height: 1.08; }
  .skyhero__line { margin-top: 1.1rem; font-size: 0.9375rem; }
}

/* ---- the market read-out, bottom left ---- */

.skyhero__hud {
  position: absolute;
  left: var(--gutter);
  bottom: clamp(1.5rem, 3.4vh, 2.75rem);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(192, 156, 96, 0);
  transition: color 420ms ease;
  pointer-events: none;
}

.skyhero__hud.is-on { color: rgba(192, 156, 96, 0.95); }
.skyhero__hud i { display: block; width: 22px; height: 1px; background: currentColor; }

/* ---- scroll cue ---- */

.skyhero__cue-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  justify-content: center;
}

.skyhero__cue {
  position: static;
  flex-direction: column;
  align-items: center;
  gap: 0;
}

.skyhero__cue .scroll-cue__rail {
  height: clamp(3rem, 7.5vh, 5.4rem);
  background: linear-gradient(180deg, rgba(192, 156, 96, 0), rgba(192, 156, 96, 0.85));
}

.skyhero__cue-pill {
  display: block;
  width: 26px;
  height: 8px;
  border-radius: 99px;
  background: var(--gold-light);
  margin: 0.75rem 0 1.4rem;
  animation: skyhero-pill 2600ms ease-in-out infinite;
}

@keyframes skyhero-pill {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(9px); opacity: 1; }
}

/* Short viewports: the canyon needs the height more than the type does. */
@media (max-height: 640px) {
  .skyhero__cue-wrap { display: none; }
  .skyhero__beat { padding-top: clamp(3.19rem, 6.38vh, 4.06rem); padding-bottom: 3rem; }
  .skyhero__beat--lead h1,
  .skyhero__figure { font-size: clamp(1.85rem, 6.2vh, 3.4rem); }
}

@media (max-width: 900px) {
  .skyhero__hud { display: none; }
}

/* One long scroll is a lot of thumb on a phone — same choreography, shorter.
   The lead also has to give back enough height for the cue to sit clear of
   the buttons, so the eyebrow and the paragraph tighten up. */
@media (max-width: 760px) {
  .skyhero__track { height: 300vh; height: 300svh; }

  .skyhero__beat--lead .eyebrow {
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    margin-bottom: 1rem;
  }

  .skyhero__beat--lead h1 { margin-bottom: 1.15rem; }

  .skyhero__beat--lead .hero__sub {
    line-height: 1.5;
    margin-bottom: 1.5rem;
  }

  .skyhero__beat--lead .hero__actions {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    max-width: 22rem;
    margin-inline: auto;
  }

  /* the buttons run full width here, so the label no longer needs the wide
     side padding — and it must be allowed to wrap rather than push out */
  .skyhero__beat--lead .btn {
    padding-inline: 1.25rem;
    font-size: 0.875rem;
    white-space: normal;
  }

  .skyhero__line { margin-top: 1.5rem; line-height: 1.5; }

  /* a shorter cue leaves the buttons room on a phone */
  .skyhero__cue .scroll-cue__rail { height: 2.5rem; }
  .skyhero__cue-pill { margin: 0.55rem 0 0.9rem; }
}

/* Reduced motion: no flight, no typing. The canyon is drawn once as a still
   and the beats become plain stacked blocks so nothing is withheld. */
.skyhero--still .skyhero__track { height: auto; }

.skyhero--still .skyhero__stage {
  position: relative;
  height: auto;
  min-height: 100svh;
}

/* the still only backs the lead — it must not stretch behind the whole stack */
.skyhero--still .skyhero__canvas { height: 100vh; height: 100svh; }

.skyhero--still .skyhero__beat {
  position: relative;
  inset: auto;
  opacity: 1 !important;
  transform: none !important;
  visibility: visible !important;
}

.skyhero--still .skyhero__beat--lead { min-height: 100svh; }

.skyhero--still .skyhero__beat--stat {
  padding-block: var(--section-sm);
  background: linear-gradient(180deg, rgba(6, 6, 8, 0.4), rgba(6, 6, 8, 0.72));
}

.skyhero--still .skyhero__caret,
.skyhero--still .skyhero__cue-wrap { display: none; }

.skyhero--still .skyhero__eyebrow,
.skyhero--still .skyhero__figure,
.skyhero--still .skyhero__line {
  opacity: 1 !important;
  transform: none !important;
}
