/* ============================================================
   MILOVISION - landing page v1 (scrolly prototype)
   Built to 06_PROJECT/MV Website.pdf (the final design).
   Image-sequence scrolly today; the same stage scrubs the
   rendered video tomorrow (see js/main.js CONFIG.source).

   TYPE: fluid/proportional (clamp), so it stays balanced across
   viewports rather than fixed px. Field Gothic No.84 DemiXWide for the
   display + giant wordmark; No.61 Light (FieldGothicTEST-No.61) for every
   light role (nav / subnav / notes / subtitle); No.51 Light Compact is
   the fallback; No.64 Demi for the small caps payoff.
   ============================================================ */

:root {
  --green: #1a4334;
  --petrol: #1a353a;   /* page/frame tone around the boxed video (Tuan's ref) */
  --green-deep: #143529;
  --green-ink: #0e2b21;
  --orange: #fa5f14; /* brand ember orange (Tuan tried #e8491f Jul 22, reverted) */
  --paper: #f0eae2;
  --paper-dim: rgba(240, 234, 226, 0.42);
  --paper-soft: rgba(240, 234, 226, 0.78);

  /* "light" = No.61 Light (spec); No.51 Light Compact as fallback */
  --font-light: "Field Gothic No61", "Field Gothic No51", "Arial", sans-serif;
  --font-demi: "Field Gothic Demi", "Arial Narrow", sans-serif;
  --font-wide: "Field Gothic Wide", "Arial Narrow", sans-serif;

  --nav-h: 84px;
  /* the whole nav row (logo + links + lens) drops by 3mm - the lens mark
     was crowding the top edge and needed air (Tuan, Jul 29). One offset
     on .nav moves all three together. */
  --nav-top: 11.34px;           /* = 3mm */
  /* PAGE MARGIN - fluid (Jul 29). A fixed 56px held its proportion only
     around 1300px: at 1920 it was 2.9% of the width, on a 2560 display
     ~2%, so the type crept toward the edges the bigger the screen got.
     The AE comp uses 101px @1920 (5.26%). This clamp keeps 56px on
     laptops (≤1333px - the layouts already approved don't move at all)
     and lets the page breathe on big screens: 1920 → 81px, 2286+ → 96px.
     Every flush-left/right formula reads var(--edge), so this value is
     safe to retune in one place. */
  --edge: clamp(56px, 4.2vw, 96px);
  /* nav item gap - measured 57px @1920 in the AE comp
     (reference_lens_element.jpg), scales down proportionally */
  --nav-gap: clamp(20px, 2.97vw, 57px);
  /* lens mark: 41px @1920 in the comp; the gap to CONTACT is 1.8× the
     item gap there - enough that it reads as a brand mark, not a 7th
     menu item */
  --lens-w: clamp(28px, 2.14vw, 41px);
  --lens-gap: calc(var(--nav-gap) * 1.8);
  --logo-w: 132px;              /* nav__logo width - TIGHT svg now (glyph edge = box
                                    edge) so its M sits exactly on the --edge line,
                                    flush with the giant wordmark; 132px renders the
                                    glyphs at the same size the padded 168px box did */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------- fonts ---------- */
@font-face {
  font-family: "Field Gothic No61";
  src: url("../assets/fonts/FieldGothic-No61-Light.woff") format("woff"),
       url("../assets/fonts/FieldGothic-No61-Light.otf") format("opentype");
  font-weight: 300;
  font-display: block;
}
@font-face {
  font-family: "Field Gothic No51";
  src: url("../assets/fonts/FieldGothic-No51-LightCompact.otf") format("opentype");
  font-weight: 300;
  font-display: block;
}
@font-face {
  font-family: "Field Gothic Demi";
  src: url("../assets/fonts/FieldGothic-No64-Demi.otf") format("opentype");
  font-weight: 600;
  font-display: block;
}
@font-face {
  font-family: "Field Gothic Wide";
  src: url("../assets/fonts/FieldGothic-No84-DemiXWide.otf") format("opentype");
  font-weight: 600;
  font-display: block;
}
@font-face {
  font-family: "Badger Valley";
  src: url("../assets/fonts/BadgerValley-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: block;
}

/* ---------- reset / base ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

/* reserve the scrollbar gutter from the very first paint - otherwise
   the scrollbar pops in when the intro unlocks scrolling and the whole
   layout shifts by its width (the "mini jump", Tuan Jul 22) */
html {
  scrollbar-gutter: stable;
  background: #1a353a; /* = --petrol, matches theme-color + revealed page */
}

body {
  background: var(--green);
  color: var(--paper);
  font-family: var(--font-light);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
body.is-locked { overflow: hidden; }

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
::selection { background: var(--orange); color: var(--green-ink); }

/* ============================================================
   LOADER
   ============================================================ */
.loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--green);
  transition: opacity 0.9s var(--ease-out), visibility 0.9s;
}
.loader.is-done { opacity: 0; visibility: hidden; }

/* riffle reveal (per Tuan's AE comp): 16 equal bars, anchor far left,
   width 100% → 0% over 20f/333ms, staggered 2f/33.3ms right→left -
   driven as a JS clip-path polygon on .loader (main.js playRiffle);
   SVG <mask> on HTML content doesn't animate in Safari. */

/* LOADING % - exact nav-link type (size + tracking), orange, sitting
   on the nav row so it occupies CONTACT's spot until the reveal */
.loader__status {
  position: absolute;
  top: var(--nav-top); /* same line as the nav row */
  right: var(--edge);
  height: var(--nav-h);
  display: flex;
  align-items: center;
  font-family: var(--font-light);
  font-size: clamp(12px, 0.92vw, 13px); /* = .nav__links a (keep in sync!) */
  letter-spacing: 0.14em;               /* = .nav__links a */
  color: var(--orange);
}

/* giant real wordmark - tight-cropped SVG, flush LEFT/RIGHT to the
   page grid (--edge, same lines as nav logo + CONTACT) with the same
   breathing room at the bottom (Tuan, Jul 22 - was full-bleed) */
.wordmark-giant {
  position: absolute;
  bottom: var(--edge);
  left: var(--edge);
  right: var(--edge);
  user-select: none;
  pointer-events: none;
}
.wordmark-giant img { width: 100%; height: auto; display: block; }

/* ============================================================
   SCROLL PROGRESS RAIL (left edge, per Part1-3 reference)
   orange fill grows top→down with page progress over a petrol
   track; ~6px at the reference's 1920 width
   ============================================================ */
.progress {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 6px;
  z-index: 30; /* above the stage, below nav + loader */
  background: var(--petrol);
  pointer-events: none;
}
.progress__fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 0%;
  background: var(--orange);
}

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed;
  top: var(--nav-top); left: 0; right: 0;
  /* ABOVE the page veil (z90): the nav is identical on every page, so
     during a page hop it stays visibly in place - logo left, links,
     lens right - and only the active link's ember changes (Tuan).
     The landing loader (z100) still owns the boot. */
  z-index: 95;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--edge);
}
/* the wordmark is painted via CSS MASK (like the lens): the svg only
   shapes it, background-color colors it - paper on film/green pages,
   ink on paper pages, paper again while the mobile menu is open */
