/* Hallmark · macrostructure: Cinematic Scenes (photographic scene sequence — NOT the previous build's Cinematic Contrast; new type, new grammar, new assets) · theme: Klyear-definitive studied-DNA (basement.studio register + flymedia loader/ticker/ghost-footer + Apple chapter spacing — see design-dna.md ROUND 4 REVISION)
   · fonts: Clash Display 600/700 (display, Fontshare) / Switzer 400/500 (body, Fontshare) / JetBrains Mono 400/500 (machine voice, sparing). Space Grotesk + Inter are BANNED this round — fallback stacks are Avenir Next/Futura, never Inter.
   · ASSET AUTHORIZATION (recorded per founder decision, 2026-08-07): the two photographic scenes are FOUNDER-APPROVED Kling-generated brand assets — candidate-a-scan-beam (hero, full-bleed, headline in its black half) and candidate-b-chaos-order (mid-page scene break). Photography on this page is licensed brand art, not stock — the "no stock photos" copy-brief rule is satisfied by explicit founder approval of these two files. Optimized to webp ≤80KB each in assets/.
   · layout grammar: scenes, not sections — full-viewport hero → marquee divider → editorial numbered services (asymmetric two-column, NO icon cards) → asset-B scene break with display statement → pipeline as a crafted instrument strip (one thin line, five mono stage names, lit progressively by scroll) → approach as three huge numerals → contact statement scene → Ft5 ghost footer with the drawn K mark.
   · logomark: MARK 02 "Aperture K, light planes" — ADOPTED per founder choice, 2026-08-07 (logo-lab pick; replaces the sliced beam-cut K). Solid ink jamb + two tapered beam planes escaping through a lit slit; geometry ported 1:1 from the lab's 64-grid symbol, lab props mapped to existing tokens (--mk-ink→currentColor/--color-ink, --mk-beam→--color-beam, --mk-hot→--color-beam-hot; no new tokens). Proven in the lab at 16px (favicon data-URI ports that exact version) and 44px (footer lockup); nav runs it at 28px.
   · MOTION OVERRIDE (documented, founder mandate "umas animações bonitas" — expanded past the ≤3-primitive budget): loader wordmark+aperture reveal (slit glows, planes escape) ≤1.2s (session-once, storage-throw-safe, no-JS never shows it) · one orchestrated hero entrance · ONE one-shot IO scene-reveal system · hero parallax (transform-only, ≥60rem, reduced-motion & no-JS OFF — deliberate exception to the parallax ban, subtle 6svh max) · instrument-strip progressive lighting via ONE rAF-written custom property · 32s marquee (pause on hover, static under reduced motion) · micro-hovers on real affordances only. transform/opacity only · named easings · full reduced-motion collapse (page renders complete and static) · native scroll always.
   · texture: one SVG-turbulence film-grain veil (5%) across page AND photographs so image and page share one emulsion. GRADIENT/GLOW NOTE: scrims over photographs are contrast infrastructure (tokens), not decorative gradients; no gradient text, no glow cards anywhere.
   · critique scores (self, round 4): distinctiveness 9 — the two brand assets + sliced-K mark + Clash/Switzer make it unmistakably Klyear's; slop-distance 9 — every named tell from the rejected build is gone (fonts, gradient text, icon cards, boxy diagram); coherence 9 — palette is sampled from the photographs, one beam color everywhere; honesty 10 — copy-brief facts only, no invented metrics; craft 8 — art-directed crops per breakpoint, storage-safe loader, no-JS/reduced-motion complete.
   · Hallmark · pre-emit critique: P5 H5 E4 S5 R4 V5
   · slop-test 2026-08-07: contrast: pass (40–41) · nav: N9 edge-aligned minimal + accent CTA · footer: Ft5 statement ghost · slop: pass (42–45) · honest: pass (46) · chrome: pass (47) · tokens: pass (48) · responsive: pass (49) · icons: pass (30) · mobile: pass (34, 49, 50–57) — verified live at 320/375/414/768 (iframe harness) + 1280/1440/1512, no-JS (sandboxed-iframe proof: 0 hidden elements, loader off, strip fully lit).
   · AUDIT round-5 (final gate, 2026-08-07): text-over-photo contrast re-verified by per-pixel composite sampling (cover-crop + parallax scale + scrim gradients replicated on canvas) at 320/375/414/768/1440 — scrim plateaus extended through every text column (hero narrow: strong→55% + new --scrim-heavy 85%; hero wide: strong→55%; break diagonal: strong→42%), break caption body→ink. Result: zero failing pixels; worst ratios — hero title ≥10.5, key ≥7.7, lede ≥7.3, meta ≥5.4, break statement ≥5.4, caption ≥4.7. Loader storage-throw + no-JS proven in sandboxed iframes; marquee track measured exactly 2× equal lists.
   · documented overrides, all founder-anchored: full-viewport photographic hero (gate 6 — left-biased, content-rich, asset-A grammar is the brief); hero parallax + expanded motion budget (motion.md ban — founder mandate, transform-only, off under reduced-motion/no-JS/<60rem); JetBrains Mono machine-voice role in >2 slots (gate 38 — single consistent role: system readouts, mandated by the brief's instrument-strip + spec-line grammar); hero lede at 36ch (gate 25 — deliberate narrow column inside the asset's black half); marquee pauses on hover, static under reduced motion, aria-hidden with visually-hidden prose equivalent (gate 18).
   · POLISH PASS 2026-08-07 ("um tchan a mais" — premium motion layer on the approved build): masked line-rises on hero + scene statements (clip wrappers, --ease-expo, 70ms stagger) · hero Ken Burns (scale property 1.06↔1.13, 24s alternate — parallax translate keeps full cover because downward drift can only expose the container top, which is always above the viewport once scrolled) + one masked beam shimmer 1.2s after reveal · scene-break clip-path inset wipe (the ONE sanctioned non-transform/opacity animation — mandated by this pass, paint-only, one-shot 900ms) · magnetic WhatsApp pills (pointermove → same rAF engine, ≤6px pull, fine-pointer + no-preference only) + hover sheen · service-row hover theatre (index→beam-hot, title 10px shift, beam hairline draws scaleX — resting design untouched since the index is already beam) · wire ambient pulse every 6s after full light (≥40rem only — mobile stays calm) · 2px scroll-progress hairline (functional, stays under reduce) · ghost-footer parallax drift (has-fx gate) · themed thin scrollbar · honest mono status line (company open for business, founded 2026) · loader beam flash inside the existing ≤1.08s lifecycle (timings untouched). Still ONE rAF engine (pointermove feeds the same scheduler; zero new scroll listeners; all layout reads in measure()), everything else transform/opacity, full reduced-motion collapse (lines visible, wipe off, loops off, magnetics off), no-JS complete and static.
   · PREMIUM AUDIT 2026-08-07 (headless-verified): loader lifecycle ≤1.08s + no repeat · CLS 0.0001 · Ken Burns Δscale 0.0023/3s (imperceptible) · magnetic clamp measured exactly 6.00px, clean spring-back to none · progress 0.0000→1.0000 exact · ghost settles 0.000rem at bottom · 57fps @4x CPU throttle, zero long tasks · reduced-motion fully static incl. mid-session flip both ways · no-JS complete · 0px overflow at 320/375/414/768 · all 5 focusables ring · marquee exactly 2× + pause. ONE FIX: .strip__pulse opacity now gated on --strip-p=1 (is-lit is one-shot but the wire re-dims on upward scroll — the pulse could travel unlit rail; now it fades with the wire).
   · ================== V3 · MOTIONSITES-STUDIED SIGNATURES · 2026-08-07 ==================
   · Experimental parallel build (v2 stays live) — three motion signatures studied from the
     motionsites.ai gallery as CONCEPTS ONLY (no pixels, no content, no metrics copied):
     ① SCAN-BEAM CURSOR SPOTLIGHT (hero) — one composited radial-light disc (~340px, beam
        tokens, low alpha) lerp-follows the cursor through the SAME rAF engine, masked off
        the text column so headline contrast gates never move. Fine pointer ≥60rem only;
        coarse ≥60rem = 12s auto-pan; reduced-motion = static light on the beam; no-JS = absent.
        NOTE on the cursor-follower ban (microinteractions tell #7): this is not a trailing
        dot decoration — it is the page's core metaphor ("the machine reads what it lights"),
        founder-mandated for this study build, excluded from text, absent everywhere motion
        or pointer capability is missing. Recorded as a documented override.
     ② LIVING DOCUMENT RIBBON (scene divider, replaces the v2 marquee) — 18 thin paper
        planes + 12 mono capability words on ONE animating track node (translate3d loop
        under 60rem perspective ⇒ depth parallax for free; the -50% jump is seamless because
        world positions repeat exactly per group). Per-plane bob rides the individual
        `translate` property (composited) ≥40rem only. Glow baked into gradients — zero
        filters/drop-shadows on animating nodes. Reduced motion: static arranged ribbon,
        duplicate group hidden. Honest words only: READ · CLASSIFY · EXTRACT · VALIDATE ·
        ROUTE · CONFIDENCE-SCORED.
     ③ SELF-TYPING TERMINAL MANIFESTO (approach, replaces the ghost-numeral steps) — one
        dark mono panel; a transparent in-flow ghost layer holds the REAL text (space
        reserved ⇒ zero CLS, accessible always), a typed overlay writes itself ~18ms/char
        through the same rAF engine on scene entry, skippable (scrolled past ⇒ instant
        complete), blinking block caret lives only inside the typed command (caret rule
        honoured). Reduced-motion + no-JS: fully typed static block, steady caret.
   · Resolved overlaps: marquee OUT (ribbon replaces), glow/ghost-numeral steps OUT
     (terminal replaces). Everything else from v2 kept verbatim: loader + aperture mark,
     masked line reveals, Ken Burns + parallax (spotlight composes ON TOP), scene-break
     wipe, pinned instrument strip + ambient pulse, magnetic CTA + sheen, progress hairline,
     ghost footer drift, grain, dual-register nav, tokens/fonts/assets, WhatsApp data-todo,
     OG/meta, styled scrollbar. Still ONE rAF engine (spotlight lerp + typing advance ride
     the same frame writer; zero new scroll/pointer listeners), transform/opacity only
     (typing manipulates text content, not layout), named easings, OKLCH tokens only,
     full reduced-motion collapse, no-JS complete, mobile 320/375/414/768 flawless
     (ribbon: fewer planes; spotlight: absent <60rem).
   · V3 FINAL GATE 2026-08-07 (live-audited, three fixes): ① typing sequencer no longer
     insta-completes on normal entry — the scene's is-in fires while the panel is below the
     fold, so the old `vt0 > winH ⇒ finish` skipped the whole signature; the clock now HOLDS
     until the panel shows and only a full scroll-past (panel above viewport) skips.
     ② ribbon words carry a scrim-heavy text halo — depth parallax slides lit planes behind
     words and muted-on-paper fell to ~2.4:1 in those beats. ③ ribbon pauses on hover
     (WCAG 2.2.2 parity the v2 marquee had — a v3 regression). Dead --text-ordinal retired.
     Measured: spotlight mask alpha = 0 at every hero text right-edge ≥60rem (beam key line
     clears mask start by ≥19px; worst white-title graze ~0.29 mask ⇒ ≥9:1, gate is 4.5:1);
     lerp 16%/frame confirmed; CLS 0 through load + typing; 60fps / worst frame 17.6ms over
     the ribbon; strip 1.0000 + one-shot pulse; ghost settles 0.000rem; magnetic ±6px clamp. */

