/* ==========================================================================
   MergeXR Studio, Hayler-class motion system
   Dark canvas · single primary #0087FE · 0-radius · masked reveals
   Motion lives in GSAP (site.js). No CSS transitions on transforms.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* ── Surfaces ─────────────────────────────────────── */
  --canvas:      #0A0C10;
  --surface:     #101318;
  --surface-hi:  #171B22;
  --void:        #05070A;

  /* ── Type ─────────────────────────────────────────── */
  --ink:    #F2F5F8;
  --muted:  rgba(242, 245, 248, 0.48);
  --faint:  rgba(242, 245, 248, 0.26);

  /* Theme-invariant foreground for copy placed directly on photography. */
  --media-ink:   #F2F5F8;
  --media-muted: rgba(242, 245, 248, 0.76);
  --media-faint: rgba(242, 245, 248, 0.58);
  --media-accent: #0087FE;

  /* ── Rules ────────────────────────────────────────── */
  --rule:        rgba(242, 245, 248, 0.10);
  --rule-strong: rgba(242, 245, 248, 0.20);

  /* ── Primary ramp, all from #0087FE ──────────────── */
  --blue-100: #9CD0FF;
  --blue-300: #4FA9FF;
  --blue-500: #0087FE;
  --blue-700: #0062BC;
  --blue-900: #013560;

  --blue-tint:   rgba(0, 135, 254, 0.12);
  --blue-tint-2: rgba(0, 135, 254, 0.24);
  --blue-glow:   0 0 48px rgba(0, 135, 254, 0.35);
  --on-blue:     #04121F;
  --theme-accent: var(--blue-500);

  /* ── Type scale (1440 ref) ────────────────────────── */
  --fs-mega: clamp(3.5rem, 9.5vw, 11rem);
  --fs-h1:   clamp(2.75rem, 6vw, 6.5rem);
  --fs-h2:   clamp(2rem, 3.6vw, 3.5rem);
  --fs-body: clamp(1rem, 1.15vw, 1.25rem);
  --fs-util: 0.6875rem;
  --fs-section-display: clamp(2.75rem, 5.8vw, 6.5rem);

  /* Text-section scale. One step per role so every capability page sets copy,
     leads and section titles identically:
       label 1.35 → lead 1.75 → copy 1.14, all Inter Tight.
     --ff-util stays for micro-data only (indices, spec values, captions). */
  --fs-label: clamp(1.15rem, 1.45vw, 1.55rem);
  --fs-lead:  clamp(1.2rem, 1.8vw, 1.75rem);
  --fs-copy:  clamp(1rem, 1.1vw, 1.14rem);

  /* One label-rail + copy-column track, shared by the page intro and every text
     section, so the copy column never shifts down the page. */
  --spine: minmax(170px, 0.85fr) minmax(0, 3fr);
  --spine-gap: clamp(20px, 2.2vw, 44px);

  /* ── Layout ───────────────────────────────────────── */
  --container: 1600px;
  --gutter: clamp(16px, 1.4vw, 24px);
  --pad: clamp(20px, 3vw, 48px);

  /* ── Block rhythm ─────────────────────────────────────
     Every gap between blocks is one of these four steps — nothing in the
     stylesheet should invent its own clamp() for block spacing. --rhythm is
     the page's base beat (a padded section's interior); the smaller steps are
     for blocks that belong to the same idea and read as one unit. */
  --rhythm:    clamp(120px, 16svh, 220px);   /* separate ideas            */
  --rhythm-md: clamp(72px, 10svh, 140px);    /* related blocks            */
  --rhythm-sm: clamp(56px, 7svh, 100px);     /* copy meeting media        */
  --rhythm-xs: clamp(28px, 3.8svh, 52px);    /* inside one unit           */

  /* One media height for every in-page block — pairs, grids, features and the
     before/after wipe all run to the same frame, so a page reads as one band of
     media. The hero and the closing teaser set their own full-bleed heights. */
  --media-h: clamp(340px, 62svh, 720px);

  --header-h: 84px;
  /* Techviz is the canonical hero frame. Every full-bleed page opener —
     homepage, standard hero, project hero, work and scroll-sequence stage —
     consumes these two tokens so individual templates cannot drift. */
  --project-hero-h: 88svh;
  --project-hero-min-h: 560px;
  --rail-w: 44px;
  --feature-media-h: 80svh;
  --feature-media-min: 560px;

  --ff-display: "Inter Tight", "Archivo", "Helvetica Neue", sans-serif;
  --ff-util: "Geist Mono", ui-monospace, "SFMono-Regular", monospace;
}

:root[data-theme="light"] {
  color-scheme: light;
  --canvas: #F4F7FA;
  --surface: #E9EEF3;
  --surface-hi: #DCE5EC;
  --void: #FFFFFF;
  --ink: #04121F;
  --muted: rgba(4, 18, 31, 0.66);
  --faint: rgba(4, 18, 31, 0.42);
  --rule: rgba(4, 18, 31, 0.13);
  --rule-strong: rgba(4, 18, 31, 0.25);
  --blue-100: #013560;
  --blue-300: #0062BC;
  --theme-accent: var(--blue-500);
  --blue-tint: rgba(0, 98, 188, 0.10);
  --blue-tint-2: rgba(0, 98, 188, 0.20);
  --blue-glow: 0 0 48px rgba(0, 98, 188, 0.22);
}

/* ========================================================================== 
   Minimal homepage
   The media moves; the interface stays quiet. This layer is intentionally
   isolated from the interaction-heavy capability and project page systems.
   ========================================================================== */
.mx-home {
  --mx-ease: cubic-bezier(.16, 1, .3, 1);
  --container: 1600px;
  width: 100%;
  overflow-x: clip;
  background: var(--canvas);
  color: var(--ink);
}
.mx-home *, .mx-home *::before, .mx-home *::after { border-radius: 0; }
.mx-home-shell {
  width: min(100%, var(--container));
  margin-inline: auto;
  padding-left: max(var(--pad), calc(var(--rail-w) + 36px));
  padding-right: var(--pad);
}
body.is-minimal-home { overflow-x: hidden; cursor: auto; }
body.is-minimal-home.is-loading { overflow-x: hidden; overflow-y: auto; }
body.is-minimal-home .preloader { display: none; }
body.is-minimal-home > .reveal { opacity: 1; }
body.is-minimal-home .cursor-dot,
body.is-minimal-home .cursor-ring,
body.is-minimal-home .cursor-stroke { display: none !important; }
body.is-minimal-home a,
body.is-minimal-home button { cursor: pointer; }

.mx-home-kicker {
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  line-height: 1.5;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--faint);
}
.mx-home-kicker--location {
  display: inline-flex;
  width: max-content;
  max-width: 100%;
  padding: 2px 4px;
  background: var(--blue-500);
  color: var(--on-blue);
  white-space: nowrap;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.mx-home h1,
.mx-home h2 { font-weight: 600; letter-spacing: -.035em; }
.mx-home h2 { font-size: clamp(2.75rem, 5.8vw, 6.5rem); line-height: .98; }
.mx-home h1 span,
.mx-home h2 span { color: var(--blue-500); }
.mx-home a:focus-visible,
.mx-home button:focus-visible { outline: 2px solid var(--blue-300); outline-offset: 5px; }

.mx-home-hero {
  position: relative;
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--void);
  border-top: 1px solid var(--rule);
  color: #F2F5F8;
}
.mx-home-hero__media { position: absolute; inset: 0; }
.mx-home-hero__media video {
  position: absolute;
  inset: -6% 0 0;
  width: 100%;
  height: 106%;
  object-fit: cover;
  opacity: .62;
}
.mx-home-hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(5,7,10,.86) 0%, rgba(5,7,10,.42) 34%, rgba(5,7,10,.78) 74%, var(--canvas) 100%),
    radial-gradient(120% 70% at 12% 92%, rgba(0,135,254,.16) 0%, rgba(0,135,254,0) 58%);
}
:root[data-theme="light"] .mx-home-hero__scrim {
  background: linear-gradient(180deg, rgba(5,7,10,.22) 0%, rgba(5,7,10,.28) 58%, rgba(5,7,10,.56) 100%);
}
:root[data-theme="light"] .mx-home-hero__media video { opacity: 1; }
.mx-home-hero__content {
  position: relative;
  z-index: 1;
  padding-top: calc(var(--header-h) + var(--rhythm-sm));
  padding-bottom: clamp(42px, 7svh, 84px);
}
.mx-home-hero h1 {
  max-width: none;
  margin-top: 20px;
  white-space: nowrap;
  font-size: clamp(3rem, 7vw, 7.4rem);
  line-height: .9;
}
.mx-home-hero__foot {
  margin-top: clamp(36px, 6vw, 72px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 22px;
}
.mx-home-hero__foot p {
  max-width: none;
  white-space: nowrap;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: rgba(242,245,248,.72);
}

.mx-home-button {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  padding: 12px 18px;
  border: 1px solid var(--rule-strong);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  letter-spacing: .14em;
  line-height: 1.3;
  text-transform: uppercase;
}
.mx-home-button--primary { background: var(--ink); color: var(--canvas); border-color: var(--ink); }
.mx-home-button--primary:hover { background: var(--blue-500); border-color: var(--blue-500); }
.mx-home-button--secondary { background: transparent; color: var(--ink); }
.mx-home-button--secondary:hover { background: rgba(242,245,248,.08); border-color: var(--ink); }

.mx-home-section,
.mx-home-pillar,
.mx-home-cta {
  border-top: 1px solid var(--rule);
}
.mx-home-section { padding-block: clamp(88px, 12vw, 180px); }
.mx-home-heading {
  display: block;
}
.mx-home-heading h2 { max-width: none; margin-top: 22px; }
.mx-home-cases .mx-home-heading h2 {
  color: var(--ink);
  white-space: nowrap;
  text-wrap: nowrap;
  font-size: clamp(2.75rem, 5.75vw, 6.5rem);
}

.mx-home-case-grid {
  margin-top: clamp(56px, 8vw, 112px);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.mx-home-case { min-width: 0; padding: clamp(14px, 1.5vw, 24px); background: var(--canvas); }
.mx-home-case:hover { background: var(--surface); }
.mx-home-media { display: block; overflow: hidden; background: var(--surface); }
.mx-home-media img,
.mx-home-media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.1s var(--mx-ease), opacity .6s var(--mx-ease);
}
.mx-home-case .mx-home-media { aspect-ratio: 4 / 3; }
.mx-home-case .mx-home-card-meta {
  min-height: 100px;
  flex-direction: column;
  justify-content: flex-start;
  gap: 10px;
}
.mx-home-case:hover .mx-home-media img,
.mx-home-case:hover .mx-home-media video,
.mx-home-pillar:hover .mx-home-media img,
.mx-home-pillar:hover .mx-home-media video,
.mx-home-studios__media:hover img { transform: scale(1.045); }
.mx-home-card-meta {
  min-height: 82px;
  padding-top: 18px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
}
.mx-home-card-meta strong { font-size: clamp(1.2rem, 1.8vw, 1.75rem); font-weight: 500; line-height: 1.05; }
.mx-home-card-meta small {
  flex: none;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  letter-spacing: .14em;
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--faint);
}

.mx-home-pillar { padding-block: clamp(64px, 6vw, 96px); background: var(--canvas); }
.mx-home-pillar:nth-child(even) { background: var(--void); }
.mx-home-pillar__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(32px, 4.5vw, 72px);
  align-items: start;
}
.mx-home-pillar__media { aspect-ratio: 4 / 3; }
.mx-home-pillar__copy { width: 100%; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }
.mx-home-pillar__copy h2 {
  width: 100%;
  margin: 0;
  color: var(--ink);
  white-space: nowrap;
  font-size: clamp(2.3rem, 4vw, 4rem);
}
.mx-home-pillar__copy > p:not(.mx-home-kicker) {
  max-width: none;
  margin-top: 30px;
  white-space: nowrap;
  font-size: clamp(1rem, 1.05vw, 1.125rem);
  line-height: 1.55;
  color: var(--muted);
}
.mx-home-capabilities { width: 100%; margin-top: clamp(36px, 5vw, 64px); border-top: 1px solid var(--rule); }
.mx-home-capabilities [data-home-pillar-link] {
  width: 100%;
  min-height: 52px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: 13px;
  padding-inline: 0;
  border-bottom: 1px solid var(--rule);
  border-top: 0;
  border-right: 0;
  border-left: 0;
  border-radius: 0;
  appearance: none;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: clamp(1rem, 1.35vw, 1.25rem);
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}
.mx-home-capabilities [data-home-pillar-link]:hover,
.mx-home-capabilities [data-home-pillar-link]:focus-visible,
.mx-home-capabilities [data-home-pillar-link].is-active { padding-left: 10px; color: var(--theme-accent); }

@media (min-width: 1101px) {
  .mx-home-pillar:nth-child(even) .mx-home-pillar__copy {
    grid-column: 1;
    grid-row: 1;
  }
  .mx-home-pillar:nth-child(even) .mx-home-pillar__media {
    grid-column: 2;
    grid-row: 1;
  }
}

.mx-home-services {
  padding-block: clamp(88px, 12vw, 180px);
  border-top: 1px solid var(--rule);
  background: var(--ink);
  color: var(--on-blue);
}
.mx-home-services h2 {
  max-width: none;
  font-size: clamp(4rem, 10vw, 10.5rem);
  line-height: .88;
}
.mx-home-services__layout {
  margin-top: clamp(54px, 8vw, 110px);
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(280px, .65fr);
  gap: clamp(40px, 6vw, 96px);
  align-items: start;
}
.mx-home-service-list { border-top: 1px solid color-mix(in srgb, var(--on-blue) 16%, transparent); }
.mx-home-service-list a {
  min-height: clamp(66px, 6.5vw, 96px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--on-blue) 16%, transparent);
  font-size: clamp(1.55rem, 2.8vw, 3rem);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -.025em;
}
.mx-home-service-list a span { flex: none; font-size: .75em; }
.mx-home-service-list a:hover,
.mx-home-service-list a:focus-visible,
.mx-home-service-list a.is-active { padding-left: 10px; color: var(--blue-500); }
.mx-home-service-preview {
  position: sticky;
  top: calc(var(--header-h) + 28px);
  margin: 0;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--void);
  color: var(--ink);
}
.mx-home-service-preview video { width: 100%; height: 100%; display: block; object-fit: cover; }
.mx-home-service-preview figcaption {
  position: absolute;
  inset: auto 0 0;
  padding: 18px;
  background: linear-gradient(180deg, transparent, var(--void));
  font-size: clamp(1.5rem, 2.2vw, 2.5rem);
  font-weight: 600;
  line-height: 1;
}

.mx-home-work { background: var(--canvas); }
.mx-home-work-filters {
  margin-top: clamp(42px, 6vw, 76px);
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.mx-home-work-filters button {
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--rule-strong);
  background: transparent;
  color: var(--faint);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.mx-home-work-filters button:hover { color: var(--ink); border-color: var(--ink); }
.mx-home-work-filters button.is-active { background: var(--ink); border-color: var(--ink); color: var(--canvas); }
.mx-home-work-grid {
  margin-top: clamp(36px, 5vw, 64px);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(36px, 4vw, 68px) clamp(12px, 1.5vw, 24px);
}
.mx-home-work-card { min-width: 0; }
.mx-home-work-card[hidden] { display: none; }
.mx-home-work-card__media { aspect-ratio: 4 / 5; }
.mx-home-work-card:hover .mx-home-work-card__media img,
.mx-home-work-card:hover .mx-home-work-card__media video { transform: scale(1.045); }
.mx-home-work-card:hover { background: var(--surface); }
.mx-home-work-card .mx-home-card-meta {
  min-height: 104px;
  flex-direction: column;
  justify-content: flex-start;
  gap: 10px;
  padding-inline: 12px;
}
.mx-home-work-card .mx-home-card-meta strong { font-size: clamp(1.05rem, 1.45vw, 1.35rem); }
.mx-home-work__more {
  min-height: 44px;
  margin-top: clamp(48px, 7vw, 92px);
  display: inline-flex;
  align-items: center;
  gap: 18px;
  border-bottom: 1px solid var(--blue-300);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.mx-home-work__more:hover { color: var(--blue-300); }

.mx-home-marquee { overflow: hidden; background: #05070A; }
.mx-home-marquee__track {
  --mx-marquee-shift: -50%;
  --mx-marquee-duration: 30s;
  width: max-content;
  display: flex;
  align-items: center;
  will-change: transform;
  animation: mxr-marquee var(--mx-marquee-duration) linear infinite;
}
.mx-home-marquee__set {
  min-height: clamp(124px, 12vw, 190px);
  display: flex;
  flex: none;
  align-items: center;
  gap: clamp(48px, 5vw, 96px);
  padding-inline: clamp(32px, 4vw, 72px);
}
.mx-home-marquee__set img {
  box-sizing: border-box;
  width: clamp(180px, 15vw, 290px);
  height: clamp(76px, 7vw, 108px);
  flex: none;
  padding: clamp(12px, 1.2vw, 20px) clamp(18px, 1.8vw, 30px);
  object-fit: contain;
  background: transparent;
  border: 0;
  filter: none;
  opacity: 1;
}
@keyframes mxr-marquee {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(var(--mx-marquee-shift), 0, 0); }
}

.mx-home-studios { background: var(--void); }
.mx-home-studios__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(42px, 7vw, 120px);
  align-items: stretch;
}
.mx-home-studios__copy { display: flex; flex-direction: column; align-items: flex-start; }
.mx-home-studios__copy h2 { margin-top: 22px; }
.mx-home-studios__copy > p:not(.mx-home-kicker) {
  max-width: 36ch;
  margin-top: 30px;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--muted);
}
.mx-home-studios__copy .mx-home-button { margin-top: 36px; }
.mx-home-locations { width: 100%; margin-top: auto; padding-top: clamp(48px, 7vw, 96px); }
.mx-home-locations div {
  min-height: 62px;
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: 20px;
  align-items: center;
  border-top: 1px solid var(--rule);
}
.mx-home-locations div:last-child { border-bottom: 1px solid var(--rule); }
.mx-home-locations dt,
.mx-home-locations small { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: .14em; text-transform: uppercase; color: var(--faint); }
.mx-home-locations dd { display: flex; justify-content: space-between; gap: 20px; font-size: 1.1rem; }
.mx-home-studios__media { min-height: 580px; }

.mx-home-cta { padding-block: clamp(110px, 16vw, 240px); background: var(--void); }
.mx-home-cta h2 {
  max-width: none;
  margin-top: 24px;
  white-space: nowrap;
  text-wrap: nowrap;
  font-size: clamp(2.75rem, 5vw, 6.5rem);
}
.mx-home-cta .mx-home-button { margin-top: clamp(42px, 6vw, 76px); }

body.is-minimal-home > .cta,
body.is-minimal-home > .site-footer { --container: 1600px; }
body.is-minimal-home > .cta .container,
body.is-minimal-home > .site-footer .container {
  padding-left: max(var(--pad), calc(var(--rail-w) + 36px));
  padding-right: var(--pad);
}

@media (max-width: 1024px) {
  .mx-home-shell { padding-inline: var(--pad); }
  .mx-home-pillar__grid,
  .mx-home-studios__grid { grid-template-columns: minmax(0, 1fr); }
  .mx-home-case-grid { grid-template-columns: minmax(0, 1fr); }
  .mx-home-services__layout { grid-template-columns: minmax(0, 1fr); }
  .mx-home-service-preview { position: relative; top: auto; order: -1; max-height: 70svh; aspect-ratio: 16 / 10; }
  .mx-home-work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mx-home-studios__media { min-height: 0; aspect-ratio: 4 / 3; }
  .mx-home-locations { margin-top: 0; }
}
@media (min-width: 1025px) and (max-width: 1100px) {
  .mx-home-pillar__grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .mx-home-hero__content { padding-bottom: 32px; }
  .mx-home-hero h1 { white-space: normal; font-size: clamp(2.75rem, 13vw, 4.5rem); line-height: .92; }
  .mx-home-hero__foot p { white-space: normal; }
  .mx-home-hero__foot { align-items: flex-start; flex-direction: column; }
  .mx-home-card-meta { min-height: 0; flex-direction: column; }
  .mx-home-services h2 { font-size: clamp(3.6rem, 18vw, 6rem); }
  .mx-home-service-list a { font-size: clamp(1.35rem, 7vw, 2rem); }
  .mx-home-work-grid { grid-template-columns: minmax(0, 1fr); }
  .mx-home h2,
  .mx-home-cta h2 { font-size: clamp(2.7rem, 13vw, 4.8rem); }
  .mx-home-pillar__copy h2 { white-space: nowrap; font-size: clamp(2rem, 8.8vw, 2.7rem); }
  .mx-home-pillar__copy > p:not(.mx-home-kicker) { white-space: normal; }
  .mx-home-cta h2 { white-space: normal; text-wrap: balance; }
  .mx-home-cases .mx-home-heading h2 { white-space: normal; text-wrap: balance; }
  .mx-home-marquee__set {
    gap: 36px;
    padding-inline: 24px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .mx-home [data-home-reveal],
  .mx-home-hero__media,
  .mx-home-media img,
  .mx-home-media video { opacity: 1 !important; transform: none !important; transition: none !important; }
}

/* Ultra-wide displays report far more CSS pixels than the 1440 reference
   layout. Keep the same cinematic stage ratio instead of leaving the site as a
   laptop-width strip in the middle of a 4K viewport. */
@media (min-width: 2000px) and (min-height: 900px) {
  :root {
    --container: min(100vw, calc(100svh * 16 / 9));
    --pad: clamp(48px, 2.5vw, 96px);
    --header-h: clamp(84px, 5svh, 132px);
    --rail-w: clamp(44px, 2.8vw, 72px);

    --fs-mega: clamp(7rem, 9.5vw, 22rem);
    --fs-h1:   clamp(5rem, 6vw, 13rem);
    --fs-h2:   clamp(3rem, 3.6vw, 7rem);
    --fs-body: clamp(1.15rem, 1.15vw, 1.65rem);
    --fs-util: clamp(0.6875rem, 0.42vw, 1rem);

    --fs-label: clamp(1.45rem, 1.45vw, 2.25rem);
    --fs-lead:  clamp(1.55rem, 1.8vw, 2.65rem);
    --fs-copy:  clamp(1.08rem, 1.1vw, 1.6rem);
  }

  .brand img,
  .brand video {
    width: clamp(138px, 9.6vw, 340px);
  }

  .nav {
    gap: clamp(32px, 2vw, 72px);
  }

  .nav__drop-toggle {
    width: clamp(28px, 1.55vw, 44px);
    height: clamp(28px, 1.55vw, 44px);
  }

  .nav__caret {
    width: clamp(10px, 0.55vw, 18px);
    height: auto;
  }

  .social-rail {
    gap: clamp(16px, 0.85vw, 30px);
  }

  .social-rail a {
    width: clamp(32px, 1.8vw, 56px);
    height: clamp(32px, 1.8vw, 56px);
  }

  .social-rail img,
  .social-rail video {
    width: clamp(22px, 1.15vw, 42px);
    height: clamp(22px, 1.15vw, 42px);
  }

  .wmap {
    width: min(100%, clamp(1560px, 72vw, 2600px));
    margin-top: clamp(56px, 4.4vw, 104px);
    padding-block: clamp(12px, 1vw, 24px);
  }

  .wmap__pins {
    inset: clamp(12px, 1vw, 24px) 0;
  }

  .wpin a {
    gap: clamp(14px, 0.75vw, 24px);
    transform: translate(clamp(-10px, -0.42vw, -7px), -50%);
  }

  .wpin--left a {
    transform: translate(calc(-100% + clamp(7px, 0.42vw, 10px)), -50%);
  }

  .wpin__dot {
    width: clamp(18px, 0.82vw, 30px);
    height: clamp(18px, 0.82vw, 30px);
    box-shadow: 0 0 clamp(20px, 1vw, 38px) rgba(0, 135, 254, 0.92);
  }

  .wpin__label {
    font-size: clamp(0.82rem, 0.52vw, 1.35rem);
  }
}

/* --- reset ---------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  background: var(--canvas);
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--ff-display);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color .25s ease, color .25s ease;
}

body.has-custom-cursor { cursor: auto; }

#main,
.cta,
.site-footer {
  /* Page-level surfaces are full bleed. Their nested .container/.mx-home-shell
     still owns the readable content measure, so ultra-wide screens get edge-
     to-edge colour and media without stretching the navigation or copy. */
  width: 100%;
  max-width: none;
  margin-inline: 0;
  overflow: hidden;
}

