/* ============================================================
   FAAV v2.0.64 — "White Cinema"
   Bright paper canvas · monochrome ink UI · video-first screens
   All Gilroy. The films provide the only color.
   ============================================================ */

/* ---------- 1. Fonts ---------- */
@font-face {
  font-family: Gilroy;
  src: url("../assets/fonts/gilroy-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: Gilroy;
  src: url("../assets/fonts/gilroy-regularitalic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: Gilroy;
  src: url("../assets/fonts/gilroy-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: Gilroy;
  src: url("../assets/fonts/gilroy-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- 2. Tokens (monochrome only — no hue in UI) ---------- */
:root {
  --paper: #f6f5f2;
  --paper-deep: #edebe6;
  --ink: #131311;
  --ink-soft: #2c2c28;
  /* Darkened from #7b7973 (2026-07-28): every small uppercase label uses this,
     and at 0.66rem it measured 3.99:1 on --paper — under the 4.5:1 WCAG AA
     minimum, flagged on all 12 pages by Lighthouse. Now 4.75:1. */
  --grey: #6f6d67;
  --line: rgba(19, 19, 17, 0.16);
  --line-strong: rgba(19, 19, 17, 0.6);
  --edge: clamp(1.1rem, 2.4vw, 2.2rem);
  --r: 14px; /* card/media rounding — matches Wild Dreams */
  --ease-site: cubic-bezier(0.25, 0.1, 0.25, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  /* unified reveal timing (Wild Dreams pace, used site-wide) */
  --rv-dur-fade: 1.6s;
  --rv-dur-move: 1.9s;
  --rv-stagger: 90ms;
  /* header color swap while the ink menu is open — tracks the overlay wipe */
  --menu-swap: 0.8s;
}

/* ---------- 3. Base ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scrollbar-width: thin; scrollbar-color: #c9c7c0 var(--paper); }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: Gilroy, sans-serif;
  font-weight: 400;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}
body.no-scroll { overflow: hidden; }

img, svg, video { display: block; max-width: 100%; }
/* width/height HTML attrs are CLS hints only — never a fixed box */
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
::selection { background: var(--ink); color: var(--paper); }

@media (hover: hover) and (pointer: fine) {
  body { cursor: none; }
  body a, body button { cursor: none; }
}

/* ---------- 4. Utility type ---------- */
.label {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--grey);
}
.label--ink { color: var(--ink); }
/* Keeps a meta phrase whole when the label wraps, so "Cel animation" and
   "2D animation" never split across two rows — breaks land at the · only. */
.nb { white-space: nowrap; }
.index-num {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--grey);
  font-variant-numeric: tabular-nums;
}
.rule { border: 0; border-top: 1px solid var(--line); }

/* ---------- 5. Grain (light, multiply) ---------- */
.grain {
  position: fixed;
  /* just enough overhang for the ±2% shift — a bigger surface multiplies
     the per-frame blend cost of mix-blend-mode over animating content */
  inset: -5%;
  width: 110%;
  height: 110%;
  pointer-events: none;
  z-index: 200;
  opacity: 0.045;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 0.9s steps(4) infinite;
}
@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  25% { transform: translate(-2%, 1.5%); }
  50% { transform: translate(1.5%, -2%); }
  75% { transform: translate(-1%, -1%); }
  100% { transform: translate(2%, 2%); }
}

/* ---------- 6. Custom cursor (ink) ---------- */
.cursor-dot, .cursor-ring {
  position: fixed;
  top: 0; left: 0;
  pointer-events: none;
  z-index: 400;
  border-radius: 50%;
  translate: -50% -50%;
  display: none;
}
.cursor-dot { width: 7px; height: 7px; background: var(--ink); transition: background-color 0.3s; }
/* Over the ink menu overlay the cursor flips to paper, else it vanishes */
body.menu-open .cursor-dot { background: var(--paper); }
body.menu-open .cursor-ring { border-color: rgba(246, 245, 242, 0.5); }
.cursor-ring {
  width: 42px; height: 42px;
  border: 1px solid rgba(19, 19, 17, 0.45);
  align-items: center;
  justify-content: center;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  transition: width 0.35s var(--ease-out), height 0.35s var(--ease-out),
    background-color 0.35s, border-color 0.35s, color 0.35s;
  background: transparent;
  color: transparent;
}
.cursor-ring span { transition: opacity 0.25s; opacity: 0; white-space: nowrap; }
.cursor-ring.is-label {
  width: 86px; height: 86px;
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
  mix-blend-mode: normal;
}
.cursor-ring.is-label span { opacity: 1; }
.cursor-ring.is-shrunk { width: 18px; height: 18px; border-color: var(--ink); }
@media (hover: hover) and (pointer: fine) {
  .cursor-dot { display: block; }
  .cursor-ring { display: flex; }
}

/* ---------- 7. Preloader ---------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
}
.preloader-inner { text-align: center; }
.preloader-logo { width: clamp(6rem, 12vw, 10rem); margin: 0 auto 1.3rem; overflow: hidden; }
.preloader-logo .logo-mark { fill: var(--ink); }
.preloader-count {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.3em;
  color: var(--grey);
  font-variant-numeric: tabular-nums;
}

/* ---------- 8. Page wipes (ink over paper) ---------- */
.wipe {
  position: fixed;
  inset: 0;
  z-index: 450;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  pointer-events: none;
}
.wipe--under { background: var(--paper-deep); z-index: 449; }

/* First-paint dip cover: an inline <head> script stamps data-dip from the
   sessionStorage handshake BEFORE first paint (deferred global.js runs too
   late and let one paper frame flash through — visible on ink arrivals from
   the menu). global.js swaps this for the animated .dip element and removes
   the attribute in the same synchronous pass. */
html[data-dip] body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 451;
  pointer-events: none;
}
html[data-dip="ink"] body::after { background: var(--ink); }
html[data-dip="paper"] body::after { background: var(--paper); }

/* ---------- 8b. Reveal system (fade + small rise, site-wide) ---------- */
.rv {
  --rv-distance: 30px;
  opacity: 0;
  transform: translate3d(0, var(--rv-distance), 0);
  will-change: opacity, transform;
}
.rv.rv-media { --rv-distance: 44px; }
/* word-by-word reveals (home meta titles): inline words ride the same .rv
   engine with a shorter travel so small type doesn't jump */
.fx-w { display: inline-block; }
.fx-w.rv { --rv-distance: 0.7em; }
/* transition lives on .in only: arming .rv (JS adds it after first paint on
   pages without baked classes) must snap to hidden instantly — with the
   transition on .rv the hide itself animated, a visible drop-then-rise */
.rv.in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--rv-dur-fade) var(--ease-out),
    transform var(--rv-dur-move) var(--ease-out);
  transition-delay: var(--rv-delay, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; will-change: auto; }
}