/* ================= base ================= */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { overflow-x: clip; }
body { overflow-x: clip; }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  background: var(--color-void);
  color: var(--color-body);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-style: normal; /* headers are roman — always */
  color: var(--color-ink);
  overflow-wrap: anywhere;
  min-width: 0;
}

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

ul[role="list"], ol[role="list"] { list-style: none; }

a { color: var(--color-beam); text-decoration-thickness: 1px; text-underline-offset: 3px; }

::selection { background: var(--color-beam); color: var(--color-void); }

/* chrome · thin scrollbar in the page's own register (standard properties win in
   Chromium; the webkit rules cover Safari) */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--color-rule-bright) var(--color-void-deep);
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--color-void-deep); }
::-webkit-scrollbar-thumb {
  background: var(--color-rule-bright);
  border-radius: var(--radius-pill);
  border: 2px solid var(--color-void-deep);
}
::-webkit-scrollbar-thumb:hover { background: var(--color-beam); }

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px; /* instant — focus rings never animate */
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.mono-label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.skip {
  position: fixed;
  top: var(--space-md);
  left: -100vw;
  z-index: var(--z-modal);
  background: var(--color-ink);
  color: var(--color-void);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-tag);
  font-family: var(--font-body);
  font-weight: 500;
  white-space: nowrap;
}
.skip:focus-visible { left: var(--space-md); }

