/* ------------------------------------------------------------------
   EB Design Agency — design system (v1 scaffold)
   One type scale, one motion language, shared across every scene so
   pacing feels deliberate. Studio base is near-black; each client
   scene brings its own colour world via --from / --to.
------------------------------------------------------------------ */
:root {
  /* Anchored to the live site: black studio base, light grey ink. */
  --studio-bg: #000000;
  --ink: #f3f3f3;
  --ink-dim: rgba(243, 243, 243, 0.55);
  --rule: rgba(243, 243, 243, 0.14);

  /* Brand colour (live EB accent). The E + B device colours into this. */
  --eb-accent: #0db5b5;

  /* Live brand type. Oswald + Archivo Black load from Google.
     europa is an Adobe Typekit face: paste the EB kit <script> into
     index.html and it resolves automatically (system fallback until). */
  /* Live brand type. Oswald is the display face across the site. */
  --font-label: "Oswald", "Archivo Black", Impact, sans-serif;
  /* Adobe Garamond Pro is a live accent only (Typekit); fallback EB Garamond. */
  --font-serif: "adobe-garamond-pro", "EB Garamond", "Adobe Garamond Pro", Garamond, Georgia, serif;
  --font-body: "europa", "Mulish", "Avenir Next", ui-sans-serif, system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", monospace;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --maxw: 1200px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--studio-bg);
  color: var(--ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

.scene { position: relative; width: 100%; }

/* Card-over-card transition: this scene scrolls up over the previous one
   (a later sibling paints on top — no z-index needed). The previous scene
   holds its final frame via .scene--hold (handled in engine.js). Used on a
   few transitions only, to break the scroll monotony. */
.scene--over {
  margin-top: -100vh;
  box-shadow: 0 -60px 120px -10px rgba(0, 0, 0, 0.82);
}

/* ---- Moments (start / end) ----------------------------------------- */
.moment { position: relative; }            /* wrapper: height set by engine */
.moment__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: linear-gradient(160deg, var(--from), var(--to));
  text-align: center;
  padding: 8vh 6vw;
}

/* hero montage video (fast-cut sizzle of the work) + scrim so white type wins */
.moment__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  z-index: 0;
}
.moment__scrim {
  position: absolute; inset: 0; z-index: 1;
  /* the same register as every other tile: the standard radial behind the
     type PLUS a flat dark veil over the film, so the hero matches the
     transparency the rest of the journey carries */
  background:
    radial-gradient(60% 46% at 50% 52%, rgba(0, 0, 0, 0.4), transparent 74%),
    linear-gradient(rgba(0, 0, 0, 0.34), rgba(0, 0, 0, 0.34));
}
.moment__inner { position: relative; z-index: 3; }
.moment .scroll-hint { z-index: 3; }

/* ambient hero graphics (from the reference): lines draw in, dots land */
.hero-grid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
}
.grid-line {
  stroke: rgba(243, 243, 243, 0.07);
  stroke-width: 1;
  stroke-dasharray: 2400;
  stroke-dashoffset: 2400;
  animation: grid-draw 2s ease-out forwards;
}
.detail-dot {
  fill: var(--eb-accent);
  opacity: 0;
  animation: dot-in 0.8s ease-out forwards;
}
@keyframes grid-draw { to { stroke-dashoffset: 0; } }
@keyframes dot-in { to { opacity: 0.55; } }
.moment__inner { max-width: 18ch; }
.moment--start .moment__inner { max-width: 16ch; }
.moment--end .moment__inner { max-width: 22ch; }
.kicker {
  display: block;
  font-family: var(--font-label);
  font-size: clamp(0.66rem, 0.85vw, 0.8rem);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 2.4rem;
}
.moment__title {
  font-family: var(--font-label);
  font-size: clamp(2.2rem, 5.6vw, 4.8rem);
  line-height: 1.06;
  letter-spacing: 0.012em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0;
}
.moment__sub {
  margin-top: 1.8rem;
  color: var(--ink-dim);
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 1.25vw, 1.1rem);
  line-height: 1.5;
  max-width: 42ch;
  margin-left: auto;
  margin-right: auto;
}

/* ---- Morph scenes -------------------------------------------------- */
.morph { background: var(--studio-bg); }
.morph__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
.morph__canvas { display: block; width: 100%; height: 100%; }
.morph__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  display: block;
}

/* ---- Still scenes -------------------------------------------------- */
.still { position: relative; }             /* wrapper: height set by engine */
.still__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
.still__layer {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transform: scale(1.12);
  will-change: transform;
}
.still__layer.is-placeholder {
  background: radial-gradient(120% 120% at 70% 30%, var(--to), var(--from));
}

/* shared caption (morph + still) — TYPE LEADS.
   Big centred cinematic statement over the footage; the imagery is the
   supporting layer behind it. A soft radial scrim guarantees contrast
   without flattening the scene. */
.caption {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 6vw;
  z-index: 2;
  pointer-events: none;
  color: var(--ink);
}
.caption::before {
  content: "";
  position: absolute;
  inset: -10%;
  background: radial-gradient(60% 46% at 50% 52%, rgba(0, 0, 0, 0.4), transparent 74%);
}
.caption .line {
  position: relative;
  display: block;
  font-family: var(--font-label);
  font-size: clamp(3.2rem, 10vw, 9rem);   /* type is the hero */
  line-height: 0.98;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  font-weight: 600;
  color: #fff;                             /* large type is always white */
  max-width: 13ch;
  text-wrap: balance;
  text-shadow: 0 2px 50px rgba(0, 0, 0, 0.55);
}
.caption .meta {
  position: relative;
  display: block;
  margin-top: 2.2rem;
  font-family: var(--font-label);
  font-size: clamp(0.66rem, 0.9vw, 0.8rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0.78;
}
.caption .meta b { font-weight: 500; opacity: 1; }
/* large type is ALWAYS white — pale scenes get a stronger dark scrim
   behind the type rather than flipping the ink dark */
.scene[data-mood="light"] .caption::before {
  background: radial-gradient(62% 48% at 50% 52%, rgba(0, 0, 0, 0.5), transparent 76%);
}

/* ---- Catalogue index (Nº 01 …) — sits ABOVE the headline, brand blue */
.caption .idx-big {
  position: relative;
  display: block;
  font-family: var(--font-label);
  font-size: clamp(0.95rem, 1.5vw, 1.35rem);
  letter-spacing: 0.36em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--eb-accent);
  font-variant-numeric: tabular-nums;
  margin-bottom: 1.6rem;
}
.caption .idx-big sup { font-size: 0.58em; letter-spacing: 0; vertical-align: 0.5em; }

/* ---- Radial glow (native port of the reference component) ----------
   A soft teal halo behind the typography segments gives the black
   stages depth without breaking the accent discipline. Sticky stages
   form stacking contexts, so z-index:-1 sits above the stage bg but
   beneath the type. */