/* Native cursor behavior: text fields keep their caret, controls use pointer. */
input, textarea, select { cursor: auto; }
@media (pointer: coarse) { body { cursor: auto; } }

img, video, iframe, svg { display: block; max-width: 100%; }
/* Every video sits on its own compositor layer. 23 rules below tint video with
   `filter` (grayscale/brightness on cards, tiles, partners, the work grid),
   which puts those frames through a render surface; without an explicit layer,
   the custom cursor passing over one folds into that surface and drags every
   pointer move down to the video's frame rate. backface-visibility promotes
   without writing `transform`, so it never fights the hover scale tweens or
   GSAP-owned transforms. */
video { backface-visibility: hidden; }
.hero__media iframe,
.project-hero__img iframe,
.teaser__media iframe,
.pfeature iframe,
.band iframe,
.work-item__frame iframe,
.project__frame iframe,
.bleed-row__media iframe,
.gallery-item iframe,
.pgrid figure iframe,
.pblock__media iframe,
.project-split__media iframe,
.project-figure iframe,
.media-row__media iframe,
.pair figure iframe {
  width: 100%;
  height: 100%;
  border: 0;
}
.work-item__frame iframe,
.gallery-item iframe,
iframe[data-mx-youtube-clean] { pointer-events: none; }
.mx-inline-video-player,
.mx-youtube-clean { position: relative; }
.mx-youtube-clean .media-reveal__strips { display: none; }
/* Card still, held over the frame until the player reports it is running.
   Sits above the iframe and the edge gradients, below the play/pause button. */
.mx-yt-cover {
  position: absolute; inset: 0; z-index: 6;
  background: inherit;
  pointer-events: none;
  transition: opacity .3s ease;
}
.mx-yt-cover.is-hidden { opacity: 0; }
.mx-youtube-clean::before,
.mx-youtube-clean::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  z-index: 5;
  height: min(88px, 24%);
  pointer-events: none;
}
.mx-youtube-clean::before {
  top: 0;
  background: linear-gradient(180deg, rgba(5, 7, 10, .9), rgba(5, 7, 10, 0));
}
.mx-youtube-clean::after {
  bottom: 0;
  background: linear-gradient(0deg, rgba(5, 7, 10, .9), rgba(5, 7, 10, 0));
}
.yt-clean-toggle {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 8;
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(54px, 4.4vw, 68px);
  height: clamp(54px, 4.4vw, 68px);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(.96);
  border: 1px solid rgba(242, 245, 248, .58);
  background: rgba(5, 7, 10, .66);
  color: var(--media-ink);
  box-shadow: 0 18px 42px rgba(0, 0, 0, .34);
  backdrop-filter: blur(10px);
  transition: opacity .18s ease, color .2s ease, background-color .2s ease, border-color .2s ease, transform .2s ease;
}
.yt-clean-toggle svg { width: 27px; height: 27px; }
.mx-inline-video-player:hover > .yt-clean-toggle,
.mx-inline-video-player:focus-within > .yt-clean-toggle,
.yt-clean-toggle:focus-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%);
}
.yt-clean-toggle:hover,
.yt-clean-toggle:focus-visible {
  color: var(--blue-300);
  border-color: var(--blue-300);
  background: rgba(8, 16, 28, .86);
  transform: translate(-50%, -50%) scale(1.04);
}
@media (hover: none) and (any-hover: none) and (pointer: coarse) {
  .mx-inline-video-player > .yt-clean-toggle {
    opacity: 1;
    pointer-events: auto;
    transform: translate(-50%, -50%);
  }
}
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a[href],
button,
[role="button"],
summary { cursor: pointer; }

body.has-custom-cursor a,
body.has-custom-cursor button,
body.has-custom-cursor [role="button"] { cursor: pointer; }
h1, h2, h3, h4, h5, h6, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

::selection { background: var(--blue-500); color: var(--on-blue); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--void); }
::-webkit-scrollbar-thumb { background: var(--rule-strong); }

:focus-visible {
  outline: 2px solid var(--blue-500);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px var(--blue-tint);
}

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

.skip-link {
  position: fixed; top: 12px; left: 12px; z-index: 9998;
  padding: 10px 18px; background: var(--blue-500); color: var(--on-blue);
  font-family: var(--ff-util); font-size: var(--fs-util);
  text-transform: uppercase; letter-spacing: 0.14em;
  transform: translateY(-160%);
}
.skip-link:focus { transform: none; }

/* --- layout --------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad);
}
.container--narrow { max-width: 980px; }

.section { padding-block: var(--rhythm); }
.section--tight { padding-block: var(--rhythm-md); }
.section--flush { padding-block: 0; }

/* --- block flow ------------------------------------------------------------
   One owner for the space between top-level blocks, so no component has to
   guess. Two kinds of block:

     PADDED  .section, .section--tight, .statement, .partners, .cta, .carousel,
             .project-detail — they carry their rhythm as interior padding.
     FLUSH   .section--flush, .blue-band, .band, .teaser — edge-to-edge blocks
             with no padding of their own.

   A padded neighbour already supplies the gap on both sides, so the only case
   that needs a rule is flush meeting flush (a full-bleed video butting into
   the blue band, say). Everything else inherits the base beat automatically,
   which is what keeps the page even top-to-bottom. */
:where(.section--flush, .blue-band, .band, .teaser)
  + :where(.section--flush, .blue-band, .band, .teaser) { margin-top: var(--rhythm-md); }

/* Corollary: inside a flush block, the media stops owning its outer margin —
   the block's neighbours decide the gap. Without this a .pfeature adds a full
   --rhythm on top of the previous section's padding and the page reads
   lopsided: double space above the media, none below it. */
.section--flush > :where(.pfeature, .band, .pair, .project-gallery, .gallery-grid):first-child { margin-top: 0; }

/* --- typography ----------------------------------------------------------- */
.display {
  font-weight: 600;
  font-size: var(--fs-h1);
  line-height: 1.02;
  letter-spacing: -0.02em;
}
.h1 { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.h2 { font-weight: 600; font-size: var(--fs-h2); line-height: 0.98; letter-spacing: -0.03em; }
.h3 { font-weight: 500; font-size: clamp(1.4rem, 2.2vw, 2rem); line-height: 1.05; letter-spacing: -0.02em; }
.h4 { font-weight: 500; font-size: 1.35rem; line-height: 1.15; letter-spacing: -0.015em; }

.eyebrow,
.util {
  font-family: var(--ff-util);
  font-weight: 400;
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
  font-feature-settings: "tnum" 1;
}

.lead { font-weight: 450; font-size: clamp(1.15rem, 1.6vw, 1.5rem); line-height: 1.4; color: var(--ink); }
.body { font-weight: 450; font-size: var(--fs-body); line-height: 1.5; color: var(--muted); max-width: 58ch; }
.body--light { color: var(--muted); }
.small { font-size: 0.875rem; line-height: 1.5; }

.accent { color: var(--blue-500); }
.center { text-align: center; }
.upper { text-transform: uppercase; }

/* Masked-reveal scaffolding (SplitText adds .line; JS wraps in .line-mask).
   The padding gives descenders (g, y, p, q, comma) room so tight line-height
   never clips them; the negative margin keeps the visual leading unchanged. */
.line-mask { display: block; overflow: hidden; padding-bottom: 0.3em; margin-bottom: -0.3em; }
.line { display: block; }
.word { display: inline-block; will-change: transform; }

/* --- roll-text link hover ------------------------------------------------- */
.roll {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  line-height: 1.05;
}
.roll__out {
  position: absolute;
  inset: 0;
  color: var(--blue-300);
}

/* --- buttons / pill ------------------------------------------------------- */
.btn,
.pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 56px;
  padding: 0 32px;
  overflow: hidden;
  border: 1px solid var(--rule-strong);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  font-weight: 600;              /* heavier than body 400 — the label read too light */
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.btn__fill,
.pill__fill {
  position: absolute;
  inset: 0;
  background: var(--blue-500);
  /* Grows from the pointer position on hover (JS animates the circle radius). */
  clip-path: circle(0px at 50% 50%);
  z-index: 0;
  pointer-events: none;
}
.btn > span:not(.btn__fill),
.pill > span:not(.pill__fill) { position: relative; z-index: 1; }
.btn--sm { height: 44px; padding: 0 22px; }
.btn--lg { height: 64px; padding: 0 40px; }

.arrow-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 0;
  border: 1px solid var(--rule-strong);
  color: var(--ink);
}
.arrow-btn svg { width: 20px; height: 20px; }

/* rotating brand star */
.star { display: inline-block; color: var(--blue-500); }
.star svg { width: 1em; height: 1em; }

/* --- header --------------------------------------------------------------- */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  z-index: 90;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: rgba(5, 7, 10, 0.86);
  border-bottom: 1px solid var(--rule);
  backdrop-filter: blur(14px) saturate(1.1);
}
:root[data-theme="light"] .site-header {
  background: rgba(244, 247, 250, 0.9);
}
:root[data-theme="light"] .brand img,
:root[data-theme="light"] .brand video { filter: invert(1) hue-rotate(180deg); }
.site-header__inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad);
  display: flex;
  align-items: center;
  gap: 24px;
}
.brand { flex: none; }
.brand img, .brand video { width: 138px; height: auto; }

.nav { margin-left: auto; display: flex; align-items: center; gap: clamp(20px, 2vw, 38px); }
.nav__item { position: relative; }
.nav__item--has-sub {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.nav__link {
  display: inline-flex;
  align-items: center;
  padding: 10px 0 8px;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink);
  white-space: nowrap;
  user-select: none;
}
/* On the desktop bar the labels are inert copy — the menu follows the pointer
   hovering the item, so the label itself takes no pointer and no click. Blue
   means one thing here: this is the section you are in, or the menu you have
   open. (In the drawer the label IS the toggle — see the 1024 block.) */
.nav__item--has-sub > .nav__link { cursor: default; pointer-events: none; }
.nav__link { transition: color .25s ease; }
.nav__item.is-current > .nav__link,
.nav__item.is-open > .nav__link {
  color: var(--blue-300);
}
.nav__drop-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: var(--ink);
  cursor: pointer;
  transition: color .25s ease;
}
.nav__drop-toggle:hover,
.nav__drop-toggle:focus-visible { color: var(--blue-300); }
.nav__caret { width: 10px; height: 6px; flex: none; transition: transform .3s ease; }
.nav__item.is-open .nav__caret { transform: rotate(180deg); }

.nav__sub {
  position: absolute;
  top: 100%;
  left: -18px;
  min-width: 220px;
  max-width: min(360px, calc(100vw - 48px));
  max-height: min(70svh, 420px);
  overflow: auto;
  padding: 12px 0;
  background: var(--surface);
  border: 1px solid var(--rule);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s;
}
.nav__item.is-open .nav__sub { opacity: 1; visibility: visible; transform: translateY(0); }
/* The open item stacks above its neighbours, and a panel closed because another
   one opened drops out instantly instead of fading across it. */
.nav__item.is-open { z-index: 2; }
.nav__item.is-instant .nav__sub { transition: none; }
body:not(.mx-editor-on) .nav__item.mx-editor-menu-open .nav__sub {
  opacity: 0 !important;
  visibility: hidden !important;
  transform: translateY(6px) !important;
}
.nav__sub a {
  display: block;
  padding: 9px 20px;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  white-space: nowrap;
  transition: color .2s ease, background-color .2s ease;
}
.nav__sub a:hover { color: var(--ink); background: var(--surface-hi); }
/* The parent page as the emphasised first entry — keeps the section's own page
   the focus while grouping its sub-pages beneath it. */
.nav__sub-lead {
  display: flex !important;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  color: var(--ink);
  font-weight: 600;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 6px;
  padding-bottom: 12px;
}
.nav__sub-lead span {
  font-size: 9px;
  letter-spacing: 0.16em;
  color: var(--blue-300);
}

.nav-cta { position: relative; }

.nav-theme { display: flex; align-items: center; }
.theme-toggle {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid var(--rule-strong);
  color: var(--ink);
  transition: color .2s ease, background-color .2s ease, border-color .2s ease;
}
.theme-toggle:hover,
.theme-toggle:focus-visible { color: var(--blue-300); background: var(--surface); border-color: currentColor; }
.theme-toggle__icon { width: 18px; height: 18px; }
.theme-toggle__icon--moon { display: none; }
:root[data-theme="light"] .theme-toggle__icon--sun { display: none; }
:root[data-theme="light"] .theme-toggle__icon--moon { display: block; }
.theme-toggle__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.nav-toggle { display: none; margin-left: auto; width: 34px; height: 34px; }
.nav-toggle span {
  display: block; height: 2px; background: var(--ink); margin: 6px 0;
  transition: transform .25s ease, opacity .25s ease;
}
body.nav-open .nav-toggle span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
body.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* --- social rail ---------------------------------------------------------- */
.social-rail {
  position: fixed;
  left: max(0px, calc((100vw - var(--container)) / 2));
  top: 50%;
  transform: translateY(-50%);
  z-index: 80;
  width: var(--rail-w);
  padding: 10px 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
}
.social-rail a {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  opacity: .82;
  background: rgba(5, 7, 10, .76);
  border: 1px solid rgba(242, 245, 248, .12);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition: opacity .3s ease, border-color .3s ease;
}
.social-rail a:hover { opacity: 1; }
/* The source PNGs are pure-black glyphs on transparent, so brightness() can
   never lift them off a dark page — invert() is what turns them white. */
.social-rail img, .social-rail video { width: 22px; height: 22px; filter: grayscale(1) invert(1) brightness(.92); }

/* --- custom cursor -------------------------------------------------------- */
/* Centering is via negative margins only. GSAP owns `transform` (x/y/scale),
   so no CSS translate here, stacking the two is what throws the ring off the
   real pointer. */
.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0; left: 0;
  /* Square pointer — matches the ClaPat "Hayler" reference (rounded-square ball,
     border-radius 2–3px), not a circle. */
  border-radius: 3px;
  pointer-events: none;
  z-index: 2147483647;
  /* Own compositor layer, always. translateZ+backface pin it there even when
     GSAP has not yet written a transform, and `contain` stops anything inside
     it from being able to dirty the page underneath. Without this the pointer
     visibly trails whenever it crosses a video: the cursor's paint gets folded
     back into the content below and every move re-rasterises that region at
     video frame rate. No `contain: paint` — it would clip the ring's label
     while the pill is still tweening out to its full width. */
  will-change: transform;
  transform: translateZ(0);
  backface-visibility: hidden;
}
.cursor-dot {
  /* Small filled square — the resting pointer (ClaPat "Hayler" reference).
     Centered on the pointer via xPercent/yPercent -50 (set in JS), so size can
     change without shifting off-centre. */
  width: 12px; height: 12px;
  border-radius: 0;              /* sharp-edged square pointer */
  background: var(--blue-500);
}
.cursor-ring {
  /* Base size; JS animates width/height per state — text states grow into a
     wide, short pill so the label stays readable (no tiny sub-pixel text). */
  width: 40px; height: 40px;
  border-radius: 999px;
  /* Invisible at rest — only the small filled square shows. Hover states
     (link/view/discover) bring the ring in via GSAP borderColor/background. */
  border: 1px solid transparent;
  display: grid;
  place-items: center;
}
.cursor-ring__label {
  font-family: var(--ff-util);
  /* Readable util size — the pill grows horizontally to fit it. */
  font-size: 11px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding-left: 0.1em;                 /* optically balance the letter-spacing */
  color: var(--on-blue);
  opacity: 0;
  white-space: nowrap;
}
/* Inversion over the full-bleed blue band. */
body.cursor--on-blue .cursor-dot { background: var(--ink); }
body.cursor--on-blue .cursor-ring { border-color: rgba(5, 7, 10, 0.6); }
body.cursor-hidden { cursor: auto; }
body.cursor-hidden .cursor-dot,
body.cursor-hidden .cursor-ring { display: none; }

/* SVG that draws the text-field outline stroke (two lines from the pointer). */
.cursor-stroke {
  /* inset, not 100vw/100vh: 100vw counts the scrollbar gutter, so the overlay
     ran wider than the viewport and pushed the document sideways. */
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 2147483646;
  overflow: visible;        /* draw even if a coord falls outside the box */
  opacity: 0;
}
.cursor-stroke path {
  fill: none;
  stroke: var(--blue-500);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
body.cursor--on-blue .cursor-stroke path { stroke: rgba(5, 7, 10, 0.7); }
body.cursor-hidden .cursor-stroke { display: none; }

/* --- preloader ------------------------------------------------------------ */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: var(--void);
  display: flex;
  /* "Loading" bottom-left, the progress counter bottom-right. */
  align-items: flex-end;
  justify-content: space-between;
  padding: var(--pad);
  padding-bottom: clamp(28px, 6svh, 72px);
}
/* Full-bleed cover split into tall vertical columns. On exit each column
   retracts upward with a stagger — the hero is revealed from behind them,
   column by column (the reference loader reveal). Built in site.js. */
.preloader__cols {
  position: absolute;
  inset: 0;
  display: flex;
  z-index: 0;
  pointer-events: none;
}
.preloader__cols i {
  flex: 1 1 0%;
  display: block;
  background: var(--void);
  transform-origin: top center;
  will-change: transform;
}
/* Text + rule sit above the column cover. */
.preloader__word,
.preloader__count { position: relative; z-index: 1; }
.preloader__word {
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  margin-bottom: 0.35em;
}
/* Vertical text roll — status words advance with progress behind a clip. */
.preloader__word--roll { overflow: hidden; height: 1.7em; }
.pw-roll { display: block; will-change: transform; }
.pw-roll span { display: block; height: 1.7em; line-height: 1.7em; }
.preloader__count {
  display: flex;
  align-items: flex-end;
  font-weight: 600;
  font-size: clamp(3rem, 10vw, 9rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  font-feature-settings: "tnum" 1;
}
.odometer { position: relative; height: 1em; overflow: hidden; }
.odometer__col { display: flex; flex-direction: column; will-change: transform; }
.odometer__col span { height: 1em; line-height: 1; }
.preloader__count .odometer:last-child .odometer__col { color: var(--blue-500); }
.preloader__bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--rule);
  z-index: 1;
}
.preloader__bar span {
  display: block;
  height: 100%;
  background: var(--blue-500);
  transform: scaleX(0);
  transform-origin: left;
}
body.is-loading { overflow: hidden; }

/* --- hero (page top) ------------------------------------------------------ */
.hero {
  position: relative;
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  color: #F2F5F8;
}
.hero .hero__sub { color: rgba(242, 245, 248, 0.72); }
.hero--tall {
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
}
/* Work is media-only, but its frame still follows the same Techviz reference
   height. A page-specific aspect-ratio clamp here was the source of the visible
   jump between routes. */
