/* ============================================================
   AI-DAPT — Global Stylesheet
   Design tokens per claude/brand-kit.md §6-7
   ============================================================ */

:root {
  --burgundy: #961B49;
  --ink-charcoal: #20262A;
  --deep-slate: #1E2E33;
  --blueprint-teal: #33535A;
  --structural-blue-grey: #5C7A80;
  --sage-mist: #8FA69B;
  --pale-steel: #C4D2CE;
  --drawing-paper: #F2EFE9;
  --warm-white: #FAF9F6;
  --graphite-grey: #6B6A67;
  --sketch-sand: #C79A5B;
  --aged-gold: #B4841F;

  --font-display: 'Space Grotesk', sans-serif;
  --font-body: 'Titillium Web', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;

  --max-width: 1180px;
  --gutter: 32px;
}

* { box-sizing: border-box; }

/* Reusable utility: keeps a short phrase from breaking across a line —
   e.g. a hyphenated term like "AI-optimised", where the browser's default
   break-opportunity at the hyphen can otherwise strand "AI-" on one line
   and "optimised" on the next. Johnny flagged this 2026-09-22 on the
   enquiry-section body copy. */
.nowrap { white-space: nowrap; }

html {
  scroll-behavior: smooth;
  /* Subtle "dock" per section: proximity snapping settles the viewport onto
     a section when scrolling comes to rest near its edge, but a deliberate
     scroll can still carry straight past it — never a hard, mandatory lock. */
  scroll-snap-type: y proximity;
}

body {
  margin: 0;
  background: var(--drawing-paper);
  color: var(--ink-charcoal);
  font-family: var(--font-body);
  line-height: 1.55;
}

img, video { max-width: 100%; display: block; }

a { color: inherit; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
}

/* ---------- Section shell ---------- */

.section {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 120px var(--gutter);
  scroll-snap-align: start;
  scroll-snap-stop: normal; /* a fast/large scroll can skip past this section entirely */
}

/* background-image (not just -color) here too: 3 of the 9 sections
   (#reactive-change, #method, #enquire) are content-only layouts with no
   .bg wrapper at all, so their flat paper margins never picked up the
   grain painted by .section .bg .texture. That .bg .texture layer is
   fully opaque and sits inset:0 over every section that has it, so
   painting the same tile here too is safe — it's completely hidden behind
   .bg .texture wherever that exists, and only becomes visible in the 3
   gap sections. Keeps "one and only background, no exceptions" true. */
.section.light { background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat; color: var(--ink-charcoal); }
.section.dark  { background: var(--deep-slate);   color: var(--warm-white); }

/* 2026-09-21: diagnosed the "erratic" scroll-snap feel Johnny flagged.
   scroll-snap-align: start assumes every section is (at most) one
   viewport tall — that's true for 6 of the 9 sections, which is why they
   feel fine, but three run noticeably taller than 100vh at common laptop
   heights (measured via Playwright): #reactive-change (+205-350px),
   #method (+200-300px), #enquire (+100-400px, the new phone illustration
   included). On those, scroll-snap-align: start still fires as soon as
   the section's top edge crosses into range — the browser then tries to
   settle/re-settle while the user is still mid-scroll through the
   section's own overflow (there's no matching point to snap TO until the
   *next* section), which is exactly what reads as jittery/erratic. Fix:
   these three opt out of snapping and just scroll through normally; the
   six correctly-sized sections keep the crisp per-section dock. */
#reactive-change, #method, #enquire { scroll-snap-align: none; }

.section .bg {
  position: absolute;
  inset: 0;
  isolation: isolate; /* contain mix-blend-mode to this layer only */
  z-index: 0;
}

.section .bg .texture {
  position: absolute;
  inset: 0;
  background-color: var(--drawing-paper);
  /* Real paper-grain asset (Johnny's own high-res scan, 2026-09-21),
     replacing the procedural SVG-noise placeholder. Source was a 5000x5000
     photo of a single sheet; a clean 2200x2200 swatch was cropped from a
     flat, evenly-lit region, made genuinely seamless (torus-roll + a local
     mirror-fill exactly at the seam band, not a blur — keeps full grain
     sharpness instead of a soft/flattened seam), then downsampled to 900px
     square. That downsample step matters: at the source's native scan
     resolution the grain read as coarse mottled blobs once painted at 1:1
     CSS pixels — the fix was shrinking the tile, not the opacity, so the
     grain reads at the scale actual paper does at normal viewing distance.
     Exported as a dark speckle-only alpha overlay (RGB ~ ink-charcoal,
     alpha = darker-than-local-baseline deviation only, capped low) rather
     than an opaque photo, so it layers over --drawing-paper exactly like
     the old SVG did and never shifts the site's base color. */
  background-image: url("../assets/img/paper-texture.png");
  background-repeat: repeat;
}
/* Removed: was swapping the compositing base to a flat deep-slate fill on
   "dark" sections. Since .bg sits inset:0 and fully opaque, that fill was
   never a subtle backdrop — it was the surface every video multiply-blends
   against, so any white/light area in the source clip (which per
   section-backgrounds-spec.md is EVERY clip's background) multiplied down
   to flat dark grey and read as a layer sitting over the illustration, not
   under it. Per Johnny's direction, the paper texture is the one and only
   page background, section 1 or section 11, no exceptions — so this
   override is gone and .texture stays drawing-paper everywhere. */

.section .bg video,
.section .bg img.bg-media,
.section .bg img.poster-fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  mix-blend-mode: multiply;
  /* multiply is correct on every section, light or dark: per
     section-backgrounds-spec.md every source clip/still is rendered on a
     flat, plain, pure-white background, and multiply is what drops white
     out against any base color — screen mode would be for a black-background
     source, which these are not. Do not vary this by .light/.dark. */
}

.section .bg video {
  /* 2026-09-21: Johnny reported the texture "turning on and off" mid-scroll
     — a flash of the raw, un-blended (flat white) video frame between dock
     points. main.js only sets video.src / calls .play() on first
     IntersectionObserver hit (threshold 0.35), which fires WHILE still
     mid-scroll, not just once settled. Starting playback on an element with
     mix-blend-mode is a known trigger for browsers (Chromium and WebKit
     both) to (re)promote it onto a fresh GPU compositing layer, and the
     very first composited frame on that new layer can paint before the
     blend shader is attached — a one-frame flash to plain opaque white,
     exactly matching what was reported. Forcing the layer to exist
     up-front, before playback ever starts, means .play() updates an
     already-blended layer instead of creating one. Purely a compositing
     hint — inset:0/100% already fix position and size, so this has no
     visible effect on layout or appearance by itself. */
  transform: translateZ(0);
  backface-visibility: hidden;
}

.section img.poster-fallback { display: none; }
/* (was scoped to ".bg img.poster-fallback" — missed the triptych cards,
   whose poster-fallback lives in .bg-wrap, so it was never hidden and sat
   painted on top of the video the whole time on desktop. That stray static
   poster — not a video tint — was very likely the real source of the
   "colored block" look Johnny flagged on section 3.) */

/* 2026-09-22: Johnny flagged a "BIG offset... misaligned frame" on the
   chair section right before its video starts playing on desktop. Root
   cause: a <video>'s native `poster` attribute is NOT reliably styled by
   `object-position` in every browser — object-fit:cover is respected, but
   a custom object-position (like #who-we-work-with's "left center" anchor
   below) can get silently ignored for the poster specifically, defaulting
   to a centered crop. The instant real playback begins, the browser
   switches to rendering actual frames, which DO respect object-position
   correctly — so the crop visibly snaps/jumps sideways at that moment.
   Same latent bug exists on every .bg video with a custom object-position
   (currently #different too), and — worse — permanently for
   prefers-reduced-motion desktop users, since their video never plays at
   all (initVideoEngine returns before ever setting .src), so they'd be
   stuck looking at the wrongly-cropped poster forever, not just briefly.
   Fix: un-hide .poster-fallback specifically inside .bg (NOT the triptych's
   unrelated .bg-wrap/.card-still elements, so the section-3 bug above stays
   fixed) and give it the exact same object-fit/object-position treatment as
   the video itself (see the shared rule above and the per-section
   overrides below) — a correctly-cropped image sits on top of the video
   until main.js's "playing" listener hides it once real frames are
   actually rendering. Reduced-motion users simply keep seeing this
   correctly-cropped image forever, since that listener never fires. */
.section .bg img.poster-fallback { display: block; }

/* Half-width background variant (section 8): the artwork occupies one
   half of the frame instead of bleeding full-width, freeing the other half
   for a text column that never overlaps it. */
.section .bg.half-right { left: 50%; }
.section .bg.half-left  { right: 50%; }

/* Section 5 (What Makes Us Different) switched from half-right to a
   full-bleed single composition on 2026-09-21: the revolving-door artwork
   is one continuous illustration whose left portion is deliberately
   low-detail (soft pencil/construction lines only, simple facade panels)
   so the copy column can sit over it, with the door itself as the inked
   feature on the right. object-fit: cover crops from the frame center by
   default, which on a narrower/taller viewport could push the door past
   the visible right edge — anchoring the crop to the right instead keeps
   the door in frame and only ever trims from the already-quiet left side. */