/* ---------- 9. Header ---------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(var(--edge) * 0.65) var(--edge);
  background: var(--paper);
  transition: background-color var(--menu-swap) var(--ease-out);
}
/* While the ink menu overlay is open, the bar goes transparent and its
   contents flip to paper so the header reads paper-on-ink.
   menu-closing holds it transparent through the whole close too — without
   it the paper bar fades in over the departing ink and the top strip
   flashes white -> black -> white */
body.menu-open .site-header,
body.menu-closing .site-header {
  background: transparent;
}
body.menu-open .header-logo .logo-mark { fill: var(--paper); }
body.menu-open .header-center { color: rgba(246, 245, 242, 0.55); }
/* overflow visible: the viewBox hugs the glyphs, so subpixel rounding can
   shave the outer edge of the letters at some zoom levels */
.header-logo svg { width: clamp(3.6rem, 5vw, 4.8rem); overflow: visible; }
.header-logo .logo-mark { fill: var(--ink); transition: fill var(--menu-swap) var(--ease-out); }
.header-center { position: absolute; left: 50%; translate: -50% 0; transition: color var(--menu-swap) var(--ease-out); }
.header-right { display: flex; align-items: center; gap: 1.8rem; }
/* FR/EN switch — visual only for now, wiring comes with the FR version */
.lang-toggle {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--grey);
}
.lang-toggle span { transition: color var(--menu-swap) var(--ease-out); }
.lang-toggle .lang-active { color: var(--ink); font-weight: 700; }
body.menu-open .lang-toggle span { color: rgba(246, 245, 242, 0.45); }
body.menu-open .lang-toggle .lang-active { color: var(--paper); }
.menu-toggle {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink);
  transition: color var(--menu-swap) var(--ease-out);
}
.menu-toggle-lines { width: 24px; height: 11px; position: relative; }
.menu-toggle-lines i {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: var(--ink);
  transition: transform 0.5s var(--ease-out), top 0.5s var(--ease-out),
    background-color var(--menu-swap) var(--ease-out);
}
.menu-toggle-lines i:first-child { top: 0; }
.menu-toggle-lines i:last-child { top: 9px; }
body.menu-open .menu-toggle-lines i:first-child { top: 4.5px; transform: rotate(45deg); }
body.menu-open .menu-toggle-lines i:last-child { top: 4.5px; transform: rotate(-45deg); }

/* Menu overlay: ink panel, paper type (inverse of the page) */
.menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 290;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 var(--edge);
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
}
body.menu-open .menu-toggle, body.menu-open .menu-toggle-lines i { color: var(--paper); }
body.menu-open .menu-toggle-lines i { background: var(--paper); }

/* While the menu closes, the header FADES out with the departing ink (GSAP
   in closeMenu) and fades back in as the page is revealed — one unified
   exit instead of the header cross-fading beside the wipe. */