/* ================= the drawn mark ================= */

.mark { color: var(--color-ink); flex: none; }

/* ================= film grain · one emulsion over everything ================= */

.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  background-image: var(--grain);
  background-size: 160px 160px;
  opacity: var(--grain-opacity);
}

/* ================= loader · first-visit wordmark moment =================
   Overlay above content that is already fully rendered: without JS it can never show
   (visibility gated on .js.is-loading); the inline bootstrap owns the lifecycle
   (~780ms hold + 300ms fade ≈ 1.08s); reduced-motion removes it entirely. */

.loader {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-void-deep);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 300ms var(--ease-in),
    visibility 0s linear 300ms;
}
.loader::after { /* the veil reaches the loader too — one emulsion */
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--grain);
  background-size: 160px 160px;
  opacity: var(--grain-opacity);
}
.js.is-loading .loader {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: none;
}

.loader__brand {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  color: var(--color-ink);
}
.js.is-loading .loader__brand {
  animation: loader-in 480ms var(--ease-out) both;
}
.loader__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-2xl);
  letter-spacing: -0.02em;
  line-height: 1;
}
/* the aperture moment · the ink jamb arrives with the brand, the slit glows on at
   140–380ms, then the two light planes slide out through it (scaleX from the slit
   mouth + opacity, --ease-expo), finishing at 700ms — the same beat the old beam-draw
   ended on. transform/opacity only, named easings, lifecycle timings untouched. */
.js.is-loading .loader__slit {
  animation: slit-glow 240ms var(--ease-out) 140ms both;
}
.js.is-loading .loader__planes {
  transform-box: fill-box;
  transform-origin: left center; /* the planes' left edge IS the slit mouth (x=31) */
  animation: planes-out 480ms var(--ease-expo) 220ms both;
}
/* the engineered beat · the planes finish escaping at 700ms, a single soft beam-blue
   flash breathes across the overlay at 580–770ms, the lift starts at 780ms. Opacity
   only, inside the existing lifecycle — total time unchanged (~1.08s). */
.js.is-loading .loader::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-beam);
  opacity: 0;
  animation: loader-flash 190ms var(--ease-in-out) 580ms 1 both;
}
@keyframes loader-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@keyframes slit-glow {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes planes-out {
  from { opacity: 0; transform: scaleX(0.12); }
  to   { opacity: 1; transform: none; }
}
@keyframes loader-flash {
  0%   { opacity: 0; }
  45%  { opacity: 0.09; }
  100% { opacity: 0; }
}

/* ================= motion engine ① · the orchestrated hero entrance =================
   CSS animation (not JS-added classes) so the entrance survives an app.js failure.
   --reveal-wait is set by the bootstrap only when the loader runs. Stagger < 500ms. */

.js .reveal {
  opacity: 0;
  animation: reveal-in var(--dur-long) var(--ease-out) forwards;
  animation-delay: calc(var(--reveal-wait, 0ms) + var(--i, 0) * 55ms);
}
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ---- masked line reveals · display type rises out of a clipped line box ----
   .lr is the clip window (padding/margin pair gives descenders headroom at rest);
   .lr__i is the risen line. Hero lines are CSS-animated off .js (survive an app.js
   failure, wait for the loader via --reveal-wait); scene-statement lines ride the
   same one-shot IO system as .r (hidden only under .io, --lg = per-scene lag). */

.lr {
  display: block;
  overflow: clip;
  padding-block: 0.09em;
  margin-block: -0.09em;
}
.lr__i { display: block; }

.js .hero__title .lr__i {
  transform: translateY(115%);
  animation: line-rise var(--dur-reveal) var(--ease-expo) both;
  animation-delay: calc(var(--reveal-wait, 0ms) + var(--l, 0) * 70ms);
}
@keyframes line-rise {
  from { transform: translateY(115%); }
  to   { transform: none; }
}

/* ================= motion engine ② · one-shot scene reveals =================
   Hiding exists only under .io, which app.js adds immediately before observing —
   no-JS pages are simply there. One system, one-shot, never re-fires. */

.io [data-reveal-group] .r {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity var(--dur-long) var(--ease-out),
    transform var(--dur-long) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.io [data-reveal-group].is-in .r {
  opacity: 1;
  transform: none;
}

/* statement lines join the same one-shot trigger as masked rises */
.io [data-reveal-group] .lr__i {
  transform: translateY(115%);
  transition: transform var(--dur-reveal) var(--ease-expo);
  transition-delay: calc(var(--lg, 0ms) + var(--l, 0) * 70ms);
}
.io [data-reveal-group].is-in .lr__i { transform: none; }

/* scene-break photograph unveils bottom-up · clip-path inset wipe — the one sanctioned
   non-transform/opacity animation of the polish pass (paint-only, one-shot, 900ms);
   statement lines follow via their --lg lag. Reduced-motion + no-JS: fully visible. */
.io .break .break__media {
  clip-path: inset(100% 0 0 0);
  transition: clip-path var(--dur-wipe) var(--ease-expo);
}
.io .break.is-in .break__media { clip-path: inset(0 0 0 0); }

/* ================= nav · edge-aligned minimal, frosting after first scroll ================= */

.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: var(--z-sticky);
  background: transparent;
  border-bottom: var(--rule-1) solid transparent;
  transition:
    background-color var(--dur-short) var(--ease-out),
    border-color var(--dur-short) var(--ease-out),
    box-shadow var(--dur-short) var(--ease-out);
}
.nav.is-frost,
html:not(.js) .nav { /* no JS ⇒ no scroll-armed frost — rest frosted so scrolled content never collides with the fixed bar */
  background: var(--frost);
  border-color: var(--color-rule);
  box-shadow: var(--shadow-nav);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.nav__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-gutter);
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  text-decoration: none;
  color: var(--color-ink);
  min-height: 2.75rem;
}
.brand__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-md);
  letter-spacing: -0.01em;
  color: var(--color-ink);
}