#different .bg video, #different .bg img.bg-media, #different .bg img.poster-fallback { object-position: right center; }

/* Section 8 (Who We Work With) switched from the half-left boxed variant
   to a full-bleed single composition on 2026-09-21 — the chair-and-windows
   artwork became the door's spatial counterpart: the chair is the inked,
   featured subject in the left half, with the same windows/skyline
   continuing across the frame at deliberately low detail (plus a
   progressive fade) into the right half where the copy column sits.
   Anchoring the crop to the left keeps the chair in frame on narrower/
   taller viewports and only ever trims from the already-quiet right side. */
#who-we-work-with .bg video, #who-we-work-with .bg img.bg-media, #who-we-work-with .bg img.poster-fallback { object-position: left center; }

.section .content {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}
.section .content.wide { max-width: var(--max-width); }

/* Legibility: no background plate/scrim behind text (round-1 feedback —
   a boxed panel reads as "masking the image"). Instead each section positions
   its text over the calmer part of the composition (see zone-* below, set
   per section in index.html to match the artwork), backed by a soft,
   section-colored glow directly on the text itself as insurance only. */
.section.light .eyebrow, .section.light h1, .section.light h2, .section.light h3, .section.light .body-copy, .section.light .phase-num {
  text-shadow: 0 0 5px rgba(242,239,233,.9), 0 0 14px rgba(242,239,233,.75), 0 0 26px rgba(242,239,233,.55);
}
.section.dark .eyebrow, .section.dark h1, .section.dark h2, .section.dark h3, .section.dark .body-copy, .section.dark .phase-num {
  text-shadow: 0 0 5px rgba(30,46,51,.9), 0 0 14px rgba(30,46,51,.75), 0 0 26px rgba(30,46,51,.55);
}

/* ---------- Positioned text zones ----------
   Pulls .content (or a purpose-built block) out of the centered-flex flow
   and docks it over an empty part of the section's artwork, per Johnny's
   round-1 page-by-page notes. Each section picks one in index.html. */
.zone {
  position: absolute;
  z-index: 2;
  margin: 0;
}
/* Text stays left-aligned within its own block even when the block is
   docked to the right side of the frame (readability) — only its
   position moves to keep clear of the artwork. */
.zone-top-left     { top: 9%; left: var(--gutter); max-width: 420px; text-align: left; }
.zone-top-right    { top: 15%; right: calc(var(--gutter) + 40px); max-width: 420px; text-align: left; }
.zone-bottom-left  { bottom: 9%; left: 12%; max-width: 460px; text-align: left; }
/* Full-width so its fade reads as a deliberate cinematic bottom vignette
   (like a subtitle-safe bar) rather than a box — needed on compositions
   that stay busy right down to the frame edge (e.g. section 7's crowd). */
.zone-bottom-band {
  left: 0; right: 0; bottom: 0; width: 100%;
  padding: 14% var(--gutter) 6%;
  text-align: center;
}
.zone-bottom-band > * { position: relative; z-index: 1; }
.zone-bottom-band::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(to top, rgba(242,239,233,.92) 0%, rgba(242,239,233,.7) 40%, rgba(242,239,233,0) 100%);
}
.section.dark .zone-bottom-band::before {
  background: linear-gradient(to top, rgba(30,46,51,.92) 0%, rgba(30,46,51,.7) 40%, rgba(30,46,51,0) 100%);
}
.zone-bottom-band .inner { max-width: 780px; margin: 0 auto; }
.zone-half-left    { top: 50%; left: var(--gutter); transform: translateY(-50%); width: min(440px, 42%); text-align: left; }
.zone-half-right   { top: 50%; right: var(--gutter); transform: translateY(-50%); width: min(440px, 42%); text-align: left; }

/* Section 5: nudged right of the default .zone-half-left gutter position,
   per Johnny's request, so the copy sits a little further off the left
   edge of the revolving-door illustration.
   2026-09-22: Johnny flagged it had drifted too far right — eased the
   nudge back from +64px to +24px.
   2026-09-22 (later): Johnny asked for it to move "very slightly" back
   toward the right again — nudged from +24px to +36px. */
#different .zone-half-left { left: calc(var(--gutter) + 36px); }

/* Section 8 (chair): Johnny flagged the copy column sitting too close to
   the right edge of frame — nudged well left of the default .zone-half-right
   gutter position (a much bigger shift than Section 5's +64px nudge above,
   per "quite a bit"). */
#who-we-work-with .zone-half-right { right: calc(var(--gutter) + 140px); }

/* Section 5 (revolving door): Johnny found the looping video a little
   distracting directly under the text column — not a legibility failure,
   just visual noise competing with the copy. Same technique as
   .zone-bottom-band's own fade above (a soft radial wash of the page's own
   paper color, not a hard boxed panel — the "no scrim" round-1 direction
   was specifically about boxes/panels reading as masking the artwork; a
   gradual falloff doesn't read that way). Extends generously past the text
   block itself on all sides so it genuinely gradates rather than forming
   a visible edge, and only ever partially fades the image (max ~85%
   paper at the very center) rather than fully erasing it. */
#different .zone-half-left > * { position: relative; z-index: 1; }
#different .zone-half-left::before {
  content: "";
  position: absolute;
  inset: -12% -22% -12% -16%;
  z-index: 0;
  background: radial-gradient(ellipse at center, rgba(242,239,233,.85) 0%, rgba(242,239,233,.55) 45%, rgba(242,239,233,0) 78%);
  pointer-events: none;
}
.zone .body-copy { margin: 0; }
.zone-top-left .body-copy, .zone-top-right .body-copy,
.zone-half-left .body-copy, .zone-half-right .body-copy { max-width: none; }

/* Section 7 (Preserving Human Capital, parking-lot concept): the headline
   sits directly on the illustration's own central "road" band — the one
   deliberately empty horizontal strip in the artwork, vertically centered
   in frame by design (see section-backgrounds-spec.md).
   MEASURED (see section-backgrounds-spec.md) against the final locked
   4200x2344 source (7-parking-lot.jpg, downsized from Johnny's own
   5504x3072 original — measured on the original at full precision):
   the road's painted lane-divider sits at 49.93% of image height, i.e.
   dead center; the clean tarmac (inside the hatched kerb lines) spans
   41.83%-58.27% (16.44% of image height); the outer hatch-to-hatch band
   spans 39.42%-60.51% (21.09%).
   Johnny removed the fade/gradient that used to sit behind this text (it
   read as an unwanted "blurry bar" and isn't used anywhere else on the
   site) — with the headline now two tight lines, it fits the clean tarmac
   band on its own without needing one. Also per Johnny: the body copy
   must stay visually anchored to the *illustration* (the gap between the
   parking islands, further down than the headline), not drift whenever
   the headline above it is retimed/retightened. So this zone is now
   BOTTOM-anchored (`bottom: 31.2%`, MEASURED against the same "nice
   balance" position Johnny approved with the headline's old, looser lane
   spacing) rather than centered as one block via translateY(-50%) — the
   body copy's position is therefore fixed independent of the eyebrow or
   the headline's own line-spacing, both of which can now be freely
   retuned above it without ever moving the body copy again. */
.zone-center-band {
  left: 0; right: 0;
  bottom: 31.2%;
  width: 100%;
  padding: 5% var(--gutter);
  text-align: center;
}
.zone-center-band .inner { max-width: 760px; margin: 0 auto; }
/* Johnny: nudge the eyebrow up slightly, off the headline, now that the
   zone is bottom-anchored (so this only moves the eyebrow — the headline
   and body copy underneath are pinned and don't shift). */
#human-capital .eyebrow { margin-bottom: 30px; }
/* Johnny: open up a bigger gap between "Not headcount." and the body copy
   — body copy only, nothing above it should move. A relative offset (not
   a margin-top) is deliberate: this zone is bottom-anchored (see above),
   so a margin-top on .body-copy would just push the whole box taller and
   shift the eyebrow/headline up to compensate, not move the body copy
   itself. `position: relative; top` shifts only this element on screen,
   outside the flow calculation the anchor depends on. */
#human-capital .body-copy { position: relative; top: 14px; }

/* The headline's two "lanes" — forced onto their own line each regardless
   of viewport width (not left to natural wrap), so the break always falls
   exactly between the two clauses, echoing the road's own two lanes either
   side of its (removed) centerline. A gap wider than normal line-height
   reads as a deliberate lane-gap rather than just tight text wrapping.
   The first lane's own sentence needs to read as ONE line (Johnny's
   correction — it was itself wrapping mid-sentence inside .inner's 760px
   reading width at the standard h2 size, undermining the one-line-per-lane
   idea). .inner stays 760px for the eyebrow/body-copy's readability, but
   .road-headline breaks out to the full viewport width (the standard
   calc(50% - 50vw) full-bleed trick — self-centering regardless of the
   narrower parent, so it doesn't depend on .inner's own width) purely to
   give each lane's sentence the room it needs; text-align:center (inherited
   from .zone-center-band) still keeps the actual glyphs centered. */