/* INLINE svg + currentColor, no mask: iOS rasterizes css masks with
   its own rounding and the mark drifted right on real iPhones (28.0,
   then 26.0 after left-pinning - ink-scanned from Tuan's screenshots).
   An inline path renders through the standard svg pipeline, flush by
   construction, and recolors via `color`. */
.nav__logo {
  position: relative; /* anchors the invisible hit-target extension */
  display: block;
  width: var(--logo-w);
  color: var(--paper);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out),
              color 0.3s var(--ease-out);
}
.nav__logo svg { width: 100%; height: auto; display: block; }
/* HITBOX ONLY (Tuan, Aug 6): the wordmark renders ~11px tall - clicks
   that barely miss it should still land. The pseudo extends the
   clickable area to ~50px height without moving a pixel visually. */
.nav__logo::before {
  content: "";
  position: absolute;
  inset: -20px -16px;
}
body.show-logo .nav__logo { opacity: 1; transform: none; }

/* links + lens travel together as one right-hand cluster, so the lens
   holds the grid line and the links sit inboard of it */
.nav__right {
  display: flex;
  align-items: center;
  gap: var(--lens-gap);
}

.nav__links {
  display: flex;
  gap: var(--nav-gap);
}
.nav__links a {
  font-family: var(--font-light);
  /* cap 13px (was 15, Tuan Aug 6): the logo is FIXED 132px while the
     links grow with vw - at 15px their cap height equalled the
     wordmark on 16" screens and the menu outweighed the brand.
     13px keeps laptops (<=1333px) pixel-identical. */
  font-size: clamp(12px, 0.92vw, 13px);
  letter-spacing: 0.14em;
  color: var(--paper);
  opacity: 0.9;
  transition: opacity 0.25s, color 0.25s;
  /* no halo/shadow on the nav - purist, per Tuan (Jul 29) */
}
.nav__links a:hover { opacity: 1; }
.nav__links a.is-active { color: var(--orange); opacity: 1; }