.manifesto__stage::after,
.divider__stage::after,
.moment--end .moment__stage::after,
.moment--start .moment__stage::after {
  content: "";
  position: absolute;
  inset: -14% 0;                 /* oversized so the drift never shows an edge */
  z-index: -1;
  pointer-events: none;
  background-image: radial-gradient(circle 600px at 50% 30%, rgba(13, 181, 181, 0.26), transparent 72%);
  transform: translateY(var(--gy, 0px));   /* parallax drift set by engine.js */
  will-change: transform;
}

/* ---- Mouse-following teal torchlight (homepage) -------------------- */
#torch {
  position: fixed; inset: 0;
  z-index: 60;
  pointer-events: none;
  overflow: hidden;
  mix-blend-mode: screen;     /* lightens dark stages, leaves white type crisp */
  opacity: 0;                 /* shown only over black typographic cards */
  transition: opacity 0.5s var(--ease);
}
body.torch-on #torch { opacity: 1; }
.torch__blob {
  position: absolute; top: 0; left: 0;
  width: 820px; height: 820px; margin: -410px;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(13, 181, 181, 0.34) 0%, rgba(13, 181, 181, 0.12) 40%, transparent 66%);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) { #torch { display: none; } }

/* ---- The aside: personality in the brand colour ---------------------
   A dry human whisper under the big Oswald statements — body face,
   italic, teal. The contrast IS the joke landing. */
.aside {
  display: block;
  margin-top: 1.5rem;
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(0.95rem, 1.3vw, 1.15rem);
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--eb-accent);
}

/* ---- Manifesto: scroll-driven typography ----------------------------
   Pinned statement; word opacity is set by engine.js from scroll
   progress. Type animated BY scroll — restrained, headline-only. */
.manifesto { background: var(--studio-bg); }
.manifesto__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  place-items: center;
  padding: 0 8vw;
}
.manifesto__inner { text-align: center; max-width: 24ch; }
.manifesto__text {
  font-family: var(--font-label);
  font-size: clamp(1.9rem, 5.2vw, 4.6rem);
  line-height: 1.22;
  letter-spacing: 0.012em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0;
  text-wrap: balance;
}
.manifesto__text .mw { opacity: 0.14; will-change: opacity; }
.manifesto__text .eb { color: var(--eb-accent); }

/* ---- Dividers — short, centred interludes (not full-screen holds) -- */
.divider { position: relative; }
.divider__stage {
  position: relative;
  min-height: 52vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--studio-bg);
  text-align: center;
  padding: 7vh 6vw;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.divider__inner { max-width: 30ch; }
.stat {
  display: block;
  font-family: var(--font-label);
  font-size: clamp(3.5rem, 11vw, 9rem);
  line-height: 0.92;
  letter-spacing: 0;
  text-transform: uppercase;
  font-weight: 500;
}
.stat__label {
  display: block;
  margin-top: 1.4rem;
  font-family: var(--font-label);
  font-size: clamp(0.72rem, 1vw, 0.9rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.divider__title {
  font-family: var(--font-label);
  font-size: clamp(2rem, 5vw, 4rem);
  line-height: 1.1;
  letter-spacing: 0.012em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0;
}
.divider__body {
  margin-top: 1.4rem;
  color: var(--ink-dim);
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 1.3vw, 1.1rem);
  line-height: 1.55;
}

/* ---- CTA ----------------------------------------------------------- */
/* small, square, brand-blue button with a looping "shine" sweep — native
   port of the ShinyButton effect (Framer Motion's animated --x → @property
   + keyframes). A bright highlight glides across the face on a loop. */
@property --x {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 120%;
}
@keyframes cta-shine {
  0%   { --x: 120%; }
  32%  { --x: -120%; }   /* sweep across… */
  100% { --x: -120%; }   /* …then rest (the repeatDelay) before looping */
}
.cta {
  position: relative;
  overflow: hidden;
  display: inline-block;
  margin-top: 2.2rem;
  padding: 0.7em 1.3em;
  border-radius: 0;                 /* square edges */
  background: var(--eb-accent);     /* brand blue */
  color: #04110f;
  text-decoration: none;
  font-family: var(--font-label);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 500;
  border: none;
  animation: cta-shine 4.5s var(--ease) infinite;
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease), opacity 0.4s var(--ease);
}
/* the moving highlight — a bright band keyed to --x, gliding over the face */
.cta::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(-75deg,
    transparent calc(var(--x) + 16%),
    rgba(255, 255, 255, 0.6) calc(var(--x) + 24%),
    transparent calc(var(--x) + 32%));
}
.cta:hover, .cta:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 0 22px rgba(13, 181, 181, 0.45);   /* teal glow on hover */
}
.cta--ghost { color: #04110f; }
@media (prefers-reduced-motion: reduce) { .cta { animation: none; } }

/* ---- Whisper-thin nav (AP register) -------------------------------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2.2vh 6vw 5vh;
  color: #fff;                  /* logo stays true brand white */
  /* faint scrim so the white logo reads on pale scenes too */
  background: linear-gradient(to bottom, rgba(0,0,0,0.42), rgba(0,0,0,0));
}
.nav__logo {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
}
.eb-logo {
  height: 34px;
  width: auto;
  display: block;
  overflow: visible;
}
@media (max-width: 640px) { .eb-logo { height: 28px; } }

/* Vector EB mark + RGB-split glitch — matched to the source GIF:
   1.72s loop, two red/green bursts with a slice shudder. */