.road-headline {
  margin: 0;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
.road-headline .lane { display: block; }
/* Johnny: "Not headcount." was sitting too far below the first lane —
   pulled up much closer (was 0.5em, a full extra line-height's worth of
   gap; line-height 1.15 alone already gives comfortable separation). */
.road-headline .lane + .lane { margin-top: 0.05em; }

/* Body copy was cut down (Johnny: end on "change management," drop the
   closing sentence) specifically so it reads as two lines instead of four —
   shorter content needs less of the fade to stay legible against the
   parking rows, which was the actual complaint about the widened fade
   below. .body-copy's sitewide max-width (620px) wraps this shortened
   copy to 3 lines; 660px is the measured minimum that gets it to 2 — 700px
   used for a small margin, still well inside .inner's 760px cap.
   margin-left/right: auto explicitly restated here because `.zone
   .body-copy { margin: 0; }` (two classes) outranks the base `.body-copy`
   rule's `margin: 0 auto` (one class) and was silently killing the
   auto-centering — Johnny caught it looking off-center once the copy got
   short enough to make the 60px left/right imbalance obvious. */
#human-capital .body-copy { max-width: 700px; margin-left: auto; margin-right: auto; }

/* Section 7's "pull out": a single 4K source image (no second AI generation
   — see section-backgrounds-spec.md for why the two-keyframe/interpolated-
   video approach was abandoned) starts zoomed in on the road/headline band
   and eases out to its true framing once scrolled into view. Reuses the
   site's established Mechanism A (CSS scroll-transform only) rather than
   Mechanism B (Higgsfield video), and follows the site-wide "no loop except
   the door" rule: main.js's initZoomRevealEngine fires the .revealed class
   at most once per element and never removes it, so once pulled out to
   maximum extent, that's it — no re-triggering on scroll back up, per
   Johnny's "motivated, not gratuitous" direction. [data-zoom-reveal] is a
   generic attribute (not scoped to #human-capital) so this same pattern can
   be reused on a future section without new CSS. */
[data-zoom-reveal] {
  transform: scale(1.55);
  transform-origin: 50% 50%;
  /* 3x the original 2.4s (Johnny: make it feel more like a "reveal"). */
  transition: transform 7.2s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}
[data-zoom-reveal].revealed { transform: scale(1); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--sage-mist);
  margin: 0 0 18px;
}
.section.dark .eyebrow { color: var(--pale-steel); }

h1, h2, h3 {
  font-family: var(--font-body);
  font-weight: 500;
  line-height: 1.15;
  margin: 0 0 20px;
  text-transform: uppercase;
}
/* CONFIRMED site-wide (was piloted per-section as "TEST" overrides on the
   hero and #shift, both now folded in here, then extended to h3): every
   heading on the site — h1 the hero statement, h2 every section's own
   narrative statement, and h3 the sub-headings (triptych card names,
   diff-item labels, phase names) — is Titillium Web Medium, all caps.
   h3 previously kept the Space Grotesk Bold display face (a deliberate
   carve-out); Johnny removed that distinction site-wide, so there is now
   one heading typeface at every level, not just h1/h2. Doing this once at
   the h1/h2/h3 level means every heading stays consistent automatically
   instead of needing its own scoped override. */
h1 { font-size: clamp(36px, 5.5vw, 64px); }
h2 { font-size: clamp(28px, 4vw, 44px); }

/* Two-phase headline pattern (2026-09-21): most H2 statements pitch a
   claim then a pointed follow-up line ("AI Transformation isn't
   optional. / How you do it is."). The second phrase is set in
   AI-DAPT Burgundy for intelligibility and to echo the wordmark. */
h1 .accent, h2 .accent {
  color: var(--burgundy);
}

.body-copy {
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--graphite-grey);
  max-width: 620px;
  margin: 0 auto 8px;
}
.section.dark .body-copy { color: var(--pale-steel); }

.cta-button {
  display: inline-block;
  margin-top: 32px;
  padding: 16px 36px;
  background: var(--burgundy);
  color: var(--warm-white);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  border: none;
  border-radius: 2px;
  cursor: pointer;
  transition: opacity 0.2s ease;
}
.cta-button:hover { opacity: 0.85; }
.cta-button.secondary {
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
}

/* ---------- Hero (section 1) ---------- */

/* Scale the building animation down ~10% so it reads as a "mounted" plate
   rather than bleeding to the frame edge — reveals a deep-slate margin
   (the .texture layer) which doubles as a natural frame. Kept centered
   per Johnny's call, rather than shifted right for the logo. */
.hero .bg video, .hero .bg img.bg-media { transform: scale(0.86); }

.hero-brand {
  top: 8%; left: calc(var(--gutter) + 36px);
  display: flex; flex-direction: column; align-items: flex-start;
}
/* "Transformation Architects" is baked into the logo artwork itself now
   (Titillium Regular, width-locked to the wordmark, supplied by the user)
   — the hero-logo image is the whole lockup, no separate coded subline
   element or rule needed here any more. */
/* +15% (300px -> 345px). .hero-brand is absolutely positioned with
   align-items: flex-start, so growing the image's width only extends it
   right/down — its top-left corner stays exactly where it was. */
.hero-brand .hero-logo { width: min(345px, 52.9vw); margin: 0; }

/* .zone-bottom-band is bottom-anchored (bottom:0), so its padding-TOP
   only changes where the gradient fade starts — it can't move the text
   itself, since the box's own height keeps the text pinned the same
   distance above the frame's bottom edge. The actual lever for "how
   close to the office block" is padding-BOTTOM: less reserved space
   below the button pulls the whole text+button group further down,
   away from the artwork. Hero-only override — the shared padding stays
   as-is for the other sections using .zone-bottom-band. */
.hero-banner { padding-bottom: 2.5%; }
/* Wide enough for the big-idea headline below to set across roughly two
   lines rather than five or six cramped ones. */
.hero-banner .inner { max-width: 1300px; }
/* The hero's "big idea" — promoted from a small body-copy line to a real
   headline (h1, the page's only one) per the user's call that it needed
   to read as bold and large, not as fine print under the building. Uses
   the same Space Grotesk/bold display treatment as every other section's
   H2, just bigger, since this is the page's primary statement. */
.hero-banner .hero-statement {
  /* CONFIRMED (was a TEST override) — Titillium Web Medium, all caps, at a
     larger size in place of Space Grotesk Bold. Bold read as heavy/generic
     and lost the typeface's technical finesse; Medium gets its "stopping
     power" from scale instead of weight. text-transform is explicit here
     (rather than relying on the copy being typed in caps) so the headline
     stays correct even if the copy itself is ever changed to sentence case. */
  font-family: var(--font-body); font-weight: 500;
  text-transform: uppercase;
  font-size: clamp(28px, 4.3vw, 48px);
  /* Closed back up 2026-09-21 -- Johnny prefers the site-wide headline
     tracking (h1/h2 above set no letter-spacing of their own, i.e. normal)
     over this section's previous 0.06em, so this no longer overrides it. */
  color: var(--ink-charcoal);
  text-align: center;
  max-width: none;
  margin: 0;
  /* As an <h1>, this would otherwise inherit .section.light h1's glow
     text-shadow (legibility insurance for text on busy artwork) — but this
     headline already sits on its own solid paper gradient scrim, so the
     glow is redundant and only softens the crisp Space Grotesk edges. */
  text-shadow: none;
}
.hero-banner .cta-button { margin-top: 30px; }

/* ---------- Triptych grids (sections 3 & 5) ---------- */

/* Section 3 gets its own wider stage than the standard --max-width (1180px)
   so the three re-rendered 9:16 towers can sit large — per the user's call
   that they need to read as "much larger... tall objects", not thumbnails. */
#reactive-change .content.wide { max-width: 1320px; }
.triptych {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 48px;
  margin-top: 56px;
  text-align: left;
}
.triptych .card h3 {
  /* Font-family/weight/case now come from the global h1,h2,h3 rule above
     (Titillium Web Medium, all caps, site-wide as of Johnny's "remove the
     Grotesk sub-headlines" direction) — this override just sets the
     smaller card-scale size and tighter spacing these three names need. */
  font-size: 18px;
  margin-bottom: 8px;
}
.triptych .card .body-copy {
  font-size: 15px;
  max-width: none;
  margin: 0;
}
.triptych .card { text-align: center; }
.triptych .card .bg-wrap {
  position: relative;
  isolation: isolate;
  /* Matches the re-rendered towers' own native 9:16 frame exactly (they
     were re-shot flat-white and portrait specifically to replace the old
     16:9 clips — see section-backgrounds-spec.md). Because the box ratio
     now equals the video's own ratio, object-fit: contain fills the box
     edge-to-edge with no pillarboxing "dead" strip left over. */
  aspect-ratio: 9 / 16;
  overflow: hidden;
  /* Scaled down ~20% from full column width and re-centered — at 100% the
     9:16 towers ran so tall that each card's name + body copy fell off
     the bottom of the viewport. Shrinking the box (aspect-ratio keeps it
     proportional) buys back that vertical space without touching the
     video content itself. */
  width: 80%;
  margin: 0 auto 16px;
  /* No card fill/border of its own — sits flush on the page's own paper
     color so there's no separate "block" behind each vignette. The old
     tint mismatch (each clip's own near-white background reading as a
     faintly different-colored held rectangle) is resolved at the source:
     all three re-renders now share one genuinely flat white background,
     so multiply-blending them here drops out identically and invisibly.
     2026-09-21: was flat --drawing-paper only (no grain), which read as a
     visible "white block" once the surrounding page picked up the paper
     texture — same tile added here so the three towers sit on the same
     paper as everything else instead of a flatter rectangle around them. */
  background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat;
}
.triptych .card .bg-wrap video,
.triptych .card .bg-wrap img {
  display: block;
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; mix-blend-mode: multiply;
}