.menu-links { display: flex; flex-direction: column; gap: 0.2em; }
.menu-link {
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
  font-size: clamp(2.4rem, 7vw, 5.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.05;
  text-transform: uppercase;
  overflow: hidden;
}
.menu-link .index-num { color: rgba(246, 245, 242, 0.4); }
.menu-link-txt { display: inline-block; transition: opacity 0.3s, transform 0.5s var(--ease-out); }
.menu-link:hover .menu-link-txt { opacity: 0.55; transform: translateX(0.35em); }

/* Projects dropdown (accordion inside the menu) */
.menu-link--toggle { width: 100%; text-align: left; }
.menu-caret {
  font-size: 0.5em;
  font-weight: 600;
  color: rgba(246, 245, 242, 0.4);
  transition: transform 0.5s var(--ease-out), color 0.3s;
}
/* the caret travels with the sliding text on hover so they never overlap */
.menu-link:hover .menu-caret { transform: translateX(0.35em); }
.menu-group.open .menu-caret { transform: rotate(45deg); color: var(--paper); }
.menu-group.open .menu-link--toggle:hover .menu-caret { transform: translateX(0.35em) rotate(45deg); }
.menu-sub {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.7s var(--ease-out);
}
.menu-group.open .menu-sub { grid-template-rows: 1fr; }
.menu-sub-inner {
  overflow: hidden;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15em;
}
.menu-sublink {
  margin-left: 2.4rem; /* sits under .menu-link-txt (index-num + gap) */
  font-size: clamp(1.5rem, 3.5vw, 2.8rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.75);
  opacity: 0;
  translate: 0 0.4em;
  transition: opacity 0.6s var(--ease-out), translate 0.7s var(--ease-out),
    color 0.3s, transform 0.5s var(--ease-out);
}
.menu-sub-inner .menu-sublink:first-child { margin-top: 0.35em; }
.menu-sub-inner .menu-sublink:last-child { margin-bottom: 0.3em; }
.menu-group.open .menu-sublink { opacity: 1; translate: 0 0; }
.menu-group.open .menu-sublink:nth-child(2) { transition-delay: 60ms; }
.menu-sublink:hover { color: var(--paper); transform: translateX(0.35em); }
.menu-foot {
  position: absolute;
  bottom: var(--edge);
  left: var(--edge);
  right: var(--edge);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}
.menu-foot a {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.55);
  transition: color 0.3s;
}
.menu-foot a:hover { color: var(--paper); }

/* ---------- 10. Cinema screens ---------- */
.screen-sec {
  position: relative; /* anchors .hero-copy at slide level, not frame level */
  height: 100svh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--edge);
}
/* Every screen snaps to the viewport top, so all four clear the fixed header equally */
.screen-sec { padding-top: calc(var(--edge) * 0.65 + 3.2rem); }
.screen {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  background: var(--paper-deep);
  display: block;
}
.screen img, .screen mux-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.06);
}
/* mux-video's shadow <video> defaults to contain; force the element itself
   to cover the screen at 16:9 so no paper bands show. Centered with the
   `translate` property (not transform — GSAP owns transform for the zoom;
   auto margins left-align once the element outgrows the frame) */
.screen mux-video {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: auto;
  height: auto;
  min-width: 100%;
  min-height: 100%;
  aspect-ratio: 16 / 9;
  transform: none;
  --media-object-fit: cover;
}
/* Hero intro copy over the showreel loop */
.screen-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, rgba(12, 12, 10, 0.55), transparent 55%);
  pointer-events: none;
}
/* Lives OUTSIDE .screen (direct child of .screen-sec): during deck moves
   home.js counter-translates it so it stays put and the slide's own
   overflow clip cuts it exactly at the incoming frame's edge.
   --hero-meta = measured gap between frame bottom and slide bottom (JS). */