.nav__right {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

/* ================= scroll progress · 2px beam hairline =================
   scaleX written by the one rAF engine (functional feedback — stays under reduced
   motion; rests invisible at scaleX(0) without JS). Sits after the header in DOM so
   it paints above the frost at the same named z level. */

.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: var(--z-sticky);
  background: var(--color-beam);
  transform: scaleX(var(--scroll-p, 0));
  transform-origin: left center;
  pointer-events: none;
}

/* ================= buttons · one primary voice, pill (flymedia DNA) ================= */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: 2.75rem;
  padding: 0.7em 1.7em;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--text-base);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--dur-micro) var(--ease-out),
    transform var(--dur-micro) var(--ease-out);
}
.btn--primary {
  background: var(--color-accent-fill);
  color: var(--color-accent-ink);
}
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover { transform: translateY(-1.5px); }
}
.btn--primary:active { transform: translateY(0); }
.btn--sm { font-size: var(--text-sm); padding: 0.55em 1.35em; }
.btn--lg { font-size: var(--text-md); padding: 0.8em 2em; }

/* magnetic CTAs · [data-magnetic] pills lean ≤6px toward the cursor inside a 60px
   proximity field. --mx/--my written by the one rAF engine; .has-mag is armed only on
   fine-pointer + no-preference, so touch never sees any of it. While engaged the pill
   tracks on a micro smoothing; on release it springs back on the long ease-out. */
.has-mag [data-magnetic] {
  transition:
    background-color var(--dur-micro) var(--ease-out),
    transform var(--dur-long) var(--ease-out);
}
.has-mag [data-magnetic]:hover,
.has-mag [data-magnetic].is-mag {
  transform: translate(var(--mx, 0px), var(--my, 0px));
}
.has-mag [data-magnetic].is-mag {
  transition:
    background-color var(--dur-micro) var(--ease-out),
    transform var(--dur-micro) linear; /* continuous pointer tracking, not an entrance */
}

/* CTA sheen · one diagonal light band per hover-enter, clipped by the pill */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .btn--primary { overflow: clip; }
  .btn--primary::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 35%, var(--sheen) 50%, transparent 65%);
    transform: translateX(-120%);
  }
  .btn--primary:hover::after { animation: btn-sheen var(--dur-sheen) var(--ease-out) 1; }
}
@keyframes btn-sheen {
  to { transform: translateX(120%); }
}

/* ================= SCENE 01 · hero — asset A, headline in the black half ================= */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.hero__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 68% 50%; /* art-directed: keep the corridor + beam in frame on narrow crops */
}
@media (min-width: 60rem) {
  .hero__img { object-position: 50% 50%; }
  /* parallax — armed by JS only when motion is welcome; transform-only, 6svh max drift.
     The old static scale(1.12) headroom moved into the Ken Burns scale below: a downward
     drift can only ever expose the container's TOP edge, and that edge sits above the
     viewport once any scrolling has happened (gap ≤ 6% of y < y), so scale ≥ 1 suffices. */
  .has-fx .hero__img {
    transform: translate3d(0, var(--hero-shift, 0px), 0);
    will-change: transform;
  }
}