.diff-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 40px;
  text-align: left;
}
.diff-list .diff-item h3 { font-size: 17px; margin-bottom: 8px; }
.diff-list .diff-item .body-copy { font-size: 15px; max-width: none; margin: 0; }

/* Vertical stack variant (section 5, in the half-width left column) */
.diff-list.vertical { grid-template-columns: 1fr; gap: 22px; margin-top: 32px; }

/* Four-Phase Blueprint (section 6): retired the .diff-list card layout on
   2026-09-21 in favor of real text overlaid directly on the board
   illustration -- see .phase-board below. */

/* ---------- Our Four-Phase Blueprint ---------- */

/* The board is a single locked-aspect-ratio image with four colored
   vertical panels; phase name + a short line sit as real HTML text over
   each panel's blank upper area -- Johnny redrew the board specifically to
   leave that space (icons pushed down) and to flatten the panel dividers to
   true verticals so the overlay lines up cleanly. Same aspect-ratio-lock +
   object-fit:contain technique as .enquiry-plate below, so percentage
   positions never drift regardless of viewport. Text color alternates for
   contrast against each panel's own color -- dark ink on the two lighter
   panels (Immersion, Architecture), white on the two darker panels
   (Construction, Hand-Off) -- mirroring how the icons themselves are
   already inked dark-on-light / white-on-dark in the illustration.
   Re-measured 2026-09-21 against Johnny's parallel-edges/raised-composition
   redraw: panel columns at left/width 7.5%/20.0%, 27.5%/21.4%, 48.9%/20.3%,
   69.2%/22.1% (unaffected by the crop below, since it's a vertical-only crop).
   That redraw's source PNG (5504x3072) baked in a large blank crown above
   the frame -- its wood top rail didn't start until 16.7% (512px) down --
   which caused an early version of this fix to double up the whitespace
   (the .phase-intro spacer below PLUS that hidden image padding), pushing
   the headline off-center high in the gap. Cropped that top 512px off the
   deployed asset instead (assets/img/6-four-phase-blueprint.jpg is now
   5504x2560, panel-icon ink still doesn't start until ~46.7% of the
   cropped height, vs. the old 33%-tall text zone), so the whitespace above
   the board is now controlled by exactly one thing -- .phase-intro. */
#method .content.wide { max-width: 1640px; width: 100%; }
/* Headline sits in the whitespace between the top of the content block and
   the visible top of the drawing board (Johnny's request, 2026-09-21), via
   a spacer sized proportionally to the board's own width-driven scaling so
   the centering holds at any viewport. Originally dead-centered; nudged
   lower in stages per follow-up feedback (before:after ratio 1.35 -> 2.6 ->
   9) using unequal flex spacers rather than a fixed margin, so the bias
   toward the bottom scales right along with everything else instead of
   drifting at odd viewports. At 9:1 the headline/eyebrow block sits close
   to the board (~32px gap at a 1440px viewport, down from ~55px) while
   still leaving clear whitespace above the eyebrow. */
.phase-intro {
  width: 100%;
  aspect-ratio: 5504 / 929;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.phase-intro::before { content: ""; flex: 9 1 0; }
.phase-intro::after { content: ""; flex: 1 1 0; }
.phase-board {
  position: relative;
  isolation: isolate;
  max-width: 1640px;
  width: 100%;
  aspect-ratio: 5504 / 2560;
}
.phase-board .texture { position: absolute; inset: 0; background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat; }
.phase-board img.bg-media {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; mix-blend-mode: multiply;
}
.phase-board .phase-overlay { position: absolute; inset: 0; z-index: 2; }
.phase-slot {
  position: absolute;
  top: 6%;
  height: 39%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  text-align: center;
  padding: 0 12px;
}
.phase-slot .phase-num {
  display: block;
  font-family: var(--font-mono); font-size: clamp(10px, 1.1vw, 13px);
  letter-spacing: 0.2em; margin: 0; opacity: 0.8;
}
.phase-slot h3 { font-size: clamp(20px, 2.6vw, 29px); margin: 34px 0 0; }
/* 2026-09-21: Johnny found this a little small for comfortable reading and
   asked for both the heading and body copy bumped up, with room to open
   the leading too — there's a genuine gap between the last line of copy
   and the icon illustration lower in the frame, so there's headroom to
   grow into without crowding it. */
/* 2026-09-21 (2): the icon drawn into each phase's own artwork sits at a
   different height per phase (baked into the single blueprint image, not
   something these text blocks can move independently) — Construction's
   hard-hat and Immersion's glasses have generous headroom, but
   Architecture's compass and Hand-Off's key sit much higher in their
   column, so at the old 78px gap the (now larger) body copy nearly
   touched them with no breathing room. Matching this margin to h3's own
   34px pulls the whole block up consistently and gives every column real
   clearance above its icon, worst case included. */
.phase-slot p { font-size: clamp(15px, 1.9vw, 20px); line-height: 1.6; margin: 34px 0 0; max-width: 30ch; }
.phase-slot--1 { left: 7.5%; width: 20.0%; }
.phase-slot--2 { left: 27.5%; width: 21.4%; }
.phase-slot--3 { left: 48.9%; width: 20.3%; }
.phase-slot--4 { left: 69.2%; width: 22.1%; }
.phase-slot--light { color: var(--ink-charcoal); }
.phase-slot--dark { color: #fff; }
/* Johnny's 2026-09-22 call, mirrored from the mobile fix above: #method is
   a .section.light section, so the site-wide glow rule (top of this file)
   was putting its cream-colored glow on these phase-slot--dark elements'
   white text too — same "worse, not better" legibility problem as mobile,
   since these slots go dark locally rather than the whole section
   switching to "dark". Extra .phase-board ancestor class to out-specify
   the site-wide rule being overridden. */
.phase-board .phase-slot--dark .phase-num,
.phase-board .phase-slot--dark h3,
.phase-board .phase-slot--dark p { text-shadow: none; }

/* ---------- Call to Enquire ---------- */

/* Johnny's call, 2026-09-22: "Start the Immersion." set in brand Burgundy
   — scoped to just this h2 (not h1/h2/h3 site-wide, which stay
   ink-charcoal by default) since it's this one closing headline he wants
   to stand out, not a general heading-color change. */
#enquire h2 { color: var(--burgundy); }

/* width:100% (not just max-width) because .content is a flex item that
   otherwise shrink-wraps to its narrowest child (the 620px body-copy line),
   which was silently capping .enquiry-plate's rendered size well below its
   own max-width and is why the illustration looked "too small within
   frame" in draft 1. */
#enquire .content { width: 100%; max-width: var(--max-width); }
.enquiry-plate {
  position: relative;
  isolation: isolate;
  /* 2026-09-21: the as-generated phone illustration baked in very
     generous white margin on all sides (by design, per the original
     brief), which made the CSS margin-top alone a poor lever — even
     dropping it to 8px still left a big visual gap, because most of that
     gap was inside the image itself. Fixed at the source instead: the
     image was cropped down to a tighter bounding box around the phone +
     its shadow (measured programmatically — see below), so the box's own
     built-in headroom shrank from ~23% to ~13% of its height. This
     margin-top is now the real fine-tuning control. */
  margin: 20px auto 0;
  max-width: 1100px;
  /* Matches the cropped asset's actual pixel ratio (1936x1068) exactly,
     so object-fit: contain never letterboxes it inside this box — that
     letterboxing was what threw off the field alignment below in the
     first render. Keep this in sync if the source image is ever replaced.
     (Original full-frame render was 2752x1536; cropped via a non-white
     pixel bounding-box scan with ~40px padding to
     432,235 - 2368,1303, i.e. 1936x1068.) */
  aspect-ratio: 1936 / 1068;
}
.enquiry-plate .texture { position: absolute; inset: 0; background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat; }
.enquiry-plate img.bg-media {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; mix-blend-mode: multiply;
}
/* 2026-09-21: Section 10 rebuilt around a flat-lay, dead-on-orthographic
   mobile-phone illustration (landscape) instead of the old desk-phone/
   notepad flat-lay — the real form now reads as the entire content of
   the phone's screen. The screen's blank rectangle was measured directly
   off the original 2752x1536 source PNG with a row/column colour-
   transition scan (same methodology as the Section 7 road-band
   measurement): screen left 731px / right 2023px / top 403px / bottom
   1127px. After the image was then cropped to 1936x1068 (see
   .enquiry-plate above) those coordinates were re-based against the new
   crop origin (432, 235): left 15.44%, top 15.73%, width 66.75%,
   height 67.79%. .enquiry-form is pinned to that exact box so the three
   fields sit precisely inside the drawn screen, laid out with flex
   instead of the old per-field "bottom: N%" notepad-line pins (that
   technique was specific to the old ruled-notepad artwork). */
.enquiry-form {
  position: absolute;
  left: 15.44%;
  top: 15.73%;
  width: 66.75%;
  height: 67.79%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 6%;
  padding: 7% 8%;
  /* 2026-09-22: Johnny wants the fields sitting on a clean, UNTEXTURED
     screen — as built, .enquiry-plate .texture (the paper grain) and
     img.bg-media both sit behind this box at full plate coverage, and
     since the illustration paints the phone's screen near-white, the
     multiply blend lets that grain show straight through, so the "screen"
     read as paper instead of a device display. .enquiry-form is already
     pinned to the exact measured screen rect (see the box-measurement
     comment above), so giving IT a flat background is the simplest fix —
     one solid rectangle, precisely screen-sized, that sits above the
     texture/bg-media layers (z-index: 2) and below the fields. Only the
     screen goes flat; the phone body/bezel around it keeps its painted,
     textured look untouched. */
  background: var(--warm-white);
  border-radius: 5px;
}
.enquiry-form .field { position: static; width: 100%; }
.enquiry-form .field-message { flex: 1; display: flex; }
/* 2026-09-22: Send moved from a separate row below the whole phone plate
   into the form itself, as the last field in the same flex column — on
   mobile the button was landing off the bottom of the screen entirely
   (the phone graphic + 4 fields already ran close to one viewport, so
   anything after it was pushed below the fold). Living inside the same
   absolutely-positioned screen-box as the other fields, it now always
   renders on screen; .field-message (flex: 1) simply gives up whatever
   height the button + this gap needs, so the message box's own bottom
   edge sits a little higher instead of the button being cut off. Sized
   down from the site's normal .cta-button (which assumes a full block
   layout, not a ~250px-wide phone screen) — smaller padding, no
   margin-top (the form's own `gap` already spaces it from the field
   above), and the same clamp() scale already used for the input text so
   it reads proportionate to the fields it sits under. */
.enquiry-form .field-submit { text-align: center; }
.enquiry-form .cta-button {
  margin-top: 0;
  padding: 8px 22px;
  font-size: clamp(9px, 1.6vw, 13px);
}

.enquiry-form input, .enquiry-form textarea {
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--pale-steel);
  font-family: var(--font-body);
  font-size: clamp(9px, 1.4vw, 14px);
  color: var(--ink-charcoal);
  padding: 0 2px 6px;
  width: 100%;
  display: block;
}
/* The message field reads as a distinct box (rather than the underline
   treatment used for the single-line fields) so it visually fills the
   remaining screen space as a proper "message box" instead of leaving it
   blank beneath a floating placeholder. */
.enquiry-form .field-message textarea {
  flex: 1;
  border: 1px solid var(--pale-steel);
  border-radius: 3px;
  padding: 10px 12px;
  box-sizing: border-box;
}
.enquiry-form textarea { resize: none; height: 100%; line-height: 1.35; }
.enquiry-form input:focus, .enquiry-form textarea:focus {
  outline: none;
  border-bottom-color: var(--burgundy);
}
.enquiry-form input::placeholder,
.enquiry-form textarea::placeholder { color: var(--graphite-grey); opacity: 0.7; }
/* 2026-09-22: Johnny asked for the Send button (sitting directly beneath
   the phone plate) to move slightly up — eased margin-top from 28px to
   16px, a small pull-up rather than a big jump. */
.enquiry-submit-row { margin-top: 16px; text-align: center; }
.form-status {
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--blueprint-teal);
  min-height: 1.2em;
}