.eb-ch { transform-box: fill-box; transform-origin: center; }
.eb-w { color: #ffffff; animation: ebShudder 1.72s steps(1) infinite; }
.eb-c { color: #ff2436; mix-blend-mode: screen; opacity: 0; animation: ebGlitchC 1.72s steps(1) infinite; }
.eb-m { color: #21ff8a; mix-blend-mode: screen; opacity: 0; animation: ebGlitchM 1.72s steps(1) infinite; }
.nav__logo:hover .eb-w,
.nav__logo:hover .eb-c,
.nav__logo:hover .eb-m { animation-duration: 0.9s; }

/* red channel — offsets right on burst 1, left on burst 2 */
@keyframes ebGlitchC {
  0%, 15%        { opacity: 0; transform: translate(0, 0); }
  18%            { opacity: 0.95; transform: translate(3px, 0); }
  22%            { opacity: 0.95; transform: translate(-2px, 1px); }
  25%, 60%       { opacity: 0; transform: translate(0, 0); }
  63%            { opacity: 0.95; transform: translate(-3px, 0); }
  67%            { opacity: 0.95; transform: translate(2px, -1px); }
  70%, 100%      { opacity: 0; transform: translate(0, 0); }
}
/* green channel — opposite offsets */
@keyframes ebGlitchM {
  0%, 15%        { opacity: 0; transform: translate(0, 0); }
  18%            { opacity: 0.95; transform: translate(-3px, 1px); }
  22%            { opacity: 0.95; transform: translate(2px, 0); }
  25%, 60%       { opacity: 0; transform: translate(0, 0); }
  63%            { opacity: 0.95; transform: translate(3px, 0); }
  67%            { opacity: 0.95; transform: translate(-2px, 1px); }
  70%, 100%      { opacity: 0; transform: translate(0, 0); }
}
/* white mark shudders horizontally during each burst (the slice feel) */
@keyframes ebShudder {
  0%, 16%, 26%, 61%, 71%, 100% { transform: translate(0, 0); }
  19% { transform: translate(-1.5px, 0); }
  23% { transform: translate(1.5px, 0); }
  64% { transform: translate(1.5px, 0); }
  68% { transform: translate(-1.5px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .eb-w, .eb-c, .eb-m { animation: none; opacity: 0; }
  .eb-w { opacity: 1; }
}
.nav__links {
  display: flex;
  gap: 2.4rem;
}
.nav__links a {
  font-family: var(--font-label);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  opacity: 0.85;
  transition: opacity 0.3s var(--ease);
}
.nav__links a:hover { opacity: 1; }
@media (max-width: 640px) { .nav__links { gap: 1.4rem; } .nav__links a { font-size: 0.62rem; } }

/* ---- Reveal motion ------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* ---- Scroll hint --------------------------------------------------- */
.scroll-hint {
  position: absolute;
  bottom: 5vh; left: 50%;
  width: 1px; height: 56px;
  transform: translateX(-50%);
  background: linear-gradient(var(--ink-dim), transparent);
  animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 0.25; transform: translateX(-50%) scaleY(0.7); }
  50%      { opacity: 0.9;  transform: translateX(-50%) scaleY(1); }
}

/* ---- Word-split reveal (signature line motion) ---------------------
   The word-appear move from the reference, quickened: rise out of blur
   with a mid-point settle, then ease home. */
/* every headline word sits dim and is illuminated on scroll by engine.js
   (no auto animation — the whole homepage shares one scroll-illuminate). */
.w { display: inline-block; vertical-align: top; }
.wi { display: inline-block; opacity: 0.14; animation: none !important; will-change: opacity; }
@keyframes word-appear {
  0%   { opacity: 0;   transform: translateY(28px) scale(0.85); filter: blur(10px); }
  50%  { opacity: 0.8; transform: translateY(8px)  scale(0.96); filter: blur(2px); }
  100% { opacity: 1;   transform: translateY(0)    scale(1);    filter: blur(0); }
}
/* lines that split words shouldn't also block-fade their own transform */
.moment__title, .caption .line, .divider__title { transform: none !important; }

/* ---- The E B device ------------------------------------------------
   The two letters lift into the brand colour: standalone (…there is EB),
   acrostic (Every Brand), or in-word (web). Applied post-split by fx.js. */
.eb {
  color: var(--eb-accent);
  font-weight: inherit;
}

/* ---- Cinematic overlays -------------------------------------------- */
/* RETIRED: these two global canvases painted over the WHOLE page (text
   included), so the multiply darkened the type. The living-shadow effect
   now lives per-scene as .scene-fx (below) — between the media and the
   text — so the type always stays the top layer at full strength. */
#fx-particles,
#fx-sheen {
  display: none;
}

/* Per-scene atmosphere — REMOVED. Every background light idea (dark living-
   shadow, teal washes, raking beams) was tried and pulled at Giles's request.
   The .scene-fx layer is kept as an inert container (engine.js still builds
   one per scene) so the layering contract stays intact, but it paints nothing:
   the films sit clean behind the type. */
.scene-fx {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}
/* The cursor "melt" signature — fx.js draws the BLURRED footage through a
   decaying cursor-trail mask into this layer of film tiles only, so the film
   goes soft where the pointer passes. Above the video, below the type. The
   small extra blur just feathers the mask edge. */
.scene-fx .melt-canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  pointer-events: none;
  opacity: 0.9;
  filter: blur(4px);
}

/* Clickable film tiles → case study. The whole pinned film is a transparent
   link (sits above everything so the entire tile is the hit area); an elegant
   "View project" cue under the caption draws in on hover, and stays faintly
   present on touch where there is no hover. */

/* GHOST — blurred background typography (igloo's "blurrytext": a slowly
   scrolling cylinder of soft blue-grey #c9d0df blurred words). Each project's
   own words drift across, heavily softened so they read as atmosphere, not
   a heading. Gated by .is-near so the blur only runs on/near screen. */
/* igloo's blurrytext does NOT scroll across — its alpha is driven by drifting
   perlin noise, so each word sits in place and slowly PULSATES (fades in/out).
   We match that: stationary, heavily-blurred brand-font words whose opacity
   breathes on a slow cycle, out of phase. Position/size/timing set per word by
   fx.js; translate(-50%,-50%) centres each on its point. */
.scene-fx .ghost {
  position: absolute;
  white-space: nowrap;
  font-family: var(--font-label);
  font-style: normal; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: #c9d0df;
  filter: blur(18px);
  opacity: 0;
  will-change: opacity, transform;
  animation-name: ghostPulse;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-play-state: paused;
  visibility: hidden;
}
.scene-fx.is-near .ghost {
  animation-play-state: running;
  visibility: visible;
}
@keyframes ghostPulse {
  0%, 100% { opacity: 0.04; transform: translate(-50%, -50%) scale(1.00); }
  50%      { opacity: 0.60; transform: translate(-50%, -50%) scale(1.06); }
}

@media (prefers-reduced-motion: reduce) {
  .scene-fx { display: none; }
}

/* Technical HUD readout — asymmetric callout annotations pointing at the card
   text (igloo-style portfolio readout): two-line PORTFOLIO/NAME top-left, a
   numeric readout on the right, CATEGORY + CLICK-TO-EXPLORE bottom-right with
   an underline lead, angular connector leads tipped with plus-node markers,
   over a faint dot field. ALWAYS white; the leads DRAW on scroll and the
   clusters parallax. Lives in the text layer (z 2) so it stays crisp. */
.hud {
  position: absolute; inset: 0;
  z-index: 2;
  pointer-events: none;
  color: #fff;   /* HUD is ALWAYS white, on every scene (light or dark) */
}
.hud__frame {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%)
    translateY(calc((var(--hud-p, 0.5) - 0.5) * -12vh))
    scale(calc(0.95 + var(--hud-draw, 0) * 0.06));
  width: min(880px, 96vw);
  height: min(80vh, 760px);
}
/* faint scattered dot field behind the leads (the reference constellation) */
.hud__field {
  position: absolute; inset: -8%;
  background-image: radial-gradient(circle, rgba(255,255,255,0.55) 0.6px, transparent 1.1px);
  background-size: 46px 46px;
  opacity: calc(var(--hud-draw, 0) * 0.16);
  -webkit-mask-image: radial-gradient(120% 100% at 50% 50%, #000 38%, transparent 86%);
  mask-image: radial-gradient(120% 100% at 50% 50%, #000 38%, transparent 86%);
}
/* connector leads — draw out from the labels toward the text on scroll */
.hud__lines {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: visible;
}
.hud__lines line {
  fill: none; stroke: #fff; stroke-width: 1px;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--hud-draw, 0));
  opacity: 0.55;
}
/* plus-mark node at line tips/joints */
.hud__node {
  position: absolute; width: 9px; height: 9px;
  transform: translate(-50%, -50%);
  opacity: calc(var(--hud-draw, 0) * 0.7);
}
.hud__node::before, .hud__node::after { content: ""; position: absolute; background: #fff; }
.hud__node::before { left: 50%; top: 0; width: 1px; height: 100%; transform: translateX(-50%); }
.hud__node::after { top: 50%; left: 0; height: 1px; width: 100%; transform: translateY(-50%); }
/* bracketed info chips — each piece of information sits inside line brackets
   (four corner brackets drawn with corner gradients), scattered asymmetrically.
   Each chip drifts on scroll along its OWN per-card vector (--mx/--my), so
   different cards move side-to-side / diagonally, not all up-and-down. */
.hud__chip {
  position: absolute;
  font-family: var(--font-mono);
  font-size: 0.5rem; letter-spacing: 0.14em;
  text-transform: uppercase; line-height: 1.45;
  white-space: nowrap;
  color: #fff;
  padding: 6px 8px;
  opacity: calc(var(--hud-draw, 0) * 0.92);
  --c: rgba(255,255,255,0.62);   /* bracket colour */
  --len: 9px;                    /* bracket arm length */
  --th: 1px;                     /* bracket thickness */
  background:
    linear-gradient(var(--c), var(--c)) left top,
    linear-gradient(var(--c), var(--c)) left top,
    linear-gradient(var(--c), var(--c)) right top,
    linear-gradient(var(--c), var(--c)) right top,
    linear-gradient(var(--c), var(--c)) left bottom,
    linear-gradient(var(--c), var(--c)) left bottom,
    linear-gradient(var(--c), var(--c)) right bottom,
    linear-gradient(var(--c), var(--c)) right bottom;
  background-repeat: no-repeat;
  background-size:
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len);
  /* centred on its point — the engine's HUD ticker sets left/top each frame
     (a subtle Lissajous drift); the tether lines meet the node's centre. */
  transform: translate(-50%, -50%);
}
.hud__chip span { display: block; }
.hud__chip .b { font-weight: 700; }
/* base / reduced-motion fallback positions on the orbit ring; JS overrides */
.hud__chip--a { left: 50%; top: 14%; }
.hud__chip--b { left: 84%; top: 50%; }
.hud__chip--c { left: 50%; top: 86%; }
/* CLICK TO EXPLORE — orbits too, brand teal */
.hud__chip--d {
  left: 16%; top: 50%;
  --c: var(--eb-accent);
  color: var(--eb-accent);
}
.hud__explore { pointer-events: auto; text-decoration: none; }
.hud__explore:hover { --c: #2ee0e0; color: #2ee0e0; }

/* the BORDER around the hero text — corner brackets OUTSIDE the headline; the
   tether lines come off this frame's corners. Big (80% × 48% of the HUD frame). */
.hud__core {
  position: absolute; left: 50%; top: 50%;
  width: 66%; height: 42%;
  transform: translate(-50%, -50%);
  opacity: calc(var(--hud-draw, 0) * 0.45);
  --c: rgba(255,255,255,0.6); --len: 18px; --th: 1px;
  background:
    linear-gradient(var(--c), var(--c)) left top,
    linear-gradient(var(--c), var(--c)) left top,
    linear-gradient(var(--c), var(--c)) right top,
    linear-gradient(var(--c), var(--c)) right top,
    linear-gradient(var(--c), var(--c)) left bottom,
    linear-gradient(var(--c), var(--c)) left bottom,
    linear-gradient(var(--c), var(--c)) right bottom,
    linear-gradient(var(--c), var(--c)) right bottom;
  background-repeat: no-repeat;
  background-size:
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len),
    var(--len) var(--th), var(--th) var(--len);
}
@media (max-width: 600px) {
  .hud__chip { font-size: 0.52rem; letter-spacing: 0.12em; padding: 6px 9px; }
}

/* Dot grid — fine blueprint dots. On the plain typographic cards (dividers,
   manifesto, end) it reads clearly over the black; engine.js also drops it on
   ~50% of the film cards as a quieter texture. Edge-masked so it never hits a
   hard rectangle border; SCREEN blend so it's light dots, never darkening. */
.dot-grid {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 1;
  mix-blend-mode: screen;
  background-image: radial-gradient(circle, rgba(255,255,255,0.22) 0.9px, transparent 1.5px);
  background-size: 30px 30px;
  background-position: center;
  -webkit-mask-image: radial-gradient(125% 105% at 50% 50%, #000 52%, transparent 92%);
  mask-image: radial-gradient(125% 105% at 50% 50%, #000 52%, transparent 92%);
}

/* Mouse-following blur lens — a soft backdrop-blur disc that glides with the
   cursor over the films (engine sets --lx/--ly to the cursor %). z-index 1, so
   it defocuses the footage but never the type. Fades in/out as the cursor
   enters/leaves a film card. */
.scene-mlens {
  position: absolute;
  left: var(--lx, 50%); top: var(--ly, 50%);
  width: 100%; aspect-ratio: 1;          /* ~5× the area — a big, soft field */
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.5s ease;
  border-radius: 50%;
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  -webkit-mask-image: radial-gradient(closest-side, #000 26%, rgba(0,0,0,0) 74%);
  mask-image: radial-gradient(closest-side, #000 26%, rgba(0,0,0,0) 74%);
  will-change: left, top, opacity;
}
@media (prefers-reduced-motion: reduce) { .scene-mlens { display: none; } }

/* Roaming lens — the soft blur you liked, set free to DANCE across each
   scene. A feather-masked backdrop-blur blob that drifts on its own slow
   path, gently blurring the imagery beneath it. Lives at z-index 1, so
   like .scene-fx it sits ABOVE the media but BELOW the type — the text
   stays crisp while the blur plays underneath. A second, independently
   timed motion layer so the page always feels quietly alive. */
.scene-lens {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}
.scene-lens::before {
  content: "";
  position: absolute;
  width: 78%; height: 78%;
  left: 11%; top: 11%;
  border-radius: 50%;
  -webkit-backdrop-filter: blur(13px);
  backdrop-filter: blur(13px);
  -webkit-mask-image: radial-gradient(closest-side, #000 24%, rgba(0,0,0,0) 70%);
  mask-image: radial-gradient(closest-side, #000 24%, rgba(0,0,0,0) 70%);
  will-change: transform;
  animation: lensDance 34s ease-in-out infinite alternate;
}
@keyframes lensDance {
  0%   { transform: translate(-32%, -12%) scale(0.85); }
  30%  { transform: translate(18%, 22%)   scale(1.12); }
  55%  { transform: translate(34%, -18%)  scale(0.95); }
  80%  { transform: translate(-18%, 14%)  scale(1.20); }
  100% { transform: translate(8%, -24%)   scale(0.90); }
}
@media (prefers-reduced-motion: reduce) {
  .scene-lens::before { animation: none; }
}

/* lens seam — a SHIMMER riding the boundary while one card hands to the
   next. Not a clean blend: the band breathes (blur/brightness pulse) and
   two displaced echo strips drift against each other inside it, smearing
   bits of both cards through the join — alive, slightly imperfect,
   hiding the cut the way a real lens would. Driven by engine.js. */
#lens-seam {
  position: fixed; left: 0; right: 0; top: -100vh;
  height: 34vh;
  pointer-events: none;
  z-index: 6;
  opacity: 0;
  backdrop-filter: blur(16px) saturate(1.3) brightness(1.08);
  -webkit-backdrop-filter: blur(16px) saturate(1.3) brightness(1.08);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 38%, #000 62%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 38%, #000 62%, transparent);
  animation: seamBreathe 2.8s ease-in-out infinite;
}
#lens-seam::before,
#lens-seam::after {
  content: "";
  position: absolute; left: 0; right: 0;
  height: 52%;
  backdrop-filter: blur(30px) brightness(1.14) saturate(1.5);
  -webkit-backdrop-filter: blur(30px) brightness(1.14) saturate(1.5);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 50%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 50%, transparent);
}
#lens-seam::before { top: 0;    animation: seamDriftA 3.7s ease-in-out infinite; }
#lens-seam::after  { bottom: 0; animation: seamDriftB 4.9s ease-in-out infinite; }
@keyframes seamBreathe {
  50% {
    backdrop-filter: blur(22px) saturate(1.5) brightness(1.18);
    -webkit-backdrop-filter: blur(22px) saturate(1.5) brightness(1.18);
  }
}
@keyframes seamDriftA {
  0%, 100% { transform: translateY(-6%); }
  50%      { transform: translateY(10%); }
}
@keyframes seamDriftB {
  0%, 100% { transform: translateY(7%); }
  50%      { transform: translateY(-9%); }
}

#fx-vignette {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 2;
  background: radial-gradient(120% 90% at 50% 42%,
    rgba(0,0,0,0) 46%, rgba(0,0,0,0.14) 80%, rgba(0,0,0,0.34) 100%);
}
#fx-grain {
  position: fixed; inset: -50%;
  width: 200%; height: 200%;
  pointer-events: none;
  z-index: 4;
  opacity: 0.06;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 0.5s steps(4) infinite;
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-3%, 2%); }
  50%  { transform: translate(2%, -2%); }
  75%  { transform: translate(-2%, -3%); }
  100% { transform: translate(3%, 2%); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .wi { opacity: 1; transform: none; animation: none !important; }
  .grid-line { animation: none; stroke-dashoffset: 0; }
  .detail-dot { animation: none; opacity: 0.55; }
  .scroll-hint, #fx-grain { animation: none; }
  #fx-particles, #fx-sheen, #lens-seam { display: none; }
}

/* ==================================================================
   Inner pages (Work, Studio, Contact, Project) + shared footer
   Same design system as the journey: black base, Oswald display,
   europa body, teal EB device.
   ================================================================== */
.page { background: var(--studio-bg); }
.page__main { display: block; }

/* ---- Page hero ----------------------------------------------------- */
.page-hero {
  padding: 14vh 6vw 4vh;
  max-width: var(--maxw);
  margin: 0 auto;
}
.page-hero__title {
  font-family: var(--font-label);
  font-size: clamp(2.6rem, 8vw, 6rem);
  line-height: 1.02;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0.6rem 0 0;
}
.page-hero__sub {
  margin-top: 1.6rem;
  max-width: 46ch;
  color: var(--ink-dim);
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  line-height: 1.55;
}

/* ---- Work grid ----------------------------------------------------- */
.work-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  padding: 0 6px 6px;
}
@media (min-width: 900px) { .work-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .work-grid { grid-template-columns: 1fr; } }
/* Work card = flipping card (native port of the shadcn FlippingCard, dark).
   Hover/focus flips the card on its Y axis: front = artwork + title,
   back = description + a teal "view project" button. */
.work-card {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  text-decoration: none;
  color: var(--ink);
  perspective: 1200px;
}
.flip-inner {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.7s var(--ease);
}
.work-card:hover .flip-inner,
.work-card:focus-visible .flip-inner { transform: rotateY(180deg); }
.flip-face {
  position: absolute; inset: 0;
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #000;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.flip-back { transform: rotateY(180deg); }

/* front face */
.work-card__img {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  transform: scale(1.02);
  transition: transform 0.9s var(--ease);
}
.work-card__img.is-placeholder { background: radial-gradient(120% 120% at 68% 28%, var(--to), var(--from)); }
.flip-front::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.78) 4%, rgba(0,0,0,0.05) 46%, rgba(0,0,0,0.18) 100%);
}
.work-card__body { position: absolute; left: 0; bottom: 0; z-index: 2; padding: 7% 8%; }
.work-card__name {
  display: block;
  font-family: var(--font-label);
  font-size: clamp(1.1rem, 2.1vw, 1.6rem);
  text-transform: uppercase; letter-spacing: 0.01em; line-height: 1.05; font-weight: 500;
}
.work-card__meta {
  display: block; margin-top: 0.5rem;
  font-family: var(--font-label);
  font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-dim);
}