@media (prefers-reduced-motion: no-preference) {
  /* the living hero · very slow Ken Burns on the independent scale property —
     composes with the parallax translate, compositor-only, all widths */
  .hero__img { animation: hero-kb var(--dur-kb) var(--ease-in-out) infinite alternate; }

  /* one beam shimmer · a masked light band sweeps the photo corridor once, ~1.2s after
     the reveal settles. Masked off the text column entirely (never touches contrast);
     transform + opacity only. */
  .hero__media::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 38%, var(--beam-veil) 50%, transparent 62%);
    -webkit-mask-image: linear-gradient(90deg, transparent 45%, #000 70%);
    mask-image: linear-gradient(90deg, transparent 45%, #000 70%);
    opacity: 0;
    transform: translateX(-70%);
    animation: hero-shimmer var(--dur-shimmer) var(--ease-in-out) calc(var(--reveal-wait, 0ms) + 1200ms) 1;
  }
}
@keyframes hero-kb {
  from { scale: 1.06; }
  to   { scale: 1.13; }
}
@keyframes hero-shimmer {
  0%   { transform: translateX(-70%); opacity: 0; }
  18%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: translateX(70%); opacity: 0; }
}
.hero__scrim {
  position: absolute;
  inset: 0;
  /* narrow crops: copy spans nearly the full width, so the whole frame carries a deep
     veil — the beam ghosts through the grain instead of competing with the lede
     (pixel-sampled at 320/375/414/768: every body glyph ≥4.5:1 with ~30% margin) */
  background:
    linear-gradient(90deg, var(--scrim-strong) 0%, var(--scrim-strong) 55%, var(--scrim-heavy) 85%, var(--scrim-heavy) 100%),
    linear-gradient(180deg, var(--scrim-none) 55%, var(--scrim-edge) 100%);
}
@media (min-width: 60rem) {
  .hero__scrim {
    /* the black wall owns the full text column (≈55%); the corridor + beam reveal
       happens right of it — sampled: every glyph ≥3:1 against the composited frame */
    background:
      linear-gradient(90deg, var(--scrim-strong) 0%, var(--scrim-strong) 55%, var(--scrim-mid) 72%, var(--scrim-none) 88%),
      linear-gradient(180deg, var(--scrim-none) 55%, var(--scrim-edge) 100%);
  }
}

/* v3 signature ① · scan-beam cursor spotlight — "the machine reads what it lights".
   One composited disc (radial gradient painted once, moved with transform only) inside the
   already-clipped hero media. The container mask excludes the text column entirely, so the
   headline/lede contrast plateaus are untouched at any cursor position. Gates:
   <60rem or no-JS → absent · fine pointer + motion → engine-lerped tracking (--spot-x/y)
   · coarse pointer + motion → slow 12s auto-pan · reduced motion → static light, resting
   where the beam falls in the photograph (the var() fallbacks below ARE that rest pose). */
.hero__spot {
  position: absolute;
  inset: 0;
  display: none;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 52%, #000 68%);
  mask-image: linear-gradient(90deg, transparent 52%, #000 68%);
}
@media (min-width: 60rem) {
  .js .hero__spot { display: block; }
}
.hero__spot-light {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--spot-d);
  height: var(--spot-d);
  margin-top: calc(var(--spot-d) / -2);
  margin-left: calc(var(--spot-d) / -2);
  border-radius: 50%;
  background: radial-gradient(circle, var(--spot-core) 0%, var(--spot-mid) 40%, transparent 68%);
  transform: translate3d(var(--spot-x, 72vw), var(--spot-y, 42svh), 0);
}
.has-spot .hero__spot-light { will-change: transform; } /* armed only while it can move */
@media (min-width: 60rem) and (pointer: coarse) and (prefers-reduced-motion: no-preference) {
  /* touch degradation · the reading light sweeps the corridor on one slow 12s circuit */
  .js .hero__spot-light { animation: spot-pan var(--dur-spot) var(--ease-in-out) infinite; }
}
@keyframes spot-pan {
  0%, 100% { transform: translate3d(58vw, 30svh, 0); }
  50%      { transform: translate3d(86vw, 60svh, 0); }
}

.hero__inner {
  position: relative;
  z-index: var(--z-base);
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding: calc(var(--nav-h) + var(--space-2xl)) var(--page-gutter) var(--space-3xl);
}

.hero__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-display);
  line-height: 1.04;
  letter-spacing: -0.015em;
  max-width: 12ch;
}
.hero__key { color: var(--color-beam); }

.hero__lede {
  margin-top: var(--space-lg);
  font-size: var(--text-md);
  max-width: 36ch;
  color: var(--color-body);
}
.hero__cta { margin-top: var(--space-xl); }
.hero__meta { margin-top: var(--space-xl); display: block; }

/* ================= scene divider · the living document ribbon (v3 signature ②) =================
   The chaos-to-order stream made literal: thin paper planes drifting through a shallow 3D
   scene. ONE animating node — the track — loops translate3d(-50%) under perspective, which
   yields true depth parallax for free (deep planes cross the screen slower than near ones).
   The -50% jump is seamless: the two groups are exact copies, so world positions — and
   therefore their perspective projections — repeat precisely. Per-plane bob animates the
   individual `translate` property (composes before `transform`, compositor-only) and runs
   ≥40rem only. All glow is baked into gradients; no filter ever touches an animating node. */

.ribbon-band {
  border-block: var(--rule-1) solid var(--color-rule);
  background:
    radial-gradient(58% 130% at 50% 50%, var(--ribbon-glow), transparent 72%),
    var(--color-void);
}
.ribbon {
  overflow: hidden;
  height: 7.5rem; /* mobile-first; opens to --ribbon-h ≥40rem */
  perspective: var(--ribbon-persp);
  perspective-origin: 50% 50%;
}
@media (min-width: 40rem) {
  .ribbon { height: var(--ribbon-h); }
}
.ribbon__track {
  display: flex;
  width: max-content;
  height: 100%;
  transform-style: preserve-3d;
}
@media (prefers-reduced-motion: no-preference) {
  .ribbon__track { animation: ribbon-drift var(--dur-ribbon) linear infinite; } /* linear is legal on ticking loops */
  /* WCAG 2.2.2 parity with the v2 marquee — hovering the stream rests it */
  .ribbon:hover .ribbon__track,
  .ribbon:hover .ribbon__plane { animation-play-state: paused; }
}
@keyframes ribbon-drift {
  to { transform: translate3d(-50%, 0, 0); }
}
.ribbon__group {
  position: relative;
  width: 76rem;
  height: 100%;
  flex: none;
  transform-style: preserve-3d;
}