/* ---------- Footer ---------- */

footer.site-footer {
  background: var(--ink-charcoal);
  padding: 96px var(--gutter) 40px;
}
.footer-inner { max-width: var(--max-width); margin: 0 auto; }
.footer-top {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: flex-end; gap: 32px;
}
.wordmark-img { display: block; height: 42px; width: auto; }
.footer-subline {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.32em;
  text-transform: uppercase; color: var(--pale-steel); margin: 14px 0 0;
}
.footer-slogan {
  font-family: var(--font-display); font-size: 15px; color: var(--sage-mist);
  margin: 0; text-align: right;
}
.footer-hairline { height: 1px; background: var(--burgundy); opacity: 0.55; margin: 40px 0 28px; }
.footer-bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: center; gap: 20px;
}
nav.footer-nav { display: flex; gap: 28px; flex-wrap: wrap; }
nav.footer-nav a {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--pale-steel); text-decoration: none; opacity: 0.85;
}
nav.footer-nav a:hover { opacity: 1; }
.footer-legal {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em;
  color: var(--graphite-grey); text-align: right;
}
.footer-legal a { color: var(--graphite-grey); text-decoration: none; border-bottom: 1px solid currentColor; }

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .triptych, .diff-list { grid-template-columns: 1fr; }
}
/* ============================================================
   MOBILE (<=768px) — portrait-only build
   Built from Johnny's own numbered mockups (MOBILE SCREENS.zip,
   2026-09-22): each .mobile-view block is a from-scratch mobile layout for
   one section, using his exact copy/positioning brief. Landscape is not a
   supported mobile layout per his direction — portrait exclusively.
   Hidden globally by default; switched on only inside the breakpoint below,
   and only for sections marked .has-mv on the <section> itself.
   ============================================================ */
.mobile-view { display: none; }