.hero-copy {
  position: absolute;
  left: calc(var(--edge) + clamp(1.2rem, 3vw, 2.8rem));
  right: calc(var(--edge) + clamp(1.2rem, 3vw, 2.8rem));
  bottom: calc(var(--hero-meta, 3.7rem) + clamp(1.3rem, 3.4vw, 3rem));
  z-index: 2;
  color: var(--paper);
  pointer-events: none;
}
.hero-copy-kicker {
  display: block;
  margin-bottom: 1rem;
  color: rgba(246, 245, 242, 0.65);
}
.hero-copy-title {
  font-size: clamp(1.5rem, 3.5vw, 3.3rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.015em;
}
.hero-copy-title .line { display: block; overflow: hidden; }
.hero-copy-title .word { display: inline-block; white-space: nowrap; }
.hero-copy-title .char { display: inline-block; }
/* FR counterpart of .hero-accent — italic cut with it (2026-07-28). The span it
   targets in fr/index.html is empty and only there to keep the FR hero's
   three-line rhythm, so this rule was already dead weight. */
/* inline accent so "motion design" keeps the bold title weight while only the
   trailing phrase gets the light italic flourish */
/* .hero-accent used to set the "is our thing." tail in a lighter italic. Cut
   2026-07-28 per Mathieu — the headline now reads in one uniform weight. The
   span is kept in the markup so the accent is one rule away if it comes back. */

.screen--paper {
  background: var(--paper);
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  text-align: center;
}
.screen--paper .big-invite {
  font-size: clamp(2.4rem, 7.5vw, 7rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.03em;
  line-height: 1;
}
.screen--paper .big-invite em { font-weight: 400; }
.screen-meta {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.95rem 0.1rem 1.1rem;
}
.screen-title { font-size: 1rem; font-weight: 600; letter-spacing: 0.01em; }
.screen-title .label { margin-left: 0.7rem; }
.screen-counter {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  font-variant-numeric: tabular-nums;
}

/* ---------- 10b. Home slide deck (vertical Swiper, V2 pattern) ---------- */
body.home-slides { height: 100svh; overflow: hidden; }
.swiper { height: 100svh; }
.swiper-slide { height: 100svh; overflow: hidden; }
.swiper-slide .screen-sec { height: 100%; }

/* Generic full-height slide wrapper: content keeps its original section
   styling (own horizontal padding); meta bar pinned to the bottom */
.slide-sec {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding-top: calc(var(--edge) * 0.65 + 3.2rem);
}
.slide-sec .screen-meta { margin-top: auto; padding-left: var(--edge); padding-right: var(--edge); }

/* Combined About + Services + Contact slide:
   About locks to the top, contact block locks to the bottom,
   the services list flexes and compresses in between */
.omni {
  height: 100%;
  min-height: 0;
  padding: 0 var(--edge) calc(var(--edge) * 0.8);
  display: flex;
  flex-direction: column;
  justify-content: space-between; /* free air splits evenly between the three zones */
}
.omni-about .about-statement {
  margin-top: 0.9rem;
  /* mockup scale: large editorial statement, capped by height on short screens;
     runs the full row width like an h1 (no measure cap) */
  font-size: clamp(1.3rem, min(2.7vw, 3.5vh), 2.6rem);
  line-height: 1.27;
}
/* What we do + Clients, side by side */
.omni-mid {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 6rem);
  align-items: stretch; /* clients column matches the services column height */
  padding: clamp(0.6rem, 1.4vh, 1.1rem) 0; /* symmetric: gap below matches gap above */
}
.omni-clients, .omni-services { display: flex; flex-direction: column; min-height: 0; }
/* both columns share one system: label + uppercase list; rows are
   CONTENT-sized (no stretch) so 10 service rows and 10 client rows are
   pixel-identical — hairlines align 1:1 and both lists end on the same
   line, with the closer note hanging right below SKYN */
.omni-mid .services-list { flex: 0 0 auto; margin-top: clamp(0.5rem, 1.4vh, 1rem); border-top: 0; grid-template-columns: 1fr; column-gap: 0; }
.omni-mid .service-row { padding: clamp(0.3rem, 1vh, 0.7rem) 0; align-items: center; } /* hairline rows (base border-bottom) + hover invert */
.omni-mid .service-name { font-size: clamp(0.85rem, 1.9vh, 1.25rem); }
/* clients closer: quiet italic sign-off, full row width, no hover invert
   (.services-list prefix outranks the later .service-row border rule) */
.services-list .clients-closer { grid-column: 1 / -1; border-bottom: 0; }
/* home About slide: the closer lives in the clients COLUMN but outside
   the row grid — snug under SKYN, and it can never inflate the rows */
.omni-clients .clients-closer { margin-top: 0.6rem; }
.clients-closer::before { display: none; }
/* typography sits on the CONTAINER (not the span): otherwise the parent's
   16px strut sets each line box and the small italic gets huge line gaps */
.clients-closer {
  font-style: italic;
  color: var(--grey);
  font-size: clamp(0.75rem, 1.6vh, 1rem);
  line-height: 1.45;
}

/* shorter screens: lists and notes step down so the contact block always
   clears the fold (full scale needs ~1050px of height) */
@media (max-height: 1050px) {
  .omni-mid .service-row { padding: clamp(0.1rem, 0.45vh, 0.3rem) 0; }
  .omni-mid .service-name { font-size: clamp(0.68rem, 1.35vh, 0.9rem); }
}
/* badge/crest marks carry tiny lettering — they need a taller cap than the
   wordmarks to read at the same optical size */
.omni-contact {
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2.6vh, 2rem);
}
.omni-contact .footer-cta {
  margin: 0.5rem 0 0;
  font-size: clamp(2.2rem, min(9vh, 7vw), 7rem);
  line-height: 0.95;
}
.omni-contact .footer-grid {
  border-top: 1px solid var(--line);
  padding-top: clamp(0.8rem, 1.8vh, 1.3rem);
  /* mirrors .omni-mid's two columns so SOCIALS starts exactly under
     CLIENTS, and STUDIO's right edge meets the right-flush logo column */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 6rem);
}
/* The hairline under SAY HELLO / ÉCRIVEZ-NOUS used to snap in while everything
   around it cascaded. A border-top can't be transformed, so home.js swaps it for
   a ::before — but the ::before rides the SAME fade+rise as every other .rv on
   the slide (values mirrored from §8b), not a treatment of its own. It can't
   just BE a .rv: that class would have to sit on .footer-grid and would fade the
   whole block, including columns that already have their own delays.
   The plain border stays the default, so with JS off or reduced motion the line
   is simply there — .fx-rule is only ever added when the animation will run. */
.omni-contact .footer-grid.fx-rule { border-top-color: transparent; position: relative; }
.omni-contact .footer-grid.fx-rule::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
  opacity: 0;
  transform: translate3d(0, 30px, 0);
  will-change: opacity, transform;
}
.omni-contact .footer-grid.fx-rule.in::before {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--rv-dur-fade) var(--ease-out),
    transform var(--rv-dur-move) var(--ease-out);
  transition-delay: var(--rv-delay, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .omni-contact .footer-grid.fx-rule::before { opacity: 1; transform: none; transition: none; }
}
.omni-contact .footer-col:nth-child(2) { grid-column: 2; grid-row: 1; justify-self: start; }
.omni-contact .footer-col:nth-child(3) { grid-column: 2; grid-row: 1; justify-self: end; }
.omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.75rem; }

/* Right-rail dots — centered in the gutter between frame and screen edge */
.deck-dots {
  position: fixed;
  right: 0;
  top: 50%;
  translate: 0 -50%;
  z-index: 300;
  width: var(--edge);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}
