/* HOW Design Studio — components. Tokens live in tokens.css. */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: "Instrument Sans", system-ui, -apple-system, sans-serif;
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: "Bricolage Grotesque", "Instrument Sans", system-ui, sans-serif;
  line-height: 0.94;
  letter-spacing: -0.042em;
  text-wrap: balance;
  margin: 0;
  font-weight: 700;
}
h3 { line-height: 1.1; letter-spacing: -0.025em; }
p { margin: 0 0 1em; }
img, video { max-width: 100%; display: block; }
a { color: inherit; }

:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; border-radius: 2px; }

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }

.eyebrow {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: var(--text-mono); letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--color-ink-faint); margin: 0;
}

/* ---------- chapters ---------- */
.chapter { position: relative; padding-block: var(--space-section); }
.chapter--clip { overflow: hidden; }
.chapter > .wrap { position: relative; z-index: 2; }

.section-head { display: flex; flex-direction: column; gap: 1.1rem; margin-bottom: clamp(2.5rem, 4vw, 4.5rem); }
/* Solid, not a clipped gradient: motion.js splits headings into spans, and those spans
   inherit `-webkit-text-fill-color: transparent` without inheriting the background,
   so a gradient heading renders as nothing at all once it is split. */
.section-head h2 { font-size: var(--text-h2); max-width: 16ch; color: var(--color-ink); }
/* the heading runs white and the key phrase carries the brand colour, as in the hero */
.section-head h2 em, .contact h2 em, .page-head h1 em { font-style: normal; color: var(--color-accent); }
.page-head h1, .contact h2 { color: var(--color-ink); }
.section-head .lead { color: var(--color-ink-muted); max-width: var(--measure); font-size: var(--text-lead); margin: 0; }

/* ---------- skip link ---------- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--color-accent); color: var(--color-on-accent);
  padding: 0.75rem 1.25rem; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--color-bg) 78%, transparent);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--color-line-soft);
}
.site-header .wrap { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 72px; }
.brand { display: inline-flex; align-items: center; gap: 0.75rem; text-decoration: none; }
.brand img { height: clamp(34px, 3.4vw, 44px); width: auto; }
/* The mark reads "HOW" on its own — a wordmark beside it was saying the name twice. */
.site-nav { display: flex; align-items: center; gap: clamp(1rem, 2vw, 2rem); }
.nav-links { display: flex; gap: clamp(1rem, 2vw, 2rem); }
.nav-links a {
  text-decoration: none; font-size: var(--text-sm); color: var(--color-ink-muted);
  transition: color var(--duration-fast) ease;
}
.nav-links a:hover, .nav-links a[aria-current] { color: var(--color-ink); }
@media (max-width: 760px) { .nav-links { display: none; } }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  padding: 0.95rem 1.75rem; border-radius: 100px; text-decoration: none;
  font-size: var(--text-sm); font-weight: 600; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; background: transparent; -webkit-appearance: none; appearance: none;
  transition: transform var(--duration-fast) var(--ease-out-expo),
              background-color var(--duration-fast) ease,
              border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn-primary {
  background: var(--gradient-accent); color: var(--color-on-accent);
  box-shadow: var(--glow-accent);
}
.btn-primary:hover { filter: brightness(1.12); box-shadow: 0 0 56px rgba(168,85,247,.5); }
/* background must be explicit: a <button> otherwise takes the UA's buttonface,
   which put near-white text on a white pill in the intro gate. */
.btn-ghost { background: transparent; border-color: rgba(255,255,255,.28); color: var(--color-ink); }
.btn-ghost:hover { border-color: var(--color-accent); color: var(--color-accent); }
.btn-sm { padding: 0.6rem 1.2rem; }

/* ---------- hero: the showreel IS the hero, type sits on it ---------- */
.hero {
  position: relative;
  min-height: min(94vh, 940px);
  display: grid; align-items: end;
  overflow: hidden;
  isolation: isolate;
}
.hero-media { position: absolute; inset: 0; z-index: 0; background: #000; }
.hero-media video { width: 100%; height: 100%; object-fit: cover; }
/* Scrim: the type has to stay legible over any frame of the reel. */
.hero-media::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(to top, var(--color-bg) 2%, rgba(8,7,10,.82) 32%, rgba(8,7,10,.35) 62%, rgba(8,7,10,.55) 100%),
    linear-gradient(to right, rgba(8,7,10,.75) 0%, rgba(8,7,10,.15) 55%, transparent 100%);
}
.hero > .wrap { position: relative; z-index: 2; padding-block: clamp(3rem, 3vw + 2rem, 6rem); width: 100%; }
.hero h1 { font-size: var(--text-display); margin: 1.4rem 0 0; max-width: 14ch; }
.hero h1 em { font-style: normal; color: var(--color-accent); }
.hero-body {
  display: grid; gap: clamp(1.5rem, 3vw, 3.5rem); align-items: end;
  grid-template-columns: 1fr; margin-top: clamp(1.75rem, 3vw, 2.75rem);
}
@media (min-width: 900px) { .hero-body { grid-template-columns: minmax(0, 46ch) 1fr; } }
.hero .lead { color: var(--color-ink); opacity: .82; font-size: var(--text-lead); max-width: 44ch; margin: 0 0 2rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.hero-meta {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.75rem;
  justify-content: flex-start;
}
@media (min-width: 900px) { .hero-meta { justify-content: flex-end; padding-bottom: .4rem; } }
.hero-meta li {
  font-family: "JetBrains Mono", monospace; font-size: var(--text-mono);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-ink-muted);
}
.hero-scroll {
  position: absolute; left: var(--gutter); bottom: 1.25rem; z-index: 2;
  font-family: "JetBrains Mono", monospace; font-size: 0.625rem;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--color-ink-faint);
}

/* ---------- services ----------
   No cell borders. The bordered version read as a spreadsheet: vertical rules on every
   cell, copy pressed against them, a hover fill that looked like a selection artefact,
   and tall voids under the shorter entries. Whitespace and a large numeral do the work
   instead, and the numeral ties back to the loader. */
.svc-grid {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(0.9rem, 1.4vw, 1.25rem);
}
@media (min-width: 640px)  { .svc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1080px) { .svc-grid { grid-template-columns: repeat(4, 1fr); } }

/* White cards on the black chapter. The bare-text version read as loose columns
   with nothing holding a service together; a card gives each one an edge and lets
   the grid breathe with a real gutter instead of guessed margins. */
.svc {
  display: flex; flex-direction: column; align-items: flex-start;
  background: var(--color-ink); color: #0A0A0C;
  border-radius: 18px; padding: clamp(1.35rem, 1.9vw, 1.75rem);
  border: 1px solid transparent;
  transition: transform var(--duration-normal) var(--ease-out-expo),
              box-shadow var(--duration-normal) var(--ease-out-expo),
              border-color var(--duration-normal) var(--ease-out-expo);
}
.svc:hover {
  transform: translateY(-4px); border-color: var(--color-accent);
  box-shadow: 0 18px 40px rgba(0,0,0,.5);
}
/* the numeral needs the deep accent to hold contrast on white; the muted-dark
   variant of .svc-num on service pages must not inherit this */
.svc > .svc-num { color: var(--color-accent-deep); }
.svc h3 { font-size: var(--text-h3); margin-top: 0.75rem; color: #0A0A0C; }
.svc p {
  color: rgba(10,10,12,.66); margin: 0.85rem 0 0; font-size: 0.9375rem;
  line-height: 1.6; max-width: none;
  padding-top: 0.85rem; border-top: 1px solid rgba(10,10,12,.14);
  transition: border-color var(--duration-normal) var(--ease-out-expo),
              color var(--duration-normal) ease;
}
.svc:hover .svc-num { transform: translateY(-3px); }
.svc:hover p { color: rgba(10,10,12,.85); border-top-color: var(--color-accent-deep); }

/* on the dark chapters the same block needs the dark tokens */
.svc-num {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 200;
  font-size: clamp(2.25rem, 3.2vw, 3rem); line-height: 1; letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent);
  transition: color var(--duration-normal) var(--ease-out-expo),
              transform var(--duration-normal) var(--ease-out-expo);
}