/* back face (dark) */
.flip-back {
  display: flex; flex-direction: column; justify-content: center; align-items: flex-start;
  gap: 1.2rem; padding: 9% 8%;
  background: linear-gradient(155deg, #0d0d0e, #050506);
}
.flip-back::before {
  content: ""; position: absolute; inset: 0; opacity: 0.24;
  background: radial-gradient(120% 90% at 85% 12%, var(--to), transparent 62%);
}
.flip-back > * { position: relative; z-index: 1; }
.flip-back__name {
  font-family: var(--font-label); text-transform: uppercase; font-weight: 500;
  font-size: clamp(1rem, 1.9vw, 1.35rem); letter-spacing: 0.01em; line-height: 1.05;
}
.flip-back__desc {
  font-family: var(--font-body); color: #cfcdc9; line-height: 1.5;
  font-size: clamp(0.85rem, 1.1vw, 0.98rem);
}
.flip-back__btn {
  font-family: var(--font-label); text-transform: uppercase; letter-spacing: 0.16em;
  font-size: 0.68rem; font-weight: 500; color: #06110f; background: var(--eb-accent);
  padding: 0.85em 1.4em; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 0.5em;
  transition: gap 0.3s var(--ease);
}
.work-card:hover .flip-back__btn { gap: 0.85em; }

/* ---- Prose / studio ------------------------------------------------ */
.prose, .approach, .personality, .cta-band, .stats-row, .contact-grid {
  max-width: var(--maxw); margin: 0 auto; padding: 4vh 6vw;
}
.prose { max-width: 64ch; }
.prose p {
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 1.7vw, 1.35rem);
  line-height: 1.7; color: #cfcdc9; margin: 0 0 1.6rem;
}
.approach { text-align: center; padding: 7vh 6vw; }
.approach__verbs {
  font-family: var(--font-label);
  font-size: clamp(1.8rem, 5vw, 4rem);
  text-transform: uppercase; letter-spacing: 0.01em; font-weight: 500; line-height: 1.1; margin: 0.8rem 0 0;
}
.approach__body { margin-top: 1.6rem; color: var(--ink-dim); font-family: var(--font-body);
  font-size: clamp(1rem, 1.4vw, 1.15rem); }