.deck-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.22;
  transition: opacity 0.35s, transform 0.35s var(--ease-out);
}
.deck-dot.is-active { opacity: 1; transform: scale(1.35); }

/* ---------- 11. Statement / about ---------- */
.about {
  padding: calc(var(--edge) * 4) var(--edge) calc(var(--edge) * 3);
  max-width: 66rem;
}
.about-statement {
  margin-top: 1.8rem;
  font-size: clamp(1.5rem, 3.6vw, 3rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.015em;
}
.about-statement .line { display: block; }
.about-statement em { font-weight: 400; }

/* Studio page only (.blog-head is shared with the blog listing — keep scoped):
   more breathing room above the kicker, tighter gap between the h1 and the
   statement, and no top rule on the services lists. */
[data-page="studio"] .blog-head {
  padding-top: calc(var(--edge) * 0.65 + 4rem + 11vh);
  padding-bottom: calc(var(--edge) * 0.8);
}
[data-page="studio"] .about { padding-top: var(--edge); }
[data-page="studio"] .services-list { border-top: 0; }
/* drop the two footer rules (above "New business" + above the contact grid)
   and pull the whole footer up a little (was --edge * 2.6). */
[data-page="studio"] .site-footer { border-top: 0; padding-top: calc(var(--edge) * 1.3); }
[data-page="studio"] .footer-grid { border-top: 0; }

/* ---------- 12. Services ---------- */
.services { padding: 0 var(--edge) calc(var(--edge) * 3); }
.services-list {
  margin-top: 1.4rem;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: clamp(2rem, 6vw, 6rem);
}
.service-row {
  display: flex;
  align-items: baseline;
  gap: 1.3rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--line);
  position: relative;
  overflow: hidden;
}
.service-row::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.4s var(--ease-out);
}
.service-row:hover::before { transform: scaleY(1); }
.service-row > * { position: relative; z-index: 1; transition: color 0.3s; }
.service-row:hover .service-name, .service-row:hover .index-num { color: var(--paper); }
.service-name {
  font-size: clamp(1.05rem, 1.9vw, 1.5rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0;
}

/* ---------- 13. Footer ---------- */
.site-footer {
  padding: calc(var(--edge) * 2.6) var(--edge) var(--edge);
  border-top: 1px solid var(--line);
}
.footer-cta {
  display: block;
  font-size: clamp(2.8rem, 10.5vw, 10rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.03em;
  line-height: 0.95;
  margin: 1.3rem 0 2.6rem;
}
.footer-cta .arrow { display: inline-block; transition: transform 0.5s var(--ease-out); }
.footer-cta:hover .arrow { transform: translate(0.12em, -0.12em); }
.footer-grid {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.footer-col { display: flex; flex-direction: column; gap: 0.4rem; }
.footer-col a, .footer-col span { font-size: 0.82rem; letter-spacing: 0.05em; color: var(--grey); transition: color 0.3s; }
.footer-col a:hover { color: var(--ink); }
.footer-col .label { margin-bottom: 0.35rem; }
.footer-legal { margin-top: 2.6rem; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
.footer-legal span { font-size: 0.66rem; letter-spacing: 0.16em; color: var(--grey); text-transform: uppercase; }

/* ---------- 14. Lightbox ---------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 420;
  background: rgba(19, 19, 17, 0.93);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--edge);
}
.lightbox.is-open { display: flex; }
.lightbox-frame { width: min(100%, 168svh); aspect-ratio: 16 / 9; background: #000; }
.lightbox-frame iframe { width: 100%; height: 100%; border: 0; }
.lightbox-frame mux-player { width: 100%; height: 100%; --controls-backdrop-color: rgba(19, 19, 17, 0.4); }
.lightbox-close {
  position: absolute;
  top: var(--edge);
  right: var(--edge);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--paper);
}

/* ---------- 15. Project pages ---------- */
.proj-hero {
  position: relative;
  height: 100svh;
  display: flex;
  flex-direction: column;
  padding: calc(var(--edge) * 0.65 + 3.2rem) var(--edge) 0;
}
.proj-hero-screen {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  background: var(--paper-deep);
}
.proj-hero-screen img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.08); }
/* same cover recipe as .screen mux-video: translate-property centering */
.proj-hero-screen mux-video {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: auto;
  height: auto;
  min-width: 100%;
  min-height: 100%;
  aspect-ratio: 16 / 9;
  transform: scale(1.08);
}
.proj-hero-titlebar {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 1rem 0.1rem 1.2rem;
}
.proj-title {
  font-size: clamp(1.6rem, 3.6vw, 3rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
}
.proj-title .line { display: inline-block; overflow: hidden; vertical-align: bottom; }
.proj-hero-meta { display: flex; gap: 1.6rem; align-items: baseline; }

.proj-player-section { padding: calc(var(--edge) * 2) var(--edge); }
.proj-player {
  aspect-ratio: 16 / 9;
  background: #000;
  transform-origin: center;
}
.proj-player iframe { width: 100%; height: 100%; border: 0; }

.proj-synopsis {
  padding: calc(var(--edge) * 1.5) var(--edge) calc(var(--edge) * 3);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2.2fr);
  gap: 2rem;
  max-width: 86rem;
}
.proj-synopsis p {
  font-size: clamp(1.15rem, 2.1vw, 1.7rem);
  font-weight: 600;
  line-height: 1.35;
}
.proj-synopsis .line { display: block; overflow: hidden; }

.proj-strip-section { padding: calc(var(--edge) * 1.6) 0; overflow: clip; }
.proj-strip-head {
  padding: 0 var(--edge) 1.3rem;
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
}
.proj-strip-head h2 {
  font-size: clamp(1.2rem, 2.4vw, 2rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.01em;
}
.proj-strip {
  display: flex;
  gap: clamp(1rem, 2.2vw, 1.8rem);
  padding: 0 var(--edge);
  width: max-content;
  will-change: transform;
}
.proj-strip img {
  height: clamp(12rem, 30vw, 23rem);
  width: auto;
  background: #fff;
  padding: 0.55rem;
  border: 1px solid var(--line);
}
.proj-strip--frames img { height: clamp(14rem, 36vw, 28rem); }

.proj-lottie-row {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 2.2vw, 1.8rem);
  padding: 0 var(--edge);
}
.proj-lottie {
  flex: 1 1 15rem;
  aspect-ratio: 1;
  background: #fff;
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  overflow: hidden;
}
.proj-lottie > div { width: 86%; height: 86%; }

.proj-bike-scene { position: relative; padding: calc(var(--edge) * 1.6) 0; overflow: clip; }
.proj-bike { width: clamp(15rem, 36vw, 28rem); margin-left: -40vw; }

.proj-credits { padding: calc(var(--edge) * 3) var(--edge); text-align: center; }
.proj-credits p {
  margin-top: 1.1rem;
  font-size: clamp(1.15rem, 2.2vw, 1.8rem);
  font-weight: 600;
}
.proj-credits em { font-weight: 400; }

.next-project {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 80svh;
  border-top: 1px solid var(--line);
  overflow: clip;
  text-align: center;
  padding: var(--edge);
  background: var(--paper);
  transition: background-color 0.5s;
}
.next-project:hover { background: var(--ink); }
.next-project-bg { position: absolute; inset: var(--edge); opacity: 0; transition: opacity 0.7s; }
.next-project-bg img { width: 100%; height: 100%; object-fit: cover; }
.next-project-inner { position: relative; z-index: 1; transition: color 0.5s; }
.next-project:hover .next-project-inner { color: var(--paper); }
.next-project:hover .next-project-inner .label { color: rgba(246, 245, 242, 0.6); }
.next-title {
  font-size: clamp(2.6rem, 9vw, 8.5rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.03em;
  line-height: 0.95;
  margin-top: 0.9rem;
}

.proj-footer {
  border-top: 1px solid var(--line);
  padding: var(--edge);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}
.proj-footer a, .proj-footer span {
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--grey);
}
.proj-footer a:hover { color: var(--ink); }

/* ---------- 15b. Blog ---------- */
.blog-head {
  padding: calc(var(--edge) * 0.65 + 3.2rem + 4vh) var(--edge) calc(var(--edge) * 1.5);
  max-width: 72rem;
}
.blog-head h1 {
  margin-top: 1rem;
  font-size: clamp(1.8rem, 4.6vw, 4rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
}
.blog-head h1 .line { display: block; overflow: hidden; }

.post-list { padding: 0 var(--edge) calc(var(--edge) * 3); }
.post-entry {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(1.6rem, 4vw, 4rem);
  align-items: center;
  padding: clamp(1.6rem, 4vh, 3rem) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.post-entry-media {
  overflow: hidden;
  border-radius: var(--r);
}
.post-entry-media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  transition: transform 1.4s var(--ease-out);
}
.post-entry:hover .post-entry-media img { transform: scale(1.03); }
.post-entry-meta { display: flex; gap: 1.6rem; margin-bottom: 1rem; }
.post-entry h2 {
  font-size: clamp(1.4rem, 2.8vw, 2.4rem);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.015em;
}
.post-entry p { margin-top: 0.9rem; color: var(--grey); font-size: clamp(0.95rem, 1.3vw, 1.05rem); }
.post-entry-read {
  display: inline-block;
  margin-top: 1.4rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 0.25em;
  transition: opacity 0.3s;
}
.post-entry:hover .post-entry-read { opacity: 0.55; }

/* Article */
.article-head {
  padding: calc(var(--edge) * 0.65 + 3.2rem + 4vh) var(--edge) calc(var(--edge) * 1.2);
  /* same container as .article-player's proj-player (100rem centered) so the
     title/deck left edge lines up with the video below */
  max-width: calc(100rem + var(--edge) * 2);
  margin: 0 auto;
}
.article-head h1 {
  margin-top: 1rem;
  font-size: clamp(1.9rem, 4.8vw, 4.2rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
}
.article-deck {
  margin-top: 1.4rem;
  font-size: clamp(1.05rem, 1.9vw, 1.5rem);
  font-weight: 600;
  color: var(--grey);
  max-width: 44rem;
}
.article-player { padding: calc(var(--edge) * 0.8) var(--edge) calc(var(--edge) * 1.5); }
.article-player .proj-player { position: relative; max-width: 100rem; margin: 0 auto; border-radius: var(--r); overflow: hidden; }
/* poster instead of an embedded iframe: the custom Play cursor can render
   over it, and clicking opens the global lightbox */
.article-player .proj-player img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease-out);
}
.article-player:hover .proj-player img { transform: scale(1.025); }

/* Poster play button — the touch-device stand-in for the custom Play cursor.
   Matches the .film-play pill on the project pages. Shown by default so that
   anything without a fine pointer (and any no-CSS/legacy case) still gets the
   affordance; the query below removes it where the cursor takes over. */
.poster-play {
  position: absolute;
  inset: 0;
  width: 100%;
  border: 0;
  padding: clamp(1.2rem, 2.5vw, 3rem);
  background: linear-gradient(to top, rgba(13, 15, 13, 0.25), transparent 40%);
  display: flex;
  align-items: flex-end;
  cursor: pointer;
}
.poster-play span {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  background: #fff;
  border-radius: 100px;
  padding: 0.95rem 1.7rem;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.poster-play svg { width: 9px; height: 11px; }
@media (hover: hover) and (pointer: fine) {
  .poster-play { display: none; }
}

.article-body {
  padding: 0 var(--edge) calc(var(--edge) * 2);
  max-width: 46rem;
  margin: 0 auto;
}
.article-body h2 {
  margin: clamp(2rem, 5vh, 3.4rem) 0 0.9rem;
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.article-body p { margin: 0 0 1.1rem; font-size: clamp(0.98rem, 1.25vw, 1.1rem); line-height: 1.65; color: var(--ink-soft); }
.article-body figure {
  margin: clamp(1.8rem, 4.5vh, 3rem) 0;
  border-radius: var(--r);
  overflow: hidden;
}
.article-body figure img { width: 100%; }
.article-outro {
  border-top: 1px solid var(--line);
  margin-top: clamp(2rem, 5vh, 3.4rem);
  padding-top: 1.4rem;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}
.article-outro a, .article-outro span {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--grey);
}
.article-outro a:hover { color: var(--ink); }

@media (max-width: 767px) {
  .post-entry { grid-template-columns: 1fr; }
  /* Standalone page footer (studio / project / blog): the flex row wrapped
     STUDIO onto its own line under CONTACT, so it sat lower than the other two
     columns. Pin it to three top-aligned columns and let the type step down with
     the viewport — same treatment the home About slide's footer already uses. */
  .footer-grid {
    display: grid;
    grid-template-columns: repeat(3, auto);
    justify-content: space-between;
    align-items: start;
    gap: clamp(0.6rem, 2.5vw, 2rem);
  }
  .footer-col a, .footer-col span { font-size: clamp(0.58rem, 2.9vw, 0.82rem); }
  .footer-col .label { white-space: nowrap; }
}

/* ---------- 16. 404 ---------- */
.nf-wrap { min-height: 100svh; display: grid; place-items: center; text-align: center; padding: var(--edge); }
.nf-wrap h1 { font-size: clamp(4rem, 18vw, 16rem); font-weight: 700; line-height: 1; }
.nf-wrap a { font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; font-size: 0.8rem; border-bottom: 1px solid var(--ink); padding-bottom: 0.2em; }

/* ---------- 17. Responsive ---------- */
@media (max-width: 767px) {
  .screen-sec { height: 86svh; }
  /* counter top-right whatever the title length: no wrap, the title column
     absorbs the extra lines; baseline keeps the counter on the title's
     FIRST-line baseline (true alignment, no padding nudge) */
  .screen-meta { flex-wrap: nowrap; align-items: baseline; }
  .screen-title { flex: 1 1 auto; }
  .screen-counter { flex: 0 0 auto; }
  .proj-synopsis { grid-template-columns: 1fr; }
  .services-list { grid-template-columns: 1fr; }

  /* menu contact block: stacked left-aligned column instead of the
     space-between wrap that scattered the links */
  .menu-foot { flex-direction: column; align-items: flex-start; gap: 0.7rem; }

  /* about slide: everything compresses so the contact block always fits
     above the fold — sections stack full-width (What we do, then Clients)
     but each LIST runs two columns, so 22 rows fold down to 11 and the
     right half of the screen carries content instead of dead air */
  .omni-about .about-statement { font-size: 1rem; line-height: 1.45; }
  /* the band absorbs the slide's free height and spreads it evenly around
     the two sections, so no dead air pools above/below the band */
  .omni-mid { flex: 1 1 auto; grid-template-columns: 1fr; align-content: space-evenly; gap: 1.4rem; padding: 0.3rem 0; }
  .omni-mid .services-list { flex: 0 0 auto; grid-template-columns: 1fr 1fr; column-gap: 1.4rem; margin-top: 0.4rem; }
  .omni-mid .service-row { padding: 0.34rem 0; }
  .omni-mid .service-name { font-size: 0.66rem; letter-spacing: 0; }
  .clients-closer { font-size: 0.66rem; }
  .omni-contact { gap: 0.9rem; }
  .omni-contact .footer-cta { font-size: 2rem; }
  .omni-contact .footer-grid { gap: 0.9rem 1.2rem; } /* column gap matches .omni-mid so SOCIALS stays under CLIENTS */
  .omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.7rem; }
  /* NOTE: cols 2 and 3 deliberately share one grid cell (start + end). That held
     until the FR footer labels were translated to "RÉSEAUX SOCIAUX", wide enough
     to collide with STUDIO at phone widths, so an [lang="fr-CA"] override used to
     drop STUDIO onto its own row. The FR labels are back to "Socials"/"Studio",
     so the override is gone (2026-07-28) and FR matches the approved EN layout —
     all three columns top-aligned. Reinstate it only if those labels get long
     again. */
}

/* tall narrow windows: the phone-tuned sizes leave big dead gaps above
   What-we-do and below Clients — step everything (about included) UP so
   the column fills the height and the slide reads balanced */
@media (min-width: 500px) and (max-width: 767px) and (min-height: 900px) {
  .omni-about .about-statement { font-size: 1.1rem; line-height: 1.42; }
  .omni-mid { gap: 0.8rem; padding: 0.7rem 0; }
  .omni-mid .services-list { margin-top: 0.45rem; }
  .omni-mid .service-row { padding: 0.18rem 0; }
  .omni-mid .service-name { font-size: 0.72rem; }
  .omni-contact { gap: 1rem; }
  .omni-contact .footer-cta { font-size: 2.4rem; }
  .omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.75rem; }
}
@media (max-width: 499px) and (min-height: 860px) {
  .omni-about .about-statement { font-size: 1rem; }
  .omni-mid { gap: 0.7rem; padding: 0.5rem 0; }
  .omni-mid .service-row { padding: 0.14rem 0; }
  .omni-mid .service-name { font-size: 0.66rem; }
  .omni-contact .footer-cta { font-size: 2.2rem; }
}
/* mid-height phones (721-859px): the one gap in the tier ladder — too tall for
   the max-height:720 step, too short for min-height:860 — so it ran at base
   size and overflowed (EN by ~16px, FR more, since French runs longer and
   wraps the statement to 6 lines and the closer to 3). Step the prose down;
   the lists are untouched so the equal-height contract is unaffected. */
@media (max-width: 499px) and (min-height: 721px) and (max-height: 859px) {
  .omni-about .about-statement { font-size: 0.88rem; line-height: 1.4; }
  .omni-mid { gap: 0.5rem; padding: 0.35rem 0; }
  .omni-mid .service-row { padding: 0.1rem 0; }
  .omni-mid .service-name { font-size: 0.6rem; }
  .clients-closer { font-size: 0.6rem; }
  .omni-contact { gap: 0.7rem; }
  .omni-contact .footer-cta { font-size: 1.9rem; }
  .omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.66rem; }
}
@media (min-width: 500px) and (max-width: 767px) and (min-height: 1140px) {
  .omni-about .about-statement { font-size: 1.35rem; line-height: 1.4; }
  .omni-mid { gap: 1rem; padding: 1rem 0; }
  .omni-mid .services-list { margin-top: 0.6rem; }
  .omni-mid .service-row { padding: 0.28rem 0; }
  .omni-mid .service-name { font-size: 0.85rem; }
  .omni-contact { gap: 1.2rem; }
  .omni-contact .footer-cta { font-size: 2.8rem; }
  .omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.8rem; }
}