/* ---------- showreel: full bleed, the page's biggest moment ---------- */

.reel-cap {
  display: flex; flex-wrap: wrap; gap: 0.4rem 2rem; justify-content: space-between;
  align-items: baseline; padding: 1rem var(--gutter) 0; max-width: var(--maxw); margin-inline: auto;
}
.reel-cap p { margin: 0; color: var(--color-ink-muted); font-size: var(--text-sm); }

/* ---------- work ---------- */
.work-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); gap: clamp(0.75rem, 1.6vw, 1.5rem); }
.work-grid.is-wide-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr)); }
/* The three featured pieces should span the row; auto-fill left an empty fourth column. */
.work-row { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) { .work-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .work-row { grid-template-columns: 1fr; } }
.work-card { display: flex; flex-direction: column; gap: 0.85rem; }
.work-media {
  position: relative; overflow: hidden; border-radius: var(--radius);
  background: var(--color-surface); transform: translateZ(0);
}
.work-media video, .work-media img { width: 100%; aspect-ratio: 9 / 16; object-fit: cover; transition: transform var(--duration-normal) var(--ease-out-expo); }
.work-media.is-wide video, .work-media.is-wide img { aspect-ratio: 16 / 9; }
.work-card:hover .work-media video, .work-card:hover .work-media img { transform: scale(1.04); }
.work-media::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.07);
  border-radius: var(--radius);
}
.work-cap { display: flex; flex-direction: column; gap: 0.2rem; }
.work-cap h3 { font-size: 1.0625rem; letter-spacing: -0.02em; }
.work-cap .meta {
  font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-ink-faint); margin: 0;
}

/* ---------- marquee ---------- */
.marquee-stack { display: flex; flex-direction: column; gap: clamp(0.5rem, 1.2vw, 1rem); }
.marquee--reverse .marquee-track { animation-direction: reverse; animation-duration: 72s; }