/* lens brand mark, right end of the nav row. Arrives TOGETHER with the
   corner wordmark (body.show-logo) so both brand marks complete the
   frame in one move at the wordmark handoff. */
/* the lens mark is painted via CSS MASK (the svg only shapes it), so
   its color is pure CSS: paper on green pages, deep green on paper
   pages, EMBER on hover - and it links home (Tuan, Aug 6) */
.nav__lens {
  display: block;
  width: var(--lens-w);
  flex: none;
  color: var(--paper);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out),
              color 0.3s var(--ease-out);
  pointer-events: none; /* released once the mark is revealed */
}
.nav__lens svg { width: 100%; height: auto; display: block; }
.nav__lens:hover,
.nav__lens:focus-visible { color: var(--orange); }
body.show-logo .nav__lens { opacity: 1; transform: none; pointer-events: auto; }

/* ============================================================
   SCROLLY STAGE
   ============================================================ */
.stage {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  /* the frame around the boxed video is PETROL, not brand green
     (measured #1a343a in MV_Website_Reference_layout_green) */
  background: var(--petrol);
  /* copy sizes/positions use cqw: same width basis as the fixed nav
     (layout viewport WITHOUT scrollbar) - vw counts the scrollbar and
     can never align exactly with the nav's CONTACT edge */
  container-type: inline-size;
}
.stage__layer { position: absolute; inset: 0; opacity: 0; }
.stage__layer img,
.stage__layer video {
  width: 100%; height: 100%;
  object-fit: cover;
}
.stage::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(10, 26, 20, 0.30) 0%, rgba(10, 26, 20, 0) 20%),
    linear-gradient(0deg, rgba(10, 26, 20, 0.20) 0%, rgba(10, 26, 20, 0) 16%);
}

/* ---------- copy layers ---------- */
.copy {
  position: absolute;
  z-index: 5;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out);
  will-change: opacity, transform;
  pointer-events: none;
}
.copy.is-visible { opacity: 1; transform: translateY(0); }

/* story block (video scrolly, per MV_Website_Reference.mp4):
   left edge ≈ 36vw, block anchored in the lower third; light No.61
   eyebrow + two giant No.84 caps lines, second line ember orange.
   Both story blocks share the same anchor and swap via .is-visible. */
/* PROPORTIONAL type, aligned to the nav grid: sizes/positions in cqw
   (basis = .stage width = nav width) so the type scales continuously
   AND respects the page's alignment lines. nowrap: lines never rewrap,
   only the authored <br> breaks.
   FLUSH-RIGHT RULE: the widest line ("A CLEAR VISION", measured
   width = 10.714 × font-size at letter-spacing 0) must END on the page's
   right grid line at 100cqw − var(--edge). With left = 36cqw:
     36cqw + 10.714·F = 100cqw − edge
     F = (64cqw − edge) / 10.714
   (−2px rounding guard so it never pokes past the line)
   Written against var(--edge), NOT a hard-coded 56px: the page margin
   can now be changed in ONE place and every flush line follows (Jul 29). */
.copy--story {
  left: 36cqw;
  bottom: 11vh; /* was 8vh - lifted so the fixed footer breathes (Aug 6) */
}

/* left-anchored story block (Part1-3 ref: WHO THINK BEYOND THEIR ROLES. ·
   AND DEEPLY HUMAN. · MOVE THROUGH THE WORLD.) - same type as
   .copy--story but the lines start ON the logo grid line (--edge),
   exactly like the nav logo's M */
.copy--left {
  left: var(--edge);
  bottom: 11vh; /* was 8vh - lifted so the fixed footer breathes (Aug 6) */
}

/* top-anchored left block (WHAT WE QUESTION., timing ref 43-47s: the
   copy holds the upper third while her hand works low in frame) */