.ribbon__plane {
  position: absolute;
  left: var(--px, 0rem);
  top: 50%;
  width: 2.6rem;
  height: 3.4rem;
  margin-top: -1.7rem;
  padding: 0.5rem 0.4rem; /* insets the ruled-lines layer via content-box below */
  border-radius: 2px;
  opacity: var(--po, 0.85);
  background:
    linear-gradient(90deg, var(--paper-lit) 0, transparent 16%),
    repeating-linear-gradient(180deg, transparent 0 0.45rem, var(--paper-rule) 0.45rem calc(0.45rem + 1px)),
    linear-gradient(160deg, var(--paper) 0%, var(--paper-dim) 58%, var(--paper-deep) 100%);
  background-origin: border-box, content-box, border-box;
  background-clip: border-box, content-box, border-box;
  transform:
    translate3d(0, var(--py, 0rem), var(--pz, 0rem))
    rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) rotateZ(var(--rz, 0deg));
}
.ribbon__plane--wide { display: none; } /* the narrow ribbon runs 12 planes, not 18 */
@media (min-width: 40rem) {
  .ribbon__plane--wide { display: block; }
}
@media (min-width: 40rem) and (prefers-reduced-motion: no-preference) {
  /* the gentle life of the stream · individual `translate` property, so it composes with
     the static 3D attitude above without touching it — compositor-only, long staggered
     cycles, mobile stays perfectly calm */
  .ribbon__plane { animation: plane-float var(--fd, 8s) var(--ease-in-out) var(--fdel, 0s) infinite alternate; }
}
@keyframes plane-float {
  from { translate: 0 -0.22rem; }
  to   { translate: 0  0.22rem; }
}

.ribbon__word {
  position: absolute;
  left: var(--wx, 0rem);
  top: 50%;
  margin-top: -0.5em;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
  white-space: nowrap;
  /* the dark halo keeps a word legible for the beats when depth parallax slides a lit
     paper plane behind it — same token as the photo veils, no new colour */
  text-shadow: 0 1px 2px var(--scrim-heavy), 0 0 6px var(--scrim-heavy);
  transform: translate3d(0, var(--wy, 0rem), var(--wz, 0rem));
}

/* ================= scene chrome ================= */

.scene__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-gutter);
}

.head { margin-bottom: var(--space-2xl); }
.head__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-head);
  line-height: 1.06;
  letter-spacing: -0.01em;
}
.head__lede {
  margin-top: var(--space-sm);
  color: var(--color-muted);
  max-width: 50ch;
}

/* ================= SCENE 02 · services — editorial numbered list ================= */

.scene--services { padding-block: var(--space-3xl) var(--space-4xl); }

.svc { border-top: var(--rule-1) solid var(--color-rule); }

.svc__row {
  position: relative; /* anchors the drawn hairline */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-sm) var(--space-xl);
  padding-block: var(--space-xl);
  border-bottom: var(--rule-1) solid var(--color-rule);
}

/* hover theatre · fine-pointer only, purely decorative (nothing is revealed by hover,
   so there is no hover-only information). The resting design is untouched — the index
   is already beam by design, so hover pushes it to the beam's white-hot core. */
.svc__row::after { /* the row hairline, redrawn in beam light */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: var(--rule-1);
  background: var(--color-beam);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
}
@media (hover: hover) and (pointer: fine) {
  .svc__num, .svc__desc { transition: color var(--dur-short) var(--ease-out); }
  .svc__row:hover .svc__num { color: var(--color-beam-hot); }
  .svc__row:hover .svc__desc { color: var(--color-ink); }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .svc__title { transition: transform var(--dur-short) var(--ease-expo); }
  .svc__row:hover .svc__title { transform: translateX(10px); }
  .svc__row::after {
    transition:
      transform var(--dur-long) var(--ease-expo),
      opacity var(--dur-short) var(--ease-out);
  }
  .svc__row:hover::after { transform: scaleX(1); opacity: 0.55; }
}
/* keyboard equivalent · instant, colour-only (rows hold no focusables today;
   this arms the same brightening the moment one exists) */
.svc__row:focus-within .svc__num { color: var(--color-beam-hot); }
.svc__row:focus-within .svc__desc { color: var(--color-ink); }
@media (min-width: 60rem) {
  .svc__row {
    grid-template-columns: 5.5rem minmax(0, 1fr);
    padding-block: var(--space-2xl);
  }
  .svc__main {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: var(--space-md) var(--space-2xl);
    align-items: start;
  }
}

.svc__num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-sm);
  letter-spacing: 0.1em;
  color: var(--color-beam);
  padding-top: 0.55em; /* optically aligns with the display title's cap height */
}

.svc__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-service);
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.svc__flag {
  display: inline-block;
  margin-left: var(--space-md);
  padding: 0.3em 0.75em;
  border: var(--rule-1) solid var(--color-rule-bright);
  border-radius: var(--radius-tag);
  color: var(--color-beam);
  vertical-align: 0.45em;
  white-space: nowrap;
}
.svc__desc {
  max-width: 52ch;
  color: var(--color-body);
}

/* ================= SCENE 03 · break — asset B, statement over its darkest region ================= */