.hero.hero--work {
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
}
.hero__media { position: absolute; inset: 0; overflow: hidden; }
.hero__media video,
.hero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* NO `filter` here. A filtered <video> loses the compositor's fast video
     path — every frame goes through a render surface, and anything composited
     over it (the custom cursor) re-rasterises that surface on every pointer
     move. The brightness(0.7) this used to carry now lives in .hero__scrim as
     a flat 30% black wash, which is free. */
  will-change: transform;
  backface-visibility: hidden;
}
.hero__scrim {
  position: absolute; inset: 0;
  /* Two layers: the original top/bottom gradient, over the flat wash that
     replaces the media's old brightness(0.7). */
  background:
    linear-gradient(180deg, rgba(5,7,10,.35) 0%, rgba(5,7,10,0) 35%, rgba(5,7,10,.85) 100%),
    linear-gradient(180deg, rgba(5,7,10,.30) 0%, rgba(5,7,10,.30) 100%);
}
.hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding: 0 var(--pad) clamp(48px, 8svh, 96px);
}
.hero__eyebrow {
  margin-bottom: 20px;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--blue-300);
}
.hero__title {
  font-weight: 600;
  font-size: var(--fs-h1);
  line-height: 1.02;
  letter-spacing: -0.02em;
  max-width: 20ch;
}
.hero__sub {
  margin-top: 24px;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
}
.hero__cta { margin-top: 34px; }

/* Give the homepage hero time to clear before the pinned card rail begins.
   Without this buffer, a fast scroll can show the carousel moving under the
   cropped hero headline, which reads like the next section is bleeding into
   the hero. */
.hero--tall + .carousel { margin-top: clamp(96px, 14svh, 220px); }

/* text-only page header */
.page-head {
  margin-top: var(--header-h);
  padding-block: clamp(90px, 14svh, 180px) 20px;
}
.page-head__title { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.page-head__sub { margin-top: 20px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }

/* --- hero carousel (scroll-driven on desktop) ---------------------------- */
.carousel {
  position: relative;
  height: 100svh;
  min-height: 720px;
  overflow: hidden;
  display: flex;
  align-items: center;
}
.carousel__track {
  display: flex;
  gap: clamp(24px, 2vw, 56px);
  /* Deeper opening margin than the page gutter: the first card sits behind the
     social rail otherwise, and the row needs somewhere to start from. */
  padding-inline: calc(var(--pad) * 2.5) var(--pad);
  will-change: transform;
}
.pcard {
  position: relative;
  flex: 0 0 clamp(520px, 38vw, 820px);
  height: clamp(560px, 72svh, 820px);
}
@media (min-width: 2000px) and (min-height: 900px) {
  .carousel__track { gap: 2vw; }
  .pcard {
    flex-basis: 38vw;
    height: 72svh;
  }
}
.pcard__frame { position: relative; width: 100%; height: 78%; overflow: hidden; will-change: transform; }
.pcard__frame img,
.pcard__frame video {
  position: absolute;
  inset: 0;
  /* Slightly oversized so the image can parallax horizontally inside the frame. */
  width: 116%; height: 100%;
  margin-left: -8%;
  object-fit: cover;
  filter: grayscale(1) brightness(0.72);
  will-change: transform, opacity, filter;
}
.pcard__frame img { z-index: 1; }
.pcard__frame video { z-index: 2; opacity: 0; pointer-events: none; transition: opacity .32s ease; }
.pcard.is-media-playing .pcard__frame video { opacity: 1; }
.pcard.is-media-playing .pcard__frame img { opacity: 0; }
.pcard__meta { margin-top: 20px; display: flex; align-items: baseline; justify-content: space-between; }
.pcard__title { font-weight: 500; font-size: var(--fs-h2); line-height: 0.98; letter-spacing: -0.03em; }
.pcard__sub { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
.pcard__rule { margin-top: 14px; height: 1px; background: var(--rule); transform: scaleX(0); transform-origin: left; will-change: transform; }

.carousel__labels {
  position: absolute;
  left: 0; right: 0; bottom: clamp(24px, 4svh, 48px);
  display: flex;
  justify-content: space-between;
  padding-inline: var(--pad);
  pointer-events: none;
}
.carousel__labels span {
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
}

/* --- statement ------------------------------------------------------------ */
/* Half rhythm at the foot: the section below opens with a full --rhythm of its
   own, and two full stacks left a dead screen between the foot copy and the
   next heading. */
.statement { padding-block: var(--rhythm) var(--rhythm-sm); }
/* Two full rhythms met at this seam and left a dead screen between the foot
   copy and the next heading; the pair now adds up to roughly one. */
.statement + .section { padding-top: var(--rhythm-md); }
.statement__lines {
  font-weight: 600;
  font-size: var(--fs-mega);
  line-height: 1.02;
  letter-spacing: -0.02em;
}
/* Keep grouped words on the same line so the last word never orphans. Survives
   the SplitText reveal because it splits the words INSIDE this span. */
.nowrap { white-space: nowrap; }
/* Authored line breaks for the statement: each .stline holds one line so the
   phrasing lands where it was written ("…finished shot in the camera" together,
   "on the day." on its own). Only above 900px — below that the mega size needs
   to wrap on its own or it runs off the side. */
@media (min-width: 901px) {
  .statement__lines .stline { white-space: nowrap; }
  /* --fs-mega's cap (11rem) makes the longest authored line a few px wider than
     the 1600px container on very wide screens; hold it one notch under. */
  .statement__lines { font-size: min(var(--fs-mega), 10.6rem); }
}
@media (min-width: 641px) and (max-width: 1400px) {
  .statement__lines {
    font-size: clamp(4rem, 7vw, 6rem);
    line-height: 1.06;
  }
  .statement__lines .stline {
    white-space: normal;
  }
}
/* Per-letter hover: the glyph under the pointer thickens to bold. Weight is
   driven through the variable-font wght axis so it interpolates smoothly
   instead of snapping between static cuts — no scale, no reflow. */
.statement__lines[data-letter-hover] .char {
  display: inline-block;
  font-variation-settings: "wght" 450;
  transition: font-variation-settings 0.32s cubic-bezier(0.22, 1, 0.36, 1),
              font-weight 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: font-variation-settings;
}
.statement__lines[data-letter-hover] .char:hover {
  font-variation-settings: "wght" 900;
  font-weight: 900;
}
@media (prefers-reduced-motion: reduce) {
  .statement__lines[data-letter-hover] .char { transition: none; }
}
.statement__foot {
  margin-top: clamp(40px, 6svh, 90px);
  padding-top: 40px;
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 40px;
  align-items: end;
}

/* --- split (heading left / body right) ------------------------------------ */
.split {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(40px, 6vw, 100px);
  align-items: start;
}
.split__title { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.split__eyebrow { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); margin-bottom: 24px; }
.split__body { font-weight: 450; font-size: var(--fs-body); line-height: 1.5; color: var(--muted); }

/* --- partner strip -------------------------------------------------------- */
/* (old static partners logo grid removed — replaced by the sticky scroll-highlight
   partners section further below; the old hover-to-colour rule lived here.) */

/* --- card grid (services) ------------------------------------------------- */
.card-grid {
  margin-top: 60px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
}
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 24px;
  background: var(--surface);
}
.card__media {
  width: 100%;
  aspect-ratio: 377 / 215;
  object-fit: cover;
  filter: grayscale(1) brightness(0.72);
 
}
.card__title {
  margin-top: 26px;
  padding-bottom: 14px;
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.05;
  letter-spacing: -0.02em;
  align-self: flex-start;
}
.card__rule { height: 1px; background: var(--rule); transform: scaleX(0); transform-origin: left; will-change: transform; }
.card__body {
  margin-top: 20px;
  font-weight: 450;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--muted);
  white-space: pre-line;
}
.card__foot { margin-top: auto; padding-top: 28px; }

/* --- asymmetric project grid ---------------------------------------------- */
.project-grid {
  margin-top: 60px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 3.5vw, 56px) clamp(24px, 3vw, 48px);
  align-items: start;
}
.project { position: relative; }
/* Stretched link — whole card is clickable and drives the page transition. */
.project__link { position: absolute; inset: 0; z-index: 4; }
.project__frame { position: relative; overflow: hidden; aspect-ratio: 4 / 3; will-change: transform; }
.project__frame img,
.project__frame video {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(1) brightness(0.72);
 
}
.project__meta { margin-top: 20px; display: flex; align-items: baseline; justify-content: space-between; gap: 20px; }
.project__title { font-weight: 500; font-size: var(--fs-h2); line-height: 0.98; letter-spacing: -0.03em; }
.project__tags { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); text-align: right; }
.project__rule { margin-top: 14px; height: 1px; background: var(--rule); transform: scaleX(0); transform-origin: left; will-change: transform; }

/* Card for a write-up that is not published yet. It carries no link and no
   data-card, so nothing here is a hover state: the rule has to be drawn at rest
   because only initCardHover ever scales it up, and the frame keeps the resting
   grayscale it would otherwise lose on hover. */
.project--soon { cursor: default; }
.project--soon .project__rule { transform: scaleX(1); }
.project__soon {
  position: absolute; z-index: 3;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  margin: 0; padding: clamp(11px, 1.3vw, 16px) clamp(20px, 2.4vw, 32px);
  /* The blur is the point: it frosts the frame behind the words rather than
     laying a flat plate over them, so the image still reads through. */
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  backdrop-filter: blur(16px) saturate(1.2);
  background: rgba(5, 7, 10, 0.66);
  border: 1px solid rgba(242, 245, 248, 0.22);
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--media-ink);
  white-space: nowrap; pointer-events: none;
}

/* Card caption title slide-reveal (ClaPat "Hayler" reference) --------------- */
/* padding-bottom gives descenders (g, y, p) room inside the clip box; the roll
   spans carry it so their offsetHeight — and thus the yPercent 100 slide — covers
   the descender, so the white copy fully clears before the blue settles. The
   negative margin on the box keeps surrounding layout unchanged. */
.title-roll { position: relative; display: inline-block; overflow: hidden; vertical-align: top; margin-bottom: -0.22em; }
.title-roll__in, .title-roll__out { display: block; padding-bottom: 0.22em; will-change: transform; }
.title-roll__out { position: absolute; inset: 0; color: var(--blue-500); }

.see-all {
  margin-top: var(--rhythm);
  display: flex;
  align-items: baseline;
  gap: 16px;
}
.see-all__link { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.see-all__count { font-family: var(--ff-util); font-size: var(--fs-util); color: var(--muted); }

/* --- blue band (the one full-bleed accent) -------------------------------- */
.blue-band {
  width: 100%;
  height: 22svh;
  min-height: 180px;
  background: var(--blue-500);
  overflow: hidden;
  display: flex;
  align-items: center;
  will-change: clip-path;
}
.blue-band__track {
  display: flex;
  flex: none;
  align-items: center;
  gap: 40px;
  white-space: nowrap;
  will-change: transform;
}
.blue-band__track span {
  font-weight: 600;
  font-size: var(--fs-h1);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--on-blue);
}
.blue-band .star { color: var(--on-blue); font-size: 0.7em; }

/* --- partners: scrolling word marquee + numbered logo-tile grid ---------- */
.partners { padding-block: var(--rhythm-md) var(--rhythm-sm); overflow: hidden; }
/* The marquee is loud enough to open on its own — it does not need a full
   rhythm on top of the padded section that precedes it. */
.section + .partners { padding-top: var(--rhythm-sm); }

/* Full-bleed marquee heading — "✦ Partnership ✦ Our clients" repeating. */
.partners__marquee { width: 100%; overflow: hidden; margin-bottom: var(--rhythm-xs); }
.partners__marquee-track {
  display: flex; align-items: center; gap: clamp(28px, 3.4vw, 64px);
  width: max-content; white-space: nowrap; will-change: transform;
}
.partners__word {
  font-weight: 600; font-size: clamp(2.6rem, 7vw, 6.5rem);
  line-height: 1; letter-spacing: -0.03em; color: var(--ink);
}
.partners__mark {
  font-weight: 400; font-size: clamp(1.6rem, 4vw, 3.6rem);
  line-height: 1; color: var(--ink);
}

/* Brand assets stay in their official colours in every theme. */
.partners__grid {
  list-style: none; margin: clamp(28px, 3vw, 48px) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.4vw, 22px);
}
.partners__grid--3 { grid-template-columns: repeat(3, 1fr); }
.partner {
  position: relative;
  aspect-ratio: 16 / 10;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(24px, 3vw, 48px);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
  transition: background .45s ease, border-color .45s ease, transform .45s ease;
}
.partner:hover { background: var(--surface-hi); border-color: var(--rule-strong); }
/* Every logo gets the SAME box and is contained inside it, so a wide wordmark
   and a compact roundel read at one scale instead of each hitting a different
   limit (width for the wordmarks, height for the marks). */
.partner img,
.partner video {
  width: 100%; max-width: 78%;
  height: clamp(52px, 4.4vw, 72px);
  object-fit: contain; object-position: center;
  filter: none; opacity: 1;
  transition: opacity .5s ease, filter .5s ease, transform .5s ease;
  will-change: opacity, filter;
}
/* Hover (or scroll-active) shows the logo's real brand colour, full opacity. */
.partner:hover img,
.partner:hover video,
.partner.is-active img,
.partner.is-active video { filter: brightness(1.05) saturate(1.05); opacity: 1; }
@media (max-width: 860px) {
  .partners__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 460px) {
  .partner { aspect-ratio: 3 / 2; }
}

.logo-marquee { overflow: hidden; padding-block: 28px; margin-top: var(--rhythm-sm); }
.logo-marquee + .logo-marquee { border-top: 1px solid var(--rule); }
.logo-marquee__track { display: flex; align-items: center; gap: 104px; will-change: transform; }
.logo-marquee__track img,
.logo-marquee__track video {
  height: 42px; width: auto;                  /* logos are trimmed to content, so height == mark size */
  filter: none;
  opacity: 1;
  transition: opacity .4s ease, filter .4s ease;
}
/* Single-line wordmarks (Mo-Sys, ARRI, XGRIDS) use the base height. Stacked /
   two-line logos need extra height so their primary mark matches the rest. */
.logo-marquee__track img[alt="Taher"] { height: 50px; }
.logo-marquee__track img[alt="Unreal Engine"] { height: 60px; }
.logo-marquee__track img[alt="Thunder Studios"] { height: 62px; }
.logo-marquee__track img:hover,
.logo-marquee__track video:hover { opacity: 1; filter: brightness(1.05) saturate(1.05); }

/* --- services section ----------------------------------------------------- */
.svc-section {
  --lt: 1;
  --lt-pct: calc(var(--lt) * 100%);
  --light-bg: #F2F5F8;
  --dark-ink: #04121F;
  background-color: var(--light-bg);
}

.svc-section .display,
.svc-section .svc-row__label,
.svc-section .svc-row__plus {
  color: color-mix(in srgb, var(--dark-ink) var(--lt-pct), var(--ink));
}
.svc-section .svc-list { border-top-color: color-mix(in srgb, rgba(4, 18, 31, 0.16) var(--lt-pct), var(--rule)); }
.svc-section .svc-row { border-bottom-color: color-mix(in srgb, rgba(4, 18, 31, 0.16) var(--lt-pct), var(--rule)); }
.svc-section + .work-section {
  background: var(--canvas);
}

/* --- services list -------------------------------------------------------- */
.svc-list {
  margin-top: 60px;
  margin-inline: calc(50% - min(50vw, var(--container) / 2));
  border-top: 1px solid var(--rule);
}
.svc-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 28px;
  min-height: clamp(76px, 8svh, 112px);
  height: auto;
  padding-block: clamp(12px, 1.2vw, 18px);
  padding-inline: var(--pad);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
}
/* The fill is a full-row overlay holding a WHITE copy of the label + plus on a
   dark strip. It wipes in via clip-path from wherever the pointer entered,
   revealing its light text in sync with the strip, so text + strip stay legible
   through the whole hover (no blank frame, no colour pop). JS clones the
   label/plus into it. */
.svc-row__fill {
  position: absolute;
  inset: 0;
  z-index: 2;                       /* above the base dark-on-light text */
  background: var(--dark-ink, #04121F);
  /* Grows from the pointer position on hover (JS animates the circle radius). */
  clip-path: circle(0px at 50% 50%);
  display: flex;
  align-items: center;
  gap: 28px;
  padding-inline: var(--pad);
  pointer-events: none;
  will-change: clip-path;
}
.svc-row__fill .svc-row__label,
.svc-row__fill .svc-row__plus { color: var(--ink) !important; }
/* Keyboard focus has no pointer origin — flood the whole row instead. */
.svc-row:focus-visible .svc-row__fill { clip-path: circle(150% at 50% 50%); }
.svc-row__idx,
.svc-row__label,
.svc-row__plus { position: relative; z-index: 1; }
/* Leading index numbers removed per design. */
.svc-row__idx { display: none; }
.svc-row__label {
  flex: 1;
  font-weight: 600;
  font-size: clamp(1.9rem, 3.1vw, 3.15rem);
  line-height: 1.04;
  letter-spacing: -0.02em;
  min-width: 0;
  overflow-wrap: normal;
  text-wrap: balance;
  will-change: transform;
}
.svc-row__plus {
  width: 22px; height: 22px;
  position: relative;
  color: var(--ink);
}
.svc-row__plus::before,
.svc-row__plus::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 18px; height: 1.5px;
  background: currentColor;
  transform: translate(-50%, -50%);
  will-change: transform;
}
.svc-row__plus::after { transform: translate(-50%, -50%) rotate(90deg); }