.copy--top {
  left: var(--edge);
  bottom: auto;
  top: 16vh;
}
/* the ONLY block that keeps its shadow (Tuan, Aug 5): it sits in the
   upper third over the brightest passage of the film - pale sky and
   sunlit dune crest - where white type alone loses its edge. Every
   other copy block runs shadow-free. */
.copy--top .copy__line {
  text-shadow: 0 2px 44px rgba(0, 0, 0, 0.35);
}

/* vertically centred block (WHO EXPAND / WHAT'S / POSSIBLE, ref 16-20s:
   block centre sits at ~50% viewport height, not in the lower third) */
.copy--mid {
  bottom: auto;
  top: 50%;
  transform: translateY(calc(-50% + 16px));
}
.copy--mid.is-visible { transform: translateY(-50%); }

.copy--story .copy__eyebrow,
.copy--left .copy__eyebrow {
  /* kicker sits at ~1:4.5 of the 109px display (was 1.55cqw ≈ 1:3.7,
     the AE ref runs ~38px ≈ 1:2.8) - smaller + tracked reads calmer
     against the big No.84 caps; tracking itself is FROM the ref
     (the AE kicker is visibly letterspaced), it also steadies small
     Light type over moving video (Jul 28) */
  font-size: 1.25cqw;
  letter-spacing: 0.04em;
  color: rgba(240, 234, 226, 0.94);
  line-height: 1.2;
  margin-bottom: 0.35cqw; /* hair more air than 0.25 now the kicker is finer */
  white-space: nowrap;
  /* Schatten entfernt - keine shadow effects auf Copy (Tuan, Aug 5) */
}
.copy__line {
  font-family: var(--font-wide);
  font-size: calc((64cqw - var(--edge) - 2px) / 10.714);
  line-height: 0.95;
  letter-spacing: 0; /* AE tracking-100 in the ref was an accident (Tuan) */
  color: var(--paper);
  text-transform: uppercase;
  white-space: nowrap;
}
.copy__line--accent { color: var(--orange); }

/* giant grid-flush display words (CURIOUS · INDEPENDENT) - the same
   treatment as the giant wordmark: flush LEFT+RIGHT to the page grid,
   resting on the bottom --edge, and they RISE from below the viewport
   edge while the standard .copy opacity fade runs (ref 20.7-21.4s).
   FLUSH-WIDTH RULE (same math as the headline formula): word width =
   K × font-size at letter-spacing 0 (No.84 DemiXWide, measured in the
   browser), and the word must span exactly 100cqw − 2×var(--edge)
   (--edge both sides):  F = (100cqw − 2·edge) / K  */
.copy--giant {
  /* resting height: --edge on wide screens, but never below 88px so
     the words clear the AI note above the hairline (Tuan, Aug 7) */
  --giant-rest: max(var(--edge), 88px);
  left: var(--edge);
  right: var(--edge);
  bottom: var(--giant-rest);
  transform: translateY(calc(100% + var(--giant-rest)));
}
.copy--giant.is-visible { transform: translateY(0); }
.copy__line--giant {
  line-height: 0.8; /* tight box so the word truly rests on the edge */
}
/* K measured in-browser at 100px (No.84 loaded): CURIOUS 6.3805,
   INDEPENDENT 9.6359 - divisor rounded UP a hair so rounding never
   pokes past the CONTACT line */
.copy__line--curious     { font-size: calc((100cqw - 2 * var(--edge)) /6.381); }
.copy__line--independent { font-size: calc((100cqw - 2 * var(--edge)) /9.636); }

/* left body note ("Eight years ago…" and the chapter lines) */
.copy--note {
  left: var(--edge);
  top: 30vh;
  max-width: 30ch;
  font-family: var(--font-light);
  font-size: clamp(15px, 1.15vw, 19px);
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--paper);
}

/* subtitle + display title block (UNCOMMON MINDS) */
.copy--display {
  left: 50%;
  top: 18vh;
  max-width: 46vw;
}
.copy__eyebrow {
  font-family: var(--font-light);
  font-size: clamp(14px, 1.05vw, 18px);
  line-height: 1.45;
  letter-spacing: 0.005em;
  color: var(--paper);
  margin-bottom: clamp(3px, 0.5vw, 8px);
}
.copy__title {
  font-family: var(--font-wide);
  /* "UNCOMMON" is one unbreakable word ≈ 8× its font-size wide; capped so
     it always clears the right edge from the 50vw origin, like the PDF. */
  font-size: clamp(42px, 5.4vw, 96px);
  line-height: 0.92;
  letter-spacing: 0;
  color: var(--paper);
  text-transform: uppercase;
  white-space: nowrap;
}