.stats-row {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  text-align: center; padding: 6vh 6vw;
}
@media (max-width: 640px){ .stats-row{ grid-template-columns: 1fr; gap: 3rem; } }
.stat-cell__n { display: block; font-family: var(--font-label); font-weight: 500;
  font-size: clamp(3rem, 8vw, 6rem); line-height: 0.9; }
.stat-cell__l { display: block; margin-top: 1rem; font-family: var(--font-label);
  font-size: 0.78rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-dim); }
.personality { max-width: 50ch; text-align: center; padding: 7vh 6vw; }
.personality__line { font-family: var(--font-body); font-size: clamp(1.1rem, 2vw, 1.5rem);
  line-height: 1.6; color: #cfcdc9; }
.cta-band { text-align: center; padding: 6vh 6vw; }
.cta-band__title { font-family: var(--font-label); text-transform: uppercase; font-weight: 500;
  font-size: clamp(1.8rem, 4.5vw, 3.4rem); letter-spacing: 0.01em; margin: 0; }

/* ---- Contact ------------------------------------------------------- */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6vw; padding: 4vh 6vw 12vh; }
@media (max-width: 760px){ .contact-grid{ grid-template-columns: 1fr; gap: 5vh; } }
.contact-line { display: flex; align-items: baseline; gap: 1.2rem; padding: 1.4rem 0;
  border-bottom: 1px solid var(--rule); }