.svc-preview {
  position: fixed;
  top: 0;
  left: 0;
  width: clamp(220px, 18vw, 360px);
  aspect-ratio: 4 / 5;
  z-index: 70;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: scale(0.97);
  transform-origin: center;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--blue-300) 38%, transparent);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.34);
  /* JS drives x/y/skewX — the lag between pointer and tween is the swing. */
  will-change: transform, opacity;
}
.svc-preview img, .svc-preview video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* --- next-page teaser ----------------------------------------------------- */
.teaser {
  position: relative;
  /* Same frame as every other media block on the page. */
  height: var(--media-h);
  overflow: hidden;
  display: flex;
  align-items: center;
  background: var(--surface);
}
/* Media-less teaser: a tinted floor that lifts toward blue on hover. */
.teaser:not(:has(.teaser__media)) { background: linear-gradient(160deg, var(--surface) 0%, var(--void) 100%); }
.teaser:not(:has(.teaser__media)):hover { background: linear-gradient(160deg, var(--blue-900) 0%, var(--void) 100%); }
.teaser__media { position: absolute; inset: 0; }
.teaser__media img,
.teaser__media video {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: brightness(0.6);
 
}
.teaser__scrim { position: absolute; inset: 0; background: rgba(10,12,16,0.6); transition: background .8s ease; }
.teaser:hover .teaser__scrim { background: rgba(10,12,16,0.38); }
.teaser__inner { position: relative; z-index: 2; width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--pad); }
.teaser__eyebrow { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
.teaser:hover .teaser__eyebrow { color: var(--blue-300); }
.teaser__title { margin-top: 18px; font-weight: 600; font-size: var(--fs-section-display); line-height: .98; letter-spacing: -.035em; }
.teaser__sub { margin-top: 18px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }

/* Photography stays dark when the page theme changes, so teaser copy uses a
   dedicated media foreground. Media-less teasers retain the page tokens. */
.teaser:has(.teaser__media) { color: var(--media-ink); }
.teaser:has(.teaser__media) .teaser__eyebrow,
.teaser:has(.teaser__media) .teaser__sub { color: var(--media-muted); }

/* --- media / text alternating rows ---------------------------------------- */
.media-row { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(40px, 5vw, 80px); align-items: center; }
.media-row + .media-row { margin-top: var(--rhythm); }
.media-row--reverse .media-row__media { order: 2; }
.media-row__media { position: relative; overflow: hidden; }
.media-row__media video,
.media-row__media img { width: 100%; height: auto; will-change: transform; }
.media-row__title { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.media-row__body { margin-top: 26px; font-weight: 450; font-size: var(--fs-body); line-height: 1.5; color: var(--muted); }

/* --- feature list columns ------------------------------------------------- */
.list-cols { margin-top: 46px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 80px; }
.list-cols li { font-family: var(--ff-util); font-size: 1rem; line-height: 1.8; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
.list-title { font-weight: 600; font-size: var(--fs-h2); letter-spacing: -0.03em; }

/* --- spec cards ----------------------------------------------------------- */
.spec-layout { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 40px; align-items: center; }
.spec-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px; }
.spec-card { padding: 26px 28px 30px; background: var(--surface); }
.spec-card__label { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--blue-300); }
.spec-card__value { margin-top: 10px; font-weight: 600; font-size: 1.75rem; letter-spacing: -0.02em; }
.spec-card__note { margin-top: 20px; font-weight: 450; font-size: 0.9375rem; line-height: 1.5; color: var(--muted); }

/* ghost word behind a heading */
.ghost-stack { position: relative; }
.ghost-word { font-weight: 600; font-size: clamp(64px, 10vw, 140px); line-height: 1; letter-spacing: -0.04em; color: var(--faint); opacity: 0.4; pointer-events: none; user-select: none; }
.ghost-stack__fore { position: relative; margin-top: -0.42em; }

/* --- masonry -------------------------------------------------------------- */
.masonry { columns: 4; column-gap: 4px; }
.masonry img, .masonry video { width: 100%; margin-bottom: 4px; break-inside: avoid; filter: brightness(0.85); }

/* --- image tiles ---------------------------------------------------------- */
.tile-grid { margin-top: 50px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.tile { position: relative; display: block; overflow: hidden; }
.tile img, .tile video { width: 100%; aspect-ratio: 455 / 650; object-fit: cover; filter: grayscale(1) brightness(0.72); }
.tile:hover img, .tile:hover video { filter: grayscale(0) brightness(1); }
.tile__label { margin-top: 18px; font-weight: 600; font-size: 1.5rem; letter-spacing: -0.02em; }
.tile:hover .tile__label { color: var(--blue-300); }

/* --- showcase ------------------------------------------------------------- */
.showcase-grid { margin-top: 48px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 34px 40px; }
.showcase img, .showcase video { width: 100%; aspect-ratio: 498 / 281; object-fit: contain; background: var(--surface); filter: brightness(0.9); }
.showcase__title { margin-top: 16px; font-weight: 600; font-size: 2rem; line-height: 1; letter-spacing: -0.03em; }

/* --- full-bleed media band ------------------------------------------------ */
.band {
  width: 100%;
  height: var(--feature-media-h);
  min-height: var(--feature-media-min);
  overflow: hidden;
}
.band img,
.band video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(0.72);
  will-change: transform;
}

/* --- gallery -------------------------------------------------------------- */
.gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.gallery-grid--1 { grid-template-columns: 1fr; }
.gallery-grid--2 { grid-template-columns: repeat(2, 1fr); }
.gallery-grid--4 { grid-template-columns: repeat(4, 1fr); }
.gallery-item { position: relative; display: block; overflow: hidden; background: var(--surface); aspect-ratio: 4 / 3; }
.gallery-item img,
.gallery-item video { width: 100%; height: 100%; object-fit: cover; filter: brightness(0.82); transition: filter .5s ease, transform .6s cubic-bezier(.16,1,.3,1); will-change: transform; }
.gallery-item:hover img,
.gallery-item:hover video { transform: scale(1.04); filter: brightness(1); }

/* --- bleed row ------------------------------------------------------------ */
.bleed-row { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 60px; max-width: var(--container); margin-inline: auto; }
.bleed-row + .bleed-row { margin-top: var(--rhythm); }
.bleed-row__media { overflow: hidden; }
.bleed-row__media img,
.bleed-row__media video { width: 100%; height: 480px; object-fit: cover; filter: brightness(0.82); will-change: transform; }
.bleed-row__copy { padding-inline: 40px; max-width: 660px; }
.bleed-row--right .bleed-row__media { order: 2; }
.bleed-row--right .bleed-row__copy { margin-left: auto; }
.bleed-row__title { font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.bleed-row__body { margin-top: 22px; font-weight: 450; font-size: var(--fs-body); line-height: 1.5; color: var(--muted); }

/* --- team ----------------------------------------------------------------- */
/* Three up, hairline-gapped. Flex rather than grid so a final short row centres
   itself instead of leaving a hole where the missing card would sit. */
.team-grid { margin-top: 60px; display: flex; flex-wrap: wrap; justify-content: center; gap: 2px; }
.team-card { flex: 0 1 calc((100% - 4px) / 3); padding: 26px; text-align: center; background: var(--surface); }
.team-card img, .team-card video { width: 100%; max-width: 303px; aspect-ratio: 1; object-fit: cover; margin: 0 auto 22px; filter: grayscale(1) brightness(0.9); transition: filter .4s ease; }
.team-card:hover img, .team-card:hover video { filter: grayscale(0) brightness(1); }
.team-card__name { font-weight: 500; font-size: 1.5rem; letter-spacing: -0.02em; }
.team-card__role { margin-top: 8px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
.team-card__social { margin-top: 16px; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; }
.team-card__social svg { width: 26px; height: 26px; }
.team-card__social:hover { color: var(--blue-300); }

/* --- forms ---------------------------------------------------------------- */
/* Starts on the container's left rail, like the heading and lead above it —
   centring it left the whole block hanging off-axis from its own section. */
.form-grid { margin-top: 48px; max-width: 940px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px 40px; }
.form-grid .field--wide { grid-column: 1 / -1; }
.form-grid .field label { text-transform: uppercase; font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; color: var(--muted); }
.form-grid .field input,
.form-grid .field textarea { background: transparent; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; padding: 12px 0; color: var(--ink); }
.form-grid .field input:focus,
.form-grid .field textarea:focus { border-bottom-color: var(--blue-500); }
.form-actions { grid-column: 1 / -1; margin-top: 24px; }
.file-field { grid-column: 1 / -1; display: flex; align-items: center; gap: 12px; margin-top: 24px; font-family: var(--ff-util); font-size: var(--fs-util); color: var(--muted); }

/* --- blog ----------------------------------------------------------------- */
.post-grid { margin-top: 56px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; }
.post-card { display: flex; flex-direction: column; background: var(--surface); overflow: hidden; }
.post-card img, .post-card video { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; filter: brightness(0.82); transition: filter .4s ease; }
.post-card:hover img, .post-card:hover video { filter: brightness(1); }
.post-card__body { padding: 26px; display: flex; flex-direction: column; flex: 1; }
.post-card__title { font-weight: 500; font-size: 1.25rem; line-height: 1.1; letter-spacing: -0.015em; }
.post-card__meta { margin-top: 14px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
.post-card__excerpt { margin-top: 14px; font-weight: 450; font-size: 0.9375rem; color: var(--muted); line-height: 1.6; }
.post-card__more { margin-top: auto; padding-top: 20px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--blue-300); }

.article { max-width: 760px; margin-inline: auto; }
.article__hero { width: 100%; margin-bottom: 44px; filter: brightness(0.9); }
.article p { margin-bottom: 22px; font-weight: 450; font-size: 1.125rem; line-height: 1.7; color: var(--body-light, rgba(242,245,248,0.72)); }
.article h2 { margin: 44px 0 18px; font-weight: 500; font-size: 1.75rem; letter-spacing: -0.02em; }
.article h3 { margin: 34px 0 14px; font-weight: 500; font-size: 1.35rem; letter-spacing: -0.015em; }
.article ul { margin-bottom: 22px; padding-left: 22px; list-style: disc; }
.article li { margin-bottom: 10px; font-weight: 450; font-size: 1.125rem; line-height: 1.7; color: var(--muted); }
.article__meta { margin-bottom: 30px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }

/* --- legal ---------------------------------------------------------------- */
.legal { max-width: 900px; margin-inline: auto; }
.legal p { margin-bottom: 20px; font-weight: 450; font-size: 1rem; line-height: 1.8; color: var(--muted); }
.legal h2 { margin: 40px 0 16px; font-weight: 500; font-size: 1.5rem; letter-spacing: -0.02em; }
.legal h3 { margin: 30px 0 12px; font-weight: 500; font-size: 1.2rem; letter-spacing: -0.015em; }
.legal ul { margin-bottom: 20px; padding-left: 22px; list-style: disc; }
.legal li { margin-bottom: 8px; font-weight: 450; font-size: 1rem; line-height: 1.8; color: var(--muted); }

/* --- store ---------------------------------------------------------------- */
.product-grid { margin-top: 50px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; }
.product-card img, .product-card video { width: 100%; aspect-ratio: 1; object-fit: cover; background: var(--surface); filter: brightness(0.85); transition: filter .4s ease; }
.product-card:hover img, .product-card:hover video { filter: brightness(1); }
.product-card__name { margin-top: 14px; font-size: 1rem; }
.product-card__price { margin-top: 6px; font-family: var(--ff-util); font-size: var(--fs-util); color: var(--muted); }
.product-detail { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: start; }
.product-detail img, .product-detail video { width: 100%; background: var(--surface); filter: brightness(0.9); }
.product-detail__price { margin-top: 14px; font-family: var(--ff-util); font-size: 1.125rem; color: var(--muted); }
.product-detail__desc { margin-top: 26px; font-weight: 450; font-size: 1rem; line-height: 1.7; color: var(--muted); }
.qty { margin-top: 26px; display: flex; align-items: center; gap: 12px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; }
.qty input { width: 74px; padding: 10px; background: transparent; color: var(--ink); border: 1px solid var(--rule); border-radius: 0; }

/* --- CTA band ------------------------------------------------------------- */
.cta { padding-block: var(--rhythm-md); }
.cta__title {
  font-weight: 600;
  font-size: var(--fs-section-display);
  line-height: .98;
  letter-spacing: -.035em;
}
.cta__actions { margin-top: 34px; display: flex; gap: 18px; flex-wrap: wrap; }
.cta__actions .btn,
.cta__actions .pill {
  width: min(100%, 408px);
  height: 64px;
  padding: 0 40px;
}

/* --- footer --------------------------------------------------------------- */
.site-footer { background: var(--void); padding-block: var(--rhythm-md) 30px; overflow: hidden; }
.site-footer__head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 40px;
  padding-bottom: clamp(40px, 6svh, 90px);
}
/* Keep the footer signature on the same display scale as the CTA headline so
   it closes the page without overpowering the section directly above it. */
.footer-wordmark {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(2.75rem, 5.8vw, 6.5rem);
  line-height: .98;
  letter-spacing: -.035em;
  color: var(--ink);
  white-space: nowrap;
  will-change: transform;
}
/* Per-letter roll cells (built by JS). Each cell masks a vertical track of two
   identical glyphs; the JS slides the track one glyph-height to re-roll the
   same letter.
   - Vertical: clip-path masks ONLY the top and bottom (the negative left/right
     insets extend the visible region sideways so glyph side-bearings are never
     shaved — overflow:hidden was cropping 'e', 'R', etc. horizontally).
   - The 1.36em line box fully contains Inter Tight's ascent + descent (~1.21em)
     so nothing is clipped top/bottom at rest and the 'g' descender can't bleed
     into the neighbouring cell. */
.wm-char {
  display: inline-block;
  clip-path: inset(0 -100vw);
  height: 1.36em;
  line-height: 1.36;
  vertical-align: top;
}
.wm-char__roll { display: block; will-change: transform; }
.wm-char__g { display: block; height: 1.36em; line-height: 1.36; }
.footer-clocks { display: grid; gap: 18px; justify-items: end; }
.footer-clock { text-align: right; }
.footer-clock__zone { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
.footer-clock__time {
  margin-top: 8px;
  font-family: var(--ff-util);
  font-size: clamp(1.25rem, 1.8vw, 1.5rem);
  letter-spacing: 0.04em;
  color: var(--blue-500);
  font-feature-settings: "tnum" 1;
  display: inline-flex;
}
.footer-clock__time .odometer { height: 1em; }

.site-footer__top { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; padding-top: 60px; border-top: 1px solid var(--rule); }
.site-footer h3, .site-footer h4 { font-family: var(--ff-util); font-weight: 400; font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); margin-bottom: 22px; }
.site-footer p,
.site-footer li { font-weight: 450; font-size: 0.9375rem; line-height: 1.5; margin-bottom: 18px; color: var(--muted); }
.site-footer a:hover { color: var(--blue-300); }
/* The head office sits under the two stages in the same column, set back a step
   so the list still reads as "these are the studios, and this is the office". */
.footer-hq { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--rule); color: var(--faint); }
.footer-explore {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}
.footer-explore a {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 5px;
  border-bottom: 1px solid rgba(79, 169, 255, 0.62);
  /* This row sits on the footer surface, not on the blue brand strip. Using
     --on-blue here made the resting label nearly black in dark mode. */
  color: var(--ink);
  transition: color .25s ease, border-color .25s ease;
}
.footer-explore a::after {
  content: "";
  width: 0.34em;
  height: 0.34em;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  transform: translateY(-0.08em) rotate(45deg);
  transition: transform .25s ease;
}
.footer-explore a:hover,
.footer-explore a:focus-visible {
  color: var(--blue-300);
  border-color: currentColor;
}
.footer-explore a:hover::after,
.footer-explore a:focus-visible::after { transform: translate(4px, -0.08em) rotate(45deg); }
.footer-explore span { color: var(--faint); }
.site-footer__bottom {
  margin-top: 60px;
  padding-top: 24px;
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: center;
  gap: 20px;
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
}
.site-footer__bottom .to-top { margin-left: auto; }

/* --- contact modal -------------------------------------------------------- */
.modal { position: fixed; inset: 0; z-index: 200; display: none; align-items: center; justify-content: center; padding: 24px; background: rgba(5, 7, 10, .85); }
.modal.is-open { display: flex; }
.modal__panel { position: relative; width: 100%; max-width: 560px; max-height: 90svh; overflow-y: auto; padding: 44px; background: var(--surface); border: 1px solid var(--rule); }
.modal__close { position: absolute; top: 16px; right: 20px; font-size: 28px; line-height: 1; color: var(--muted); }
.modal__close:hover { color: var(--ink); }
.modal h3 { font-weight: 600; font-size: 2rem; letter-spacing: -0.03em; margin-bottom: 10px; }
.modal p { font-weight: 450; font-size: 0.9375rem; color: var(--muted); margin-bottom: 26px; }

.field { margin-bottom: 18px; }
.field label { display: block; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; margin-bottom: 8px; color: var(--muted); }
.field input,
.field select,
.field textarea { width: 100%; padding: 13px 15px; background: var(--void); border: 1px solid var(--rule); border-radius: 0; color: var(--ink); font: inherit; font-size: 0.9375rem; }
/* Native arrow is redrawn so the select matches the flat, square inputs. */
.field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 19px) calc(50% + 1px), calc(100% - 13px) calc(50% + 1px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 40px;
}
.field select option { background: var(--void); color: var(--ink); }

/* --- select, our own listbox ---------------------------------------------
   site.js swaps the native select for .mselect: the real <select> stays in the
   DOM (it is what the form submits) but goes visually hidden, and the button +
   list below are what you see. No JS, no .mselect — the native select is still
   sitting there, styled by the rules above. */
.mselect { position: relative; }
.mselect__native {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.mselect__button {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 15px;
  background: var(--void); border: 1px solid var(--rule); border-radius: 0;
  color: var(--ink); font: inherit; font-size: 0.9375rem; text-align: left;
  cursor: pointer;
  transition: border-color .2s ease, background-color .2s ease;
}
.mselect__button:hover { border-color: var(--blue-700); }
.mselect__button:focus-visible,
.mselect.is-open .mselect__button { outline: none; border-color: var(--blue-500); }
.mselect__caret {
  width: 8px; height: 8px; flex: none; margin-right: 4px;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: translateY(-2px) rotate(45deg);
  transition: transform .24s ease, border-color .2s ease;
}
.mselect__button:hover .mselect__caret { border-color: var(--ink); }
.mselect.is-open .mselect__caret { transform: translateY(1px) rotate(225deg); border-color: var(--blue-300); }
.mselect__list {
  position: absolute; z-index: 40; top: calc(100% - 1px); left: 0; right: 0;
  margin: 0; padding: 0; list-style: none;
  max-height: 260px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--blue-500);
  box-shadow: 0 20px 44px rgba(0, 0, 0, 0.5);
}
.mselect__option {
  position: relative; padding: 12px 15px 12px 30px;
  color: var(--muted); font-size: 0.9375rem; cursor: pointer;
  transition: color .16s ease, background-color .16s ease;
}
/* The blue tick rail marks the current value, and slides in under the pointer,
   so keyboard and mouse read the same way. */
.mselect__option::before {
  content: ""; position: absolute; left: 15px; top: 50%;
  width: 6px; height: 1px; background: var(--blue-500);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .2s ease;
}
.mselect__option.is-active { color: var(--ink); background: var(--blue-tint); }
.mselect__option.is-active::before { transform: scaleX(1); }
.mselect__option.is-selected { color: var(--ink); }
.mselect__option.is-selected::before { transform: scaleX(1); }
.field input:focus,
.field select:focus,
.field textarea:focus { outline: none; border-color: var(--blue-500); }
.field textarea { min-height: 120px; resize: vertical; }

/* --- lightbox ------------------------------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 210;
  display: none; align-items: center; justify-content: center;
  background: rgba(5, 7, 10, .98);
  color: var(--media-ink);
}
.lightbox.is-open { display: flex; }
.lightbox [data-stage] {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(14px, 3vw, 48px);
}
.lightbox.is-open [data-stage] { animation: lightbox-stage-in .28s cubic-bezier(.16, 1, .3, 1) both; }
@keyframes lightbox-stage-in {
  from { opacity: 0; transform: scale(.985); }
  to { opacity: 1; transform: scale(1); }
}
.lightbox img,
.lightbox video,
.lightbox iframe {
  width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: contain;
}
/* YouTube always ships a 16:9 player. Sizing the frame to that ratio (capped by
   the stage box) plays the clip at the ratio it was uploaded at instead of
   stretching it to the viewport and letting YouTube pad the sides. */
.lightbox iframe {
  border: 0;
  aspect-ratio: 16 / 9;
  width: min(100%, calc((100svh - 2 * clamp(14px, 3vw, 48px)) * 16 / 9));
  height: auto;
  max-width: 100%;
  margin: auto;
}
.lightbox__close,
.lightbox__nav,
.lightbox__play,
.lightbox__mute {
  position: absolute; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(242, 245, 248, .22);
  background: rgba(5, 7, 10, .78);
  color: var(--media-ink);
  transition: opacity .24s ease, visibility .24s ease, color .2s ease, background-color .2s ease, border-color .2s ease;
}
.lightbox__close,
.lightbox__nav { width: 48px; height: 48px; }
.lightbox__close { top: max(20px, env(safe-area-inset-top)); right: max(20px, env(safe-area-inset-right)); font-size: 30px; line-height: 1; }
.lightbox__nav { top: 50%; transform: translateY(-50%); font-size: 30px; }
.lightbox__play {
  left: 50%;
  top: 50%;
  width: clamp(54px, 4.4vw, 68px);
  height: clamp(54px, 4.4vw, 68px);
  padding: 0;
  transform: translate(-50%, -50%);
  background: rgba(5, 7, 10, .66);
  backdrop-filter: blur(10px);
}
.lightbox__play svg { width: 27px; height: 27px; }
/* Video opens muted — this is the way back to sound. */
.lightbox__mute {
  left: max(20px, env(safe-area-inset-left));
  bottom: max(20px, env(safe-area-inset-bottom));
  width: 48px; height: 48px;
  padding: 0;
  background: rgba(5, 7, 10, .66);
  backdrop-filter: blur(10px);
}
.lightbox__mute svg { width: 22px; height: 22px; }
.lightbox__mute:hover { color: var(--blue-300); background: var(--surface-hi); border-color: var(--blue-300); }
.lightbox__mute[hidden] { display: none; }
/* Scrub bar — the timeline for the open clip. Sits in the same bottom row as
   the mute button, for the native <video> and the YouTube embed alike (the
   embed's own controls stay hidden under .lightbox__cover). */
.lightbox__scrub {
  position: absolute; z-index: 3;
  left: calc(max(20px, env(safe-area-inset-left)) + 60px);
  right: max(20px, env(safe-area-inset-right));
  bottom: max(20px, env(safe-area-inset-bottom));
  height: 48px;
  display: flex; align-items: center; gap: 14px;
  padding: 0 18px;
  border: 1px solid rgba(242, 245, 248, .22);
  background: rgba(5, 7, 10, .66);
  backdrop-filter: blur(10px);
  transition: opacity .24s ease, visibility .24s ease;
}
.lightbox__scrub[hidden] { display: none; }
.lightbox__time {
  font-family: var(--ff-util); font-size: var(--fs-util);
  color: var(--media-muted); font-variant-numeric: tabular-nums;
  min-width: 42px; text-align: center;
}
/* The played portion is painted into the track background from --p, so there
   is one element to style instead of a track/fill pair per engine. */
.lightbox__range {
  -webkit-appearance: none; appearance: none;
  flex: 1 1 auto; min-width: 0;
  height: 4px; margin: 0; border: 0; border-radius: 2px;
  cursor: pointer;
  background: linear-gradient(to right,
    var(--blue-500) 0%, var(--blue-500) var(--p, 0%),
    rgba(255, 255, 255, .22) var(--p, 0%), rgba(255, 255, 255, .22) 100%);
}
.lightbox__range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; border: 0; border-radius: 50%;
  background: var(--media-ink);
}
.lightbox__range::-moz-range-thumb {
  width: 13px; height: 13px; border: 0; border-radius: 50%;
  background: var(--media-ink);
}
.lightbox__range:focus-visible { outline: 2px solid var(--blue-300); outline-offset: 6px; }
/* Sits exactly over the 16:9 frame and hides YouTube's furniture in every
   state but playing. Same sizing maths as the iframe above. */
.lightbox__cover {
  position: absolute; z-index: 2;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  aspect-ratio: 16 / 9;
  width: min(calc(100% - 2 * clamp(14px, 3vw, 48px)), calc((100svh - 2 * clamp(14px, 3vw, 48px)) * 16 / 9));
  background: #05070a center / cover no-repeat;
  transition: opacity .25s ease;
}
.lightbox__cover.is-hidden { opacity: 0; pointer-events: none; }
.lightbox__activity {
  position: absolute; z-index: 2;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  aspect-ratio: 16 / 9;
  width: min(calc(100% - 2 * clamp(14px, 3vw, 48px)), calc((100svh - 2 * clamp(14px, 3vw, 48px)) * 16 / 9));
  cursor: default;
  background: transparent;
}
.lightbox__close:hover,
.lightbox__nav:hover,
.lightbox__play:hover { color: var(--blue-300); background: var(--surface-hi); border-color: var(--blue-300); }
.lightbox__nav:disabled { opacity: 0; visibility: hidden; pointer-events: none; }
.lightbox__play[hidden] { display: none; }
.lightbox__prev { left: max(20px, env(safe-area-inset-left)); }
.lightbox__next { right: max(20px, env(safe-area-inset-right)); }
.lightbox__caption {
  position: absolute; z-index: 2;
  right: max(24px, env(safe-area-inset-right));
  bottom: max(24px, env(safe-area-inset-bottom));
  max-width: min(520px, calc(100vw - 48px));
  padding: 14px 16px;
  text-align: right;
  background: rgba(5, 7, 10, .82);
  border-right: 2px solid var(--blue-500);
  pointer-events: none;
  transition: opacity .24s ease, visibility .24s ease;
}
.lightbox.has-video:not(.is-ui-visible) .lightbox__close,
.lightbox.has-video:not(.is-ui-visible) .lightbox__nav,
.lightbox.has-video:not(.is-ui-visible) .lightbox__play,
.lightbox.has-video:not(.is-ui-visible) .lightbox__mute,
.lightbox.has-video:not(.is-ui-visible) .lightbox__scrub,
.lightbox.has-video:not(.is-ui-visible) .lightbox__caption {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.lightbox.has-video .lightbox__caption { bottom: max(78px, calc(env(safe-area-inset-bottom) + 66px)); }
.lightbox__caption[hidden] { display: none; }
.lightbox__title { font-weight: 600; font-size: clamp(1.15rem, 2vw, 1.65rem); line-height: 1.05; }
.lightbox__meta {
  margin-top: 6px;
  font-family: var(--ff-util); font-size: var(--fs-util);
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--media-muted);
}