.break {
  position: relative;
  min-height: clamp(22rem, 74svh, 54rem);
  display: flex;
  align-items: flex-start;
  overflow: hidden;
}
@media (min-width: 60rem) {
  .break { min-height: clamp(26rem, 88svh, 54rem); }
}
.break__media { position: absolute; inset: 0; }
.break__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 74% 50%; /* art-directed: the ordered wall stays in frame on narrow crops */
}
@media (min-width: 60rem) {
  .break__img { object-position: 50% 50%; }
}
.break__scrim {
  position: absolute;
  inset: 0;
  /* the diagonal shadow holds through the statement/caption zone (pixel-sampled) —
     chaos sinks into shadow top-left, the ordered wall keeps the light bottom-right */
  background:
    linear-gradient(180deg, var(--scrim-edge) 0%, var(--scrim-none) 16%, var(--scrim-none) 84%, var(--scrim-edge) 100%),
    linear-gradient(125deg, var(--scrim-strong) 0%, var(--scrim-strong) 42%, var(--scrim-mid) 62%, var(--scrim-none) 82%);
}
.break__inner {
  position: relative;
  z-index: var(--z-base);
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--space-3xl) var(--page-gutter) var(--space-3xl);
}
.break__statement {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-statement);
  line-height: 1.05;
  letter-spacing: -0.01em;
  max-width: 14ch;
}
.break__caption { display: block; margin-top: var(--space-lg); color: var(--color-ink); } /* ink, not body — small text over a photograph needs the full 4.5:1 margin */

/* ================= the pipeline · crafted instrument strip =================
   One thin line, five mono stage names, lit progressively as it enters the viewport
   (--strip-p written by the rAF engine, 0→1). Without JS or with reduced motion the
   instrument renders fully lit — .has-strip is the only thing that arms the draw. */

.strip-band { padding-block: var(--space-3xl); }

.has-strip .strip { --strip-p: 0; }

.strip__caption {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}
.strip__caption h2 { color: var(--color-muted); font-style: normal; }
.strip__caption-right { display: none; }
@media (min-width: 40rem) {
  .strip__caption-right { display: inline; }
}

/* mobile-first: vertical instrument (line on the left, stages descending) */
.strip__instrument {
  display: flex;
  gap: var(--space-lg);
}
.strip__rail {
  position: relative;
  width: var(--rule-1);
  align-self: stretch;
  flex: none;
}
.strip__wire {
  position: absolute;
  inset: 0;
  background: var(--color-rule);
}
.strip__wire--lit {
  background: var(--color-beam);
  transform: scaleY(var(--strip-p, 1));
  transform-origin: center top;
}
.strip__stages {
  list-style: none;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-lg);
}
.strip__stage {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  opacity: calc(0.3 + 0.7 * clamp(0, (var(--strip-p, 1) - var(--t, 0)) * 6, 1));
}
.strip__tick {
  width: 12px;
  height: var(--rule-1);
  background: var(--color-beam-hot);
  flex: none;
}
.strip__name {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink);
}

@media (min-width: 40rem) {
  /* horizontal instrument: names above their ticks, ticks descend to the wire */
  .strip__instrument {
    display: block;
  }
  .strip__stages {
    flex-direction: row;
    justify-content: space-between;
    gap: var(--space-md);
    margin-bottom: var(--space-sm);
  }
  .strip__stage {
    flex-direction: column;
    align-items: center;
    gap: var(--space-xs);
  }
  .strip__tick {
    width: var(--rule-1);
    height: 12px;
  }
  .strip__rail {
    width: auto;
    height: var(--rule-1);
    align-self: auto;
  }
  .strip__wire--lit {
    transform: scaleX(var(--strip-p, 1));
    transform-origin: left center;
  }
}

/* ambient pulse · once the instrument is fully lit (.is-lit, set by the engine when
   --strip-p reaches 1 — so it can never precede full light), a small highlight travels
   the wire every 6s. Horizontal instrument only (≥40rem) — mobile stays calm; off under
   reduced motion (media gate + .has-strip never arming + explicit collapse below). */
.strip__pulse {
  position: absolute;
  inset: 0;
  overflow: clip;
  display: none;
  pointer-events: none;
  /* the pulse may only ever SHOW at full light — .is-lit is one-shot, but --strip-p
     re-dims when the user scrolls back up; this gate fades the pulse with the wire so
     a highlight can never travel unlit rail (1 at p=1, 0 by p≤0.99) */
  opacity: clamp(0, (var(--strip-p, 1) - 0.99) * 100, 1);
  transition: opacity var(--dur-short) var(--ease-out);
}
@media (min-width: 40rem) and (prefers-reduced-motion: no-preference) {
  .strip__pulse { display: block; }
  .strip__pulse::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 18%;
    background: linear-gradient(90deg, transparent, var(--beam-pulse), transparent);
    opacity: 0;
    transform: translateX(-100%);
  }
  .is-lit .strip__pulse::before {
    /* linear travel is legal here — a ticking ambient loop, like the marquee */
    animation: strip-pulse var(--dur-pulse) linear infinite;
  }
}
@keyframes strip-pulse {
  0%        { transform: translateX(-100%); opacity: 0; }
  4%        { opacity: 1; }
  24%       { opacity: 1; }
  28%, 100% { transform: translateX(560%); opacity: 0; }
}

.strip__note {
  margin-top: var(--space-xl);
  color: var(--color-muted);
  font-size: var(--text-sm);
  max-width: 60ch;
}

/* ================= SCENE 04 · approach — the self-typing terminal manifesto (v3 signature ③) ====
   One dark mono panel in a hairline frame — deliberately bare (no window dots: the copy
   brief bans fake browser/phone chrome). Two layers per line: the GHOST is the real text,
   in flow, in the accessibility tree — under .has-type it turns transparent but keeps
   reserving exact space (identical structure + font ⇒ identical wrapping ⇒ zero CLS).
   The TYPED overlay sits absolutely on top and is written character by character by the
   one rAF engine. No-JS / reduced-motion: .has-type never arms, the ghost simply IS the
   block, fully typed, with the steady static caret. */

.scene--approach { padding-block: var(--space-4xl) var(--space-3xl); }