.contact-line__l { font-family: var(--font-label); font-size: 0.72rem; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ink-dim); min-width: 5.5rem; }
.contact-line a, .contact-line span { font-family: var(--font-label); text-transform: uppercase;
  font-size: clamp(1.1rem, 2.3vw, 1.7rem); color: var(--ink); text-decoration: none; }
.contact-line a:hover { color: var(--eb-accent); }
.contact-form { display: flex; flex-direction: column; gap: 1.2rem; }
.contact-form label { display: flex; flex-direction: column; gap: 0.5rem;
  font-family: var(--font-label); font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-dim); }
.contact-form input, .contact-form textarea {
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: rgba(255,255,255,0.04); border: 1px solid var(--rule); border-radius: 8px;
  padding: 0.9em 1em; outline: none; transition: border-color 0.3s var(--ease); resize: vertical;
}
.contact-form input:focus, .contact-form textarea:focus { border-color: var(--eb-accent); }
.contact-form .cta { align-self: flex-start; }
.contact-form__note { font-family: var(--font-body); font-size: 0.8rem; color: var(--ink-dim); }

/* ---- Project page -------------------------------------------------- */
.proj-hero { position: relative; height: 86vh; overflow: hidden; display: grid; place-items: end start; }
.proj-hero__img { position: absolute; inset: 0; background-size: cover; background-position: center; }
.proj-hero__img.is-placeholder { background: radial-gradient(120% 120% at 70% 30%, var(--to), var(--from)); }
.proj-hero::after { content:""; position:absolute; inset:0;
  background: linear-gradient(to top, rgba(0,0,0,0.75), rgba(0,0,0,0.05) 55%); }
.proj-hero__caption { position: relative; z-index: 2; padding: 7vh 6vw; }
.proj-hero__line { font-family: var(--font-label); text-transform: uppercase; font-weight: 500;
  font-size: clamp(2rem, 5vw, 4rem); letter-spacing: 0.01em; line-height: 1.05; margin: 1rem 0 0; max-width: 16ch; }
.proj-body { max-width: 60ch; margin: 0 auto; padding: 5vh 6vw 3vh; }
.proj-body__blurb { font-family: var(--font-body); font-size: clamp(1.2rem, 2.2vw, 1.6rem);
  line-height: 1.55; color: #d6d4d0; margin: 1rem 0 1.6rem; }
.proj-body__note { font-family: var(--font-body); color: var(--ink-dim); line-height: 1.6; margin-bottom: 2.4rem; }
.proj-next { position: relative; overflow: hidden; display: block; text-align: center;
  padding: 16vh 6vw; text-decoration: none; color: #fff;
  background: linear-gradient(160deg, var(--from), var(--to)); }
.proj-next__video { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center; z-index: 0; transition: transform 0.6s var(--ease); }
.proj-next:hover .proj-next__video { transform: scale(1.04); }
.proj-next__scrim { position: absolute; inset: 0; z-index: 1;
  background: radial-gradient(70% 80% at 50% 50%, rgba(0,0,0,0.42), rgba(0,0,0,0.72)); }
.proj-next__label, .proj-next__name { position: relative; z-index: 2; }
.proj-next__label { display: block; font-family: var(--font-label); font-size: 0.72rem;
  letter-spacing: 0.26em; text-transform: uppercase; color: var(--eb-accent); }
.proj-next__name { display: block; margin-top: 0.8rem; font-family: var(--font-label);
  text-transform: uppercase; font-weight: 600; font-size: clamp(2rem, 5vw, 3.8rem);
  text-shadow: 0 2px 40px rgba(0,0,0,0.5); }

/* ---- Footer -------------------------------------------------------- */
.site-footer { background: #060606; border-top: 1px solid var(--rule); }
.footer__inner { max-width: var(--maxw); margin: 0 auto; padding: 7vh 6vw 5vh;
  display: grid; grid-template-columns: 2fr 1fr 1fr 1.2fr; gap: 3vw; }
@media (max-width: 820px){ .footer__inner{ grid-template-columns: 1fr 1fr; gap: 6vw 3vw; } }
@media (max-width: 480px){ .footer__inner{ grid-template-columns: 1fr; } }
.footer__mark .eb-logo { height: 30px; }
.footer__line { font-family: var(--font-label); text-transform: uppercase; font-weight: 500;
  font-size: clamp(1.3rem, 2.4vw, 1.8rem); margin: 1.4rem 0 0.8rem; }
.footer__sub { font-family: var(--font-body); color: var(--ink-dim); max-width: 34ch; line-height: 1.55;
  font-size: 0.92rem; }
.footer__col h4 { font-family: var(--font-label); font-size: 0.72rem; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ink-dim); margin: 0 0 1.2rem; }
.footer__col a { display: block; font-family: var(--font-body); color: var(--ink); text-decoration: none;
  padding: 0.35rem 0; font-size: 0.95rem; transition: color 0.3s var(--ease); }
.footer__col a:hover { color: var(--eb-accent); }
.footer__city { display: block; padding-top: 0.35rem; color: var(--ink-dim); font-family: var(--font-body); }
.footer__base { max-width: var(--maxw); margin: 0 auto; padding: 2.5vh 6vw 4vh;
  border-top: 1px solid var(--rule); display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  font-family: var(--font-label); font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-dim); }