/* --- chatbot -------------------------------------------------------------- */
.chatbot {
  position: fixed;
  right: max(18px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  z-index: 180;
  display: grid;
  justify-items: end;
  gap: 12px;
  pointer-events: none;
}
.chatbot.is-open { z-index: 220; }
.chatbot__launcher,
.chatbot__nudge,
.chatbot__panel { pointer-events: auto; }
.chatbot__launcher {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: 78px;
  height: 56px;
  padding: 0 18px;
  background: var(--blue-500);
  color: var(--ink);
  border: 1px solid rgba(156, 208, 255, 0.42);
  box-shadow: var(--blue-glow);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.chatbot__launcher svg { width: 20px; height: 20px; }
.chatbot__launcher:hover,
.chatbot__launcher:focus-visible { background: var(--blue-300); color: var(--void); }
.chatbot__nudge {
  position: relative;
  display: none;
  width: min(320px, calc(100vw - 36px));
  padding: 16px 44px 16px 18px;
  background: rgba(16, 19, 24, 0.96);
  border: 1px solid var(--rule-strong);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.chatbot__nudge.is-visible { display: block; }
.chatbot__nudge p {
  font-weight: 520;
  font-size: 0.95rem;
  line-height: 1.35;
  color: var(--ink);
}
.chatbot__nudge-close,
.chatbot__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--muted);
  font-size: 24px;
  line-height: 1;
}
.chatbot__nudge-close {
  position: absolute;
  top: 2px;
  right: 2px;
}
.chatbot__close:hover,
.chatbot__nudge-close:hover { color: var(--ink); background: var(--surface-hi); }
.chatbot__panel {
  display: none;
  width: min(420px, calc(100vw - 36px));
  height: min(620px, calc(100svh - 120px));
  background: rgba(10, 12, 16, 0.98);
  border: 1px solid var(--rule-strong);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.48);
  overflow: hidden;
}
.chatbot.is-open .chatbot__panel {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
}
.chatbot.is-open .chatbot__nudge { display: none; }
.chatbot__head {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 18px;
  padding: 18px 18px 14px;
  border-bottom: 1px solid var(--rule);
}
.chatbot__eyebrow {
  margin-bottom: 6px;
  color: var(--blue-300);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.chatbot__head h2 {
  font-weight: 650;
  font-size: 1.12rem;
  line-height: 1.15;
  letter-spacing: 0;
}
.chatbot__messages {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px;
  overflow-y: auto;
}
.chatbot__message {
  max-width: 88%;
  padding: 11px 13px;
  border: 1px solid var(--rule);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.95rem;
  line-height: 1.42;
  white-space: pre-line;
}
.chatbot__message--user {
  align-self: flex-end;
  background: var(--blue-500);
  border-color: var(--blue-500);
}
.chatbot__message--bot { align-self: flex-start; }
.chatbot__message--system {
  max-width: 100%;
  align-self: stretch;
  background: transparent;
  border-color: var(--rule-strong);
  color: var(--muted);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.chatbot__quick {
  display: flex;
  gap: 8px;
  padding: 0 18px 14px;
  overflow-x: auto;
}
.chatbot__quick button {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--rule-strong);
  color: var(--ink);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.chatbot__quick button:hover,
.chatbot__quick button:focus-visible { border-color: var(--blue-500); color: var(--blue-300); }
.chatbot__form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 48px;
  gap: 10px;
  padding: 14px 18px 18px;
  border-top: 1px solid var(--rule);
}
.chatbot__form textarea {
  min-height: 48px;
  max-height: 130px;
  resize: none;
  padding: 13px 14px;
  background: var(--void);
  border: 1px solid var(--rule);
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
  line-height: 1.35;
}
.chatbot__form textarea:focus { outline: none; border-color: var(--blue-500); }
.chatbot__send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: var(--blue-500);
  color: var(--ink);
  border: 1px solid var(--blue-500);
}
.chatbot__send svg { width: 20px; height: 20px; }
.chatbot__send:hover,
.chatbot__send:focus-visible { background: var(--blue-300); color: var(--void); }

/* --- page transition panel ------------------------------------------------ */
.transition-panel {
  position: fixed;
  inset: 0;
  z-index: 9500;
  background: var(--void);
  clip-path: inset(100% 0 0 0);
  pointer-events: none;
  will-change: clip-path;
}
.transition-panel::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--blue-500);
}

/* --- project morph transition (thumbnail → hero) -------------------------- */
/* A fixed clone of the clicked thumbnail that scales up to fill the viewport,
   morphing into the project page's hero image (ClaPat "Hayler" reference). */
.morph-clone {
  position: fixed;
  z-index: 9600;
  overflow: hidden;
  pointer-events: none;
  will-change: transform, width, height, top, left;
}
/* Stacked, so the clicked thumbnail can crossfade to the destination page's
   hero frame while the square sits on the loading curtain. */
.morph-clone img,
.morph-clone video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Top loading bar during the fetch. */
.transition-bar {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 0%;
  background: var(--blue-500);
  z-index: 9700;
  pointer-events: none;
  opacity: 0;
}
/* Branded loading curtain: a row of blue vertical strips that roll down to
   cover while the page fetches (the thumbnail sits centred on them as a square
   + spinner), then roll back up to reveal — the ClaPat "Hayler" strip curtain,
   kept in the MergeXR accent blue. Strips are built + animated in site.js. */
.load-curtain {
  position: fixed;
  inset: 0;
  z-index: 9550;
  display: flex;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}
.load-curtain > i {
  flex: 1 1 0%;
  display: block;
  background: var(--blue-500);
  transform: scaleY(0);
  transform-origin: top center;
  will-change: transform;
}
.load-spinner {
  position: fixed;
  top: 72%; left: 50%;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top-color: #fff;
  border-radius: 50%;
  z-index: 9660;
  opacity: 0;
  pointer-events: none;
}
.load-spinner.spin { animation: load-spin 0.8s linear infinite; }
@keyframes load-spin { to { transform: rotate(360deg); } }

/* --- strip (blinds) reveal for image / video media ----------------------- */
/* A row of vertical cover strips sits over the media and rolls up one after
   another (staggered), revealing the media column-by-column — the ClaPat
   "Hayler" media reveal. Cover colour is set per-instance in JS to match the
   section background, so it works on light and dark sections alike. */
.media-reveal__strips {
  position: absolute; inset: 0; z-index: 2;
  display: flex; pointer-events: none;
}
.media-reveal__strips > i {
  flex: 1 1 0%;
  display: block;
  background: var(--strip-cover, #0A0C10);
  transform: scaleY(1);
  transform-origin: top center;
  will-change: transform;
}

/* --- project detail page (dark MergeXR brand) ---------------------------- */
.project-hero {
  position: relative;
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
  overflow: hidden;
}
.project-hero__img { position: absolute; inset: 0; }
.project-hero__img img, .project-hero__img video { width: 100%; height: 100%; object-fit: cover; }
.project-hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(5,7,10,0.55) 0%, rgba(5,7,10,0.15) 40%, rgba(5,7,10,0.75) 100%);
}
.project-hero__inner {
  position: relative; z-index: 2;
  height: 100%;
  padding: var(--header-h) var(--pad) clamp(28px, 5svh, 64px);
  display: grid;
  grid-template-rows: auto 1fr auto;
}
.project-hero__head { align-self: start; }
.project-hero__lead {
  grid-row: 2;
  align-self: end; justify-self: end;
  max-width: 40ch;
}
.project-hero__lead-h {
  display: block; margin: 0 0 14px;
  font-family: var(--ff-util); font-weight: 500;
  font-size: clamp(1.05rem, 1.5vw, 1.5rem);
  letter-spacing: 0.08em; text-transform: uppercase;
  line-height: 1.15;
  color: var(--blue-300);
}
.project-hero__lead p {
  font-size: clamp(1rem, 1.3vw, 1.35rem);
  line-height: 1.5;
  color: var(--ink);
}
.project-hero__title {
  font-weight: 600;
  font-size: var(--fs-h1);
  line-height: 1;
  letter-spacing: -0.03em;
  margin-top: 8svh;
  max-width: 16ch;
}
.project-hero__year { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.project-hero__foot {
  grid-row: 3;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding-top: 20px; border-top: 1px solid var(--rule);
  font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}

.project-detail { padding-block: var(--rhythm); }

/* Intro: a deliberate typographic contrast — a small, quiet meta column on the
   left set against a large, heavy statement on the right. The tighter column
   ratio pushes the statement wider so it dominates the row. */
/* Shares its column track with .psec (see the v2 block) so the copy column has
   one spine down the whole page — a wider rail here made the statement and the
   section bodies below it start at different x. */
.project-intro {
  display: grid; grid-template-columns: var(--spine); gap: var(--spine-gap);
  align-items: start;
}
.project-intro__meta { display: grid; gap: clamp(20px, 3svh, 34px); align-content: start; }
.project-intro__meta h4 { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
.project-intro__meta p { font-weight: 500; font-size: clamp(1rem, 1.2vw, 1.3rem); color: var(--ink); }
.project-detail__statement {
  font-weight: 600; font-size: clamp(2.3rem, 4.6vw, 4.75rem); line-height: 1.04; letter-spacing: -0.02em;
}
/* Neutral grey, same as every other run of body copy: the statement above it is
   the page's voice, the lead is support. */
.project-detail__lead { margin-top: clamp(24px, 3.5svh, 44px); color: var(--muted); max-width: 62ch; }

/* Centered statement set between two image rows — a large, quiet interlude that
   lets the media breathe on either side (ClaPat "Hayler" mid-page statement). */
.project-interlude {
  margin: var(--rhythm) auto;
  max-width: 22ch;
  text-align: center;
  font-weight: 600;
  font-size: clamp(2.2rem, 5vw, 5rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
}
.project-interlude .accent { color: var(--blue-500); }
.mt-rhythm { margin-top: var(--rhythm); }
.project-detail__statement .accent { color: var(--blue-500); }

/* Full-bleed band: image with a big statement overlaid, centred. */
.project-band {
  position: relative; margin-top: var(--rhythm);
  height: var(--feature-media-h); min-height: var(--feature-media-min); overflow: hidden;
  display: grid; place-items: center;
}
.project-band__img { position: absolute; inset: 0; }
.project-band__img img, .project-band__img video { width: 100%; height: 100%; object-fit: cover; }
.project-band__scrim { position: absolute; inset: 0; background: rgba(5, 7, 10, 0.4); }
.project-band__text {
  position: relative; z-index: 2;
  font-weight: 500; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.03em;
  text-align: center; max-width: 20ch; padding: 0 var(--pad); color: var(--media-ink);
}
.project-band__text .accent { color: var(--blue-300); }
.project-detail__statement--right { margin-left: auto; text-align: left; }

/* Editorial text + image rhythm (ClaPat "Hayler" subpage reference) --------- */
.project-figure { overflow: hidden; margin-top: var(--rhythm); }
.project-figure img, .project-figure video { width: 100%; height: 100%; object-fit: cover; display: block; }
.project-figure--wide { aspect-ratio: 16 / 8; }

.project-split {
  margin-top: var(--rhythm);
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(32px, 5vw, 96px);
  align-items: center;
}
.project-split--reverse .project-split__media { order: -1; }
.project-split__eyebrow { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; text-transform: uppercase; color: var(--blue-300); margin-bottom: 18px; }
.project-split__body { font-size: clamp(1.05rem, 1.4vw, 1.4rem); line-height: 1.5; color: var(--ink); max-width: 42ch; }
.project-split__media { overflow: hidden; aspect-ratio: 4 / 5; }
.project-split__media img, .project-split__media video { width: 100%; height: 100%; object-fit: cover; }

/* Alternating text + image blocks (image-left/text-right and the reverse). */
.pblock {
  margin-top: var(--rhythm);
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(32px, 5vw, 110px);
  align-items: center;
}
.pblock__media { overflow: hidden; aspect-ratio: 5 / 6; }
.pblock__media img, .pblock__media video { width: 100%; height: 100%; object-fit: cover; }
.pblock--reverse .pblock__media { order: 2; }
.pblock__text { max-width: 36ch; }
.pblock__text .pblock__h {
  font-weight: 500; font-size: clamp(1.5rem, 2.6vw, 2.4rem); line-height: 1.12; letter-spacing: -0.02em;
}
.pblock__text p { margin-top: 20px; color: var(--muted); font-size: clamp(1rem, 1.2vw, 1.2rem); line-height: 1.55; }

/* Media rhythm (ClaPat "Hayler" ref): a 2-column row, then a full-width big one. */
.pair {
  margin-top: var(--rhythm);
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(16px, 2vw, 32px);
}
.pair--1 { grid-template-columns: minmax(0, 1fr); }
.pair--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.pair--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* The tint keeps an empty slot (media not dropped in yet) readable as a frame
   rather than a hole in the page. */
.pair figure { position: relative; overflow: hidden; height: var(--media-h); margin: 0; background: rgba(255, 255, 255, 0.04); }
.pair figure video,
.pair figure img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The big media directly follows the pair as one gallery block (tight gap). */
.pair + .pfeature { margin-top: clamp(16px, 2vw, 32px); }

/* Before/after video wipe. Two clips stacked full-bleed; the top one is clipped
   at --wipe (0-100%) so both stay put — no scaling, no reflow. The handle rests
   dead centre with a "Move" hint and drops the hint once the pointer is inside,
   after which JS eases --wipe toward the pointer. */
.vwipe {
  position: relative;
  overflow: hidden;
  margin-top: clamp(16px, 2vw, 32px);
  height: var(--media-h);
  --wipe: 50%;
  touch-action: pan-y;
}
.pair + .vwipe { margin-top: clamp(16px, 2vw, 32px); }
.vwipe + .psec, .vwipe + .pfeature, .vwipe + .pair { margin-top: var(--rhythm); }
/* Either media type can be a wipe layer; the box height comes from .vwipe. */
.vwipe video, .vwipe img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vwipe__top {
  position: absolute; inset: 0; z-index: 1;
  clip-path: inset(0 0 0 var(--wipe));
}
.vwipe__handle {
  position: absolute; top: 0; bottom: 0; left: var(--wipe);
  z-index: 2;
  transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.vwipe__line {
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 2px; margin-left: -1px;
  background: var(--blue-500);
  box-shadow: var(--blue-glow);
}
.vwipe__hint {
  position: relative;
  padding: 9px 18px;
  border: 1px solid var(--blue-500);
  border-radius: 999px;
  background: rgba(5, 7, 10, 0.55);
  backdrop-filter: blur(6px);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: #fff;
  white-space: nowrap;
  transition: opacity .35s ease, transform .35s ease;
}
.vwipe.is-live .vwipe__hint { opacity: 0; transform: scale(.86); }
/* Side labels. Each one rides its own layer, so the wipe cuts the word exactly
   where it cuts the footage instead of floating both tags over the seam: the
   "Final" tag lives inside .vwipe__top and inherits its clip, and this pane is
   the mirror of that clip for the "Previz" tag under it. */
.vwipe__under {
  position: absolute; inset: 0; z-index: 1;
  clip-path: inset(0 calc(100% - var(--wipe)) 0 0);
  pointer-events: none;
}
.vwipe__tag {
  position: absolute; bottom: 12px; z-index: 2;
  font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--media-ink); background: rgba(5, 7, 10, 0.68); padding: 5px 10px;
  white-space: nowrap; pointer-events: none;
}
.vwipe__tag--l { left: 12px; }
.vwipe__tag--r { right: 12px; }
/* Tags at the top, for a wipe that sits under overlay copy in the bottom-right. */
.vwipe--tags-top .vwipe__tag { top: 12px; bottom: auto; }

/* Big feature card: full-width video with its copy anchored bottom-right.
   Height is capped to the viewport so it never overwhelms the fold. */
.pfeature { position: relative; margin-top: var(--rhythm); overflow: hidden; height: var(--media-h); }
/* A fifth taller than the shared media height, for a banner that has to carry a
   frame the standard block crops too hard. */
.pfeature--tall { height: calc(var(--media-h) * 1.2); }
/* A second big in the same gallery block sits tight under a pair, too. */
.pfeature + .pair { margin-top: var(--rhythm); }
.pfeature video,
.pfeature img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pfeature__scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(5,7,10,0) 38%, rgba(5,7,10,0.78) 100%);
}
.pfeature__text {
  position: absolute; z-index: 2;
  right: clamp(20px, 3vw, 56px); bottom: clamp(22px, 3.2vw, 52px);
  max-width: 44ch; color: var(--media-ink);
}
/* Placeholder frame standing in for media that has not been supplied yet.
   Deliberately unfinished-looking, so it cannot be mistaken for art direction
   if it survives to a review. .ph--banner fills a .pfeature card. */
.ph {
  display: grid; place-items: center;
  background: var(--surface); border: 1px dashed var(--rule-strong);
}
.ph--banner { width: 100%; height: 100%; }
.ph__label {
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--faint);
}

/* Fit variant: the card takes the clip's own aspect ratio, passed in as
   --media-ar, instead of the shared --media-h. Every other .pfeature crops to a
   landscape box with object-fit: cover, which is right for a normal frame and
   wrong for an ultra-wide letterboxed strip — cover would enlarge it to fill the
   height and throw the sides away. Here the box matches the source, so the whole
   frame shows and the only scaling is down, never up. */
.pfeature--fit { height: auto; aspect-ratio: var(--media-ar, 16 / 9); }
/* Crop from the bottom instead of both edges, so the top of the frame survives. */
.pfeature--top video,
.pfeature--top img { object-position: top center; }
.pfeature--fit video,
.pfeature--fit img { object-fit: contain; background: var(--void); }

/* Wipe variant: the block holds a .vwipe comparison instead of a single clip.
   The wipe fills the card, so it drops the component's own top margin and takes
   the card's height; the copy and scrim stay exactly as on every other one. */
.pfeature--wipe .vwipe { margin: 0; height: 100%; }
/* The handle hides the system cursor while it is live, so the card must not
   also claim a custom one. */
.pfeature--wipe { cursor: default; }

.pfeature__h { font-weight: 500; font-size: clamp(1.5rem, 2.6vw, 2.4rem); line-height: 1.1; letter-spacing: -0.02em; }
.pfeature__h .accent { color: var(--blue-300); }
.pfeature__text p { margin-top: 14px; font-size: clamp(0.98rem, 1.15vw, 1.18rem); line-height: 1.55; color: rgba(242, 245, 248, 0.84); }

/* --- scrubbed frame sequence ---------------------------------------------
   Base state is the no-JS one: a poster frame the height of a .pfeature with
   the three titles set as a list under each other, bottom-right. site.js adds
   .is-live, which grows the section to --seq-scroll, sticks the stage and
   stacks the titles on one another so it can play them one at a time against
   the canvas. Titles are masked cells; the roll itself lives in GSAP. */
.seq {
  --seq-y: 0px;
  /* Scroll run. This is the section's whole height while it is live, and it is
     the single biggest source of "the page resists after the video loads": the
     block ships ~1 screen tall as the static poster, then goes live and becomes
     this. Every extra svh here is another screen the reader has to grind through
     to reach the next section. Kept long enough that the scrub reads as motion
     rather than as steps, and no longer.
     Note the scrub only gets height - stage - stick, not the whole section: at
     260svh that left 1383px of travel for 242 frames, i.e. 5.7px a frame, so a
     single 50px flick tore through ~9 frames and the clip read as fast-forward.
     360svh gives 2195px, 9.1px a frame, ~5 frames a flick. */
  --seq-scroll: 220svh;
  /* The stage is --void on a --canvas page. A full --rhythm of the lighter
     colour above it left a dead band with a visible edge where the two met, so
     the run-up is short now and ::before carries canvas → void across it: the
     stage arrives instead of being pasted on. Below still gets full rhythm —
     the blue band must not butt straight onto the frame's bottom edge. */
  position: relative; margin-block: 0 var(--rhythm-sm); background: var(--void);
}
.section:has(+ .seq) {
  padding-bottom: 0;
  background: var(--canvas);
}
.seq__stage { position: relative; overflow: hidden; height: clamp(340px, 62svh, 720px); }
.seq__frame { position: absolute; inset: 0; }
.seq__canvas { display: none; width: 100%; height: 100%; }
.seq__poster { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
.seq__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(5,7,10,0.50) 0%, rgba(5,7,10,0) 26%),
    linear-gradient(302deg, rgba(5,7,10,0.88) 0%, rgba(5,7,10,0.42) 34%, rgba(5,7,10,0) 62%);
}
.seq__eyebrow {
  /* Clears the fixed header: while the stage is stuck the label would otherwise
     sit right under the logo. */
  position: absolute; z-index: 2;
  top: calc(var(--header-h) + clamp(12px, 2svh, 28px)); left: clamp(20px, 3vw, 56px);
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--media-muted);
}
.seq__beats {
  position: absolute; z-index: 2;
  right: clamp(20px, 3vw, 56px); bottom: clamp(22px, 3.2vw, 52px);
  margin: 0; padding: 0; list-style: none; text-align: right; color: var(--media-ink);
}
.seq__beat {
  font-weight: 600; font-size: clamp(1.75rem, 4.4vw, 4.5rem);
  line-height: 0.98; letter-spacing: -0.035em;
}
.seq__beat + .seq__beat { margin-top: 0.3em; }
.seq__beat-line { display: block; }
.seq__beat-line--sub { font-size: 0.56em; color: var(--blue-300); letter-spacing: -0.02em; }