/* payoff line, lower-centre */
.copy--payoff {
  left: 50%;
  top: 72vh;
  transform: translate(-50%, 14px);
  font-family: var(--font-demi);
  font-size: clamp(12px, 0.92vw, 15px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--paper);
  white-space: nowrap;
}
.copy--payoff.is-visible { transform: translate(-50%, 0); }

/* hero giant wordmark (tight white logo) - identical box to
   .wordmark-giant so the riffle's dark→white swap stays superimposed */
.stage__wordmark {
  position: absolute;
  z-index: 6;
  bottom: var(--edge);
  left: var(--edge);
  right: var(--edge);
  pointer-events: none;
  will-change: opacity, transform;
}
.stage__wordmark img { width: 100%; height: auto; display: block; }

/* subnav removed (Leonie, Jul 21) */

/* ============================================================
   SCROLL TRACK (invisible; provides scroll length)
   ============================================================ */
.track { position: relative; z-index: 2; pointer-events: none; }
.track__step { height: 130vh; }
.track__tail { height: 30vh; }

/* (old block-footer from the scrapped first layout removed Aug 6 -
   it had background:green + min-height:48vh and, once a real
   <footer> existed, painted a green wall over the film. The live
   footer bar lives in the SITE FOOTER section further down.) */

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 900px) {
  :root {
    --edge: 24px; --nav-h: 68px;
    /* 118px (Tuan, Aug 7): the links live in the lens menu now, so the
       wordmark is the nav's only brand text - near-desktop glyph size
       (~9.7px) without tipping into loud (~30% of a 390px screen) */
    --logo-w: 118px;
    --lens-w: 26px; --lens-gap: 22px;
    --nav-top: 8px; /* proportionally less drop on the shorter nav */
  }
  /* the one-line footer bar cannot hold icons + all links at 390px -
     on phones the socials live in the menu's social row instead */
  .footer .footer__social { display: none; }
  /* the link row leaves, the lens becomes the menu (see .menu below,
     where the <=900px enable ALSO lives - it must follow the base
     display:none in the cascade) */
  .nav__links { display: none; }
  /* the giant words rested on --edge (24px here) - the footer row is
     38px tall, so they collided with the hairline on phones; the extra
     26px lifts them over the AI note line (top ~60px) with a breath */
  .copy--giant { --giant-rest: calc(64px + 2.5vh); }
  /* same collision for the giant wordmark; loader + stage copies MUST
     move together - the riffle's dark->white swap needs identical
     boxes (Tuan saw the footer hairline cross the wordmark) */
  .wordmark-giant,
  .stage__wordmark { bottom: calc(38px + 2.5vh); }
  /* the footer joins the LOADING screen on phones (above the loader
     only while body.is-locked): the giant wordmark stands on the bar
     instead of floating (Tuan) - after the reveal it returns to z35 */
  body.is-locked .footer { z-index: 101; }
  .copy--story { left: var(--edge); max-width: 88vw; }
  .copy--story .copy__eyebrow,
  .copy--left .copy__eyebrow { font-size: clamp(13px, 3vw, 18px); white-space: normal; }
  .copy__line { font-size: clamp(28px, 8.6vw, 56px); white-space: normal; }
  .copy__line--curious     { font-size: calc((100cqw - 2 * var(--edge)) /6.381); }
  .copy__line--independent { font-size: calc((100cqw - 2 * var(--edge)) /9.636); }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .loader { transition: none; }
  .copy { transition: none; transform: none; }
}

/* ============================================================
   FILM GRAIN OVERLAY (Aug 5) - replaces grain baked into the
   video. Rendered at DISPLAY resolution, so it stays crisp
   instead of averaging away like 4K grain does on downscale,
   and it costs ~220KB instead of ~700MB of extra video bitrate.
   Lives INSIDE .stage__layer, so the intro's boxed clip-path
   clips the grain exactly like the video.

   STILL + FINE (Tuan, Aug 5): no animation at all - a quiet,
   static emulsion texture, not a moving grain effect. The tile
   is 384px so the repeat never reads as a pattern when it holds
   still, and --grain-scale halves it so one grain lands near one
   device pixel on Retina.

   PERF (measured Aug 5): the first version used
   mix-blend-mode:overlay and spiked frame times to p95 1751ms -
   blending forces the compositor to read back every 4K video
   frame. The tile now carries its own ALPHA (light AND dark
   grains), so plain source-over compositing does the job, and
   with no animation the layer is painted exactly once.

   Reversible: CONFIG.grain.enabled = false in main.js injects
   nothing; delete this block + the grain block in main.js to
   remove for good. Tuning knobs are the two variables below.
   ============================================================ */