/* ---- Project artwork: vertical 3D image stack (scroll-driven) -------
   Native port of vertical-image-stack. The section is tall; a sticky
   stage holds landscape cards that recede above/below the active one
   (scale + opacity + rotateX). Page scroll advances the stack. */
.proj-stack { position: relative; }            /* height set inline (N * 80vh) */
.stack-stage {
  position: sticky; top: 0;
  height: 100vh;
  overflow: hidden;
  perspective: 1500px;
}
.stack-card {
  position: absolute; top: 50%; left: 50%;
  width: min(68vw, 640px);
  aspect-ratio: 3 / 2;                          /* landscape */
  transform: translate(-50%, -50%);
  transform-style: preserve-3d;
  will-change: transform, opacity;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #0b0b0c;
  box-shadow: 0 34px 70px -22px rgba(0, 0, 0, 0.78);
}
.stack-card img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
.stack-card::after {
  content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 34%;
  background: linear-gradient(to top, rgba(0,0,0,0.5), transparent);
}
/* counter (left) */
.stack-counter { position: absolute; left: 6vw; top: 50%; transform: translateY(-50%); z-index: 20;
  display: flex; flex-direction: column; align-items: center; }
.stack-counter__nwrap {
  position: relative;
  display: block;
  height: clamp(2.2rem, 4vw, 3.4rem);
  min-width: 2ch;
}
.stack-counter__n { font-family: var(--font-label); font-weight: 300; font-size: clamp(2.2rem, 4vw, 3.4rem);
  line-height: 1; font-variant-numeric: tabular-nums;
  position: absolute; inset: 0; display: block; text-align: center; }
.stack-counter__n--cur { color: var(--ink); }
.stack-counter__n--next { color: var(--eb-accent); opacity: 0; }  /* incoming = brand teal */
.stack-counter__rule { width: 28px; height: 1px; background: rgba(255,255,255,0.25); margin: 0.7rem 0; }
.stack-counter__t { font-family: var(--font-label); font-size: 0.9rem; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
/* dots (right) */
.stack-dots { position: absolute; right: 6vw; top: 50%; transform: translateY(-50%); z-index: 20;
  display: flex; flex-direction: column; gap: 0.6rem; }
.stack-dot { width: 8px; height: 8px; padding: 0; border: none; border-radius: 9px; cursor: pointer;
  background: rgba(255,255,255,0.28); transition: height 0.3s var(--ease), background 0.3s var(--ease); }
.stack-dot:hover { background: rgba(255,255,255,0.5); }
.stack-dot.is-active { height: 24px; background: var(--ink); }
/* hint (bottom) */
.stack-hint { position: absolute; bottom: 5vh; left: 50%; transform: translateX(-50%); z-index: 20;
  display: flex; flex-direction: column; align-items: center; gap: 0.4rem; color: var(--ink-dim); }
.stack-hint svg { width: 20px; height: 20px; }
.stack-hint svg:first-child { animation: stackBobUp 1.6s var(--ease) infinite; }
.stack-hint svg:last-child { animation: stackBobDown 1.6s var(--ease) infinite; }
.stack-hint__label { font-family: var(--font-label); font-size: 0.64rem; letter-spacing: 0.24em; text-transform: uppercase; }
@keyframes stackBobUp { 0%,100%{ transform: translateY(0);} 50%{ transform: translateY(-6px);} }
@keyframes stackBobDown { 0%,100%{ transform: translateY(0);} 50%{ transform: translateY(6px);} }
@media (max-width: 640px) { .stack-counter, .stack-dots { display: none; } }

/* ---- Quick word reveal on scroll (blur-in) ------------------------- */
.rw { display: inline-block; }
.rw-i {
  display: inline-block;
  opacity: 0;
  transform: translateY(22px) scale(0.96);
  filter: blur(8px);
  transition: opacity 0.5s ease-out, transform 0.5s ease-out, filter 0.5s ease-out;
}
.reveal-words.in .rw-i { opacity: 1; transform: none; filter: blur(0); }

@media (prefers-reduced-motion: reduce) {
  .flip-inner { transition: none; }
  .stack-hint svg { animation: none; }
  .rw-i { opacity: 1; transform: none; filter: none; transition: none; }
}

/* ==================================================================
   Case study (project page) — editorial, typography-led.
   Type leads; the hero image is a compact banner, not a takeover.
   ================================================================== */
.case-head {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 16vh 6vw 5vh;
}
.case-head__name {
  font-family: var(--font-label);
  font-size: clamp(2.6rem, 8.5vw, 6.6rem);
  line-height: 1.0;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 1rem 0 0;
  max-width: 14ch;
}
.case-head__line {
  margin-top: 1.6rem;
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 1.7vw, 1.35rem);
  color: var(--ink-dim);
  max-width: 40ch;
}

.case-hero {
  margin: 2vh 0 0;
  height: 46vh;                 /* compact banner — image supports, type leads */
  overflow: hidden;
  border-block: 1px solid var(--rule);
  background: linear-gradient(160deg, var(--from), var(--to));
}
.case-hero img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }

.case-intro {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 8vh 6vw 6vh;
  display: grid;
  grid-template-columns: 1.6fr 0.8fr;
  gap: 6vw;
  align-items: start;
}
@media (max-width: 760px) { .case-intro { grid-template-columns: 1fr; gap: 4vh; } }
.case-intro__lede {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(1.25rem, 2.2vw, 1.7rem);
  line-height: 1.55;
  color: #dad8d4;
}
.case-facts h4 {
  font-family: var(--font-label);
  font-size: 0.66rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--eb-accent);
  margin: 0 0 0.9rem;
}
.case-facts h4 + h4, .case-facts .case-fact + h4 { margin-top: 2rem; }
.case-fact {
  display: block;
  font-family: var(--font-label);
  font-size: 0.82rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--rule);
}