.seq--static .seq__stage {
  height: clamp(360px, 58svh, 680px);
  min-height: 360px;
  background: #111820;
}
.seq--static .seq__poster {
  opacity: 1 !important;
  object-position: center;
  filter: brightness(1.05) contrast(1.02);
}
.seq--static .seq__scrim {
  background:
    linear-gradient(180deg, rgba(5,7,10,0.18) 0%, rgba(5,7,10,0.10) 38%, rgba(5,7,10,0.82) 100%),
    linear-gradient(300deg, rgba(5,7,10,0.72) 0%, rgba(5,7,10,0.34) 34%, rgba(5,7,10,0) 64%);
}
.seq--static .seq__eyebrow { top: clamp(20px, 3vw, 52px); }
.seq--static .seq__beat { text-shadow: 0 14px 48px rgba(0,0,0,0.52); }

.seq.is-live { height: auto; }
/* site.js pins this stage with ScrollTrigger. CSS sticky exposes the section's
   black background when it unsticks near the end of the scrub. */
.seq.is-live .seq__stage { position: relative; height: 76svh; }
.seq.is-live .seq__eyebrow { top: clamp(16px, 2.2svh, 32px); }   /* stage now clears the header */
.seq.is-live .seq__canvas { display: block; }
.seq.is-live .seq__poster { position: absolute; inset: 0; }
/* Hugs the frame's corner — the shorter stage already ends above the fixed
   chat launcher, so the titles no longer have to sit up off the edge. */
.seq.is-live .seq__beats { right: clamp(20px, 3.4vw, 72px); bottom: clamp(24px, 4svh, 56px); }
/* Live titles all sit on the same bottom-right anchor and take turns. Each
   line is a masked cell so it can roll in and out of its own edge. */
.seq.is-live .seq__beat {
  position: absolute; right: 0; bottom: 0; margin: 0;
  opacity: 0; visibility: hidden; white-space: nowrap;
}
.seq.is-live .seq__beat-line {
  overflow: hidden;
  padding-bottom: 0.22em; margin-bottom: -0.22em;   /* room for descenders */
}
.seq.is-live .seq__beat-line > span { display: block; will-change: transform; }
.seq.is-ready .seq__poster { opacity: 0; }

/* The sequence used as a project hero: it opens the page, so the stage runs
   full-bleed from under the fixed header instead of the inset 80svh card, and
   the static fallback matches the .project-hero it replaced. */
/* No bottom margin: .project-detail below brings its own rhythm, same as the
   .project-hero this replaces. */
.seq--hero { margin-block: 0; }
.seq--hero { --seq-y: 0px; --seq-frame-top: var(--header-h); }
.seq--hero .seq__stage {
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
}
.seq--hero .seq__frame { top: var(--seq-frame-top); bottom: 0; }
.seq--hero .seq__poster { transform: none; }
.seq--hero.is-live { --seq-scroll: 180svh; }
.seq--hero.is-live .seq__stage {
  height: var(--project-hero-h);
  min-height: var(--project-hero-min-h);
}
/* Stage now starts at the very top, so the label has to clear the header. */
.seq--hero.is-live .seq__eyebrow { top: calc(var(--header-h) + clamp(12px, 2svh, 28px)); }
/* Unlike the homepage stage, this one runs to the bottom of the viewport, so
   the titles have to sit above the fixed chat launcher (56px + its 18px inset)
   instead of hugging the corner. */
.seq--hero .seq__beats,
.seq--hero.is-live .seq__beats { bottom: clamp(44px, 6svh, 80px); }
.seq--hero .seq__scrim {
  background:
    linear-gradient(180deg, rgba(5,7,10,0.55) 0%, rgba(5,7,10,0.15) 34%, rgba(5,7,10,0.64) 76%, var(--canvas) 100%),
    linear-gradient(300deg, rgba(5,7,10,0.62) 0%, rgba(5,7,10,0.28) 36%, rgba(5,7,10,0) 66%);
}
.seq--hero .seq__stage::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  height: clamp(40px, 7svh, 80px);
  pointer-events: none;
  background: linear-gradient(180deg, rgba(10, 12, 16, 0) 0%, rgba(10, 12, 16, 0.72) 58%, var(--canvas) 100%);
}

/* Asymmetric staggered image pair with caption tags. */
.ppair {
  margin-top: var(--rhythm);
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 4vw, 80px);
  align-items: start;
}
.ppair figure { position: relative; overflow: hidden; }
.ppair img, .ppair video { width: 100%; height: 100%; object-fit: cover; }
.ppair__a { aspect-ratio: 4 / 5; }
.ppair__b { aspect-ratio: 3 / 4; margin-top: clamp(56px, 12svh, 160px); }   /* offset down */
.pcap {
  position: absolute; right: 12px; bottom: 12px; z-index: 7;
  font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--media-ink); background: rgba(5, 7, 10, 0.68); padding: 5px 10px;
}
/* Left-anchored variant, for split-screen media that needs a word per side. */
.pcap--l { right: auto; left: 12px; }

.project-gallery { margin-top: var(--rhythm); display: grid; grid-template-columns: 1fr 1fr; gap: clamp(16px, 2vw, 32px); }
.project-gallery figure { overflow: hidden; aspect-ratio: 4 / 3; }
.project-gallery img, .project-gallery video { width: 100%; height: 100%; object-fit: cover; }
.project-gallery figure:first-child { grid-column: 1 / -1; aspect-ratio: 16 / 7; }

.project-credits {
  margin-top: var(--rhythm);
  display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(24px, 3vw, 48px);
  padding-top: clamp(32px, 5svh, 64px); border-top: 1px solid var(--rule);
}
.project-credits h4 { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 10px; }
.project-credits p { font-size: clamp(1rem, 1.2vw, 1.25rem); color: var(--ink); }

/* --- section label ------------------------------------------------------- */
.section-eyebrow {
  font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--blue-300);
}

/* --- capabilities: custom-icon cards (bordered technical grid) ------------ */
.cap-grid {
  margin-top: clamp(28px, 4.5svh, 56px);
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 1px; background: var(--rule); border: 1px solid var(--rule);
}
.cap {
  position: relative;
  background: var(--canvas);
  padding: clamp(24px, 2.2vw, 38px);
  display: flex; flex-direction: column; gap: clamp(14px, 1.5vw, 22px); min-height: clamp(236px, 24vw, 360px);
  isolation: isolate;
  overflow: hidden;
  transition: background .5s ease;
}
.cap::before,
.cap::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cap::before {
  z-index: -2;
  background-image: var(--cap-bg);
  background-size: cover;
  background-position: center;
  filter: grayscale(1) saturate(0.9);
  transform: scale(1.02);
  transition: transform .7s ease, filter .7s ease, opacity .7s ease;
}
.cap::after {
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(5, 7, 10, 0.74) 0%, rgba(5, 7, 10, 0.9) 56%, rgba(5, 7, 10, 0.96) 100%),
    linear-gradient(90deg, rgba(5, 7, 10, 0.46), rgba(5, 7, 10, 0.18));
}
.cap:hover { background: var(--surface); }
.cap:hover::before { filter: grayscale(0.65) saturate(1.05); transform: scale(1.06); }
.cap:not([style*="--cap-bg"]) { min-height: clamp(180px, 18vw, 260px); }
.cap-grid--partner-summary .cap {
  background: var(--surface);
  box-shadow: inset 0 0 0 0 var(--rule-strong);
  transition: background .45s ease, box-shadow .45s ease, transform .45s ease;
}
.cap-grid--partner-summary .cap::before { display: none; }
.cap-grid--partner-summary .cap::after {
  z-index: 0;
  inset: auto 0 0;
  height: 3px;
  background: var(--blue-500);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s ease;
}
.cap-grid--partner-summary .cap:hover {
  background: var(--surface-hi);
  box-shadow: inset 0 0 0 1px var(--rule-strong);
}
.cap-grid--partner-summary .cap:hover::after { transform: scaleX(1); }
.cap-grid--partner-summary .cap__h,
.cap-grid--partner-summary .cap__p { transition: color .45s ease; }
.cap-grid--partner-summary .cap:hover .cap__h { color: var(--blue-300); }
.cap-grid--partner-summary .cap:hover .cap__p { color: var(--muted); }
.cap__h {
  font-weight: 600;
  font-size: clamp(1.45rem, 2vw, 2.25rem);
  letter-spacing: -0.02em;
  line-height: 1.04;
  margin-top: auto;
}
/* Qualifier on a location heading — "Headquarters" next to Luton, so the card
   is not read as a third stage. Set at the util step so it never competes with
   the city name it qualifies. */
.cap__tag {
  display: block; margin-top: 10px;
  font-family: var(--ff-util); font-weight: 400; font-size: var(--fs-util);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--blue-300);
}
.cap__p {
  color: var(--muted);
  font-weight: 450;
  font-size: clamp(1rem, 1.15vw, 1.25rem);
  line-height: 1.45;
  letter-spacing: 0.005em;
  max-width: 34ch;
}

/* --- stat strip ---------------------------------------------------------- */
.project-stats { padding-block: var(--rhythm-sm); border-block: 1px solid var(--rule); background: var(--void); }
.project-stats__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(24px, 3vw, 48px); }
.stat { display: flex; flex-direction: column; gap: 12px; }
.stat__num { font-weight: 500; font-size: clamp(2.6rem, 5vw, 4.4rem); line-height: 0.86; letter-spacing: -0.04em; }
.stat__num .accent { color: var(--blue-500); }
.stat__label { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); max-width: 22ch; line-height: 1.4; }

/* --- 3-up gallery -------------------------------------------------------- */
.pgrid { margin-top: var(--rhythm); display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 1.4vw, 26px); }
.pgrid--1 { grid-template-columns: minmax(0, 1fr); }
.pgrid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.pgrid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.pgrid figure { position: relative; overflow: hidden; height: var(--media-h); aspect-ratio: auto; margin: 0; }
.pgrid img, .pgrid video, .pgrid iframe { width: 100%; height: 100%; object-fit: cover; }

/* 3-up capability grid variant (used for subpage benefit infographics) */
.cap-grid--3 { grid-template-columns: repeat(3, 1fr); }
/* compact icon-chip variant: short labels, no body copy, shorter cells */
.cap--chip { min-height: 150px; gap: 14px; }
.cap--chip .cap__h { font-size: clamp(1rem, 1.15vw, 1.18rem); }

/* --- limitations note (honest trade-off callout) ------------------------- */
.limit-note {
  margin-top: 26px; display: flex; gap: 12px; align-items: flex-start;
  font-size: 0.98rem; line-height: 1.5; color: var(--blue-300);
}
.limit-note svg { flex: none; width: 20px; height: 20px; margin-top: 2px; }

/* --- process flow (numbered horizontal steps) --------------------------- */
.flow {
  margin-top: clamp(24px, 3.4svh, 40px);
  display: grid; grid-template-columns: repeat(6, 1fr);
  gap: 1px; background: var(--rule); border: 1px solid var(--rule);
}
/* Short step labels (Script / Previz / Shoot) left a tall void under the number
   at the old 200px floor, so the cell now sizes closer to its content. */
.flow__step {
  background: var(--canvas); padding: clamp(24px, 2vw, 34px);
  display: flex; flex-direction: column; gap: 18px; min-height: clamp(150px, 17svh, 196px);
  transition: background .5s ease;
}
.flow__step:hover { background: var(--surface); }
.flow__num { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; color: var(--blue-500); }
/* Big enough to break the run of hairline rows, but held at the same neutral
   grey as the row copy — bold white in a boxed grid shouted over the section
   titles, which are the page's actual headings. */
.flow__label {
  margin-top: auto;
  font-weight: 500; font-size: clamp(1.15rem, 1.55vw, 1.6rem);
  letter-spacing: -0.02em; line-height: 1.14; color: var(--muted);
  text-wrap: balance; transition: color .4s ease;
}
.flow__step:hover .flow__label { color: var(--ink); }

/* --- contact section (homepage) ----------------------------------------- */
.contact { padding-block: var(--rhythm-md); }
.contact__grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(40px, 6vw, 110px); align-items: start; }
.contact__title { margin-top: 20px; font-weight: 600; font-size: var(--fs-h1); line-height: 1.02; letter-spacing: -0.02em; }
.contact__title .accent { color: var(--blue-500); }
.contact__lead { margin-top: 24px; max-width: 42ch; font-weight: 450; font-size: clamp(1rem, 1.3vw, 1.2rem); line-height: 1.5; color: var(--muted); }
.contact__details { list-style: none; margin: clamp(30px, 5svh, 54px) 0 0; padding: 0; }
.contact__details li { display: grid; grid-template-columns: 88px 1fr; align-items: baseline; gap: 20px; padding: 15px 0; border-top: 1px solid var(--rule); font-size: 1.05rem; }
.contact__details li:last-child { border-bottom: 1px solid var(--rule); }
.contact__label { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }
/* Form stretches to the full grid-row height; the Message field grows to take
   up the slack so the Send Mail button drops to the bottom, lining up with the
   base of the contact details on the left. */
.contact__form { align-self: stretch; display: flex; flex-direction: column; position: relative; }
.contact__form .field:has(textarea) { flex: 1 1 auto; display: flex; flex-direction: column; }
.contact__form .field:has(textarea) textarea { flex: 1 1 auto; min-height: 120px; }
.contact__form .btn { margin-top: 18px; width: 100%; }
/* Status sits just below the form so the Send Mail button is the last in-flow
   item — its bottom lines up with the contact list's last divider on the left. */
.contact__status { position: absolute; top: 100%; left: 0; margin-top: 10px; min-height: 0; font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.06em; color: var(--blue-300); }
.contact__status[data-state="error"] { color: #ff7a7a; }

@media (max-width: 980px) {
  .cap-grid { grid-template-columns: repeat(2, 1fr); }
  .cap-grid--3 { grid-template-columns: repeat(2, 1fr); }
  .flow { grid-template-columns: repeat(3, 1fr); }
  .project-stats__grid { grid-template-columns: repeat(2, 1fr); row-gap: 40px; }
  .contact__grid { grid-template-columns: 1fr; gap: 48px; }
}

@media (max-width: 720px) {
  :root {
    --feature-media-h: 70svh;
    --feature-media-min: 480px;
    --media-h: clamp(300px, 56svh, 520px);   /* same single height, one step shorter */
  }
  .cap-grid { grid-template-columns: 1fr; }
  .flow { grid-template-columns: repeat(2, 1fr); }
  .pgrid { grid-template-columns: 1fr; }
  .project-intro,
  .project-credits { grid-template-columns: 1fr; gap: 32px; }
  .project-gallery { grid-template-columns: 1fr; }
  .project-gallery figure:first-child { aspect-ratio: 4 / 3; }
  .project-hero__title { font-size: clamp(2.75rem, 14vw, 5rem); }
  .project-hero__lead { justify-self: start; }
  .pfeature__text { right: clamp(16px, 5vw, 28px); left: clamp(16px, 5vw, 28px); max-width: none; }
  .pair { grid-template-columns: 1fr; }
  /* Shorter run and a tighter type step: the clip is 16:9 and letterboxes on a
     phone, so the titles need to clear the frame, not sit over the subject. */
  .seq { --seq-scroll: 200svh; }
  .seq__stage { height: clamp(300px, 56svh, 520px); }
  .seq--hero.is-live { --seq-scroll: 175svh; }
  .seq__beat { font-size: clamp(1.6rem, 8.5vw, 2.6rem); }
  .seq__poster { object-fit: contain; background: #000; }   /* matches the canvas fit */
  .seq.is-live .seq__beats { right: clamp(16px, 5vw, 28px); bottom: clamp(20px, 3.5svh, 40px); }
  .seq--hero .seq__beats,
  .seq--hero.is-live .seq__beats { bottom: clamp(88px, 11svh, 120px); }
}

/* --- scroll reveal fallback (no-JS / reduced motion) ---------------------- */
.reveal { opacity: 0; }
.reveal.is-visible,
.no-gsap .reveal { opacity: 1; }

/* --- responsive ----------------------------------------------------------- */
@media (max-width: 1200px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
  .product-grid { grid-template-columns: repeat(3, 1fr); }
  .post-grid { grid-template-columns: repeat(2, 1fr); }
  .site-footer__top { grid-template-columns: 1fr 1fr; }
  .site-header__inner { gap: 18px; }
  .nav { gap: clamp(14px, 1.45vw, 22px); }
  .brand img,
  .brand video { width: 124px; }
  .nav-cta { height: 52px; padding-inline: 24px; }
}

@media (max-width: 1024px) {
  body { cursor: auto; }
  button { cursor: pointer; }
  .cursor-dot, .cursor-ring, .cursor-stroke, .svc-preview { display: none !important; }

  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 12px var(--pad) 30px;
    background: var(--void);
    border-bottom: 1px solid var(--rule);
    max-height: calc(100svh - var(--header-h));
    overflow-y: auto;
    display: none;
  }
  body.nav-open .nav { display: flex; }
  .nav__item--has-sub { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center; }
  .nav__link { padding: 16px 0 10px; font-size: 15px; justify-content: flex-start; width: fit-content; }
  /* No hover on a touch screen, so the whole row opens the section — tapping
     the word is the obvious move and the caret is a small target. */
  .nav__item--has-sub > .nav__link { pointer-events: auto; cursor: pointer; }
  .nav__drop-toggle { width: 44px; height: 44px; justify-self: end; }
  .nav__sub { position: static; opacity: 1; visibility: visible; transform: none; border: 0; padding: 0 0 8px 16px; min-width: 0; display: none; }
  .nav__item--has-sub .nav__sub { grid-column: 1 / -1; }
  .nav__item.is-open .nav__sub { display: block; }
  .nav__item.is-open .nav__caret { transform: rotate(180deg); }
  .nav-cta { margin-top: 16px; }
  .nav-theme { margin-top: 12px; }
  .theme-toggle { width: 100%; justify-content: space-between; padding: 0 16px; }
  .theme-toggle__label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    font-family: var(--ff-util);
    font-size: var(--fs-util);
    text-transform: uppercase;
    letter-spacing: .12em;
  }
  .nav-toggle { display: block; }
  .social-rail { display: none; }

  /* The carousel keeps its desktop behaviour here: the section pins and the
     page scroll drives the cards sideways. Only the fallback (no JS, reduced
     motion, or a track that already fits) degrades to a native snap scroller,
     and that styling lives with .carousel--native further down. */
  .carousel { min-height: 0; }
  .carousel__track { padding-inline: var(--pad); }
  .pcard { flex: 0 0 46vw; }
  .carousel--native .pcard { scroll-snap-align: center; height: auto; }
  .carousel--native .pcard__frame { height: 56svh; }
  .carousel__labels { display: none; }

  .split, .media-row, .product-detail, .spec-layout, .statement__foot { grid-template-columns: 1fr; gap: 32px; }
  .media-row--reverse .media-row__media { order: 0; }
  .project-grid { grid-template-columns: 1fr; }
  .list-cols { grid-template-columns: 1fr; gap: 4px; }
  .gallery-grid, .gallery-grid--2, .gallery-grid--4, .pgrid--3 { grid-template-columns: repeat(2, 1fr); }
  .site-footer__head { grid-template-columns: 1fr; }
  .footer-clocks { justify-items: start; }
  .footer-clock { text-align: left; }
}

@media (max-width: 640px) {
  /* One card fills the phone screen, so the scroll-driven track reads as a
     sequence of full frames rather than a row of thumbnails. */
  .pcard { flex: 0 0 78vw; height: 66svh; }
  .card-grid, .post-grid, .product-grid { grid-template-columns: 1fr; }
  .team-card { flex-basis: 100%; }
  .site-footer__top { grid-template-columns: 1fr; gap: 28px; }
  .gallery-grid, .gallery-grid--2, .gallery-grid--4, .pgrid--2, .pgrid--3 { grid-template-columns: 1fr; }
  .modal__panel { padding: 30px 22px; }
  .lightbox [data-stage] { padding: 8px; }
  .lightbox__close { top: max(12px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right)); }
  .lightbox__prev { left: max(8px, env(safe-area-inset-left)); }
  .lightbox__next { right: max(8px, env(safe-area-inset-right)); }
  .lightbox__scrub { left: calc(max(12px, env(safe-area-inset-left)) + 56px); right: max(12px, env(safe-area-inset-right)); bottom: max(12px, env(safe-area-inset-bottom)); padding: 0 12px; gap: 10px; }
  .lightbox__time { min-width: 38px; }
  .lightbox__caption {
    right: max(12px, env(safe-area-inset-right));
    bottom: max(12px, env(safe-area-inset-bottom));
    max-width: calc(100vw - 24px);
    padding: 11px 12px;
  }
  .lightbox.has-video .lightbox__caption { bottom: max(66px, calc(env(safe-area-inset-bottom) + 54px)); }
  .chatbot {
    right: max(12px, env(safe-area-inset-right));
    bottom: max(12px, env(safe-area-inset-bottom));
  }
  .chatbot__panel {
    width: calc(100vw - 24px);
    height: min(640px, calc(100dvh - 88px));
  }
  .chatbot__message { max-width: 94%; }
  .chatbot__launcher { height: 52px; min-width: 72px; padding-inline: 15px; }
  .blue-band { height: auto; min-height: 0; padding-block: 40px; }
  .svc-row__label { font-size: 1.5rem; }
}

/* ==========================================================================
   v2 sitemap: components the expanded capability / case-study pages need.
   Everything here reuses the existing scale, rules and blue ramp so the new
   pages read as the same system as the originals.
   ========================================================================== */

/* A media feature that is itself a link (home showreel, case-study openers). */
a.pfeature { display: block; }

/* Base .cap pushes its heading to the bottom of the cell, which only aligns
   when every cell holds the same amount of copy. Cards that carry a link list
   or uneven paragraphs use --top instead: icon, heading, copy from the top,
   links pinned to the bottom, so the headings line up across the row. */
.cap--top .cap__h { margin-top: clamp(26px, 4svh, 56px); }
.cap--top:not([style*="--cap-bg"]) .cap__h { margin-top: auto; }
.cap--top .cap__links { margin-top: auto; padding-top: 20px; }