:root {
  --grain-opacity: 0.3;   /* 0.2 whisper · 0.4 fine · 0.7 present */
  --grain-scale: 150px;   /* tile at half size = finest grain */
}
.grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("../assets/img/grain.png");
  background-repeat: repeat;
  background-size: var(--grain-scale) var(--grain-scale);
  opacity: var(--grain-opacity);
  z-index: 2; /* over the video, under copy (.copy is z5) */
}

/* ============================================================
   SITE FOOTER (Aug 6) - fixed micro-bar over the scrolly video.
   ©-line left, legal links right, in the nav's quiet No.61 voice.
   A hairline above the row differentiates it from the film
   without introducing a second surface color (Tuan: "fine line",
   same world, no separate footer background).
   ============================================================ */
.footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 35; /* above stage + progress rail, below nav/loader */
  padding: 0 var(--edge);
  pointer-events: none; /* only the links catch the pointer */
}
.footer__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 44px;
  border-top: 1px solid rgba(240, 234, 226, 0.16); /* the fine line */
}
.footer__copy,
.footer__links a {
  pointer-events: auto;
  font-family: var(--font-light);
  font-size: clamp(10px, 0.72vw, 12px);
  letter-spacing: 0.14em; /* nav voice */
  color: var(--paper-dim);
  text-transform: uppercase;
  white-space: nowrap;
}
.footer__links {
  display: flex;
  align-items: center;
  gap: clamp(16px, 1.8vw, 34px);
}
/* social marks: monoline inline-svg on currentColor - they inherit
   the footer voice (paper-dim, hover paper) like the text links */
.footer__social { display: flex; align-items: center; }
.footer__social svg { width: 16px; height: 16px; display: block; }
.footer__links a {
  transition: color 0.3s var(--ease-out);
}
.footer__links a:hover,
.footer__links a:focus-visible {
  color: var(--paper);
}
@media (max-width: 900px) {
  .footer__row { height: 38px; }
}

/* ============================================================
   PAGE-TO-PAGE VEIL (Aug 6) - nav clicks no longer hard-blink:
   a brand-green curtain fades over the old page (~240ms), the
   navigation happens under it, and the new page lifts the same
   curtain on arrival. Plain fixed div + opacity, so it behaves
   identically in every browser. (The native cross-document
   view-transition API was tried first and HARD-FROZE rendering
   in Chromium - rAF dead, screenshots hung - so it was dropped.)
   Lift/departure logic lives in js/veil.js.
   ============================================================ */
.veil {
  position: fixed;
  inset: 0;
  z-index: 90; /* above everything except the boot loader (z100) */
  background: var(--green);
  opacity: 1;  /* pages ARRIVE covered; veil.js lifts it */
  pointer-events: none;
  transition: opacity 0.32s var(--ease-out);
}
.veil--lifted { opacity: 0; }
.veil--instant { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .veil { transition: none; }
}

/* ============================================================
   MOBILE MENU (Aug 7) - "look through the lens": on phones the
   lens mark itself opens a full-surface menu (no burger, no new
   furniture - the mark gains a purpose). It sits UNDER the nav
   row (z94 < z95), so logo + lens stay visible; the lens turns
   ember while open. Links navigate through the veil like every
   internal hop. Desktop (>900px) never shows any of this.
   ============================================================ */