@media (max-width: 768px) {
  /* No video background on mobile by default — static poster only, per
     brand-kit.md §14 — EXCEPT a video explicitly marked [data-mobile-ok]
     (Hero, Integration, and eventually the door/chair once their portrait
     renders exist), which gets real playback; main.js's video engine has
     the matching per-video gate. */
  .section video:not([data-mobile-ok]) { display: none; }
  .section img.poster-fallback { display: block; }
  .section { padding: 90px 20px; }

  /* 2026-09-22: paper-texture.png's 900px tile was deliberately downsampled
     (see the big comment on .section .bg .texture above) so the grain reads
     at the scale real paper does "at normal viewing distance" — but that
     distance assumption was a laptop/desktop arm's length. A phone is
     typically held far closer, so the same 900px tile reads coarser/blotchier
     than intended, and on top of that a fixed-size raster background gets
     upscaled (softened) on high-DPI phone screens since the box it fills is
     the same 900 CSS px but 2-3x the device pixels. Confirmed visually
     (Playwright @3x DPR) — shrinking the tile fixes both at once: finer,
     denser grain that's obviously still visible, closer to true paper scale
     at arm's-length-from-face phone distance. Every surface that paints this
     texture gets the smaller tile on mobile; nothing else about them changes. */
  .section.light,
  .section .bg .texture,
  .phase-board .texture,
  .enquiry-plate .texture {
    background-size: 320px 320px;
  }
  /* .mv-text-outcomes and .mv-text-human-capital paint their own full
     background: ...repeat shorthand further down this same media query —
     that shorthand resets background-size back to auto, so a
     background-size override placed here (before them, in source order)
     would just get clobbered. Their background-size: 320px is set inline
     on those rules themselves instead, right after the shorthand. */

  /* 2026-09-22: REVERSED the 2026-09-21 decision directly above this
     comment (scroll-snap was disabled site-wide on mobile because section
     heights varied unpredictably) — Johnny flagged that exact
     inconsistency as the root problem and asked for one fixed page height
     site-wide instead of working around the symptom. Root cause found:
     .mv-hero and .mv-integration never carried the .mv-section class in
     index.html, so they never got ANY height rule at all (pure
     content-height, well under one screen — this is what let Page 2 bleed
     in at the bottom of Johnny's Page 1 screenshot); .mv-outcomes
     separately opted itself out with `min-height: auto`. Both fixed below
     (class added in index.html; auto-override removed) so every one of
     the 8 mobile pages now goes through the exact same .mv-section rule,
     no exceptions — which is what makes re-enabling snap here safe rather
     than "jittery" as it was before this fix.
     100dvh over 100vh: dvh tracks the mobile browser's actual visible
     viewport (collapses/expands as Safari's address-bar chrome
     shows/hides), where vh is pinned to the LARGEST possible viewport and
     is exactly the kind of "doesn't stay consistent" behavior Johnny
     asked to eliminate. 100vh is kept as the first declaration purely as
     a fallback for a browser that doesn't understand dvh (which then just
     ignores the second line) — not a hedge on which is correct. */
  html { scroll-snap-type: y mandatory; }

  .section.has-mv { padding: 0; min-height: auto; display: block; }
  .section.has-mv .bg,
  .section.has-mv .zone,
  .section.has-mv > .content { display: none; }
  .mobile-view { display: block; }

  /* #reactive-change/#method opt OUT of snap-align on desktop (see that
     rule, above) because their desktop layouts aren't reliably ~100vh —
     but their MOBILE layouts (.mv-reactive / .mv-phases, both carousels)
     already are, via the same .mv-section rule as everything else, so
     re-included here for mobile specifically.
     2026-09-22 correction: #enquire was ALSO left excluded here on the
     theory that it's not a .mobile-view/.mv-section page (same shared
     form + illustration as desktop, naturally sized) so a snap point
     would fight its own organic height — that was wrong in practice.
     scroll-snap-align:none on #enquire, combined with mandatory type +
     scroll-snap-stop:always on #who-we-work-with right before it, left
     mandatory snapping with no valid landing point past that section —
     Johnny found the page hard-locked on the Eames Chair section with no
     way to scroll to Contact. scroll-snap-align only decides where the
     browser aligns to when snapping — it doesn't force or constrain the
     section's height — so #enquire keeping its own organic size while
     still being a valid snap target is exactly what fixes this. */
  #reactive-change, #method, #enquire { scroll-snap-align: start; }
  .section.has-mv { scroll-snap-stop: always; }
  /* 2026-09-22: same bug as the #enquire fix directly above, one section
     further down the chain — .site-footer comes right after #enquire (the
     last mandatory snap point) with no snap-align of its own, so once a
     user was sitting on #enquire, scrolling further had nowhere valid to
     land and mandatory snapping pulled them straight back. Johnny reported
     it as "can't scroll down past the phone [Contact form]" — couldn't
     reach the footer at all. Same fix: give the footer a real snap point
     of its own so it's the final, reachable stop in the sequence. */
  .site-footer { scroll-snap-align: start; }

  /* ---------- Shared mobile primitives ---------- */
  .mv-section {
    position: relative;
    width: 100%;
    min-height: 100vh;
    min-height: 100dvh;
    overflow: hidden;
  }
  /* mix-blend-mode: multiply — shared by every page that uses this class
     (the shift crane art, the chair video on Who We Work With), both
     rendered on flat white per the site-wide convention; see the hero
     comment above for the full explanation. */
  .mv-bg-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; mix-blend-mode: multiply; }
  .mv-text { position: relative; z-index: 1; }
  .mv-text h2 { font-size: clamp(24px, 7vw, 30px); }
  /* 2026-09-22: Johnny's iPhone 16 Pro Max check found body copy getting
     "lost" (too small) on several pages, and felt most pages actually had
     room to size the type up. Standardized on 18px site-wide for mobile
     body copy — the size already in use, un-overridden, on the
     Integration Model and Start the Immersion pages (both just inherit
     the base .body-copy rule, ~line 470) — by raising this shared .mv-text
     default to match instead of leaving it at the old 15px. The one
     explicit exception is Built for Outcomes (the revolving-door page):
     no room there, so .mv-diff-item .body-copy below keeps its own more
     specific 15px rule, which still wins over this one. */
  .mv-text .body-copy { font-size: 18px; }

  /* ---------- 1. Hero ---------- */
  .mv-hero {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    padding: 64px 24px 56px;
  }
  /* mix-blend-mode: multiply added 2026-09-22 — every source still/video on
     this site (desktop included) is rendered on a flat pure-white
     background by design (see section-backgrounds-spec.md standing-spec
     point 7); multiply is what drops that white out against the paper
     texture underneath. Desktop's .bg video/img rule has always done this;
     this element (and 4 others below — the shift crane art, the triptych
     carousel stills, the integration video, the door video) never got the
     same treatment on mobile, which is exactly the "white blocks around
     the images, paper texture not visible" Johnny flagged. */
  /* Johnny's 2026-09-22 mockup ("LANDING PAGE FIXES") upscales this by 170%
     — measured directly off his CURRENT vs FIXES screenshots (building
     bounding-box width ratio came back 1.70x almost exactly), headline/logo
     left at their original size and simply pushed lower by the taller flex
     item above them, no separate repositioning needed. Both constraints
     scaled by the same 1.7x factor so the rendered size scales uniformly
     at any viewport. Confirmed via the actual poster/video asset
     (assets/poster/1-hero.jpg, a 1920x1080 frame where the building
     drawing itself only occupies the center ~51% of the frame's width —
     the rest is transparent margin) that the resulting box, even though it
     now exceeds .mv-hero's content-box width and gets edge-clipped by
     .mv-section's overflow:hidden, only clips into that transparent
     margin — the visible drawing itself lands at ~65% of full screen
     width with ~17.5% clear margin either side, matching Johnny's FIXES
     screenshot almost exactly. */
  .mv-hero-building { width: 142.8%; max-width: 646px; margin: 0 0 40px; mix-blend-mode: multiply; }
  /* Johnny 2026-09-22: wanted the mobile hero headline bigger — there's
     enough clear space on this page (no competing elements below the
     fold) for a stronger, more defining statement, matching the crane
     page's confidence. Follow-up same day: 700 read as too heavy — back
     to the site-wide h1/h2/h3 Titillium Web Medium (line ~444), size
     increase kept. */
  .mv-hero-headline {
    font-family: var(--font-body); font-weight: 500; text-transform: uppercase;
    font-size: clamp(30px, 9.5vw, 40px); line-height: 1.22;
    color: var(--ink-charcoal); max-width: 320px; margin: 0 0 48px;
    text-shadow: none;
  }
  .mv-hero-logo { width: 72%; max-width: 280px; margin: 0; }

  /* ---------- 2. The Inevitable Shift ---------- */
  /* Full-bleed crane plate: art runs the full height on the right (the
     tower mast), so copy is docked lower-left, clear of it — same idea as
     the desktop .zone-bottom-left docking, just re-measured for the new
     portrait plate. */
  /* 2026-09-22: Johnny re-rendered this plate with an extended crane base
     (2160x4800, taller than the first pass) specifically to open up more
     clear vertical room under the arm/cab for the text block — re-measured
     (row-content scan) at 40% down, was 44% on the shorter first plate. */
  .mv-text-shift { position: absolute; top: 43%; left: 8%; width: 70%; text-align: left; }
  .mv-text-shift h2 { font-size: clamp(22px, 6.2vw, 27px); }
  .mv-text-shift h2 { margin: 0 0 16px; }
  .mv-text-shift .body-copy { max-width: none; margin: 0; }

  /* ---------- 3. The Cost of Reactive Change (swipe carousel) ---------- */
  /* Johnny's 2026-09-22 headline trim (3 lines -> 2, to stop an awkward
     line-break) freed up vertical space that the old flex:1-on-.mv-carousel
     approach put ALL of underneath the carousel content, not around the
     composition as a whole — headline pinned to the top via padding-top,
     carousel vertically centered only within its own leftover box below.
     Net effect: a big dead gap directly under the headline. Switched to
     centering the whole headline+carousel group as one unit instead, so
     the freed-up space redistributes evenly above and below it. */
  /* 2026-09-22: padding trimmed (was 72/48 top/bottom) to make room for
     the taller 4-line headline + 10%-bigger carousel below without this
     section growing past one screen — every other mobile page is exactly
     one viewport tall (the scroll-snap-stop:always "no skipping past a
     section" rule depends on that), and the bigger content pushed this
     one over that line before the trim. */
  .mv-reactive { min-height: 100vh; display: flex; flex-direction: column; justify-content: center; padding: 8px 24px 2px; box-sizing: border-box; }
  /* 2026-09-22: Johnny felt this page read thinner than the rest of the
     site — smaller headline than its neighbours, then a sizeable dead
     gap down to the carousel and another down to its body copy. Split
     over 4 short lines (his own line breaks) at a bigger size closer to
     the site's normal h2 scale, instead of the unstyled 2-line version
     that was just inheriting the base h2 clamp. */
  .mv-reactive-headline { text-align: center; margin: 0 0 8px; font-size: clamp(28px, 8vw, 34px); line-height: 1.08; }
  .mv-carousel { position: relative; display: flex; align-items: center; overflow: hidden; }
  /* 2026-09-22 (third pass): every previous attempt here was custom JS
     simulating a slider on top of a transform-positioned track — first
     touch-action:pan-y with a JS axis-locking handler (a WebKit bug made
     the PAGE's own vertical scroll-snap mis-claim repeat gestures), then
     touch-action:none with a live window.scrollBy passthrough during the
     gesture (fought scroll-snap-stop:always and jammed), then
     touch-action:none with a single scrollIntoView jump on release
     (verified clean in Playwright's CDP touch simulation, but Johnny's
     real device STILL showed both the stuck-horizontal-swipe and the
     jammed-vertical-scroll bugs — simulated touch was never reproducing
     whatever WebKit is actually doing here).
     Giving up on simulating a slider in JS entirely. The track is now a
     REAL native horizontally-scrolling element (overflow-x + scroll-snap-
     type:x), the same mechanism the page itself already uses for its own
     vertical section-to-section snap — this hands the whole gesture,
     including the axis handoff to the page's vertical scroller, to the
     browser's own first-class scrolling engine instead of a hand-rolled
     touch heuristic. main.js no longer touches touchstart/touchmove/
     touchend on the track at all; it only reads track.scrollLeft (to
     color the arrows per-slide) and drives arrow-tap navigation via
     track.scrollTo().
     touch-action deliberately left at its default (auto), NOT pan-x —
     tried pan-x first (on the theory that explicitly restricting the
     track to horizontal-only would hand vertical drags to the page
     immediately) but testing showed the opposite: pan-x made the browser
     swallow a vertical touch that started on the track instead of
     bubbling it to the page's scroller, silently reintroducing the
     "can't scroll off the carousel" bug this whole rewrite exists to fix.
     Left at auto, the track's own overflow-y:hidden already means it has
     nothing to scroll vertically, so a vertical drag correctly falls
     through to the page's own vertical scroll-snap with no override
     needed — verified via Playwright touch simulation, both straight and
     with realistic diagonal drift on the horizontal swipes. */
  .mv-carousel-track {
    display: flex; width: 100%;
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain; /* stops an edge swipe from triggering Safari's page-back gesture */
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .mv-carousel-track::-webkit-scrollbar { display: none; }
  .mv-carousel-slide { flex: 0 0 100%; width: 100%; text-align: center; padding: 0 8px; box-sizing: border-box; scroll-snap-align: start; scroll-snap-stop: always; }
  /* .mv-carousel-stage paints its own paper-texture background (matching
     .section.light's) rather than relying on the section's background
     showing through from behind. Reason: main.js sets an inline
     `transform` on .mv-carousel-track the first time you advance the
     carousel, and a transform creates a new stacking context — which
     isolates mix-blend-mode from anything outside it. Slide 1 (no
     transform yet on first load) blended fine against the section
     background; slides 2/3, once the track had been transformed, lost
     that backdrop entirely and showed the illustrations' white source
     background as a flat white box instead of blending away. Giving the
     stage its own matching texture means the img's multiply blend has a
     backdrop within the SAME (now-isolated) stacking context, so it
     blends correctly no matter which slide is active or whether the
     track has been transformed. Johnny 2026-09-22. */
  /* 2026-09-22: sized up ~10% (66%/264px, was 60%/240px) — part of the
     same "page feels thinner than its neighbours" fix as the headline
     above, giving the tower illustrations more presence. */
  /* 2026-09-22: Johnny found the caption block (title + body copy) sitting
     too close to the base of the model illustration — nudged down via
     this margin rather than touching the caption's own spacing. */
  .mv-carousel-stage {
    width: 66%; max-width: 264px; margin: 0 auto 16px; aspect-ratio: 9 / 16; overflow: hidden;
    background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat;
    background-size: 320px 320px;
  }
  .mv-carousel-stage img { width: 100%; height: 100%; object-fit: contain; mix-blend-mode: multiply; }
  .mv-carousel-slide h3 { font-size: 17px; margin: 0 0 4px; }
  .mv-carousel-slide .body-copy { font-size: 18px; max-width: 340px; margin: 0 auto; line-height: 1.28; }
  .mv-arrow {
    position: absolute; top: 50%; transform: translateY(-50%);
    background: none; border: none; font-size: 34px; line-height: 1;
    color: var(--graphite-grey); padding: 10px 6px; cursor: pointer; z-index: 2;
  }
  .mv-arrow-left { left: -6px; }
  .mv-arrow-right { right: -6px; }
  /* 2026-09-22: Four Phases' arrow pair is positioned once per carousel
     (not duplicated per-slide), so it sits on top of 2 light AND 2 dark
     panels — a flat color can't read on both. main.js toggles this class
     on the carousel root to match the CURRENT slide's light/dark state
     (see goTo() in initMobileCarousels), so the graphite-grey default
     above still serves Reactive Adoption (always on paper) and Phase 1/2,
     while Phase 3/4's dark photo panels get white arrows instead of
     vanishing against the dark background. */
  .mv-carousel--on-dark .mv-arrow { color: #fff; }

  /* ---------- 5. What Makes Us Different (stacked, not full-bleed) ----------
     The door plate's own art ends ~47% down the source (MEASURED via a
     row-content scan) with nothing but blank space below — so unlike Shift
     and Who (below), this one is simplest as a plain stacked layout: the
     cropped art as a normal block image, copy flowing beneath it on the
     page's own paper background, rather than an absolute overlay.
     2026-09-22: Johnny supplied the dedicated 9x20 door plate (2160x4800)
     for this section — re-measured (row-content scan) at 43.7% down, cropped
     at 47% to keep the same small margin as before. The full plate is
     archived at assets/img-mobile-src/5-door-9x20-KEYFRAME.jpg as the still
     reference used for the motion pass.
     2026-09-22 (later same day): .mv-art-top is now a real looping video
     (assets/video/5-revolving-door-mobile.mp4, 1080x1128, matching the same
     47% crop) instead of a static image — Johnny's own methodology won out
     over the Higgsfield motion-transfer route: rather than fighting the
     generator's aspect-ratio/style drift, he placed the ORIGINAL desktop
     door footage directly into a 1080x2400 Premiere sequence and cropped it
     down here exactly like the still was. Confirmed a perfect loop, so the
     video gets the `loop` attribute (main.js's video engine leaves looping
     ones alone rather than freezing on the last frame). Poster is the
     video's own first frame, so it matches exactly under reduced-motion. */
  /* 2026-09-22: was `.mv-outcomes { min-height: auto; }`, deliberately
     opting this page out of the shared one-screen page height — removed
     as part of unifying every mobile page onto the same .mv-section rule
     (see the scroll-snap comment above). This page's real content (door +
     headline + 3 full diff-items) is likely to run taller than one screen
     even with the min-height floor removed as a ceiling concern (min-
     height never clips, only guarantees a floor) — flagged to Johnny as
     the one page that may need its copy/spacing condensed to actually hit
     "one screen, maybe a sliver," rather than being a pure CSS fix like
     the rest of this pass. */
  .mv-art-top { width: 100%; display: block; mix-blend-mode: multiply; }
  /* Johnny's 2026-09-22 device check confirmed the overflow flagged above —
     the last diff-item ("Strategic Symbiosis") was disappearing off the
     bottom of the screen on his phone. His fix: pull the headline+copy
     block upward, door art untouched. Trimmed ~92px total out of this
     block's own padding/gaps (40->12 top, 32->20 under the headline,
     26->18 between diff-items, 64->28 bottom) rather than touching the
     video or the copy itself. */
  .mv-text-outcomes {
    text-align: center; padding: 4px 28px 16px;
    background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat;
    background-size: 320px 320px;
  }
  /* Johnny 2026-09-22: headline moves up, on its own — the diff-list below
     (body copy) stays exactly where it is. Negative margin-top pulls the
     headline closer to the door video above; margin-bottom is bumped by
     the same amount so .mv-diff-list's own position is unaffected —
     net effect is a bigger, cleaner gap between the headline and the
     copy below it, not the whole block shifting. */
  .mv-text-outcomes h2 { margin: -14px 0 30px; }
  .mv-diff-list { display: flex; flex-direction: column; gap: 14px; }
  .mv-diff-item h3 { font-size: 18px; margin: 0 0 8px; }
  /* 2026-09-22: deliberately NOT part of the site-wide 18px body-copy bump
     (see .mv-text .body-copy above) — Johnny flagged this as the one page
     with no room for it, door art + headline + 3 full diff-items already
     fill exactly one screen at 15px. */
  .mv-diff-item .body-copy { font-size: 15px; max-width: none; margin: 0; }

  /* ---------- 6. Our Four-Phase Blueprint (swipe carousel) ---------- */
  .mv-phases { min-height: 100vh; display: flex; flex-direction: column; }
  .mv-phases-headline { text-align: center; margin: 56px 24px 0; }
  .mv-carousel-phases { flex: 1; margin-top: 28px; align-items: stretch; }
  .mv-carousel-phases .mv-carousel-track { align-items: stretch; }
  .mv-phase-slide {
    flex: 0 0 100%; width: 100%; box-sizing: border-box;
    background-size: cover; background-position: center;
    display: flex; flex-direction: column; align-items: center;
    padding: 20px 28px 0; text-align: center;
    scroll-snap-align: start; scroll-snap-stop: always;
  }
  .mv-phase-slide .phase-num { display: block; font-family: var(--font-mono); font-size: 12px; letter-spacing: .2em; opacity: .8; margin: 0 0 14px; }
  .mv-phase-slide h3 { font-size: 28px; margin: 0 0 14px; }
  /* 2026-09-22: Johnny replaced the phase artwork with his own new
     white-line panels (see the asset comment on the mobile carousel
     images themselves) — the compass and key sit higher in these than
     the old art did, and on his device the body copy was landing right
     on top of them. Pulled the whole text block up (slide's own
     top padding, plus the phase-num/h3 margins above body-copy) rather
     than touching body-copy's own margin, so the copy block stays intact
     and just gets more clearance before the icon starts, uniformly
     across all four phases. */
  .mv-phase-slide .body-copy { font-size: 18px; max-width: 30ch; margin: 14px 0 0; }
  .mv-phase-slide--light { color: #000; }
  .mv-phase-slide--dark { color: #fff; }
  /* 2026-09-22: Johnny's explicit ask for Phase 1/2 (light) — lose the glow
     entirely and make both the headline and body copy solid black, rather
     than the site's usual --ink-charcoal/--graphite-grey pairing, to
     maximize legibility against the busy line-art background. */
  .mv-phase-slide--light .body-copy { color: #000; }
  .mv-phase-slide--dark .body-copy { color: var(--pale-steel); }
  /* Johnny's 2026-09-22 call: the site-wide text glow (see the .section.light
     / .section.dark rule near the top of this file) is meant as legibility
     insurance, but it was reading as worse, not better, on ALL FOUR phase
     panels — phases 3 & 4 (white-on-dark-photo text) and now phases 1 & 2
     as well, where he'd rather have plain solid-black text than a cream
     halo around it. Removed here (needs the extra .mv-phases ancestor
     class to out-specify the site-wide rule it's overriding) and the
     --dark half of this is mirrored on desktop's .phase-slot--dark below. */
  .mv-phases .mv-phase-slide--dark .phase-num,
  .mv-phases .mv-phase-slide--dark h3,
  .mv-phases .mv-phase-slide--dark .body-copy,
  .mv-phases .mv-phase-slide--light .phase-num,
  .mv-phases .mv-phase-slide--light h3,
  .mv-phases .mv-phase-slide--light .body-copy { text-shadow: none; }

  /* ---------- 7. Preserving Human Capital ---------- */
  /* Two cropped bands of the SAME existing parking-lot artwork (top rows /
     bottom rows), framing a plain text band — matches the mockup's
     torn-edge/shadowed card without needing any new artwork. */
  .mv-human-capital { display: flex; flex-direction: column; min-height: 100vh; }
  /* Johnny's 2026-09-22 call: the source crops (7-parking-top/bottom.jpg,
     1080x253, already an edge-to-edge 2-row strip with no dead margin —
     see the mix-blend-mode note above, these never needed the multiply
     treatment for the same reason) were rendering at their own intrinsic
     ~4.3:1 aspect ratio, which at any phone width comes out as a fairly
     thin sliver — leaving most of the section's height to the plain-paper
     text block between them, instead of the tighter "strip of road"
     framing the headline that his own mockup goes for. Fixed with the same
     scale-and-let-the-edges-crop approach used on the hero building and
     the integration illustration earlier in this pass: an explicit height
     forces object-fit:cover to zoom in and crop the left/right edges
     (losing a car or so off each end, fine for a repeating row) rather
     than deriving height from the image's own aspect ratio. No new
     Photoshop crop needed. */
  .mv-parking-band { width: 100%; height: 29vh; object-fit: cover; object-position: center; display: block; }
  .mv-text-human-capital {
    flex: 1; display: flex; flex-direction: column; justify-content: center;
    text-align: center; padding: 36px 24px; box-sizing: border-box;
    background: var(--drawing-paper) url("../assets/img/paper-texture.png") repeat;
    background-size: 320px 320px;
    box-shadow: 0 6px 14px -10px rgba(0,0,0,.35) inset, 0 -6px 14px -10px rgba(0,0,0,.35) inset;
    position: relative; z-index: 1;
  }
  .mv-road-headline { margin: 0 0 20px; }
  .mv-road-headline .lane { display: block; }
  .mv-text-human-capital .body-copy { max-width: none; margin: 0; }

  /* ---------- 4. The Architecture of Adaptation (Integration) ---------- */
  /* Stacked layout (like Outcomes) — headline top, the SAME existing
     building+server video/poster in the middle, body below. Real video
     playback here too (see main.js's [data-mobile-ok] gate). */
  .mv-integration { padding: 64px 24px 56px; text-align: center; }
  .mv-integration h2 { margin: 0 0 28px; }
  .mv-integration-media { width: 200%; max-width: none; border-radius: 2px; margin: 0 0 28px; mix-blend-mode: multiply; }
  .mv-integration .body-copy { max-width: none; margin: 0 auto; }

  /* ---------- 8. Who We Work With ---------- */
  /* Full-bleed chair plate anchored to the bottom of frame (object-position)
     so the chair itself stays in view regardless of viewport height; copy
     docks in the top zone, which is deliberately faint/blank artwork
     (windows only) in the source plate.
     2026-09-22: .mv-bg-img is now the real Eames-turn video
     (assets/video/8-who-we-work-with-mobile.mp4, native 1080x2400 — already
     the full 9x20 canvas, no crop/pad needed like the door required) instead
     of the static front-chair plate. Built from the front+back keyframes
     prepared earlier this session, then packaged to the mobile frame with
     Johnny's own Premiere methodology (see the Outcomes/door section above)
     rather than fighting Higgsfield's aspect-ratio/style drift again. No
     `loop` attribute — it's a single front-to-side-to-front turn that
     settles on a clean resting pose, so it plays once and freezes on the
     last frame like every other non-door video on the site. */
  .mv-who .mv-bg-img { object-position: bottom center; }
  .mv-text-who { position: absolute; top: 6%; left: 0; right: 0; padding: 0 28px; text-align: center; box-sizing: border-box; }
  /* 2026-09-22 (superseded, see below): with the OLD copy ("Built for
     executive teams." / "Not technical teams alone.") each accent-colored
     half was wrapping to 2 lines at the shared .mv-text h2 size — shrunk
     specifically for this headline so both halves fit on one line each.
     2026-09-22 follow-up: with the copy now shortened again ("Made for
     executive teams." / "Not only tech teams.") Johnny felt that shrunk
     size read as too small next to the rest of the site's headlines.
     There genuinely isn't room in this 28px-padded column to size this
     headline up AND keep both halves on one line at every phone width —
     so this now matches the site's normal h2 scale (clamp, same shape as
     the base h2 rule) and simply wraps like any other headline on the
     site when a narrow phone needs it to; that reads as normal, not
     broken. */
  .mv-text-who h2 { margin: 0 0 16px; font-size: clamp(22px, 6.6vw, 28px); line-height: 1.12; }
  /* Body copy nudged down (margin-top, not touching the headline above it)
     so it isn't stranded in a big empty gap above the chair — per
     Johnny's 2026-09-22 note that the space between the copy and the
     chair below read as "massive."
     2026-09-22: was EXCLUDED from the site-wide 18px body-copy bump
     (kept at 15px, same reasoning as Revolving Door — this is the
     longest copy block on the site, 57 words, over a full-bleed chair
     plate with no room to grow). Johnny's later pass flagged this as a
     miss: he wants the same larger size used everywhere else (Integration
     Model etc.) applied here too — there's enough room. Bumped to the
     site-wide 18px, with a tighter line-height (matching Reactive
     Adoption's carousel body-copy, the other place 18px shares a page
     with a lot of other content) so it still clears the chair's headrest
     rather than reverting to the roomier global 1.55. */
  .mv-text-who .body-copy { font-size: 18px; line-height: 1.28; max-width: none; margin: 22px 0 0; }

  /* ---------- 10. Call to Enquire (Connect) ---------- */
  /* Not a .mobile-view/.has-mv section — same #enquiry-form as desktop
     (initEnquiryForm() binds it by id, so there's exactly one in the DOM),
     just re-plated against Johnny's own 9x20 phone artwork (a <picture>
     swap, see index.html) with the overlay box re-measured against IT
     directly, same "aspect-ratio lock + percentage box" technique as the
     desktop version. Screen box MEASURED via a bezel-edge pixel scan on
     the new plate (2160x4800 source, downscaled to 1080x2400): left
     20.37%, top 37.54%, width 59.6%, height 48.08%. */
  #enquire .eyebrow { display: none; }
  /* 2026-09-22: Johnny's device check found the phone + Send button
     "miles away" from the headline/body copy above. Root cause was the
     same one already fixed on the desktop plate (see the comment on
     .enquiry-plate above, ~line 771) but never applied to this mobile
     asset: the source render (10-connect-bg.jpg, 1080x2400) had the phone
     sitting well down inside a very tall canvas — ~28% blank headroom
     above it, ~7% blank below. Cropped the source image itself down to a
     tight bounding box around the phone + its shadow (same non-white
     pixel-scan methodology as the desktop crop and the Section 4
     integration-model measurements), 69,653 - 1007,2265 of the original
     2400-tall canvas → 938x1612. The screen-box percentages below are
     re-derived against that new crop origin so the form fields still land
     exactly on the phone's drawn screen. */
  .enquiry-plate {
    margin: 4px auto 0;
    max-width: 420px;
    aspect-ratio: 938 / 1612;
  }
  .enquiry-form {
    left: 16.1%;
    top: 15.38%;
    width: 68.63%;
    height: 71.59%;
    gap: 5%;
    padding: 6% 7%;
  }
  /* 2026-09-22: Johnny's device check — "move the phone up very slightly
     and the SEND button even more." Trimmed #enquire's own top/bottom
     section padding (was the shared .section 90px/90px) and tightened the
     gap above the Send button specifically, so the button closes more of
     the distance than the phone does. Deliberately NOT touching the phone
     artwork size, the form fields, or the headline/body-copy sizing —
     Johnny flagged the current visual balance as already working well and
     asked not to get too fussy with it. */
  #enquire { padding: 64px 20px 48px; }
  .enquiry-submit-row { margin-top: 8px; }
}

@media (max-width: 640px) {
  .footer-top { flex-direction: column; align-items: flex-start; }
  .footer-slogan { text-align: left; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }
  .footer-legal { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  .section .bg video { display: none; }
  .section .bg img.poster-fallback { display: block; }
  html { scroll-behavior: auto; scroll-snap-type: none; }
  [data-zoom-reveal] { transform: scale(1); transition: none; }
}