/* Gateway cards on the home page: the section's sub-pages listed in the cell. */
.cap__links { margin-top: 18px; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px; }
.cap__links a {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: fit-content;
  min-height: 32px;
  padding-block: 4px;
  border-bottom: 1px solid rgba(79, 169, 255, 0.68);
  font-family: var(--ff-util); font-size: var(--fs-util); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--ink); transition: color .25s ease, border-color .25s ease;
}
.footer-explore .roll__in { color: var(--ink); }
.cap[style*="--cap-bg"] .cap__links a { color: rgba(242, 245, 248, 0.88); }
.cap__links a::after {
  content: "";
  width: 0.46em;
  height: 0.46em;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform .25s ease;
}
.cap__links a:hover,
.cap__links a:focus-visible { color: var(--blue-300); border-color: currentColor; }
.cap__links a:hover::after,
.cap__links a:focus-visible::after { transform: translateX(4px) rotate(45deg); }

/* Editorial section inside .project-detail. The reading column is what carries
   the page, so the label rail stays narrow and the body starts early — at the
   1600px container a wide rail left a dead gap mid-row. Each section opens on a
   hairline, which is what gives a long text page its structure; --rhythm (up to
   220px) is for media blocks, far too loose between paragraphs. */
.psec {
  --psec-rhythm: var(--rhythm-sm);
  margin-top: var(--psec-rhythm);
  padding-top: var(--rhythm-xs);
  border-top: 1px solid var(--rule);
  display: grid; grid-template-columns: var(--spine); gap: var(--spine-gap);
  align-items: start;
}
/* The section title, not an eyebrow: Inter Tight at the label step, ink, in
   sentence case. Mono uppercase at 11px read as a footnote next to the copy it
   was introducing, and sat too far out to bind to it.
   No leading mark: the section's own hairline already signals the break, and the
   title starting flush at the rail keeps its first line level with the body. */
.psec__label {
  position: sticky; top: calc(var(--header-h) + 28px);
  font-weight: 500; font-size: var(--fs-label); line-height: 1.25;
  letter-spacing: -0.015em; color: var(--ink); text-transform: none;
}
/* Prose keeps a readable measure; rows, tables and step grids run the full
   column so each section's hairline resolves at the same edge. */
.psec__body { min-width: 0; }

/* First paragraph of a section reads as its lead: larger, ink, tighter. */
.psec__body > p:first-child {
  font-weight: 450; font-size: var(--fs-lead); line-height: 1.34;
  letter-spacing: -0.015em; color: var(--ink); max-width: 50ch;
}
.psec__body > p { font-size: var(--fs-copy); line-height: 1.58; color: var(--muted); max-width: 68ch; }
.psec__body > p + p { margin-top: 18px; }
.psec__body > p strong { color: var(--ink); font-weight: 500; }
.psec__body > :first-child { margin-top: 0; }
.psec__body h3 { margin-top: 44px; font-weight: 500; font-size: var(--fs-lead); line-height: 1.2; letter-spacing: -0.02em; }
.psec__body h3 + p { margin-top: 12px; }
/* A linked sub-heading (case-study index) has to read as clickable without
   turning the heading blue. */
.psec__body h3 a { border-bottom: 1px solid var(--rule-strong); transition: color .3s ease, border-color .3s ease; }
.psec__body h3 a:hover { color: var(--blue-300); border-color: currentColor; }
/* Sub-heading between two lists: the label step at 0.86, on its own hairline so
   the groups read as separate blocks rather than one run of bullets. */
.psec__body h4 {
  margin-top: 44px; padding-top: 14px; border-top: 1px solid var(--rule);
  font-weight: 500; font-size: calc(var(--fs-label) * 0.86); line-height: 1.2;
  letter-spacing: -0.01em; color: var(--ink); text-transform: none;
}

/* Capability lists (What we build / What you get / Built for) as indexed rows.
   Numbered hairline rows echo the services list and the spec tables, and hold
   the eye far better than a loose column of discs. */
.pbullets {
  counter-reset: pb;
  margin-top: 22px; padding: 0; list-style: none;
  border-top: 1px solid var(--rule);
}
/* The index is absolutely positioned rather than a grid/flex track: a row can
   open with <strong>, and every child of a grid container becomes its own grid
   item, which would break the label away from the rest of the sentence. */
.pbullets li {
  counter-increment: pb;
  position: relative;
  padding: 18px 0 19px 3.4em;
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-copy); line-height: 1.55; color: var(--muted);
  transition: color .3s ease;
}
.pbullets li::before {
  content: counter(pb, decimal-leading-zero);
  position: absolute; left: 0; top: 18px;
  padding-top: 0.34em;
  font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.12em;
  color: var(--blue-500);
}
.pbullets li:hover { color: var(--ink); }
.pbullets li strong { color: inherit; font-weight: 500; }
/* A list opening a section leans on the section hairline instead of carrying its
   own, so its first row sits on the same line as the section title. */
.psec__body > .pbullets:first-child { margin-top: 0; border-top: 0; }
.psec__body > .pbullets:first-child > li:first-child { padding-top: 0; }
.psec__body > .pbullets:first-child > li:first-child::before { top: 0; }

/* Inside a bleed-row column the measure is already narrow, so pull the index in. */
.bleed-row__copy .pbullets li { padding-left: 2.8em; }

/* Specification rows: label left, value right, one hairline per row. */
.dl-spec { margin-top: 22px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(30px, 4vw, 80px); }
.dl-spec > div { display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: baseline; padding: 16px 0; border-top: 1px solid var(--rule); }
.dl-spec dt { font-size: 0.98rem; line-height: 1.4; color: var(--muted); }
.dl-spec dd { margin: 0; font-family: var(--ff-util); font-size: 0.92rem; color: var(--ink); text-align: right; white-space: nowrap; }

/* Two facing columns: who provides what, capability against capability. */
.duo {
  margin-top: clamp(28px, 4.5svh, 56px);
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 1px; background: var(--rule); border: 1px solid var(--rule);
}
.duo__col { background: var(--canvas); padding: clamp(24px, 2.2vw, 38px); }
/* Same step as a psec sub-heading, so a card title and a section sub-heading
   never disagree about size or face. */
.duo__h { font-weight: 500; font-size: calc(var(--fs-label) * 0.86); line-height: 1.25; letter-spacing: -0.01em; color: var(--ink); }
/* Same hairline rows as .pbullets, minus the index — these are facing sets, not
   an ordered list, so a short dash marker echoes the section labels instead. */
.duo__col ul { margin-top: 20px; padding: 0; list-style: none; border-top: 1px solid var(--rule); }
.duo__col li {
  position: relative; padding: 15px 0 16px 22px;
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-copy); line-height: 1.45; color: var(--muted);
  transition: color .3s ease;
}
.duo__col li::before { content: ""; position: absolute; left: 0; top: 1.5em; width: 9px; height: 1px; background: var(--blue-500); }
.duo__col li:hover { color: var(--ink); }

/* Text and media sit on different rhythms: --rhythm keeps big media apart, the
   tighter psec rhythm takes over wherever copy meets a media block. */
.psec + .pair, .psec + .pfeature, .psec + .pgrid, .psec + .project-grid, .psec + .vwipe,
.pair + .psec, .pfeature + .psec, .pgrid + .psec, .vwipe + .psec,
.psec + .psec { margin-top: var(--rhythm-sm); }

/* Page-level CTA closing a capability page. Laid out on the same rail + copy
   spine as the sections above it (rail column left empty), so the closing line
   starts exactly where every other block on the page does. */
.pcta {
  margin-top: var(--rhythm-md); padding-top: var(--rhythm-xs);
  border-top: 1px solid var(--rule);
  display: grid; grid-template-columns: var(--spine); gap: var(--spine-gap);
  align-items: start;      /* title level with the first line, as in .psec */
}
/* Built on --spine like every other section on the page, so the closing line
   starts on the same rail as the copy above it.

   It used to put the line and the button in the SAME grid cell, one pushed
   left and one pushed right: on a wide page that left a hole down the middle
   and the rail column empty, and on a narrow one the two could collide. Now
   the rail carries a title like any other section, and the action sits under
   the line — button first, because it is the thing to press. */
.pcta__label {
  margin: 0;
  font-weight: 500; font-size: var(--fs-label); line-height: 1.25;
  letter-spacing: -0.015em; color: var(--ink);
}
.pcta__body { min-width: 0; }
.pcta__text {
  margin: 0;
  font-weight: 500; font-size: clamp(1.5rem, 2.6vw, 2.5rem);
  line-height: 1.1; letter-spacing: -0.025em; max-width: 22ch;
}
/* Button then links, stacked on the same left rail. Sending the links to the
   far right of the row left them stranded in their own corner with nothing
   around them; under the button they read as a footnote to it, which is what
   they are. */
.pcta__row {
  margin-top: clamp(26px, 3.4svh, 44px);
  display: flex; flex-direction: column; align-items: flex-start;
  gap: clamp(18px, 2.2svh, 26px);
}
.pcta__related { margin: 0; }
.pcta__related { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.pcta__related a { color: var(--blue-300); }

/* Work filters use the same service language as the primary navigation. */
.work-filter { margin-top: clamp(28px, 4svh, 52px); }
.work-filter__controls {
  margin-top: clamp(28px, 4svh, 52px);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.work-filter > .work-filter__controls { margin-top: 0; }
.work-filter__button {
  position: relative;
  flex: none;
  min-height: 44px;
  padding: 0 20px;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--rule);
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: nowrap;
  color: var(--muted);
  cursor: pointer;
  touch-action: manipulation;
  transition: color .25s ease, border-color .25s ease, background-color .25s ease;
}
.work-filter__fill {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--blue-500);
  clip-path: circle(0 at 50% 50%);
}
.work-filter__label { position: relative; z-index: 1; }
@media (pointer: fine) { .work-filter__button { cursor: pointer; } }
.work-filter__button:hover { color: var(--ink); border-color: var(--rule-strong); }
.no-gsap .work-filter__button:hover .work-filter__fill,
.work-filter__button:focus-visible .work-filter__fill {
  clip-path: circle(150% at 50% 50%);
}
.work-filter__button.is-active,
.work-filter__button[aria-pressed="true"] {
  color: var(--on-blue);
  background: var(--blue-500);
  border-color: var(--blue-500);
}

.work-grid {
  position: relative;
  width: 100%;
  margin-top: 40px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: start;
  gap: clamp(28px, 3.2vw, 52px) clamp(14px, 1.4vw, 26px);
}
.work-item { position: relative; display: block; min-width: 0; transform-origin: center; }
.work-item[hidden] { display: none; }
.work-item.is-leaving { z-index: 5; pointer-events: none; }
.work-item__frame { position: relative; overflow: hidden; aspect-ratio: 3 / 4; background: var(--surface); }
/* YouTube cards: the downloaded still holds the frame until the player boots. */
.work-item__frame--yt { background-size: cover; background-position: center; }
.work-item[data-lightbox] .work-item__frame iframe { pointer-events: none; }
.work-item__frame img, .work-item__frame video { width: 100%; height: 100%; object-fit: cover; filter: brightness(0.86); transition: filter .5s ease, transform .7s cubic-bezier(.16,1,.3,1); }
.work-item:hover .work-item__frame img, .work-item:hover .work-item__frame video { filter: brightness(1); transform: scale(1.03); }
.work-item__title { margin-top: 16px; font-weight: 500; font-size: 1.12rem; letter-spacing: -0.01em; }
.work-item__tags { margin-top: 6px; font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.work-item__cat {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  max-width: calc(100% - 24px);
  padding: 5px 9px;
  font-family: var(--ff-util); font-size: var(--fs-util); line-height: 1.35;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--media-ink); background: rgba(5,7,10,0.78);
}

/* Service-page galleries sit between the page detail and the next-service
   teaser. They reuse the work card language, with a landscape frame suited to
   production stills and clips. */
.related-work {
  border-top: 1px solid var(--rule);
  background: var(--void);
}
.related-work__eyebrow {
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--blue-300);
}
.related-work__title { margin-top: 14px; }
.related-work__grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: var(--rhythm-xs);
}
.related-work .work-item__frame { aspect-ratio: 4 / 3; }
/* A single piece of work takes the whole row rather than sitting in one third
   of it, and stands the same height as every other full-width media block on a
   project page (.vwipe, .pfeature) — one --media-h, no per-block ratio. */
.related-work__grid--one { grid-template-columns: 1fr; }
.related-work__grid--one .work-item__frame { aspect-ratio: auto; height: var(--media-h); }
.related-work .work-item__title { transition: color .25s ease; }
.related-work .work-item:hover .work-item__title { color: var(--blue-300); }
.work-section__more {
  margin-top: var(--rhythm-xs);
  font-family: var(--ff-util); font-size: var(--fs-util);
  text-transform: uppercase; letter-spacing: 0.12em;
}
.work-section__more a {
  display: inline-flex; align-items: center; gap: 10px;
  padding-bottom: 5px; border-bottom: 1px solid var(--blue-300);
  transition: color .25s ease, border-color .25s ease;
}
.work-section__more a:hover { color: var(--blue-300); }

/* Showreel: half the row each, so a reel plays at a size worth watching
   rather than in a poster-shaped card. Landscape frame to match. */
.work-grid--showreel { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.work-grid--showreel .work-item__frame { aspect-ratio: 16 / 9; }
@media (max-width: 980px) { .work-grid { grid-template-columns: repeat(3, 1fr); }
  .work-grid--showreel { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 980px) { .related-work__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) {
  .work-filter__controls {
    margin-inline: calc(0px - var(--pad));
    padding-inline: var(--pad);
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scrollbar-width: none;
  }
  .work-filter__controls::-webkit-scrollbar { display: none; }
}
@media (max-width: 640px) {
  .work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px 12px; }
  .work-grid--showreel { grid-template-columns: 1fr; }
  .related-work__grid { grid-template-columns: 1fr; gap: 36px; }
  .work-item__title { font-size: 1rem; }
  .work-item__cat { top: 8px; left: 8px; max-width: calc(100% - 16px); }
}

/* .flow assumes six steps; most v2 processes have three to five. */
.flow--3 { grid-template-columns: repeat(3, 1fr); }
.flow--4 { grid-template-columns: repeat(4, 1fr); }
.flow--5 { grid-template-columns: repeat(5, 1fr); }

/* ── Distributor plate ─────────────────────────────────────────────────
   The XGRIDS partnership drawn as an equipment data plate rather than a card:
   hairline cells over a --rule background (so every divider is a true 1px at
   any zoom), mono micro-labels, the scanner's numbers doing the talking, and
   exactly one blue seal. The capture chain runs left to right with a top rule
   that brightens step by step. */
.plate {
  --plate-pad: clamp(22px, 2.4vw, 40px);
  position: relative;
  margin-top: var(--rhythm);
  border: 1px solid var(--rule);
  background:
    radial-gradient(120% 100% at 6% 0%, rgba(0, 135, 254, 0.10) 0%, rgba(0, 135, 254, 0) 58%),
    var(--surface);
  overflow: hidden;
}
/* Faint measurement grid, faded out down the plate: reads as instrument face. */
.plate::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(242, 245, 248, 0.045) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(242, 245, 248, 0.045) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 70%);
          mask-image: linear-gradient(to bottom, #000 0%, transparent 70%);
}
.plate > * { position: relative; z-index: 1; }

.plate__head {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 20px clamp(20px, 3vw, 48px);
  padding: var(--plate-pad);
  border-bottom: 1px solid var(--rule);
}
.plate__marks { display: flex; align-items: center; gap: clamp(22px, 2.8vw, 44px); }
.plate__mark { height: clamp(38px, 3.4vw, 52px); width: auto; display: block; }
/* The XGRIDS wordmark is long and short-capped, so it needs less height than
   the stacked MergeXR lockup to read at the same weight. */
.plate__mark--alt { height: clamp(26px, 2.4vw, 34px); }
.plate__join { font-family: var(--ff-util); font-size: 1.4rem; color: var(--muted); opacity: 0.7; }

.plate__claim {
  padding: clamp(30px, 3.6vw, 58px) var(--plate-pad) clamp(26px, 3vw, 46px);
  max-width: 30ch;
  font-weight: 600; font-size: clamp(1.5rem, 2.7vw, 2.7rem);
  line-height: 1.06; letter-spacing: -0.03em;
}

.plate__figs, .plate__chain, .plate__why { list-style: none; margin: 0; padding: 0; }

.plate__figs {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 1px; background: var(--rule);
  border-block: 1px solid var(--rule);
}
.plate__figs li {
  background: var(--void);
  padding: clamp(20px, 2vw, 32px) var(--plate-pad);
  display: flex; flex-direction: column; justify-content: space-between;
}
.plate__fig {
  display: block;
  font-size: clamp(1.55rem, 2.5vw, 2.5rem); font-weight: 600;
  line-height: 1; letter-spacing: -0.04em;
}
.plate__fig i { font-style: normal; font-weight: 400; color: var(--blue-300); }
.plate__unit {
  display: block; margin-top: 14px;
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}

.plate__chain { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--rule); }
.plate__chain li {
  position: relative;
  background: var(--surface);
  padding: clamp(24px, 2.4vw, 36px) var(--plate-pad);
  transition: background .5s ease;
}
.plate__chain li:hover { background: var(--void); }
/* Top rule brightens along the chain, so the row reads as one direction. */
.plate__chain li::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--blue-500); opacity: 0.24;
}
.plate__chain li:nth-child(2)::before { opacity: 0.48; }
.plate__chain li:nth-child(3)::before { opacity: 0.72; }
.plate__chain li:nth-child(4)::before { opacity: 1; }
.plate__step-n {
  display: block;
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.14em; color: var(--blue-500);
}
.plate__step-l {
  display: block; margin-top: 18px;
  font-weight: 500; font-size: clamp(1.05rem, 1.35vw, 1.35rem); letter-spacing: -0.02em;
}
.plate__step-d {
  display: block; margin-top: 10px;
  font-size: var(--fs-copy); line-height: 1.5; color: var(--muted);
}
.plate__step-d em { font-style: normal; color: var(--ink); }

.plate__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px clamp(20px, 2.4vw, 40px);
  padding: var(--plate-pad);
  border-top: 1px solid var(--rule);
}
.plate__foot-l {
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted);
}
.plate__why { display: flex; flex-wrap: wrap; gap: 10px 12px; }
.plate__why li {
  padding: 8px 15px;
  border: 1px solid var(--rule-strong); border-radius: 999px;
  font-size: 0.95rem; color: var(--ink);
}

/* ── Plate, pipeline variant (previz: script → previz → shoot) ─────────
   Same instrument panel, three columns instead of four, and one step in the
   chain is ours: it keeps a full-strength rail, a blue mark, and its copy in
   ink while the steps either side stay grey. The bracket over it is the
   iteration loop, drawn in borders (dashed rule, two legs, one arrowhead) so
   it tracks the middle column at any width instead of being an image. */
/* A full-width plate still has to open like a section: same hairline break,
   same rhythm, and its title set exactly as .psec__label — Inter Tight at the
   label step, ink, sentence case, flush to the container rail — so "Where
   previz sits" and "What we build" read as the same rank. The title stays
   outside the panel because --plate-pad would otherwise indent it off that
   rail, and it does not stick: a plate scrolls as one object. */
.plate-block {
  margin-top: var(--rhythm-sm);
  padding-top: var(--rhythm-xs);
  border-top: 1px solid var(--rule);
}
.plate-block__title {
  margin: 0;
  font-weight: 500; font-size: var(--fs-label); line-height: 1.25;
  letter-spacing: -0.015em; color: var(--ink);
}
.plate-block .plate { margin-top: clamp(20px, 2.6svh, 34px); }

.plate--flow .plate__chain { grid-template-columns: repeat(3, 1fr); }
.plate--flow .plate__chain li:nth-child(3)::before { opacity: 1; }
.plate__head--text { justify-content: space-between; }
.plate__eyebrow {
  margin: 0;
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted);
}
.plate__eyebrow--end { color: var(--blue-300); }

/* With the figures row gone, the bracket is the only thing between the claim
   and the chain, so it carries the iteration idea on its own: taller legs, a
   blue rail, and a label at the copy step in ink rather than a mono caption. */
.plate__loop {
  position: relative;
  height: clamp(44px, 4.6vw, 66px);
  margin-top: clamp(26px, 3vw, 46px);
  /* Legs land inside the middle chain cell, a hair in from its dividers. */
  margin-inline: calc(33.333% + clamp(14px, 2vw, 32px));
  border: 1px dashed rgba(79, 169, 255, 0.45);
  border-bottom: 0;
}
.plate__loop::after {                  /* arrowhead, back into step 02 */
  content: ''; position: absolute; right: -5px; bottom: -1px;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 7px solid var(--blue-300);
}
.plate__loop-label {
  position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
  padding: 0 clamp(14px, 1.6vw, 22px); background: var(--surface);
  font-weight: 600; font-size: clamp(1.05rem, 1.5vw, 1.5rem);
  line-height: 1.1; letter-spacing: -0.025em; color: var(--ink);
  white-space: nowrap;
}
/* The step we own. Marked with type and one rail, not a filled card — the
   section headings are still the loudest thing on the page. */
.plate__chain li.plate__now { background: var(--void); }
.plate__chain li.plate__now::before { opacity: 1; box-shadow: var(--blue-glow); }
.plate__step-tag {
  position: absolute; top: clamp(24px, 2.4vw, 36px); right: var(--plate-pad);
  font-family: var(--ff-util); font-size: var(--fs-util);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--blue-300);
}
.plate__now .plate__step-d { color: var(--ink); }
/* Footnote line: the consequence of the step, held under a hairline so the
   cell reads as fact then implication. */
.plate__step-f {
  display: block; margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--rule);
  font-size: var(--fs-copy); line-height: 1.45; color: var(--faint);
}
.plate__now .plate__step-f { color: var(--muted); }