.marquee {
  --marquee-duration: 60s;
  overflow: hidden; position: relative;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.marquee-track { display: flex; gap: clamp(0.5rem, 1.2vw, 1rem); width: max-content; animation: marquee-scroll var(--marquee-duration) linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-item {
  /* Uniform height, width derived from the aspect — mixed heights read as an accident. */
  --marquee-h: clamp(190px, 21vw, 290px);
  flex: 0 0 auto; display: block; text-decoration: none;
  height: var(--marquee-h); width: calc(var(--marquee-h) * 9 / 16);
  border-radius: var(--radius); overflow: hidden; background: var(--color-surface);
}
.marquee-item img { width: 100%; height: 100%; object-fit: cover; }
.marquee-item.is-wide { width: calc(var(--marquee-h) * 16 / 9); }
@keyframes marquee-scroll { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
  .marquee { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
}

/* ---------- reality cards ---------- */

/* This block sits on dark. The paper tokens it used to carry rendered near-black
   body text on a near-black ground — unreadable. Dark is now the default. */

/* ---------- process ----------
   A rail down the centre. Title sits on one side, the detail on the other, and each
   row arrives out of focus as you reach it. Stacks to a single left rail on narrow
   screens, where a centre line would leave two unusably thin columns. */
.process-list { position: relative; }

.process-rail {
  position: absolute; top: 0; bottom: 0; width: 2px; z-index: 1;
  left: clamp(6px, 1.4vw, 10px);
  background: var(--color-line); overflow: hidden;
}
@media (min-width: 900px) { .process-rail { left: 50%; transform: translateX(-50%); } }
.process-rail i {
  display: block; width: 100%; height: 100%; transform-origin: 0 0;
  transform: scaleY(0); background: var(--gradient-accent);
  transition: transform 120ms linear;
}

.step {
  position: relative;
  display: grid; grid-template-columns: 1fr; gap: 0;
  padding: clamp(1.25rem, 2.4vw, 2rem) 0 clamp(1.25rem, 2.4vw, 2rem) clamp(2.5rem, 5vw, 4rem);
}
/* Each half of a step is a block, matching the service cards. Stacked on narrow
   screens the two halves meet with squared inner corners so they read as one
   card rather than two fragments. */
.step > .step-side,
.step > .step-detail {
  background: var(--color-ink);
  padding: clamp(1.1rem, 1.7vw, 1.5rem) clamp(1.2rem, 1.9vw, 1.6rem);
}
/* Set the colour per half, not on the shared rule: `.step > .step-detail` (0,2,0)
   beat `.step p` (0,1,1), so the shared `color` was forcing the description to
   full black instead of the softer body tone the service cards use. */
.step > .step-side   { color: #0A0A0C; }
.step > .step-detail { color: rgba(10,10,12,.66); }
.step > .step-side   { border-radius: 18px 18px 0 0; padding-bottom: 0.35rem; }
.step > .step-detail { border-radius: 0 0 18px 18px; padding-top: 0.35rem; }

@media (min-width: 900px) {
  .step {
    grid-template-columns: minmax(0, 1fr) clamp(2.5rem, 4vw, 4rem) minmax(0, 1fr);
    align-items: stretch; padding-left: 0; gap: 0;
  }
  /* Side by side they are two separate blocks again, each pulled back from the
     centre gutter so the rail and its node stay clear of both. */
  .step > .step-side {
    grid-column: 1; text-align: right; border-radius: 18px;
    padding: clamp(1.25rem, 1.9vw, 1.7rem) clamp(1.4rem, 2vw, 1.8rem);
    margin-right: clamp(1.5rem, 2.5vw, 2.5rem);
    display: flex; flex-direction: column; justify-content: center;
    /* hug the title instead of filling the column — a full-width block left a
       large empty white field to the left of right-aligned text */
    width: fit-content; max-width: 100%; margin-left: auto;
  }
  .step > .step-detail {
    grid-column: 3; border-radius: 18px;
    padding: clamp(1.25rem, 1.9vw, 1.7rem) clamp(1.4rem, 2vw, 1.8rem);
    margin-left: clamp(1.5rem, 2.5vw, 2.5rem);
    display: flex; flex-direction: column; justify-content: center;
  }
}

/* the node that sits on the rail */
.step::before {
  content: ""; position: absolute; z-index: 2;
  left: clamp(6px, 1.4vw, 10px); top: 50%;
  width: 13px; height: 13px; margin-left: -5.5px; margin-top: -6.5px;
  border-radius: 50%; background: var(--color-bg);
  border: 2px solid var(--color-line);
  transition: border-color var(--duration-normal) var(--ease-out-expo),
              box-shadow var(--duration-normal) var(--ease-out-expo),
              transform var(--duration-normal) var(--ease-out-expo);
}
@media (min-width: 900px) { .step::before { left: 50%; margin-left: -6.5px; } }
.step.is-active::before {
  border-color: var(--color-accent); transform: scale(1.4);
  box-shadow: var(--glow-accent);
}

.step-num {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 200;
  font-size: clamp(1.9rem, 2.6vw, 2.6rem); line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; display: block;
  color: rgba(10,10,12,.28); margin-bottom: 0.3rem;
  transition: color var(--duration-normal) ease;
}
.step.is-active .step-num { color: var(--color-accent-deep); }
.step h3 { font-size: var(--text-h3); color: #0A0A0C; margin: 0; }
.step p { color: rgba(10,10,12,.66); margin: 0; max-width: 42ch; font-size: 0.9375rem; line-height: 1.6; }
@media (min-width: 900px) {
  /* `width: 100%` here was a leftover from the wrapping fix and beat the
     `fit-content` above, so the title block kept filling the whole column. */
  .step > .step-side h3 { text-wrap: pretty; }
}

.motion .step {
  opacity: 0; filter: blur(12px); transform: translate3d(0, 34px, 0);
  transition: opacity 700ms var(--ease-out-expo),
              filter 850ms var(--ease-out-expo),
              transform 900ms var(--ease-out-expo);
}
.motion .step.is-in { opacity: 1; filter: blur(0); transform: none; }

@media (prefers-reduced-motion: reduce) {
  .motion .step { opacity: 1; filter: none; transform: none; }
  .process-rail i { transform: scaleY(1); }
}

/* ---------- contact ---------- */

@media (min-width: 980px) { .contact-grid { grid-template-columns: 1fr 1fr; } }

/* form */

@media (min-width: 620px) { .form-grid { grid-template-columns: 1fr 1fr; } .form-grid .col-full { grid-column: 1 / -1; } }

/* ---------- sound toggle ---------- */

/* Uneven resting heights so it reads as an equalizer, not an ellipsis
   (the label is hidden on small screens, so the icon has to carry the meaning). */

@keyframes eq { 0%,100% { height: 25%; } 50% { height: 100%; } }
@media (max-width: 640px) { .sound-toggle .label { display: none; } .sound-toggle { padding: 0.5rem 0.7rem; } }

/* ---------- work page ---------- */

/* ---------- footer ---------- */
/* One colour, one size, one weight across the whole footer. It previously mixed
   muted grey, accent purple and full white at two sizes, which read as unbranded. */

/* ---------- reveal (visible by default; never gated on JS) ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .js .reveal { opacity: 1; transform: none; }
  .work-card:hover .work-media video, .work-card:hover .work-media img { transform: none; }
}

/* ---------- line-mask heading reveal (added by motion.js) ---------- */

/* ---------- staggered children ---------- */

/* ---------- reading progress ---------- */

/* parallax layer needs room to move without exposing an edge */

/* ---------- intro sequence ----------
   Motion vocabulary follows the reference sites: a light, oversized numeral, a hairline
   progress bar with a gradient fill, and buttons that lift with a layered shadow and a
   sliding arrow. Everything here is our own type and colour. */

/* ambient pulse behind the numeral */

@keyframes intro-pulse {
  from { opacity: .35; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: .8;  transform: translate(-50%, -50%) scale(1.22); }
}

/* gate buttons: translucent, with a radial glow and an arrow that slides on hover */

/* skip sits in the corner, out of the way of the decision */

@media (prefers-reduced-motion: reduce) {
  .intro-pulse { animation: none; }
  .motion .is-split .line-inner { transform: none; }
  .motion .stagger-item { opacity: 1; transform: none; }
}

/* ---------- capability strip: the pacing device the reference sites use ---------- */

@media (prefers-reduced-motion: reduce) {
  .cap-track { animation: none; }
  .cap-marquee { width: 100%; overflow-x: auto; }
}

/* ---------- brand carousel ----------
   Two rows on a slight tilt, scrolling against each other. The block is wider than the
   viewport and clipped by the section, so rotating it never exposes a corner. */
.logo-marquee {
  width: 116%; margin-left: -8%;
  transform: rotate(-2.6deg);
  display: flex; flex-direction: column; gap: clamp(0.75rem, 1.4vw, 1.25rem);
}
.logo-row {
  display: flex; width: 100%; overflow: hidden;
  background: var(--color-ink);            /* white band against the black page */
}
.logo-track {
  display: flex; align-items: center; flex: 0 0 auto;
  list-style: none; margin: 0; padding: 0;
  animation: marquee-scroll 46s linear infinite;
}
.logo-row--reverse .logo-track { animation-direction: reverse; animation-duration: 54s; }
.logo-marquee:hover .logo-track { animation-play-state: paused; }

.logo-cell {
  flex: 0 0 auto; display: grid; place-items: center;
  width: clamp(150px, 15vw, 220px); height: clamp(78px, 7.8vw, 108px);
  padding: 0 clamp(0.9rem, 1.6vw, 1.75rem);
  border-right: 1px solid rgba(10,10,12,.1);
}
.logo-cell img {
  max-width: 82%; max-height: 30px; width: auto; height: auto;
  object-fit: contain;
}
.logo-cell.is-text span {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 600;
  font-size: clamp(0.85rem, 1.1vw, 1rem); letter-spacing: -0.02em;
  color: #0A0A0C; white-space: nowrap;
  transition: color var(--duration-fast) ease;
}
.logo-cell.is-text:hover span { color: #0A0A0C; }

@media (prefers-reduced-motion: reduce) {
  .logo-marquee { transform: none; width: 100%; margin-left: 0; }
  .logo-track { animation: none; }
  .logo-row { overflow-x: auto; }
  .logo-row .logo-track + .logo-track { display: none; }
}

/* ---------- reality cards ---------- */

/* This block sits on dark. The paper tokens it used to carry rendered near-black
   body text on a near-black ground — unreadable. Dark is now the default. */

/* ---------- contact ---------- */

@media (min-width: 980px) { .contact-grid { grid-template-columns: 1fr 1fr; } }

/* form */

@media (min-width: 620px) { .form-grid { grid-template-columns: 1fr 1fr; } .form-grid .col-full { grid-column: 1 / -1; } }

/* ---------- sound toggle ---------- */

/* Uneven resting heights so it reads as an equalizer, not an ellipsis
   (the label is hidden on small screens, so the icon has to carry the meaning). */

@keyframes eq { 0%,100% { height: 25%; } 50% { height: 100%; } }
@media (max-width: 640px) { .sound-toggle .label { display: none; } .sound-toggle { padding: 0.5rem 0.7rem; } }

/* ---------- work page ---------- */

/* ---------- footer ---------- */
/* One colour, one size, one weight across the whole footer. It previously mixed
   muted grey, accent purple and full white at two sizes, which read as unbranded. */

/* ---------- reveal (visible by default; never gated on JS) ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .js .reveal { opacity: 1; transform: none; }
  .work-card:hover .work-media video, .work-card:hover .work-media img { transform: none; }
}

/* ---------- line-mask heading reveal (added by motion.js) ---------- */

/* ---------- staggered children ---------- */

/* ---------- reading progress ---------- */

/* parallax layer needs room to move without exposing an edge */

/* ---------- intro sequence ----------
   Motion vocabulary follows the reference sites: a light, oversized numeral, a hairline
   progress bar with a gradient fill, and buttons that lift with a layered shadow and a
   sliding arrow. Everything here is our own type and colour. */

/* ambient pulse behind the numeral */

@keyframes intro-pulse {
  from { opacity: .35; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: .8;  transform: translate(-50%, -50%) scale(1.22); }
}

/* gate buttons: translucent, with a radial glow and an arrow that slides on hover */

/* skip sits in the corner, out of the way of the decision */

@media (prefers-reduced-motion: reduce) {
  .intro-pulse { animation: none; }
  .motion .is-split .line-inner { transform: none; }
  .motion .stagger-item { opacity: 1; transform: none; }
}

/* ---------- capability strip: the pacing device the reference sites use ---------- */

@media (prefers-reduced-motion: reduce) {
  .cap-track { animation: none; }
  .cap-marquee { width: 100%; overflow-x: auto; }
}

/* ---------- brand wall ----------
   Uniform cells so a logo image and a name set in our own type read as one wall.
   Drop a PNG/SVG into assets/img/logos/<slug>.png and that cell upgrades itself. */

@media (min-width: 640px)  { .brands { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 980px)  { .brands { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1360px) { .brands { grid-template-columns: repeat(6, 1fr); } }

/* ---------- reality cards ---------- */
.reality-grid { display: grid; gap: clamp(1.5rem, 3vw, 3rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
/* This block sits on dark. The paper tokens it used to carry rendered near-black
   body text on a near-black ground — unreadable. Dark is now the default. */
.reality-card { display: flex; flex-direction: column; gap: 0.8rem; padding-top: 1.5rem; border-top: 1px solid var(--color-line); }
.reality-card h3 { font-size: var(--text-h3); }
.reality-card p { color: var(--color-ink-muted); margin: 0; font-size: 1rem; line-height: 1.6; }

/* ---------- contact ---------- */
.contact-grid { display: grid; gap: clamp(2rem, 3.4vw, 3.25rem); grid-template-columns: 1fr; }
@media (min-width: 980px) { .contact-grid { grid-template-columns: 1fr 1fr; } }
/* The contact block ran 1187px tall with a 72px heading eating 249px of it.
   It closes the page, it is not another chapter opener — so it gets its own,
   smaller scale rather than the shared --text-h2. */
.chapter.contact { padding-block: clamp(3rem, 6vw, 5.5rem); }
.contact h2 { font-size: clamp(1.9rem, 1rem + 3vw, 3.1rem); margin-bottom: 0.9rem; line-height: 1.06; }
.contact .lead { color: var(--color-ink-muted); font-size: 1.0625rem; max-width: 40ch; line-height: 1.6; }
.contact-direct { margin-top: 1.75rem; display: flex; flex-direction: column; gap: 0.7rem; }
.contact-direct a {
  display: inline-flex; align-items: center; gap: 0.85rem; text-decoration: none;
  color: var(--color-ink); font-size: 1.0625rem;
  transition: color var(--duration-fast) ease;
}
.contact-direct a svg {
  width: 34px; height: 34px; flex: 0 0 34px; padding: 8px; border-radius: 50%;
  border: 1px solid var(--color-line); color: var(--color-ink-muted);
  transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease,
              background-color var(--duration-fast) ease;
}
.contact-direct a:hover svg {
  border-color: var(--color-accent); color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}
.contact-direct a:hover { color: var(--color-accent); }
/* the icon says which channel it is, so the written label is gone */
.contact-direct .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* form */
.form-grid { display: grid; gap: 0.9rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .form-grid { grid-template-columns: 1fr 1fr; } .form-grid .col-full { grid-column: 1 / -1; } }
.field { display: flex; flex-direction: column; gap: 0.5rem; }
.field label {
  font-family: "JetBrains Mono", monospace; font-size: var(--text-mono);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-ink-muted);
}
.field .opt { color: var(--color-ink-faint); text-transform: none; letter-spacing: 0; }
.field input, .field textarea {
  font-family: inherit; font-size: var(--text-base); color: var(--color-ink);
  background: transparent; border: 0; border-bottom: 1px solid var(--color-line);
  border-radius: 0; padding: 0.7rem 0; width: 100%;
  transition: border-color var(--duration-fast) ease;
}
.field textarea { resize: vertical; min-height: 88px; }
.field input:focus, .field textarea:focus { border-bottom-color: var(--color-accent); outline: none; }
.field input::placeholder, .field textarea::placeholder { color: var(--color-ink-faint); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-foot { display: flex; flex-direction: column; gap: 0.85rem; margin-top: 1.35rem; align-items: flex-start; }
.form-note { font-size: 0.8125rem; color: var(--color-ink-faint); margin: 0; }
.form-status { font-size: var(--text-sm); margin: 0; padding: 0.85rem 1rem; border-radius: var(--radius); display: none; }
.form-status[data-state="ok"]  { display: block; background: rgba(120,200,150,.1); border: 1px solid rgba(120,200,150,.35); color: #8FD4AC; }
.form-status[data-state="err"] { display: block; background: rgba(224,123,103,.1); border: 1px solid rgba(224,123,103,.35); color: #E8A091; }
.form-status a { color: inherit; }

/* ---------- sound toggle ---------- */
.sound-toggle {
  /* Fixed in the bottom-right corner rather than in the header — it is an ambient
     control, not navigation. */
  position: fixed; z-index: 120;
  right: calc(1.5rem + env(safe-area-inset-right, 0px));
  bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--color-bg) 82%, transparent);
  backdrop-filter: blur(14px);
  display: none; align-items: center; gap: 0.5rem;
  border: 1px solid var(--color-line); border-radius: 100px;
  background: transparent; color: var(--color-ink-muted);
  padding: 0.5rem 0.9rem; cursor: pointer;
  font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  transition: color var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.sound-toggle.is-available { display: inline-flex; }
.sound-toggle:hover { color: var(--color-ink); border-color: var(--color-accent); }
.sound-toggle .bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 11px; }
/* Uneven resting heights so it reads as an equalizer, not an ellipsis
   (the label is hidden on small screens, so the icon has to carry the meaning). */
.sound-toggle .bars i { display: block; width: 2px; background: currentColor; }
.sound-toggle .bars i:nth-child(1) { height: 40%; }
.sound-toggle .bars i:nth-child(2) { height: 75%; }
.sound-toggle .bars i:nth-child(3) { height: 55%; }
.sound-toggle .bars i:nth-child(4) { height: 90%; }
.sound-toggle[aria-pressed="true"] { color: var(--color-accent); border-color: var(--color-accent); }
.sound-toggle[aria-pressed="true"] .bars i { animation: eq 900ms ease-in-out infinite; }
.sound-toggle[aria-pressed="true"] .bars i:nth-child(2) { animation-delay: 150ms; }
.sound-toggle[aria-pressed="true"] .bars i:nth-child(3) { animation-delay: 300ms; }
.sound-toggle[aria-pressed="true"] .bars i:nth-child(4) { animation-delay: 450ms; }
@keyframes eq { 0%,100% { height: 25%; } 50% { height: 100%; } }
@media (max-width: 640px) { .sound-toggle .label { display: none; } .sound-toggle { padding: 0.5rem 0.7rem; } }

/* ---------- work page ---------- */
.page-head { padding-block: clamp(3.5rem, 2rem + 5vw, 7rem) 0; }
.page-head h1 { font-size: var(--text-h2); margin: 1.2rem 0 1rem; max-width: 14ch; }
.page-head .lead { color: var(--color-ink-muted); font-size: var(--text-lead); max-width: var(--measure); margin: 0; }
.back-link { display: inline-flex; align-items: center; gap: 0.45rem; text-decoration: none; color: var(--color-ink-muted); font-size: var(--text-sm); }
.back-link:hover { color: var(--color-accent); }

/* ---------- footer ---------- */
/* One colour, one size, one weight across the whole footer. It previously mixed
   muted grey, accent purple and full white at two sizes, which read as unbranded. */
.site-footer { border-top: 1px solid var(--color-line); padding-block: clamp(2.5rem, 4vw, 4rem) clamp(3rem, 5vw, 5rem); }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 1.5rem 2rem; justify-content: space-between; align-items: center; }
.site-footer p,
.site-footer nav a {
  margin: 0; font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-ink-muted);
  text-decoration: none;
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: 1.25rem 2rem; }
.site-footer nav a { transition: color var(--duration-fast) ease; }
.site-footer nav a:hover { color: var(--color-accent); }

/* ---------- reveal (visible by default; never gated on JS) ---------- */
.reveal { opacity: 1; transform: none; }
.js .reveal {
  opacity: 0; filter: blur(10px); transform: translateY(24px);
  transition: opacity var(--duration-normal) var(--ease-out-expo),
              filter 780ms var(--ease-out-expo),
              transform var(--duration-normal) var(--ease-out-expo);
}
.js .reveal.is-in { opacity: 1; filter: blur(0); transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .js .reveal { opacity: 1; transform: none; }
  .work-card:hover .work-media video, .work-card:hover .work-media img { transform: none; }
}

/* ---------- line-mask heading reveal (added by motion.js) ---------- */
.line-mask {
  display: block; overflow: hidden;
  /* Room for the blur to sit inside the clip. The horizontal and top offsets cancel out,
     but the bottom one is NOT cancelled — doing so pulled the next element up into the
     descenders of the last line. */
  padding: 0.14em 0.12em 0.16em;
  margin: -0.14em -0.12em 0;
}
.line-inner { display: block; }
.motion .is-split .line-inner {
  transform: translate3d(0, 105%, 0);
  filter: blur(14px);
  opacity: 0;
  transition: transform 1050ms var(--ease-out-expo),
              filter 900ms var(--ease-out-expo),
              opacity 700ms ease;
}
.motion .is-split.lines-in .line-inner {
  transform: translate3d(0, 0, 0);
  filter: blur(0);
  opacity: 1;
}
.word.is-accent { color: var(--color-accent); }

/* ---------- staggered children ---------- */
.motion .stagger-item {
  opacity: 0; filter: blur(10px); transform: translate3d(0, 28px, 0);
  transition: opacity 650ms var(--ease-out-expo),
              filter 800ms var(--ease-out-expo),
              transform 800ms var(--ease-out-expo);
}
.motion .stagger-item.is-in { opacity: 1; filter: blur(0); transform: none; }

/* ---------- reading progress ---------- */
.progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 300;
  background: var(--gradient-accent); transform: scaleX(0); transform-origin: 0 50%;
  will-change: transform; pointer-events: none;
}

/* parallax layer needs room to move without exposing an edge */
[data-parallax] { will-change: transform; }

/* ---------- intro sequence ----------
   Motion vocabulary follows the reference sites: a light, oversized numeral, a hairline
   progress bar with a gradient fill, and buttons that lift with a layered shadow and a
   sliding arrow. Everything here is our own type and colour. */
.intro {
  position: fixed; inset: 0; z-index: 500;
  background: var(--color-bg); color: var(--color-ink);
  display: grid; place-items: center;
  transition: opacity 900ms var(--ease-out-expo), visibility 900ms;
}
.intro[hidden] { display: none; }
.intro.is-done { opacity: 0; visibility: hidden; pointer-events: none; }
.intro.is-done .intro-stage { transform: translateY(-24px) scale(.985); }

/* ambient pulse behind the numeral */
.intro-pulse {
  position: absolute; left: 50%; top: 50%; width: 46vmin; height: 46vmin;
  transform: translate(-50%, -50%); border-radius: 50%; z-index: 1; pointer-events: none;
  background: radial-gradient(circle, color-mix(in srgb, var(--color-accent) 26%, transparent) 0%, transparent 62%);
  animation: intro-pulse 4.5s ease-in-out infinite alternate;
}
@keyframes intro-pulse {
  from { opacity: .35; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: .8;  transform: translate(-50%, -50%) scale(1.22); }
}

.intro-stage { position: relative; z-index: 2; text-align: center; padding: var(--gutter); width: 100%; max-width: 48rem; }

.intro-label {
  font-family: "JetBrains Mono", monospace; font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.42em; text-transform: uppercase;
  color: rgba(244,241,246,.32); margin: 0 0 1.25rem;
}
.intro-count {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 200;
  font-size: clamp(5.5rem, 12vw, 8rem); line-height: 1; letter-spacing: -0.02em;
  text-shadow: 0 8px 48px rgba(0,0,0,.35);
  display: flex; align-items: flex-start; justify-content: center; margin: 0 0 2.25rem;
  font-variant-numeric: tabular-nums;
}
.intro-count sup { font-size: 0.36em; font-weight: 400; opacity: .45; letter-spacing: 0; margin-left: .06em; }
.intro-bar { width: 160px; height: 1px; margin: 0 auto; background: rgba(244,241,246,.12); border-radius: 1px; overflow: hidden; }
.intro-bar i {
  display: block; height: 100%; width: 0%; border-radius: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 70%, transparent), var(--color-ink));
  transition: width 120ms ease-out;
}

.intro-gate[hidden] { display: none; }
.intro-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.intro-media video { width: 100%; height: 100%; object-fit: cover; opacity: .42; }
.intro-media::after { content: ""; position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(8,7,10,.5) 0%, rgba(8,7,10,.94) 72%); }
.intro-gate h2 { font-size: clamp(2rem, 5.6vw, 3.9rem); margin: 0 0 1.1rem; }
.intro-gate h2 em { font-style: italic; color: var(--color-accent); }
.intro-gate p { color: var(--color-ink-muted); max-width: 40ch; margin: 0 auto 2.5rem; }
.intro-actions { display: flex; flex-wrap: wrap; gap: 0.9rem; justify-content: center; }

/* gate buttons: translucent, with a radial glow and an arrow that slides on hover */
.intro-btn {
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 0.9rem;
  padding: 1rem 2.5rem; border-radius: 100px; cursor: pointer;
  background: rgba(244,241,246,.06); border: 1px solid rgba(244,241,246,.22);
  color: var(--color-ink); font-family: "JetBrains Mono", monospace;
  font-size: 0.6875rem; letter-spacing: 0.2em; text-transform: uppercase;
  transition: transform 300ms var(--ease-out-expo), border-color 300ms ease, background-color 300ms ease;
}
.intro-btn .glow {
  position: absolute; inset: 0; z-index: -1; border-radius: 100px; opacity: 0;
  background: radial-gradient(at 65% center, color-mix(in srgb, var(--color-accent) 34%, transparent) 0%, transparent 65%);
  transition: opacity 400ms ease;
}
.intro-btn:hover {
  transform: translateY(-3px);
  border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  box-shadow: 0 16px 48px rgba(0,0,0,.35), 0 4px 16px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
.intro-btn:hover .glow { opacity: 1; }
.intro-btn:active { transform: translateY(-1px); }
.intro-btn svg { width: 15px; height: 15px; flex-shrink: 0; transition: transform 350ms var(--ease-out-expo); }
.intro-btn:hover svg { transform: translateX(5px); }
.intro-btn.is-primary {
  background: var(--gradient-accent); border-color: transparent;
  color: var(--color-on-accent); box-shadow: var(--glow-accent);
}
.intro-btn.is-primary:hover { filter: brightness(1.12); box-shadow: 0 0 56px rgba(168,85,247,.5); }

/* skip sits in the corner, out of the way of the decision */
.intro-skip {
  position: fixed; right: calc(1.75rem + env(safe-area-inset-right, 0px));
  bottom: calc(1.75rem + env(safe-area-inset-bottom, 0px)); z-index: 3;
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: rgba(16,14,20,.8); border: 1px solid var(--color-line);
  border-radius: 100px; padding: 0.6rem 1.1rem; cursor: pointer;
  font-family: "JetBrains Mono", monospace; font-size: 0.5625rem;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-ink-muted);
  transition: transform 250ms var(--ease-out-expo), border-color 250ms ease, color 250ms ease;
}
.intro-skip:hover { color: var(--color-ink); border-color: var(--color-accent); transform: translateY(-2px); }
.intro-skip svg { width: 12px; height: 12px; opacity: .65; transition: transform 250ms ease, opacity 250ms ease; }
.intro-skip:hover svg { opacity: 1; transform: translateX(3px); }

body.intro-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .intro-pulse { animation: none; }
  .motion .is-split .line-inner { transform: none; }
  .motion .stagger-item { opacity: 1; transform: none; }
}

/* ---------- capability strip: the pacing device the reference sites use ---------- */
.cap-strip {
  border-block: 0;
  padding-block: clamp(1.15rem, 1.9vw, 1.7rem);
  overflow: hidden;
  background: linear-gradient(90deg, var(--color-accent-deep) 0%, #6D28D9 50%, var(--color-accent-deep) 100%);
}
.cap-marquee { display: flex; width: max-content; }
.cap-track { display: flex; align-items: center; gap: 1.5rem; padding-right: 1.5rem; animation: marquee-scroll 42s linear infinite; }
.cap-strip:hover .cap-track { animation-play-state: paused; }
.cap {
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 600;
  font-size: clamp(1.05rem, 1.6vw, 1.6rem); letter-spacing: -0.025em;
  color: var(--color-ink); white-space: nowrap;
}
.cap-dot { color: rgba(255,255,255,.45); font-size: 0.7em; }
@media (prefers-reduced-motion: reduce) {
  .cap-track { animation: none; }
  .cap-marquee { width: 100%; overflow-x: auto; }
}

/* ---------- brand wall ---------- */

/* sound is opt-in at the gate: a quiet secondary choice, never the default action */
.intro-sound {
  display: inline-flex; align-items: center; gap: 0.6rem; margin-top: 1.5rem;
  background: transparent; border: 0; cursor: pointer;
  font-family: "JetBrains Mono", monospace; font-size: 0.625rem;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--color-ink-faint);
  transition: color var(--duration-fast) ease;
}
.intro-sound:hover { color: var(--color-accent); }
.intro-sound .bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 10px; }
.intro-sound .bars i { display: block; width: 2px; background: currentColor; }
.intro-sound .bars i:nth-child(1) { height: 40%; }
.intro-sound .bars i:nth-child(2) { height: 75%; }
.intro-sound .bars i:nth-child(3) { height: 55%; }
.intro-sound .bars i:nth-child(4) { height: 90%; }

/* ---------- form pickers: services + budget ---------- */
.field-set { border: 0; padding: 0; margin: 0 0 2rem; }
.field-set legend {
  padding: 0; margin-bottom: 0.9rem;
  font-family: "JetBrains Mono", monospace; font-size: var(--text-mono);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-ink-muted);
}
.pick-grid { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pick { position: relative; cursor: pointer; }
.pick input { position: absolute; opacity: 0; width: 0; height: 0; }
.pick span {
  /* Same treatment as .brand-chip — these are the same component in two places,
     and one in mono with the other in sans read as an oversight. */
  display: inline-block; border: 1px solid var(--color-line); border-radius: 100px;
  padding: 0.6rem 1.05rem; color: var(--color-ink-muted);
  font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease,
              background-color var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
.pick:hover span { border-color: var(--color-ink-faint); color: var(--color-ink); }
.pick input:checked + span {
  border-color: transparent; color: var(--color-on-accent);
  background: var(--gradient-accent); box-shadow: var(--glow-accent);
}
/* keyboard users must still see focus, since the real input is visually hidden */
.pick input:focus-visible + span { outline: 2px solid var(--color-accent); outline-offset: 3px; }

/* ---------- service pages ---------- */
.does-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
@media (min-width: 760px) { .does-list { grid-template-columns: repeat(2, 1fr); column-gap: clamp(2rem, 4vw, 5rem); } }
.does-list li {
  padding: 1.15rem 0; border-top: 1px solid var(--color-line);
  font-size: var(--text-lead); color: var(--color-ink);
}
.does-list li:last-child { border-bottom: 1px solid var(--color-line); }
@media (min-width: 760px) { .does-list li:nth-last-child(2):nth-child(odd) { border-bottom: 1px solid var(--color-line); } }

.svc-links { display: grid; grid-template-columns: 1fr; border-top: 1px solid var(--color-line); }
@media (min-width: 700px) { .svc-links { grid-template-columns: repeat(2, 1fr); column-gap: clamp(2rem, 4vw, 5rem); } }
.svc-link {
  display: flex; align-items: baseline; gap: 1.25rem; text-decoration: none;
  padding: 1.25rem 0; border-bottom: 1px solid var(--color-line);
  font-family: "Bricolage Grotesque", sans-serif; font-weight: 700;
  font-size: var(--text-h3); letter-spacing: -0.025em; color: var(--color-ink);
  transition: color var(--duration-fast) ease, padding-left var(--duration-normal) var(--ease-out-expo);
}
.svc-link .svc-num { font-size: var(--text-mono); }
.svc-link:hover { color: var(--color-accent); padding-left: 0.75rem; }

/* service cards on the home page link through */
.svc > a.svc-more {
  margin-top: auto; padding-top: 1.1rem; text-decoration: none; align-self: flex-start;
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  letter-spacing: 0.16em; text-transform: uppercase; color: rgba(10,10,12,.55);
  transition: color var(--duration-fast) ease, gap var(--duration-fast) ease;
}
.svc:hover > a.svc-more { color: var(--color-accent-deep); gap: 0.75rem; }

/* ---------- required marker ---------- */
.req {
  /* was 9px — the smallest visible text on the site, on the one label a visitor
     must not miss. 10px keeps it below the 12px legend it annotates. */
  font-family: "JetBrains Mono", monospace; font-size: 0.625rem;
  letter-spacing: 0.16em; color: var(--color-accent); margin-left: 0.5rem;
}
.field-set.is-missing .pick span { border-color: rgba(224,123,103,.55); }

/* submit + booking sit side by side, wrapping on narrow screens */
.form-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

/* ---------- contact: the brief sits on a white panel ----------
   Black page, white block, purple accent — the three colours, used as a hard
   split rather than a tint. Everything inside has to invert. */
.form-panel {
  background: var(--color-ink); color: #0A0A0C;
  border-radius: 14px; padding: clamp(1.75rem, 3vw, 2.75rem);
}
.form-panel .field-set legend,
.form-panel .field label { color: rgba(10,10,12,.62); }
.form-panel .req { color: var(--color-accent-deep); }
.form-panel .field .opt { color: rgba(10,10,12,.42); }

/* What the visitor typed is full ink; only the placeholder hint is faded. */
.form-panel .field input,
.form-panel .field textarea,
.form-panel .field input:focus,
.form-panel .field textarea:focus { color: #0A0A0C; }
.form-panel .field input::placeholder,
.form-panel .field textarea::placeholder { color: rgba(10,10,12,.38); }
/* Browser autofill paints its own pale-blue box over the field; keep the panel's white ground and ink. */
.form-panel .field input:-webkit-autofill,
.form-panel .field input:-webkit-autofill:hover,
.form-panel .field input:-webkit-autofill:focus {
  -webkit-text-fill-color: #0A0A0C;
  box-shadow: 0 0 0 1000px var(--color-ink) inset;
  transition: background-color 9999s ease-out 0s;
}

.form-panel .pick span { border-color: rgba(10,10,12,.2); color: rgba(10,10,12,.68); }
.form-panel .pick:hover span { border-color: rgba(10,10,12,.45); color: #0A0A0C; }
.form-panel .pick input:checked + span {
  border-color: transparent; color: #FFFFFF;
  background: var(--gradient-accent); box-shadow: none;
}
.form-panel .btn-ghost { border-color: rgba(10,10,12,.24); color: #0A0A0C; }
.form-panel .btn-ghost:hover { border-color: var(--color-accent-deep); color: var(--color-accent-deep); }
.form-panel .btn-primary { box-shadow: none; }
.form-panel .form-note { color: rgba(10,10,12,.5); }
.form-panel .form-status[data-state="ok"]  { background: rgba(22,120,70,.1);  border-color: rgba(22,120,70,.3);  color: #14663F; }
.form-panel .form-status[data-state="err"] { background: rgba(170,45,25,.08); border-color: rgba(170,45,25,.3); color: #9A2B18; }
.form-panel .field-set.is-missing .pick span { border-color: rgba(170,45,25,.5); }

/* ---------- custom dropdown ----------
   A native <select> draws its option list with the OS, in the system font, so it never
   matched the rest of the form. This is a button + listbox so every part uses our type. */
.combo { position: relative; max-width: 26rem; margin-bottom: 2rem; }
.combo-label {
  display: block; margin-bottom: 0.5rem;
  font-family: "JetBrains Mono", monospace; font-size: var(--text-mono);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-ink-muted);
}
.combo-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; padding: 0.7rem 0; cursor: pointer;
  background: transparent; border: 0; border-bottom: 1px solid var(--color-line);
  font-family: "Instrument Sans", system-ui, sans-serif; font-size: var(--text-base);
  color: var(--color-ink); text-align: left;
  transition: border-color var(--duration-fast) ease;
}
.combo-trigger:hover { border-bottom-color: var(--color-ink-faint); }
.combo-trigger[aria-expanded="true"] { border-bottom-color: var(--color-accent); }
.combo-trigger svg { width: 12px; height: 8px; flex-shrink: 0; opacity: .55; transition: transform var(--duration-fast) ease; }
.combo-trigger[aria-expanded="true"] svg { transform: rotate(180deg); }
.combo-value { color: var(--color-ink); }
.combo.is-empty .combo-value { color: var(--color-ink-faint); }

.combo-list {
  position: absolute; top: calc(100% + 0.5rem); left: 0; right: 0; z-index: 20;
  margin: 0; padding: 0.35rem; list-style: none;
  background: var(--color-surface-2); border: 1px solid var(--color-line);
  border-radius: 10px; box-shadow: 0 24px 60px rgba(0,0,0,.55);
  max-height: 16rem; overflow-y: auto;
}
.combo-list[hidden] { display: none; }
.combo-option {
  padding: 0.65rem 0.8rem; border-radius: 6px; cursor: pointer;
  font-family: "Instrument Sans", system-ui, sans-serif; font-size: 0.9375rem;
  color: var(--color-ink-muted);
}
.combo-option:hover, .combo-option.is-active { background: rgba(255,255,255,.07); color: var(--color-ink); }
.combo-option[aria-selected="true"] {
  background: var(--gradient-accent); color: var(--color-on-accent);
}
.combo.is-missing .combo-trigger { border-bottom-color: rgba(224,123,103,.7); }

/* inside the white brief panel */
.form-panel .combo-label { color: rgba(10,10,12,.62); }
.form-panel .combo-trigger { color: #0A0A0C; border-bottom-color: rgba(10,10,12,.22); }
.form-panel .combo-trigger:hover { border-bottom-color: rgba(10,10,12,.45); }
.form-panel .combo-trigger[aria-expanded="true"] { border-bottom-color: var(--color-accent-deep); }
.form-panel .combo-value { color: #0A0A0C; }
.form-panel .combo.is-empty .combo-value { color: rgba(10,10,12,.4); }
.form-panel .combo-list { background: #FFFFFF; border-color: rgba(10,10,12,.14); box-shadow: 0 24px 60px rgba(0,0,0,.18); }
.form-panel .combo-option { color: rgba(10,10,12,.7); }
.form-panel .combo-option:hover, .form-panel .combo-option.is-active { background: rgba(10,10,12,.06); color: #0A0A0C; }
.form-panel .combo-option[aria-selected="true"] { background: var(--gradient-accent); color: #FFFFFF; }


/* centred call-to-action row */
.actions-center { display: flex; justify-content: center; }
.btn-wide { padding: 1.05rem 2.75rem; font-size: 0.9375rem; }

/* ---------- social ---------- */
.social { display: flex; gap: 0.6rem; margin-top: 2rem; }
.social a {
  display: grid; place-items: center; width: 42px; height: 42px;
  border: 1px solid var(--color-line); border-radius: 50%;
  color: var(--color-ink-muted);
  transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease,
              background-color var(--duration-fast) ease, transform var(--duration-fast) var(--ease-out-expo);
}
.social a svg { width: 19px; height: 19px; }
.social a:hover {
  color: var(--color-accent); border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  transform: translateY(-2px);
}

/* ---------- floating WhatsApp ---------- */
/* Bottom-left, mirroring the sound toggle on the right. WhatsApp's own green on
   request — the one place the three-colour palette gives way, because a visitor
   scans for this button by its colour. */
.wa-float {
  position: fixed; z-index: 120;
  left: calc(1.5rem + env(safe-area-inset-left, 0px));
  bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
  width: 52px; height: 52px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #25D366; color: #FFFFFF;
  box-shadow: 0 10px 26px rgba(0,0,0,.45), 0 4px 18px rgba(37,211,102,.32);
  transition: transform var(--duration-normal) var(--ease-out-expo),
              box-shadow var(--duration-normal) var(--ease-out-expo);
}
.wa-float svg { width: 27px; height: 27px; display: block; }
.wa-float:hover, .wa-float:focus-visible { transform: translateY(-3px) scale(1.04); }
@media (max-width: 560px) {
  .wa-float { width: 48px; height: 48px; left: calc(1rem + env(safe-area-inset-left, 0px)); bottom: calc(1rem + env(safe-area-inset-bottom, 0px)); }
  .wa-float svg { width: 25px; height: 25px; }
}
@media (prefers-reduced-motion: reduce) { .wa-float { transition: none; } .wa-float:hover { transform: none; } }

/* ---------- narrow-screen corrections ---------- */
/* The hero is bottom-anchored, which works on desktop where the reel fills the
   space above the type. On a phone the reel reads as a black void: 94vh of
   near-empty screen before anything is said. Shorter hero, and svh so the
   browser chrome does not push it taller than the visible viewport. */
@media (max-width: 700px) {
  .hero { min-height: min(76vh, 620px); }
  .hero > .wrap { padding-block: 2rem 1.4rem; }
  /* five items at a 1.75rem column gap left "3D & CGI" stranded on its own line.
     Tighter gap alone was not enough — the mono tracking had to come down too. */
  .hero-meta { gap: 0.4rem 0.8rem; }
  .hero-meta li { letter-spacing: 0.07em; }
  /* absolutely positioned at the bottom, with no room left for it once the meta
     list wraps */
  .hero-scroll { display: none; }
}
@supports (height: 100svh) {
  @media (max-width: 700px) { .hero { min-height: min(76svh, 620px); } }
}

/* Both floating controls sit in the bottom corners, so at the very end of the
   page they cover the last row of footer links. Reserve the space. */
@media (max-width: 700px) {
  .site-footer { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom, 0px)); }
}

/* ---------- about ---------- */
/* Kept dark and editorial on purpose: services, process and the brief form are
   already white blocks, and a fourth white field here would flatten the run. */
.about-grid {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(1.5rem, 2.6vw, 2.5rem);
  border-top: 1px solid var(--color-line); padding-top: clamp(1.75rem, 3vw, 2.75rem);
}
@media (min-width: 860px) { .about-grid { grid-template-columns: repeat(3, 1fr); column-gap: clamp(2rem, 4vw, 4.5rem); } }
.about-point h3 {
  font-size: var(--text-h3); margin: 0 0 0.6rem; color: var(--color-ink);
  padding-left: 0.9rem; border-left: 2px solid var(--color-accent);
}
.about-point p { margin: 0; color: rgba(255,255,255,.68); font-size: 1rem; line-height: 1.65; max-width: 40ch; }

/* ---------- faq ---------- */
/* <details>/<summary>: open/close works with JS off, and the disclosure state is
   announced without any ARIA of our own. */
.faq-list { display: grid; gap: clamp(0.6rem, 1vw, 0.85rem); }
.faq {
  background: var(--color-ink); color: #0A0A0C;
  border-radius: 16px; overflow: hidden;
  border: 1px solid transparent;
  transition: border-color var(--duration-normal) var(--ease-out-expo);
}
.faq[open] { border-color: var(--color-accent); }
.faq summary {
  display: flex; align-items: center; gap: 1rem; justify-content: space-between;
  padding: clamp(1rem, 1.6vw, 1.35rem) clamp(1.1rem, 1.8vw, 1.6rem);
  cursor: pointer; list-style: none;
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.15rem); font-weight: 600;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:focus-visible { outline: 2px solid var(--color-accent-deep); outline-offset: -3px; }
/* the +/- is drawn, not typed, so it never depends on a font */
.faq summary i {
  position: relative; flex: 0 0 20px; width: 20px; height: 20px;
  transition: transform var(--duration-normal) var(--ease-out-expo);
}
.faq summary i::before, .faq summary i::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--color-accent-deep); border-radius: 2px;
}
.faq summary i::before { width: 14px; height: 2px; }
.faq summary i::after  { width: 2px; height: 14px; transition: opacity var(--duration-fast) ease; }
.faq[open] summary i { transform: rotate(180deg); }
.faq[open] summary i::after { opacity: 0; }
/* The rule belongs on the wrapper, not on the paragraph: the paragraph is capped
   at a readable measure, so a border there stopped short of the card edge and
   read as a hanging line. */
.faq-a {
  padding: 0.9rem clamp(1.1rem, 1.8vw, 1.6rem) clamp(1.1rem, 1.7vw, 1.45rem);
  border-top: 1px solid rgba(10,10,12,.12);
  margin: 0 clamp(1.1rem, 1.8vw, 1.6rem);
  padding-left: 0; padding-right: 0;
}
.faq-a p { margin: 0; color: rgba(10,10,12,.68); font-size: 0.9375rem; line-height: 1.65; max-width: 68ch; }

/* ---------- anchors clear the sticky header ---------- */
html { scroll-padding-top: 88px; }

/* ---------- mobile menu ---------- */
/* Below 760px the nav links were simply hidden, so on a phone Services, Process,
   About and FAQ were reachable only from the footer. The toggle ships `hidden` and
   site.js reveals it, so with JS off the header is exactly what it was. */
.nav-toggle {
  display: none; position: relative; flex: 0 0 44px; width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--color-line); border-radius: 50%;
  background: transparent; color: var(--color-ink); cursor: pointer;
}
.nav-toggle span {
  position: absolute; left: 13px; right: 13px; height: 1.5px; border-radius: 1px;
  background: currentColor;
  transition: transform var(--duration-fast) ease, top var(--duration-fast) ease;
}
.nav-toggle span:nth-child(1) { top: 17px; }
.nav-toggle span:nth-child(2) { top: 25px; }
.menu-open .nav-toggle span:nth-child(1) { top: 21px; transform: rotate(45deg); }
.menu-open .nav-toggle span:nth-child(2) { top: 21px; transform: rotate(-45deg); }
@media (max-width: 760px) {
  .nav-toggle { display: inline-block; }
  .nav-toggle[hidden] { display: none; }
  .site-header.menu-open .nav-links {
    display: flex; flex-direction: column; gap: 0;
    position: absolute; left: 0; right: 0; top: 100%;
    padding: 0.25rem var(--gutter) 1rem;
    background: var(--color-bg); border-bottom: 1px solid var(--color-line);
  }
  .site-header.menu-open .nav-links a {
    display: flex; align-items: center; min-height: 52px;
    font-size: 1.125rem; color: var(--color-ink);
    border-bottom: 1px solid var(--color-line-soft);
  }
  .site-header.menu-open .nav-links a:last-child { border-bottom: 0; }
}
/* logo + CTA + toggle have to share 280px at a 320px viewport */
@media (max-width: 400px) {
  .site-nav { gap: 0.6rem; }
  .site-nav .btn-sm { padding: 0.55rem 0.95rem; }
}

/* ---------- touch targets ---------- */
/* Measured at 320-768px: footer links were 18px tall, the service-page back link
   22px, the sound toggle 29px and the tablet nav links 22px. Padding grows the hit
   area; an equal negative margin keeps the visible layout exactly where it was.
   0.5rem keeps adjacent footer rows' hit areas from overlapping (rows sit 38px apart). */
@media (hover: none), (max-width: 760px) {
  .site-footer nav a { display: inline-block; padding-block: 0.5rem; margin-block: -0.5rem; }
  .back-link { padding-block: 0.5rem; margin-block: -0.5rem; }
  .sound-toggle { min-width: 44px; min-height: 44px; justify-content: center; }
}
@media (min-width: 761px) {
  .nav-links a { padding-block: 0.75rem; }
}

/* ---------- site opening: intro shutter + hero entrance ---------- */
/* The hero used to run its reveal behind the intro, so when the intro faded the page
   was simply already there. Everything above the fold is now held in its pre-reveal
   state while `body.intro-open` is set and released the moment it is removed: the
   intro lifts away like a shutter, the reel settles out of a slight zoom, the headline
   rises line by line, and the header drops in last. */
.intro {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1150ms cubic-bezier(.77, 0, .18, 1), visibility 0s linear 1150ms;
}
.intro.is-done { opacity: 1; clip-path: inset(0 0 100% 0); }
.intro-stage { transition: transform 900ms var(--ease-out-expo), opacity 500ms ease; }
.intro.is-done .intro-stage { transform: translateY(-48px) scale(.97); opacity: 0; }

/* the video carries an inline parallax transform, so the zoom goes on its wrapper */
.hero-media { transition: transform 2600ms var(--ease-out-expo); }
body.intro-open .hero-media { transform: scale(1.14); }

body.intro-open .hero .reveal,
body.intro-open .hero .reveal.is-in { opacity: 0; filter: blur(10px); transform: translateY(28px); }
body.intro-open .hero .is-split .line-inner,
body.intro-open .hero .is-split.lines-in .line-inner {
  opacity: 0; filter: blur(14px); transform: translate3d(0, 105%, 0);
}
.hero h1 { --line-delay: 380ms; }
.js .hero .eyebrow.reveal { transition-delay: 240ms; }
.js .hero h1.reveal { transition-delay: 320ms; }
.js .hero .hero-body > .reveal { transition-delay: 720ms; }
.js .hero .hero-meta.reveal { transition-delay: 860ms; }

.site-header { transition: opacity 700ms ease 700ms, transform 900ms var(--ease-out-expo) 700ms; }
body.intro-open .site-header { opacity: 0; transform: translateY(-100%); }

/* Music plays during the intro, so its off switch has to be reachable there too: the
   sound control sits above the intro, and Skip moves to the top corner to make room. */
.sound-toggle { z-index: 520; }
.intro-skip { top: calc(1.5rem + env(safe-area-inset-top, 0px)); bottom: auto; }

/* ---------- phone: country code + number ---------- */
.phone-row { display: flex; align-items: flex-end; gap: 0.75rem; }
.phone-row > input { flex: 1 1 auto; min-width: 0; }
.combo.combo--code { flex: 0 0 5.75rem; max-width: none; margin: 0; }
.combo--code .combo-trigger { gap: 0.4rem; }
/* the list is wider than its trigger so country names fit; capped so it never pushes
   past a narrow screen */
.combo--code .combo-list { right: auto; width: min(17rem, calc(100vw - 3rem)); max-height: 18rem; }
.combo--code .combo-option { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.combo--code .combo-option .code {
  font-family: "JetBrains Mono", monospace; font-size: 0.8125rem;
  font-variant-numeric: tabular-nums; opacity: .7; white-space: nowrap;
}
.combo--code .combo-option[aria-selected="true"] .code { opacity: 1; }
/* The list anchors to the whole phone row rather than the narrow code button, so it is
   exactly as wide as the field it belongs to. With a fixed 17rem it ran to the very edge
   of a 320px screen. */
.phone-row { position: relative; }
.combo.combo--code { position: static; }
.combo--code .combo-list { left: 0; right: 0; width: auto; }
.sr-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.field.is-missing input,
.field.is-missing textarea,
.form-panel .field.is-missing input,
.form-panel .field.is-missing textarea { border-bottom-color: rgba(170,45,25,.6); }