.term {
  max-width: 56rem;
  border: var(--rule-1) solid var(--color-rule-bright);
  border-radius: var(--radius-tag);
  background: var(--color-term);
  padding: var(--space-lg);
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--text-sm);
  line-height: 1.85;
  color: var(--color-body);
}
@media (min-width: 40rem) {
  .term {
    padding: var(--space-xl) var(--space-2xl);
    font-size: var(--text-base);
  }
}
.term__line {
  position: relative;
  display: block;
  overflow-wrap: anywhere;
  min-width: 0;
}
.term__line + .term__line { margin-top: 0.55em; }
.term__line--gap { margin-top: 1.5em; } /* the beat between command and output */

.term__ghost { display: inline; }
.term__typed {
  position: absolute;
  inset: 0;
  display: none;
}
.js.has-type .term__ghost,
.js.has-type .term__ghost span { color: transparent; }
.js.has-type .term__caret--static { display: none; }
.js.has-type .term__typed { display: block; }

.t-prompt { color: var(--color-beam); }
.t-cmd    { color: var(--color-ink); }
.t-num    { color: var(--color-beam); }
.t-key    { color: var(--color-ink); font-weight: 500; letter-spacing: 0.06em; }

.term__caret {
  display: inline-block;
  width: 0.55em;
  height: 1em;
  margin-left: 0.12em;
  vertical-align: -0.12em;
  background: var(--color-beam);
}
@media (prefers-reduced-motion: no-preference) {
  /* the caret blinks only inside the typed command — never as standalone decoration */
  .js.has-type .term__caret { animation: caret-blink var(--dur-caret) steps(1) infinite; }
}
@keyframes caret-blink {
  50% { opacity: 0; }
}

.scene__coda {
  margin-top: var(--space-3xl);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--text-lg);
  line-height: 1.35;
  color: var(--color-body);
  max-width: 34ch;
}
.scene__coda em { font-style: normal; color: var(--color-beam); } /* Clash has no italic — emphasis by colour, never synthesized */

/* ================= SCENE 05 · contact ================= */

.scene--contact {
  padding-block: var(--space-4xl);
  border-top: var(--rule-1) solid var(--color-rule);
}

.contact__beam {
  display: block;
  width: 64px;
  height: 2px;
  background: var(--color-beam);
  transform: rotate(-8deg);
  transform-origin: left center;
  margin-bottom: var(--space-xl);
}
.contact__status {
  display: block;
  margin-bottom: var(--space-lg);
  color: var(--color-beam); /* honest system readout — the machine voice in beam light */
}
.contact__statement {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-statement);
  line-height: 1.05;
  letter-spacing: -0.01em;
  max-width: 16ch;
}
.contact__body {
  margin-top: var(--space-lg);
  max-width: 48ch;
}
.contact__cta { margin-top: var(--space-xl); }
.contact__spec { display: block; margin-top: var(--space-xl); }

/* ================= footer · Ft5 ghost statement + the mark at display size ================= */

.footer {
  border-top: var(--rule-1) solid var(--color-rule);
  overflow: hidden;
}
.footer__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--space-2xl) var(--page-gutter) var(--space-xl);
}
.footer__ghost {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-ghost);
  line-height: 0.9;
  letter-spacing: -0.02em;
  color: var(--color-ghost);
  margin-left: -0.04em; /* optical left alignment */
  overflow-wrap: anywhere;
  min-width: 0;
}
/* ghost drift · the wordmark rises the last 2.5rem slower than the page as you arrive
   (--ghost-shift written by the one rAF engine; settles to 0 exactly at page bottom).
   has-fx gate = ≥60rem + no-preference + JS; everywhere else it simply stands still. */
.has-fx .footer__ghost {
  transform: translateY(var(--ghost-shift, 0rem));
}
.footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-top: var(--space-2xl);
}
.footer__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  color: var(--color-ink);
}
.footer__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
  letter-spacing: -0.01em;
  color: var(--color-ink);
}
.footer__tagline { color: var(--color-muted); }
.footer__copyright {
  margin-top: var(--space-2xl);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ================= reduced motion · the page renders complete and static =================
   Loader never shows, the entrance and scene reveals render final-state, the ribbon is a
   static arranged single group, the spotlight is a fixed soft light on the beam, the
   terminal is fully typed with a steady caret, the strip is fully lit, parallax never
   arms (JS gate + CSS guard). */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 150ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 150ms !important;
  }
  html { scroll-behavior: auto; }
  .loader { display: none; }
  .js .reveal {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .io [data-reveal-group] .r {
    transition: none;
    opacity: 1;
    transform: none;
  }
  /* polish pass collapse · lines standing, wipe off, loops off, theatre still */
  .js .hero__title .lr__i,
  .io [data-reveal-group] .lr__i {
    animation: none;
    transition: none;
    transform: none;
  }
  .io .break .break__media { transition: none; clip-path: none; }
  .hero__img { animation: none !important; scale: none; }
  .hero__media::after,
  .strip__pulse::before,
  .btn--primary::after { animation: none !important; }
  .has-mag [data-magnetic] { transition: none; transform: none; }
  .has-fx .footer__ghost { transform: none; }
  .svc__row:hover .svc__title { transform: none; }
  .svc__row:hover::after { transform: scaleX(0); opacity: 0; }
  /* v3 collapses · ribbon stands arranged, spotlight rests on the beam, caret steady */
  .ribbon__track { animation: none; }
  .ribbon__plane { animation: none !important; translate: none; }
  .ribbon__group[data-dup] { display: none; }
  .hero__spot-light { animation: none !important; } /* falls back to the static rest pose */
  .term__caret { animation: none !important; }
  .has-strip .strip { --strip-p: 1; }
  .has-fx .hero__img { transform: none; will-change: auto; }
}