.menu {
  position: fixed;
  inset: 0;
  z-index: 94;
  background: var(--green);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s var(--ease-out), visibility 0.4s;
  display: none; /* enabled in the <=900px block */
}
.menu__links {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  top: calc(var(--nav-h) + var(--nav-top) + 5vh);
  display: flex;
  flex-direction: column;
  gap: 3.2vh;
}
.menu__links a {
  font-family: var(--font-wide);
  font-size: clamp(34px, 9.5vw, 54px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--paper);
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.45s var(--ease-out), transform 0.45s var(--ease-out),
              color 0.3s var(--ease-out);
}
.menu__links a.is-active { color: var(--orange); }
.menu__bottom {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  bottom: env(safe-area-inset-bottom, 0px);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out) 0.25s;
}
.menu__social-row {
  display: flex;
  gap: 24px;
  margin-bottom: 2.6vh;
}
.menu__social-row a { display: flex; align-items: center; color: var(--paper-dim); transition: color 0.3s var(--ease-out); }
.menu__social-row a:hover,
.menu__social-row a:focus-visible { color: var(--paper); }
.menu__social-row svg { width: 21px; height: 21px; display: block; }
/* the .footer__row inside inherits the real footer styles - hairline,
   heights, link voice - so menu foot and page foot are ONE design */
body.menu-open { overflow: hidden; }
body.menu-open .menu { opacity: 1; visibility: visible; pointer-events: auto; }
body.menu-open .menu__links a { opacity: 1; transform: translateY(0); }
body.menu-open .menu__links a:nth-child(1) { transition-delay: 0.04s; }
body.menu-open .menu__links a:nth-child(2) { transition-delay: 0.08s; }
body.menu-open .menu__links a:nth-child(3) { transition-delay: 0.12s; }
body.menu-open .menu__links a:nth-child(4) { transition-delay: 0.16s; }
body.menu-open .menu__links a:nth-child(5) { transition-delay: 0.20s; }
body.menu-open .menu__links a:nth-child(6) { transition-delay: 0.24s; }
body.menu-open .menu__bottom { opacity: 1; }
/* the lens is the switch: ember while the menu is open; the logo
   returns to paper even on paper pages (green would vanish) */
body.menu-open .nav__lens { color: var(--orange); }
body.menu-open .nav__logo { color: var(--paper); }
@media (max-width: 900px) {
  .menu { display: block; } /* AFTER the base display:none - same
     specificity, so source order decides (bug found in verification) */
}

/* ============================================================
   SCROLL CUE (Aug 7) - the quiet handover after the auto-drift:
   "SCROLL" in the footer's empty centre, footer voice, with a
   hairline tick whose ember segment drips downward. Appears only
   after a NATURAL drift landing (interrupters obviously know how
   to scroll), leaves forever on the first scroll. Desktop only -
   touch scrolls by instinct and the 390px bar has no idle centre.
   ============================================================ */
.scrollcue {
  position: absolute;
  left: 50%;
  bottom: 0;
  height: 44px; /* = footer row */
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-light);
  font-size: clamp(10px, 0.72vw, 12px);
  letter-spacing: 0.2em;
  color: var(--paper-dim);
  opacity: 0;
  transition: opacity 0.6s var(--ease-out);
  pointer-events: none;
}
.scrollcue.is-on { opacity: 1; }
.scrollcue__tick {
  position: relative;
  width: 1px;
  height: 13px;
  background: rgba(240, 234, 226, 0.22); /* form hairline tone */
  overflow: hidden;
}
.scrollcue__tick::after {
  content: "";
  position: absolute;
  left: 0;
  top: -5px;
  width: 1px;
  height: 5px;
  background: var(--orange);
  animation: cueDrip 2.2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes cueDrip {
  0%   { transform: translateY(0); }
  62%  { transform: translateY(18px); }
  100% { transform: translateY(18px); }
}
@media (prefers-reduced-motion: reduce) {
  .scrollcue__tick::after { animation: none; }
}
@media (max-width: 900px) {
  .scrollcue { display: none; }
}

/* ============================================================
   AI TRANSPARENCY NOTE (index only) - EU AI Act disclosure for
   the film. Rests just above the footer hairline in the spot the
   giant wordmark vacates; opacity is scroll-driven in main.js
   (exact inverse of the wordmark fade), so the two never meet.
   ============================================================ */
.footer__ai {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  bottom: calc(44px + 12px); /* hairline row + one breath */
  margin: 0;
  font-family: var(--font-light);
  font-size: clamp(9px, 0.6vw, 11px);
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: rgba(240, 234, 226, 0.4); /* a step under the footer meta */
  opacity: 0;
  pointer-events: none;
}
@media (max-width: 900px) {
  .footer__ai {
    bottom: calc(38px + 9px);
    font-size: clamp(8px, 2.3vw, 10px);
    letter-spacing: 0.02em;
  }
}