.case-story {
  max-width: 62ch;
  margin: 0 auto;
  padding: 6vh 6vw 8vh;
  text-align: center;
}
.case-story__text {
  margin: 1.2rem 0 0;
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.65;
  color: #cfcdc9;
}

/* ---- Easter-egg link: subliminal small print on the hero ---------- */
.egg-link {
  display: inline-block;
  margin-top: 3.5rem;
  font-family: var(--font-label);
  font-size: 0.6rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  text-decoration: none;
  color: rgba(243, 243, 243, 0.28);
  transition: color 0.4s var(--ease), letter-spacing 0.4s var(--ease);
}
.egg-link:hover { color: var(--eb-accent); letter-spacing: 0.4em; }

/* ==================================================================
   UNIFORM H1 — one headline system across the whole homepage:
   hero, manifesto, dividers and every client line share size, weight,
   case and tracking. (Overrides earlier per-element sizes via source order.)
   ================================================================== */
.caption .line,
.moment__title,
.divider__title,
.manifesto__text {
  font-family: var(--font-label);
  font-size: clamp(2.8rem, 11.76vw, 11.2rem);   /* +40% across the unified H1 system; break-word keeps long words on-screen */
  letter-spacing: 0.01em;
  text-transform: uppercase;
  font-weight: 600;
  line-height: 1.02;
  overflow-wrap: break-word;             /* never let a long word clip off-screen */
}
/* the manifesto wraps to several lines — a touch more leading only */
.manifesto__text { line-height: 1.12; }
/* hero second line — set in the small Oswald label style (matches the
   per-scene meta/credit line), a tracked uppercase tagline under the H1 */
.moment__lead {
  font-family: var(--font-label);
  font-size: clamp(0.72rem, 1vw, 0.9rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  font-weight: 400;
  line-height: 1.6;
  margin-top: 2rem;
}
.moment__lead .eb { color: var(--eb-accent); }
/* type-on caret: a thin bar that blinks while the subtext types, then fades */
.type-caret {
  display: inline-block;
  width: 1px;
  height: 1em;
  margin-left: 0.14em;
  vertical-align: -0.1em;
  background: currentColor;
  opacity: 0.7;
  animation: caretBlink 0.9s steps(1) infinite;
}
.type-caret--done { animation: none; opacity: 0; transition: opacity 0.5s var(--ease); }
@keyframes caretBlink { 50% { opacity: 0; } }

/* ==================================================================
   SCROLL-ILLUMINATE — ALL homepage text. The block-fade is neutralised
   on the journey so engine.js drives every text element's opacity from
   scroll position (dim 0.14 → bright). Baselines set so nothing flashes.
   ================================================================== */
#journey .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
#journey .kicker,
#journey .moment__sub,
#journey .caption .idx-big,
#journey .caption .meta,
#journey .divider__body,
#journey .stat,
#journey .stat__label,
#journey .aside,
#journey .cta { opacity: 0.14; }

/* ==================================================================
   Intro, scroll progress, and the journey exhibit index
   ================================================================== */
/* ---- Intro: the EB mark draws itself in, then reveals the site ---- */
body.intro-active { overflow: hidden; }
#intro {
  position: fixed; inset: 0; z-index: 9999;
  background: #000;
  display: grid; place-items: center;
  transition: opacity 0.7s var(--ease);
}
#intro.intro--out { opacity: 0; pointer-events: none; }
.intro__mark { width: clamp(92px, 13vw, 150px); height: auto; overflow: visible; }
.intro__mark path {
  stroke-dasharray: 230; stroke-dashoffset: 230;
  animation: introDraw 1s var(--ease) forwards;
}
.intro__mark path:nth-child(1) { animation-delay: 0.05s; }
.intro__mark path:nth-child(2) { animation-delay: 0.13s; }
.intro__mark path:nth-child(3) { animation-delay: 0.21s; }
.intro__mark path:nth-child(4) { animation-delay: 0.3s; }
.intro__mark path:nth-child(5) { animation-delay: 0.4s; }
@keyframes introDraw { to { stroke-dashoffset: 0; } }
.intro__bar {
  position: absolute; bottom: 17%; left: 50%; transform: translateX(-50%);
  width: 120px; height: 1px; background: rgba(243,243,243,0.16); overflow: hidden;
}
.intro__bar span { display: block; height: 100%; width: 0; background: var(--eb-accent);
  animation: introBar 1.5s var(--ease) forwards; }
@keyframes introBar { to { width: 100%; } }

/* ---- Top scroll progress hairline -------------------------------- */
#scroll-progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 0;
  background: var(--eb-accent); z-index: 55; pointer-events: none;
}

/* ---- Journey exhibit index (right edge) — clickable catalogue nav -- */
#journey-index {
  position: fixed; right: 2.2vw; top: 50%; transform: translateY(-50%);
  z-index: 45; display: flex; flex-direction: column; gap: 0.85rem; align-items: center;
  mix-blend-mode: difference;
}
#journey-index button {
  width: 7px; height: 7px; padding: 0; border: none; border-radius: 9px; cursor: pointer;
  background: rgba(243,243,243,0.32);
  transition: height 0.35s var(--ease), background 0.35s var(--ease);
}
#journey-index button:hover { background: rgba(243,243,243,0.6); }
#journey-index button.is-active { height: 22px; background: var(--eb-accent); }
@media (max-width: 760px) { #journey-index { display: none; } }

/* ==================================================================
   VIDEO-IN-TYPE HERO — the brand statement at huge scale with the
   showreel playing through the letterforms. Typography IS the design.
   ================================================================== */
.hero-vt .moment__stage { background: #000; display: block; padding: 0; }
.hero-vt__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center; z-index: 0;
}
.hero-vt__svg { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.hero-vt__text {
  font-family: var(--font-label);
  font-weight: 600;
  letter-spacing: 0.005em;
  text-transform: uppercase;
}
/* solid UI (kicker + sub) sits over the black, outside the letterforms */
.hero-vt__ui { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.hero-vt__ui .kicker {
  position: absolute; top: 13vh; left: 0; right: 0; margin: 0;
  text-align: center; color: rgba(243,243,243,0.7);
}
.hero-vt__ui .moment__sub {
  position: absolute; bottom: 12vh; left: 50%; transform: translateX(-50%);
  margin: 0; max-width: 38ch; text-align: center; color: rgba(243,243,243,0.55);
}
.hero-vt .scroll-hint { z-index: 3; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