/* short phones: the 22 stacked rows need one more step down */
@media (max-width: 767px) and (max-height: 720px) {
  .omni-about .about-statement { font-size: 0.78rem; line-height: 1.35; }
  .omni-mid { gap: 0.4rem; padding: 0.3rem 0; }
  .omni-mid .service-row { padding: 0.03rem 0; }
  .omni-mid .service-name { font-size: 0.53rem; }
  .omni-contact { gap: 0.5rem; }
  .omni-contact .footer-cta { font-size: 1.6rem; }
  .omni-contact .footer-grid { padding-top: 0.5rem; }
  .omni-contact .footer-col a, .omni-contact .footer-col span { font-size: 0.62rem; }
}

/* ---------- 18. Scroll-driven scrubs (compositor-side; JS fallback in project.js) ---------- */
@supports (animation-timeline: view()) {
  .proj-hero-screen img, .proj-hero-screen mux-video {
    /* the animated `scale` property replaces the static base zoom — kill the
       transform so the two don't compose to ~1.16 */
    transform: none;
    animation: sd-hero linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  .proj-player {
    animation: sd-player linear both;
    animation-timeline: view();
    animation-range: entry 20% entry 100%;
  }
  .proj-strip[data-dir="left"] {
    animation: sd-strip-left linear both;
    animation-timeline: view();
    animation-range: cover;
  }
  .proj-strip[data-dir="right"] {
    animation: sd-strip-right linear both;
    animation-timeline: view();
    animation-range: cover;
  }
}
@keyframes sd-hero { from { scale: 1.08; } to { scale: 1; } }
@keyframes sd-player { from { scale: 0.9; } to { scale: 1; } }
@keyframes sd-strip-left { from { transform: translateX(0); } to { transform: translateX(calc(100vw - 100%)); } }
@keyframes sd-strip-right { from { transform: translateX(calc(100vw - 100%)); } to { transform: translateX(0); } }

/* ---------- 19. Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .grain { animation: none; }
  *, *::before, *::after { transition-duration: 0.01ms !important; }
  .screen img, .screen mux-video, .proj-hero-screen img, .proj-hero-screen mux-video { transform: none; }
  .proj-hero-screen img, .proj-hero-screen mux-video, .proj-player, .proj-strip { animation: none; }
}