@media (max-width: 1100px) {
  .plate__figs, .plate__chain { grid-template-columns: repeat(2, 1fr); }
  .plate__claim { max-width: none; }
  /* A three-step chain broken over two columns leaves an orphan and stops
     reading as a sequence, so this variant holds three across until it can't. */
  .plate--flow .plate__chain { grid-template-columns: repeat(3, 1fr); }
  .plate__step-tag { position: static; display: block; margin-top: 10px; }
}
@media (max-width: 760px) {
  .plate--flow .plate__chain { grid-template-columns: 1fr; }
  /* Stacked, the bracket keys off a column that no longer exists, so the label
     stands on its own line instead of vanishing with the geometry. */
  .plate__loop {
    height: auto; margin-inline: 0; border: 0;
    padding: 0 var(--plate-pad) clamp(22px, 5vw, 34px);
  }
  .plate__loop::after { display: none; }
  .plate__loop-label {
    position: static; transform: none; padding: 0;
    display: block; white-space: normal;
  }
}
@media (max-width: 620px) {
  .plate__figs, .plate__chain { grid-template-columns: 1fr; }
  .plate__head { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 980px) {
  /* The rail column stops paying for itself here, so the title sits above its
     body — the closing CTA follows the same rule as the sections above it. */
  .psec, .pcta { grid-template-columns: 1fr; gap: 20px; }
  .psec__label { position: static; }
  .dl-spec { grid-template-columns: 1fr; }
  .duo { grid-template-columns: 1fr; }
  .flow--4, .flow--5 { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 720px) {
  .flow--3, .flow--4, .flow--5 { grid-template-columns: repeat(2, 1fr); }
}

/* Long spec values (power, per-axis envelopes) stop fitting beside their label
   on a phone, so stack them and let them wrap. */
@media (max-width: 520px) {
  .dl-spec > div { grid-template-columns: 1fr; gap: 4px; }
  .dl-spec dd { text-align: left; white-space: normal; }
}

/* When the drag layer is off (no JS or reduced motion), the carousel must stay
   reachable, make it a native horizontal scroller. */
.no-gsap .carousel,
.carousel.carousel--native {
  height: auto;
  min-height: 0;
  overflow-x: auto;
  overflow-y: hidden;
  display: block;
  padding-block: clamp(88px, 12svh, 150px) clamp(64px, 9svh, 120px);
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}
.no-gsap .carousel__track,
.carousel.carousel--native .carousel__track {
  width: max-content;
  min-width: 100%;
}
.no-gsap .pcard,
.carousel.carousel--native .pcard { scroll-snap-align: center; }

/* --- dot-matrix world map ------------------------------------------------- */
/* The svg carries the aspect ratio (118 × 45 land grid), so the pin list can be
   an overlay positioned in plain percentages off the same grid coordinates. */
/* The 118 × 45 grid is 2.6:1, so a full-width map runs ~600px tall on a desktop
   and swamps the section. Cap the width instead of the height, so the svg and
   the pin overlay keep scaling together and the pins stay on their cities. */
.wmap {
  position: relative;
  width: min(100%, clamp(1080px, 72vw, 2600px));
  margin: clamp(36px, 4vw, 64px) auto 0;
  padding-block: clamp(8px, 1.2vw, 18px);
}
.wmap__grid { display: block; width: 100%; height: auto; overflow: visible; }
.wmap__grid circle { fill: rgba(242, 245, 248, 0.16); }
.wmap__pins { position: absolute; inset: clamp(8px, 1.2vw, 18px) 0; list-style: none; margin: 0; padding: 0; }
.wpin {
  position: absolute;
  left: calc(var(--x) / 118 * 100%);
  top: calc(var(--y) / 45 * 100%);
}
.wpin a {
  display: flex;
  align-items: center;
  gap: 12px;
  transform: translate(-7px, -50%);
  color: var(--blue-100);
  text-decoration: none;
  white-space: nowrap;
}
/* Dubai sits between London and Hyderabad; flipping its label left keeps the
   three from colliding on the Gulf. */
.wpin--left a { flex-direction: row-reverse; transform: translate(calc(-100% + 7px), -50%); }
.wpin__dot {
  position: relative;
  flex: none;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--blue-500);
  box-shadow: 0 0 14px rgba(0, 135, 254, 0.9);
}
/* Two rings on the same keyframes, half a cycle apart: a steady sonar pulse. */
.wpin__dot::before,
.wpin__dot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 1.5px solid var(--blue-500);
  animation: wpin-pulse 2.8s cubic-bezier(0.22, 1, 0.36, 1) infinite;
  animation-delay: var(--delay, 0s);
}
.wpin__dot::after { animation-delay: calc(var(--delay, 0s) + 1.4s); }
@keyframes wpin-pulse {
  0%   { transform: scale(1); opacity: 0.65; }
  70%  { opacity: 0; }
  100% { transform: scale(3.8); opacity: 0; }
}
.wpin__label {
  font-family: var(--ff-util);
  font-size: var(--fs-util);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink);
  transition: color 0.3s ease;
}
.wpin a:hover .wpin__label { color: var(--blue-300); }

/* --- studio list ---------------------------------------------------------- */
.studio-list { list-style: none; margin: clamp(40px, 5vw, 72px) 0 0; padding: 0; }
.studio-list__item { border-top: 1px solid var(--rule); }
.studio-list__item:last-child { border-bottom: 1px solid var(--rule); }
.studio-list__item a {
  display: grid;
  grid-template-columns: 70px minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 24px;
  padding: 26px 0;
  text-decoration: none;
  color: inherit;
  transition: padding-inline 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.studio-list__item a:hover { padding-inline: 14px; }
.studio-list__idx { font-family: var(--ff-util); font-size: var(--fs-util); letter-spacing: 0.14em; color: var(--blue-300); }
.studio-list__city { font-weight: 600; font-size: var(--fs-h2); letter-spacing: -0.03em; transition: color 0.3s ease; }
.studio-list__item a:hover .studio-list__city { color: var(--blue-300); }
.studio-list__meta { font-family: var(--ff-util); font-size: var(--fs-util); text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); }

@media (max-width: 900px) {
  .wpin__label { display: none; }
  .studio-list__item a { grid-template-columns: 48px minmax(0, 1fr); gap: 6px 16px; }
  .studio-list__meta { grid-column: 2; }
}

/* --- reduced motion ------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .wpin__dot::before, .wpin__dot::after { animation: none; opacity: 0; }
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; }
  .carousel { overflow-x: auto; scroll-snap-type: x mandatory; }
  .pcard { scroll-snap-align: center; }
  .line, .hero__media img, .hero__media video, .card__media,
  .project__frame img, .project__frame video, .band img, .band video, .media-row__media img, .media-row__media video { transform: none !important; }
  .blue-band__track, .logo-marquee__track, .partners__marquee-track { animation: none !important; }
  .work-filter__button, .work-item__frame img, .work-item__frame video { transition: none !important; }
  .work-filter__button:hover .work-filter__fill,
  .work-filter__button:focus-visible .work-filter__fill { clip-path: circle(150% at 50% 50%); }
  .lightbox.is-open [data-stage] { animation: none !important; }
  .partners__marquee { justify-content: center; display: flex; }
  .cursor-dot, .cursor-ring, .cursor-stroke { display: none !important; }
}
html.mx-motion-full body:not(.cursor-hidden) .cursor-dot { display: block !important; }
html.mx-motion-full body:not(.cursor-hidden) .cursor-ring { display: grid !important; }
html.mx-motion-full body:not(.cursor-hidden) .cursor-stroke { display: block !important; }

.cursor-dot,
.cursor-ring,
.cursor-stroke,
html.mx-motion-full body:not(.cursor-hidden) .cursor-dot,
html.mx-motion-full body:not(.cursor-hidden) .cursor-ring,
html.mx-motion-full body:not(.cursor-hidden) .cursor-stroke {
  display: none !important;
}

/* ==========================================================================
   MOBILE / TOUCH HARDENING
   Everything above lays the site out; this block is the phone pass. It is last
   in the file so it wins on equal specificity, and it is split by what it is
   actually reacting to — the input device (pointer: coarse), the notch
   (env(safe-area-*)), and the narrow viewport (max-width) — rather than
   lumping all three under one phone-shaped breakpoint.
   ========================================================================== */

/* --- 1. touch input ------------------------------------------------------- */
@media (hover: none) and (any-hover: none) and (pointer: coarse) {
  /* Kill the ~300ms wait the browser holds every tap for in case it turns into
     a double-tap zoom. Scoped to controls, so pinch-zooming the page still
     works — that is an accessibility affordance, not a bug to design out. */
  a, button, input, select, textarea, summary,
  [role="button"], [data-lightbox], .work-filter__button, .nav__link {
    touch-action: manipulation;
  }

  /* Hover states latch on touch: a tap leaves the element stuck in :hover
     until you tap elsewhere, so a "hovered" card sits highlighted while you
     scroll. Neutralise the ones that move or recolour; :active and the
     .is-active class still give real touch feedback. */
  .studio-list__item a:hover { padding-inline: 0; }
  .partner:hover img,
  .partner:hover video { filter: none; opacity: 1; }
  .partner.is-active img,
  .partner.is-active video { filter: grayscale(0) brightness(1) saturate(1.1); opacity: 1; }

  /* Work thumbnails rest at brightness(0.86) and come up to full only on
     hover. With no pointer that hover never arrives, so every tile on the work
     grid sits permanently dimmed — show them at full brightness instead, and
     let the tap itself be the feedback. */
  .work-item__frame img,
  .work-item__frame video { filter: brightness(1); }
  .work-item:hover .work-item__frame img,
  .work-item:hover .work-item__frame video { transform: none; }
  .work-item:active .work-item__frame img,
  .work-item:active .work-item__frame video { filter: brightness(1.08); }
}

/* --- 2. iOS form zoom ----------------------------------------------------- */
/* Mobile Safari zooms the whole page in when you focus a field whose text is
   under 16px, and never zooms back out — the page is left scrolled sideways
   and the layout looks broken for the rest of the session. 16px is the
   threshold, so hold every field at or above it on touch. The desktop sizes
   are untouched. */
@media (hover: none) and (any-hover: none) and (pointer: coarse) {
  .field input,
  .field select,
  .field textarea,
  .form-grid .field input,
  .form-grid .field textarea,
  .chatbot__form textarea {
    font-size: 16px;
  }
  /* 16px text needs the box to grow with it or the caret crowds the border. */
  .field input,
  .field select,
  .field textarea { padding: 14px 15px; }
  .chatbot__form textarea { min-height: 52px; padding: 14px; }
}

/* --- 3. tap targets ------------------------------------------------------- */
@media (max-width: 1024px) {
  /* The burger was 34px in a 44px minimum world. Grow the hit area without
     growing the mark: the bars stay 34px wide, the padding does the work. */
  .nav-toggle {
    width: 44px; height: 44px;
    padding: 5px;
  }
  /* Drawer links ran to fit-content, so the tappable strip was as narrow as
     the word. Full-width rows, with the divider making the row legible. */
  .nav__link {
    width: 100%;
    min-height: 48px;
    padding: 13px 0;
    align-items: center;
  }
  .nav__item + .nav__item { border-top: 1px solid var(--rule); }
  .nav__sub a { display: block; min-height: 44px; padding: 11px 0; }
  /* The drawer is a scroller of its own inside a locked page — keep its
     momentum from chaining back to the document behind it. */
  .nav { overscroll-behavior: contain; }
}

/* --- 4. safe areas -------------------------------------------------------- */
/* The lightbox and chatbot already inset themselves; the header, the drawer
   and the footer did not, so on a notched phone in landscape the logo and the
   footer's last row sat under the rounded corner / home indicator. */
@supports (padding: env(safe-area-inset-left)) {
  .site-header__inner {
    padding-left: max(var(--pad), env(safe-area-inset-left));
    padding-right: max(var(--pad), env(safe-area-inset-right));
  }
  @media (max-width: 1024px) {
    .nav {
      padding-left: max(var(--pad), env(safe-area-inset-left));
      padding-right: max(var(--pad), env(safe-area-inset-right));
      padding-bottom: max(30px, env(safe-area-inset-bottom));
    }
  }
  /* Keeps the stylesheet's 30px base and only grows for the home indicator —
     max(--pad) would have shrunk it on a phone. (.chatbot and .lightbox
     already inset themselves.) */
  .site-footer { padding-bottom: max(30px, env(safe-area-inset-bottom)); }
}

/* --- 5. narrow-viewport layout ------------------------------------------- */
@media (max-width: 640px) {
  /* A hero title at 6vw of a 390px screen is 23px — the clamp floor is doing
     all the work and the display type stops reading as display type. Let the
     preferred term scale off the small viewport instead. */
  :root {
    --fs-mega: clamp(2.75rem, 13vw, 4.5rem);
    --fs-h1:   clamp(2.25rem, 10.5vw, 3.5rem);
    --fs-h2:   clamp(1.75rem, 7.5vw, 2.5rem);
    --fs-section-display: clamp(2.7rem, 13vw, 4.8rem);
    /* Block rhythm on a phone: the desktop steps leave half a screen of empty
       canvas between sections, which reads as a broken page rather than as
       space. One step down across the board, proportions intact. */
    --rhythm:    clamp(72px, 11svh, 110px);
    --rhythm-md: clamp(52px, 8svh, 80px);
    --rhythm-sm: clamp(40px, 6svh, 60px);
    --rhythm-xs: clamp(24px, 3.5svh, 36px);
  }
  .footer-wordmark { font-size: clamp(2.7rem, 13vw, 4.8rem); }
  /* Long single words in display type (capability names, the wordmark) are
     wider than a 360px screen and were the main source of sideways drift. */
  .hero__title,
  .project-hero__title,
  .page-head__title,
  .h1, .h2,
  .see-all__link,
  .seq__beat { overflow-wrap: anywhere; hyphens: auto; }
  /* max-width in ch was tuned for the desktop measure; on a phone it forces a
     column narrower than the gutter allows. */
  .hero__title { max-width: none; }
}

/* --- 6. overlap on small screens ----------------------------------------- */
/* Absolutely-positioned overlays are the one thing a fluid layout cannot
   reflow: they keep their corner while the box under them shrinks, until the
   text lands on top of something. These are the three that collide. */
@media (max-width: 720px) {
  /* Sequence captions sat bottom-right over a letterboxed 16:9 clip and ran
     into the chat launcher. Full width, above the launcher, left-aligned. */
  .seq.is-live .seq__beats,
  .seq--hero .seq__beats,
  .seq--hero.is-live .seq__beats {
    left: clamp(16px, 5vw, 28px);
    right: clamp(16px, 5vw, 28px);
    bottom: max(84px, calc(env(safe-area-inset-bottom) + 84px));
    text-align: left;
  }
  /* The pfeature caption has to stay an overlay — .pfeature is a fixed-height
     clipped box, so anything in flow after the video is cut off. The block
     above already widens it; the problem left is contrast, because the same
     caption is now three or four lines deep over the middle of the picture
     where the scrim has run out. Raise the scrim to meet it. */
  .pfeature__scrim {
    background: linear-gradient(180deg, rgba(5,7,10,0) 12%, rgba(5,7,10,0.5) 45%, rgba(5,7,10,0.88) 100%);
  }
  .pfeature__text p { margin-top: 10px; }
}

/* --- 7. horizontal drift -------------------------------------------------- */
/* body { overflow-x: hidden } hides sideways overflow but still lets the page
   be dragged; these constrain the elements that actually cause it, so there is
   nothing to hide. */
.blue-band__track,
.logo-marquee__track,
.partners__marquee-track { will-change: transform; }
@media (max-width: 720px) {
  /* Marquee gaps were set for a 1440px stage; at 390px two logos and one gap
     are wider than the screen, so the track jumps rather than rolls. */
  .logo-marquee__track { gap: clamp(40px, 12vw, 64px); }
  .logo-marquee__track img,
  .logo-marquee__track video { height: 30px; }
  /* Tables and pre are the two elements with no intrinsic max-width; give them
     their own scroller instead of letting them widen the page. */
  table, pre { display: block; max-width: 100%; overflow-x: auto; }
}

/* --- 8. short viewports (phone in landscape) ------------------------------ */
/* The full-height blocks all carry a px min-height so they never collapse on a
   short desktop window. Turn a phone sideways and that floor is taller than
   the whole screen — a 640px hero on a 390px-tall viewport means the title is
   off-screen and the first scroll is dead travel through an image. Below 520px
   of height the svh value is the honest one, so drop the floor to it. */
@media (max-height: 520px) and (orientation: landscape) {
  :root {
    --project-hero-h: 92svh;
    --project-hero-min-h: 0px;
  }
  .mx-home-hero,
  .hero,
  .hero--tall,
  .hero.hero--work,
  .project-hero,
  .seq--hero .seq__stage,
  .seq--hero.is-live .seq__stage {
    height: var(--project-hero-h);
    min-height: var(--project-hero-min-h);
  }
  .seq__stage { height: 82svh; }
  .carousel { min-height: 0; }
  .blue-band { height: auto; min-height: 128px; }

  /* A fixed 84px header eats a fifth of a landscape phone. */
  :root { --header-h: 60px; }

  /* The drawer has no room to breathe here; it becomes a compact scroller. */
  .nav__link { min-height: 40px; padding: 8px 0; }

  /* Sequence titles set at 8.5vw of a 844px-wide landscape screen are 72px and
     cover the frame. Height is the real constraint in this orientation. */
  .seq__beat { font-size: clamp(1.25rem, 5.5svh, 2.2rem); }
  .seq--hero .seq__beats,
  .seq--hero.is-live .seq__beats,
  .seq.is-live .seq__beats { bottom: max(16px, env(safe-area-inset-bottom)); }
}

/* ========================================================================== 
   Production coherence pass
   One content rail, one hero hierarchy, and theme-safe media treatment.
   ========================================================================== */
@media (min-width: 1025px) {
  .container,
  .hero__inner,
  .project-hero__inner,
  .teaser__inner {
    padding-left: max(var(--pad), calc(var(--rail-w) + 36px));
    padding-right: var(--pad);
  }
}

/* Media heroes retain a white foreground in both themes; theme tokens apply
   to page surfaces, not text sitting directly on photography. */
.hero .btn,
.project-hero__title {
  color: #F2F5F8;
  border-color: rgba(242, 245, 248, 0.38);
}

/* Media foreground contract. Controls and copy that cross from a themed page
   surface onto dark photography must not inherit the light-theme ink colour. */
.yt-clean-toggle { color: var(--media-ink); }
.theme-toggle { color: var(--ink); }
.teaser:has(.teaser__media) .teaser__title,
.project-band__text,
.pfeature__h,
.seq__beat {
  color: var(--media-ink);
  text-shadow: 0 2px 28px rgba(0, 0, 0, .48);
}
.teaser:has(.teaser__media):hover .teaser__eyebrow,
.project-band__text .accent,
.pfeature__h .accent,
.seq__beat-line--sub { color: var(--media-accent); }
.project-band__scrim { background: rgba(5, 7, 10, .56); }
.lightbox__close:hover,
.lightbox__nav:hover,
.lightbox__play:hover,
.lightbox__mute:hover { background: rgba(16, 19, 24, .94); }

/* The hero must remain cinematic in the light theme; fading a dark image all
   the way into the page canvas produces the large grey/white fog bank. */
:root[data-theme="light"] .seq--hero .seq__scrim {
  background:
    linear-gradient(180deg, rgba(5,7,10,.52) 0%, rgba(5,7,10,.12) 38%, rgba(5,7,10,.56) 100%),
    linear-gradient(300deg, rgba(5,7,10,.56) 0%, rgba(5,7,10,.22) 38%, rgba(5,7,10,0) 68%);
}
:root[data-theme="light"] .seq--hero .seq__stage::after {
  height: clamp(18px, 3svh, 36px);
  background: linear-gradient(180deg, rgba(5,7,10,0), rgba(5,7,10,.36));
}

@media (max-width: 640px) {
  .chatbot__launcher {
    width: 52px;
    min-width: 52px;
    padding: 0;
  }
  .chatbot__launcher span { display: none; }
}

/* --- 9. scroll gesture ---------------------------------------------------- */
/* Scroll containers that sit inside the page need to stop handing their
   overscroll back to the document, or reaching the end of a carousel keeps
   going and scrolls the page — the gesture "jumps" out of the thing you were
   swiping. `contain` ends the chain at the element. */
.carousel--native,
.no-gsap .carousel,
.nav__sub,
.modal__panel,
.chatbot__messages,
.work-filter__controls { overscroll-behavior: contain; }

/* A horizontal scroller must not eat the vertical swipe: pan-x lets the
   browser hand an up/down gesture straight to the page while still owning the
   sideways one, which is what makes a snap track feel native rather than
   sticky. */
.carousel--native,
.no-gsap .carousel { touch-action: pan-x pinch-zoom; }
/* The pinned carousel is not a scroller at all — it moves because the *page*
   moves — so it has to hand the vertical swipe straight to the document. The
   old blanket pan-x here swallowed that gesture on phones and the page could
   not be scrolled past the cards. */
.carousel:not(.carousel--native) { touch-action: pan-y pinch-zoom; }

/* iOS only gives momentum to a scroller that asks for it. */
.carousel,
.nav,
.nav__sub,
.modal__panel,
.chatbot__messages { -webkit-overflow-scrolling: touch; }
.chatbot__quick { overscroll-behavior-x: contain; touch-action: pan-x pinch-zoom; scrollbar-width: none; }
.chatbot__quick::-webkit-scrollbar { display: none; }

/* --- 10. landscape header ------------------------------------------------- */
/* --header-h shrinks in landscape (block 8), so the wordmark has to come down
   with it or it overflows a 60px bar. */
@media (max-height: 520px) and (orientation: landscape) {
  .brand img, .brand video { width: 108px; max-height: 34px; object-fit: contain; }
}
