/* ═══════════════════════════════════════════════════════════════
   VASHKA — vashka.works
   Caps sans. Photographic wing device. Everything on the wing angle.
   ═══════════════════════════════════════════════════════════════ */

@import url("tokens.css");

/* ── Reset ─────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; background: none; border: 0; }
button { cursor: pointer; }
ul, ol { list-style: none; }
a { color: inherit; text-decoration: none; }

/* ── Base ──────────────────────────────────────────────────────── */
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: var(--text-body);
  font-weight: 400;
  line-height: 1.5;
  font-variation-settings: "wdth" 100;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

::selection { background: var(--color-marker); color: #fff; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* A themed element owns its own ground. [data-theme] only swaps tokens. */
[data-theme] { background: var(--bg); color: var(--fg); }

/* Grain. Keeps the flat fields from reading as pure vector. */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 9000;
  pointer-events: none; opacity: .04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

.skip-link {
  position: absolute; left: var(--gutter); top: -100px; z-index: 9999;
  background: var(--color-ink); color: var(--color-bone);
  padding: var(--space-3) var(--space-6);
  font-size: var(--text-small); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track-caps);
  transition: top var(--t-micro) var(--ease);
}
.skip-link:focus { top: var(--space-4); }

/* ── Type ──────────────────────────────────────────────────────── */
.mega, h1, h2 {
  font-family: var(--font-display);
  font-weight: 400;   /* Bebas ships one weight · asking for 700 fakes it */
  text-transform: uppercase;
  letter-spacing: var(--track-mega);
  line-height: var(--lh-mega);
  font-variation-settings: "wdth" 100;
  text-wrap: balance;
}
.mega { font-size: var(--text-mega); }
h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 {
  font-size: var(--text-h3);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  line-height: 1.15;
}

/* The single expressive move left in the type: one word steps out in
   oxblood. Never more than one per screen. */
.hot { color: var(--accent); }
[data-theme="dark"] .hot { color: var(--color-ox-lift); }

.lead {
  font-size: var(--text-lead);
  line-height: 1.5;
  color: var(--fg-soft);
  max-width: 48ch;
}
p { max-width: 62ch; }

.eyebrow, .micro {
  font-size: var(--text-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--fg-muted);
}
.eyebrow { display: flex; align-items: center; gap: var(--space-3); }
.muted { color: var(--fg-muted); }

/* The blue square. A marker, never a surface, never type. */
.mark-sq {
  display: inline-block;
  width: 10px; height: 10px;
  background: var(--color-marker);
  flex: none;
}
.mark-sq--abs { position: absolute; z-index: 3; }

/* ── Layout ────────────────────────────────────────────────────── */
.container {
  width: 100%; max-width: var(--container-max);
  margin-inline: auto; padding-inline: var(--gutter);
}
.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); }
.section { padding-block: var(--section-gap); position: relative; }
.section--tight { padding-block: clamp(3.5rem, 7vw, 7rem); }
.rule { height: 1px; background: var(--rule); border: 0; width: 100%; }

/* ── Buttons & links ───────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: 16px 28px;
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  position: relative; overflow: hidden; isolation: isolate;
  transition: color var(--t-std) var(--ease);
}
.btn__label, .btn__arrow { position: relative; z-index: 2; }
.btn__arrow { transition: transform var(--t-std) var(--ease); }
.btn:hover .btn__arrow { transform: translateX(5px); }
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  clip-path: polygon(0% 100%, 0% 100%, -60% 100%, -60% 100%);
  transition: clip-path 420ms var(--ease);
}
.btn:hover::before { clip-path: polygon(0% 0%, 160% 0%, 100% 100%, -60% 100%); }

.btn--primary { background: var(--color-ink); color: var(--color-bone); }
.btn--primary::before { background: var(--color-ox); }
.btn--secondary { color: var(--fg); box-shadow: inset 0 0 0 1px currentColor; }
.btn--secondary::before { background: var(--fg); }
.btn--secondary:hover { color: var(--bg); }
.btn--inverse { background: var(--color-bone); color: var(--color-ink); }
.btn--inverse::before { background: var(--color-ox); }
.btn--inverse:hover { color: var(--color-bone); }

.alink {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  position: relative; padding-bottom: 5px;
}
.alink::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px; width: 100%;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 260ms var(--ease);
}
.alink:hover::after { transform: scaleX(1); }
.alink > span:last-child { transition: transform var(--t-std) var(--ease); }
.alink:hover > span:last-child { transform: translateX(5px); }

/* ── Logo ──────────────────────────────────────────────────────── */
/* inherit, not var(--fg): the nav sits outside any themed section, so --fg
   resolves to the light-theme ink and the logo overrode the bar's own colour
   the moment .nav.on-dark flipped it. */
.logo { display: flex; align-items: center; gap: 12px; color: inherit; }
.logo__mark { width: 32px; height: auto; }
.logo__word { height: 17px; width: auto; }
.logo svg { fill: currentColor; }

/* ── Nav ───────────────────────────────────────────────────────── */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 500;
  padding-block: var(--space-6);
  transition: background var(--t-std) var(--ease), padding var(--t-std) var(--ease), color var(--t-std) var(--ease);
}
.nav::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--rule); transform: scaleX(0); transform-origin: left;
  transition: transform 450ms var(--ease);
}
.nav.is-stuck { background: var(--color-bone); padding-block: var(--space-4); }
.nav.is-stuck::after { transform: scaleX(1); }
.nav.on-dark { background: var(--color-black); color: var(--color-bone); }
/* The overlay is black, so the bar has to flip whatever the section under it
   happens to be · otherwise the logo and Close vanish into the menu. */
body.menu-open .nav { background: transparent; color: var(--color-bone); }
body.menu-open .nav::after { opacity: 0; }
.nav.on-dark::after { background: #2A2826; transform: scaleX(1); }
.nav.on-dark .btn--primary { background: var(--color-bone); color: var(--color-ink); }
.nav.on-dark .btn--primary::before { background: var(--color-ox); }
.nav.on-dark .btn--primary:hover { color: var(--color-bone); }

.nav__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-8); }
.nav__right { display: flex; align-items: center; gap: clamp(var(--space-4), 2vw, var(--space-8)); }
.nav__links { display: flex; align-items: center; gap: clamp(var(--space-4), 1.8vw, var(--space-8)); }
.nav__link {
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  position: relative; padding-block: 4px;
}
.nav__link::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 220ms var(--ease);
}
.nav__link:hover::after { transform: scaleX(1); }
.nav__toggle { display: none; }

/* ═══ THE WING DEVICE ═══════════════════════════════════════════
   Four shards, each carrying its own photograph, each on its own
   outward vector. Assembles on load, breathes, tracks the pointer. */
.wing { display: block; width: 100%; height: auto; overflow: visible; }
.wing__s { transform-box: fill-box; transform-origin: center; }
/* Blend the specular locally so it lights the shard, not the page. */
.wing__clip { isolation: isolate; }

/* The apertures. These are the clipPath polygons in the sprite, shared by
   every instance of the device, and they are the only thing the pointer
   moves. The image plane behind them never shifts, which is what sells the
   depth: you are looking through moving holes at a world that stays put. */
#ap-ul, #ap-ur, #ap-ll, #ap-lr,
.wing__shade, .wing__edge {
  transform-box: view-box;
  transform-origin: center;
  will-change: transform;
}

/* Light source. Tracks the pointer across the facets, as if a real lamp
   moved over lacquered material. */
.wing__spec { mix-blend-mode: screen; pointer-events: none; }

/* One shard carries a Lottie instead of a still. It sits above the image
   layers but below the facets and the specular, so the shard's shading and
   light still read across it and it stays part of the object. */
.wing__fo { overflow: visible; pointer-events: none; }
.wing__fo > div { width: 100%; height: 100%; }
.wing__lottie { width: 100%; height: 100%; }
.wing__lottie svg { display: block; width: 100% !important; height: 100% !important; }

/* Entrance: each shard arrives along its own vector. */
.wing__s {
  opacity: 0;
  animation: shardIn 1000ms var(--ease-out) forwards;
}
.wing__s--ul { --ox: -11px; --oy: -9px; --or: -9deg; animation-delay: 120ms; }
.wing__s--ur { --ox:  11px; --oy: -9px; --or:  9deg; animation-delay: 260ms; }
.wing__s--ll { --ox: -10px; --oy:  8px; --or:  8deg; animation-delay: 400ms; }
.wing__s--lr { --ox:  10px; --oy:  9px; --or: -8deg; animation-delay: 540ms; }
@keyframes shardIn {
  from { opacity: 0; transform: translate(var(--ox), var(--oy)) rotate(var(--or)) scale(.94); }
  to   { opacity: 1; transform: none; }
}

/* Reference layers. The incoming image wipes in on the wing angle, inside
   the shard that is already clipping it. Same diagonal as every other
   reveal on the site. */
.wing__img, .wing__fo {
  clip-path: polygon(0% 0%, 0% 0%, -55% 100%, -55% 100%);
  opacity: 0;
  transition: clip-path 900ms var(--ease), opacity 300ms linear;
}
.wing__img.is-in, .wing__fo.is-in { clip-path: polygon(0% 0%, 155% 0%, 100% 100%, -55% 100%); opacity: 1; }
/* No transition when a layer is being reset underneath the visible one. */
.wing__img.is-reset, .wing__fo.is-reset { transition: none; }

.wing__facet { mix-blend-mode: multiply; }
.wing__facet--lift { mix-blend-mode: screen; }
.wing__edge { fill: none; stroke: var(--color-bone); stroke-width: .18; opacity: .22; pointer-events: none; }

.br-sm { display: none; }
/* A hint, not a heading · it should be findable, not announced. */
.showcase__hint {
  display: none; margin-top: var(--space-8);
  font-size: .6875rem; font-weight: 400; letter-spacing: .06em;
  color: color-mix(in srgb, var(--fg) 40%, transparent);
}

/* ── Hero ──────────────────────────────────────────────────────────
   A full-bleed field of marks, each one a window onto a reference. The
   logo stops being an object placed on the page and becomes the page's
   own grain. Dark, because a dense wall of imagery cannot carry type on
   a light ground; the rest of the site stays on the cool light one, and
   the switch makes the first scroll land harder. */
.hero {
  /* A card, not a full-bleed band: inset on every side with the page ground
     showing around it. The radius earns its keep by cutting the mosaic at
     the corners rather than trimming empty space. */
  margin: calc(var(--nav-h) + var(--card-inset)) var(--card-inset) var(--card-inset);
  border-radius: var(--card-radius);
  /* clip-path, not just overflow:hidden. The fragments carry 3D transforms,
     which promotes them to their own compositing layers, and a promoted
     descendant is NOT clipped by an ancestor's border-radius · it only gets
     the rectangular bounds, so any corner a shard reached went square. */
  clip-path: inset(0 round var(--card-radius));
  box-shadow: inset 0 0 0 1px var(--rule);
  min-height: calc(100svh - var(--nav-h) - var(--card-inset) * 2);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: clamp(2rem, 4.5vh, 3.5rem);
  padding-bottom: var(--space-8);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  /* Light on purpose. A near-black hero behind a wall of work is what every
     other studio ships; the same mosaic on a pale cool ground reads as a
     gallery wall instead of a showreel. */
  /* Black card on the light page. Not the same thing as a black hero: the
     ground around it stays pale, so the card reads as an object rather than
     as the site opening in darkness. */
  background: var(--color-black);
}

/* The mosaic keeps the drawing's own proportions. Stretching it to the
   hero's aspect would shear every shard, so the field is a fixed-ratio box
   anchored right · which also leaves the left side clear for the copy. */
.field {
  position: absolute;
  top: 50%; right: -12%;
  transform: translateY(-50%);
  height: 128%;
  aspect-ratio: 403.97 / 338.95;
  width: auto;
  z-index: 0;
}

/* The shattered plane. Fragments come from assets/shards.json, generated by
   make_shards.py: a Voronoi partition of jittered seeds, each piece pulled in
   toward its own centroid so the edges line up with a consistent gap. A grid
   of the logo's four shapes was the wrong model · repeated at any density it
   reads as a pattern, not as a broken surface. */
/* The one moving layer in the hero. */
.field__wall { position: absolute; inset: 0; transform-origin: 50% 50%; }

.frag {
  position: absolute;
  background-color: color-mix(in srgb, var(--bg) 70%, #000);
  background-image: var(--src, none);
  /* cover, and the references keep their NATIVE aspect · a landscape photo
     in a portrait shard overflows sideways on its own, which is what gives
     background-position something to slide against. Squaring and padding
     the sources instead left black bars inside the shard. */
  background-size: cover;
  background-position: var(--bpos, center);
  /* The SHARD travels, not the picture inside it · and it travels RADIALLY,
     away from the cluster's centre by an amount proportional to how far out
     it already sits (--ux/--uy, set once in JS). The whole mosaic therefore
     scales about one point: gaps only ever widen, so no two pieces can
     close on each other and overlap. --k is the single driver, never
     negative, so contraction past the resting geometry cannot happen.

     Static. The shard never animates: WebKit repaints a clip-path'd element
     on every transform change, so sixteen of them moving each frame is a
     repaint storm. The motion lives on the wall, one layer above. */
  transform: scale(var(--sc, 1));
  /* No transition on the transform: --k is rewritten every frame, so a CSS
     transition would restart on each one and never arrive. The easing lives
     in the rAF loop instead. */
  transition: opacity 620ms var(--ease);
  opacity: 0;
  transition-delay: calc(var(--i) * 14ms), 0ms;
}
.field.is-in .frag.is-ready { opacity: var(--o, .9); }

/* A shard with no reference yet. Flat orange on purpose · it should be
   obvious how many are still waiting to be filled. */
.frag--empty {
  background-color: var(--color-ox);
  background-image: none;
}

.field__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Built from var(--bg) so it fades the mosaic into whatever the hero's
     ground happens to be, rather than hard-coding one colour. */
  background:
    linear-gradient(100deg,
      color-mix(in srgb, var(--bg) 96%, transparent) 0%,
      color-mix(in srgb, var(--bg) 80%, transparent) 34%,
      color-mix(in srgb, var(--bg) 22%, transparent) 60%,
      transparent 78%),
    radial-gradient(120% 100% at 72% 45%,
      transparent 58%,
      color-mix(in srgb, var(--bg) 26%, transparent) 100%);
}



.hero__inner { position: relative; z-index: 2; width: 100%; }

.hero__sq { display: block; width: 10px; height: 10px; margin: 0 0 var(--space-6); }
.hero__eyebrow {
  display: grid; gap: 2px; justify-items: start;
  margin-bottom: clamp(var(--space-6), 4vh, var(--space-12));
}
.hero__eyebrow .eyebrow { justify-content: flex-start; }

.hero__title { position: relative; margin-left: -0.02em; max-width: 15ch; }
.hero__title .line { display: block; }
.hero__title .line--2 { padding-left: clamp(0px, 2.5vw, 3rem); }
.hero__title .wipe > * { --wipe-x: 1.93em; }
/* Solid ink, no difference blend. The blend existed to keep the headline
   legible where the device crossed it; the mosaic is anchored right now, and
   on orange white fails contrast anyway (3.2:1 against 6.1:1 for ink). */
.hero__title { color: var(--fg); }
.hero__title .hot { color: var(--accent); }   /* ox-lift on dark · 7.75:1 */

.hero__lead {
  margin: clamp(var(--space-6), 4vh, var(--space-12)) 0 0;
  /* Held a step back from the headline · it is a supporting line, not a
     second announcement. */
  color: var(--fg-muted);
  font-size: var(--text-body);
  max-width: 56ch;
  /* Stops the last line becoming a single orphaned word · at 44ch this
     broke as "…and sell like / it." */
  text-wrap: pretty;
}
.hero__cta {
  margin-top: var(--space-8);
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: flex-start; gap: var(--space-6);
}

.hero__foot {
  position: absolute; left: 0; right: 0; bottom: var(--space-6); z-index: 2;
  padding-inline: var(--gutter);
}
.hero__foot-in {
  max-width: var(--container-max); margin-inline: auto;
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--space-6); flex-wrap: wrap;
}
.scroll-cue { display: inline-flex; align-items: center; gap: var(--space-3); }
.scroll-cue__bar { display: block; width: 44px; height: 1px; background: var(--rule); position: relative; overflow: hidden; }
.scroll-cue__bar::after {
  content: ""; position: absolute; inset: 0; background: var(--color-bone);
  animation: cue 2.6s var(--ease) infinite;
}
@keyframes cue {
  0%   { transform: scaleX(0); transform-origin: left; }
  45%  { transform: scaleX(1); transform-origin: left; }
  55%  { transform: scaleX(1); transform-origin: right; }
  100% { transform: scaleX(0); transform-origin: right; }
}

/* ── Wedge band — a panel cut on the wing angle ────────────────── */
.wedge {
  position: relative;
  clip-path: polygon(0 var(--wedge), 100% 0, 100% calc(100% - var(--wedge)), 0 100%);
  padding-block: calc(var(--section-gap) + var(--wedge));
  margin-block: calc(var(--wedge) * -0.5);
}

/* ── Manifesto panel (black, wing centre stage) ────────────────── */
.manifesto__grid { align-items: center; }
.manifesto__copy {
  grid-column: 1 / 7;
  display: grid; align-content: start;
  gap: clamp(var(--space-6), 2.4vw, var(--space-12));
}
.manifesto__wing { grid-column: 7 / 13; }
.manifesto__wing .wing { width: 100%; }
.manifesto__list { display: grid; gap: var(--space-2); }
.manifesto__list li { font-size: var(--text-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .16em; color: var(--fg-muted); }

/* ── Section header ────────────────────────────────────────────── */
.shead { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); margin-bottom: clamp(var(--space-12), 5vw, var(--space-24)); }
/* The intro starts on the same column as the service descriptions below it,
   so the whole section hangs off one vertical line. */
.shead__l { grid-column: 1 / 6; }
.shead__r { grid-column: 6 / 13; align-self: end; }
.shead h2, .why__head h2 { margin-top: var(--space-6); }

/* ── Service rows ──────────────────────────────────────────────── */
/* Equal-height rows: the tallest sets the measure, so the band and the
   rule spacing read as one system rather than four different blocks. */
.rows { display: grid; grid-auto-rows: 1fr; border-top: 1px solid var(--rule); }
.row {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap);
  padding-block: clamp(var(--space-8), 3vw, var(--space-12));
  border-bottom: 1px solid var(--rule);
  position: relative; align-items: start;
}

.row > * { position: relative; z-index: 1; transition: color var(--t-std) var(--ease); }
.row:hover .row__title { color: var(--color-bone); }

/* Each service shows a real piece of work instead of describing itself.
   The shard shape is the logo's own geometry, so the section speaks the same
   language as the hero. clip-path stays on this element · never on an
   ancestor, which is what stops clipped children painting. */
.row__head { display: contents; }
.row__peek {
  position: absolute;
  top: -1px; bottom: 0;
  /* Bleeds out through the gutter so the title sits inside the image with
     room to its left, instead of starting on the band's own seam. */
  left: calc(-1 * var(--gutter));
  width: calc(clamp(300px, 46%, 620px) + var(--gutter));
  /* Scrim rides on the same layer as the photo so both wipe in together
     and the title never sits on raw imagery. */
  background-image:
    linear-gradient(90deg,
      color-mix(in srgb, var(--peek-scrim) 50%, transparent) 0%,
      color-mix(in srgb, var(--peek-scrim) 50%, transparent) 100%),
    var(--src);
  background-size: cover, cover;
  background-position: center, var(--peekpos, center);
  /* Wipes open on the wing angle · the right edge keeps that same slant. */
  clip-path: polygon(0% 0%, 0% 0%, -7% 100%, -7% 100%);
  transition: clip-path 640ms var(--ease);
  pointer-events: none;
}
/* The motion row moves instead of sitting still · the client's own gold
   under the same scrim, so it belongs to the set rather than shouting. */
.row__peek--anim { background: var(--bg); }
.row__peek--anim::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--peek-scrim) 50%, transparent) 0%,
    color-mix(in srgb, var(--peek-scrim) 50%, transparent) 100%);
}
.row__anim, .row__anim svg { position: absolute; inset: 0; width: 100% !important; height: 100% !important; }

.row:hover .row__peek.is-ready { clip-path: polygon(0% 0%, 107% 0%, 100% 100%, -7% 100%); }
.row__title { position: relative; z-index: 1; grid-column: 1 / 6; padding-top: .15em; justify-self: start; font-size: clamp(1.5rem, 2.4vw, 2.25rem); }
.row__body { grid-column: 6 / 12; color: var(--fg-soft); text-wrap: pretty; }
.row__sub { margin-top: var(--space-4); font-size: var(--text-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .12em; color: var(--fg-muted); }
.row__arrow { grid-column: 12 / 13; justify-self: end; padding-top: .1em; opacity: 0; transform: translateX(-10px); transition: opacity var(--t-std) var(--ease), transform var(--t-std) var(--ease); }
.row:hover .row__arrow { opacity: 1; transform: translateX(0); }

/* ── Proof · the numbers ─────────────────────────────────────────
   The figures are the argument here, so they are set like display type and
   stepped rather than lined up: four equal columns on one baseline read as a
   table, which is what made the block feel like a spec sheet. Behind them the
   mark itself, at scale and in its own textures, bleeding off the right. */
.proofsec { isolation: isolate; }
.proofsec__bg { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.proofsec__mark {
  --mw: clamp(420px, 54vw, 940px);
  position: absolute; right: -13%;
  top: calc(50% - var(--mw) * 0.428);
  width: var(--mw);
  opacity: .26;
}
/* No edge highlight at this scale: on the small device it reads as a bevel,
   blown up to a metre wide it reads as an outline drawn round the logo. */
.proofsec__mark .wing__edge { display: none; }
.proofsec .container { position: relative; z-index: 1; }

.proof { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); }
.proof__item { grid-column: span 3; }
/* Stepped on the same irregular rhythm as the mosaic. */
.proof__item:nth-child(2) { margin-top: clamp(1rem, 3.4vw, 3.5rem); }
.proof__item:nth-child(3) { margin-top: clamp(.35rem, 1.2vw, 1.25rem); }
.proof__item:nth-child(4) { margin-top: clamp(1.6rem, 4.8vw, 5rem); }

/* The rule draws left to right with an orange tick leading it. */
.proof__rule {
  display: block; height: 2px;
  background: linear-gradient(90deg,
    var(--accent) 0 20%,
    color-mix(in srgb, var(--fg) 24%, transparent) 20% 100%);
}
.proof__n {
  display: block; margin-top: var(--space-5);
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(3.4rem, 6.6vw, 6.75rem);
  line-height: .82; letter-spacing: .004em;
  font-variant-numeric: tabular-nums;
}
.proof__arw {
  font-family: var(--font); font-weight: 600;
  font-size: .4em; vertical-align: .38em;
  margin-inline: .1em; color: var(--accent);
}
.proof__u {
  display: block; margin-top: var(--space-3);
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em; color: var(--accent);
}
.proof__item p {
  margin-top: var(--space-5);
  font-size: var(--text-small); line-height: 1.55;
  color: var(--fg-muted); max-width: 30ch;
}
@media (max-width: 1180px) {
  /* Sized by width and centred once the copy goes full-bleed underneath. */
  .field { top: 0; right: auto; left: 50%; transform: translateX(-50%); width: 132%; height: auto; }

  /* The wide veil runs left-to-right because the copy sits in the left
     third. Once the column goes full-width that same gradient buries the
     whole wall, so darken from the bottom instead and leave the top open. */
  .field__veil {
    background:
      linear-gradient(to top,
        color-mix(in srgb, var(--bg) 97%, transparent) 0%,
        color-mix(in srgb, var(--bg) 88%, transparent) 46%,
        color-mix(in srgb, var(--bg) 26%, transparent) 74%,
        transparent 92%),
      radial-gradient(120% 80% at 50% 30%,
        transparent 40%,
        color-mix(in srgb, var(--bg) 45%, transparent) 100%);
  }
 .proof__item { grid-column: span 6; }
  .proof__item:nth-child(2), .proof__item:nth-child(3) { margin-top: 0; }
  .proof__item:nth-child(3), .proof__item:nth-child(4) { margin-top: var(--space-12); }
  .proofsec__mark { opacity: .14; right: -32%; } }
@media (max-width: 640px)  {
  .proof__item { grid-column: span 12; }
  .proof__item:nth-child(n+2) { margin-top: var(--space-10); } }

/* ── Value props ─────────────────────────────────────────────────
   Three equal columns made three equal claims. Stacked against a heading
   that holds its place while they pass, the argument reads in order and the
   section stops looking like a feature grid. */
.why { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); }
.why__head {
  grid-column: 1 / 5;
  position: sticky; top: calc(var(--nav-h) + var(--space-10));
  align-self: start;
}
.props { grid-column: 6 / 13; }

/* VÁŠA · VÁŽKA. The mark's namesake, hanging under the claim it belongs to.
   Two elements, two jobs: the wrapper carries the scroll drift, the image its
   own idle hover. Both animate `translate`, so they cannot share a box. */
.why__fly { width: min(100%, 380px); margin-top: clamp(var(--space-16), 11vw, var(--space-32)); }
.why__fly img {
  display: block; width: 100%; height: auto;
  filter:
    drop-shadow(0 34px 38px color-mix(in srgb, var(--color-navy) 15%, transparent))
    drop-shadow(0 8px 12px color-mix(in srgb, var(--color-navy) 9%, transparent));
}
.why__fly.is-in { transition-duration: 900ms; }
@media (prefers-reduced-motion: no-preference) {
  .why__fly { transform: translateY(calc(16px - var(--fly, .5) * 38px)) rotate(calc((var(--fly, .5) - .5) * 3deg)); }
  .why__fly img { animation: flyHover 7.5s var(--ease) infinite alternate; }
}
@keyframes flyHover { from { transform: translateY(-5px); } to { transform: translateY(9px); } }

.prop {
  position: relative;
  border-top: 1px solid var(--rule);
  padding-top: var(--space-8);
}
.prop + .prop { margin-top: clamp(var(--space-10), 5vw, var(--space-20)); }
/* The accent draws across the rule as the claim arrives. */
.prop::before {
  content: ""; position: absolute; top: -1px; left: 0;
  width: clamp(48px, 6vw, 88px); height: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 700ms var(--ease); transition-delay: var(--d, 0ms);
}
.prop.is-in::before { transform: scaleX(1); }
.prop__t {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.9rem, 3.1vw, 3.1rem);
  line-height: .95; letter-spacing: .004em;
  text-transform: none;
  margin-bottom: var(--space-6);
  max-width: 18ch;
}
.prop p { color: var(--fg-soft); max-width: 52ch; font-size: var(--text-body); line-height: 1.6; }

/* ── Marquee ─────────────────────────────────────────────────────
   A list of rooms we have worked in, so each entry is one room. The glyph
   carries the sector and the logo shard does the separating, which keeps the
   line reading as a sentence instead of a row of tags. */
.marquee {
  overflow: hidden; border-block: 1px solid var(--rule);
  padding-block: clamp(var(--space-3), 1.2vw, var(--space-6));
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.marquee + .marquee { border-top: 0; }
.marquee__track { display: flex; width: max-content; animation: slide var(--dur, 68s) linear infinite; will-change: transform; }
.marquee--rev .marquee__track { animation-direction: reverse; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__group { display: flex; flex-shrink: 0; }
.marquee__word {
  display: inline-flex; align-items: center; gap: .42em;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.6rem, 3vw, 3rem);
  letter-spacing: .01em;
  color: var(--fg-muted); white-space: nowrap;
  padding-inline-end: 1.15em;
  transition: color 260ms var(--ease);
}
.marquee__word:hover { color: var(--fg); }
.marquee__i {
  width: .58em; height: .58em; flex: none;
  color: inherit; opacity: .72;
  translate: 0 -.04em;
  transition: color 260ms var(--ease);
}
.marquee__word:hover .marquee__i { color: var(--accent); opacity: 1; }
/* The track is doubled in JS, so half a turn is one seamless loop. */
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }



/* ── Work grid ─────────────────────────────────────────────────── */
/* ── Selected work ───────────────────────────────────────────────
   One frame, one index. The grid of captioned tiles it replaces was a blog
   listing by construction: image, caption, repeat. Here the project names are
   the layout, and the picture changes under them on the wing angle · the same
   diagonal every other reveal on the site uses. */
.showcase {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
}
/* The frame sets the row height; the index stretches to match it and shares
   that height out between its rows. Two columns that start together and end
   together is the whole difference between a layout and a pile. */
.showcase__frame {
  grid-column: 1 / 8; grid-row: 1; align-self: start;
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  background: var(--color-black);
}
/* Two layers: the incoming picture wipes in over the one already there, so
   the change is a move rather than a swap. */
.showcase__img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  clip-path: polygon(0% 0%, 0% 0%, -55% 100%, -55% 100%);
}
.showcase__img.is-in {
  clip-path: polygon(0% 0%, 155% 0%, 100% 100%, -55% 100%);
  transition: clip-path 720ms var(--ease);
}
.showcase__img.is-reset { transition: none; }

.showcase__meta { grid-column: 1 / 8; grid-row: 2; margin-top: var(--space-5); display: flex; gap: var(--space-8); align-items: baseline; flex-wrap: wrap; }
.showcase__line { font-size: var(--text-body); color: var(--fg-soft); max-width: 40ch; }
.showcase__scope { color: var(--fg-muted); }

/* The section intro already says the archive is bigger than this. The note
   that used to sit here said it a second time, so only the action is left. */
.showcase__ask { margin-top: clamp(var(--space-10), 5vw, var(--space-16)); }

.showcase__list {
  grid-column: 8 / 13; grid-row: 1;
  align-self: stretch;
  display: flex; flex-direction: column;
}
.showcase__item {
  flex: 1 1 0; min-height: 0;
  display: flex; width: 100%; gap: var(--space-4);
  justify-content: space-between; align-items: center; text-align: left;
  border-bottom: 1px solid var(--rule);
  color: var(--fg-muted); cursor: pointer;
  transition: color 260ms var(--ease);
}
.showcase__item:first-child { border-top: 1px solid var(--rule); }
.showcase__name {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.4rem, 2.4vw, 2.5rem); line-height: 1;
  letter-spacing: .006em;
}
.showcase__sector { flex: none; color: inherit; opacity: .75; }
.showcase__item:hover, .showcase__item:focus-visible { color: var(--fg); }
.showcase__item.is-active { color: var(--fg); }
.showcase__item.is-active .showcase__sector { color: var(--accent); opacity: 1; }

/* ── Steps ───────────────────────────────────────────────────────
   A sequence, so the numbering stays: it is the one place on the page where
   the order carries meaning. Each step lights its numeral as it passes the
   reader, which turns the list into a progress bar without adding one. */
.steps { border-top: 1px solid var(--rule); }
.step {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap);
  padding-block: clamp(var(--space-6), 2.6vw, var(--space-12));
  border-bottom: 1px solid var(--rule); align-items: baseline;
}
.step__n {
  grid-column: 1 / 2;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.5rem, 2.4vw, 2.5rem); line-height: 1;
  color: var(--fg-muted);
  transition: color 420ms var(--ease);
}
.step.is-live .step__n { color: var(--accent); }
.step__t {
  grid-column: 2 / 6;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.5rem, 2.6vw, 2.5rem); line-height: 1;
  letter-spacing: .004em; text-transform: none;
}
.step__c { grid-column: 6 / 12; }
.step p { color: var(--fg-soft); font-size: var(--text-small); line-height: 1.6; max-width: 56ch; }

/* ── What to expect ──────────────────────────────────────────────
   Four commitments, each labelled by what it is about. A bulleted list read
   as small print; this reads as terms. */
.expect {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin-top: clamp(var(--space-12), 6vw, var(--space-20));
}
.expect > .eyebrow { grid-column: 1 / 3; }
.expect__list {
  grid-column: 3 / 13;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--space-6), 2.6vw, var(--space-10)) var(--grid-gap);
}
.expect__list li {
  border-top: 2px solid var(--fg);
  padding-top: var(--space-4);
  font-size: var(--text-small); line-height: 1.55;
  color: var(--fg-soft); text-wrap: pretty;
}
.expect__list span {
  display: block; margin-bottom: var(--space-2);
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em; color: var(--accent);
}

/* ── Close ───────────────────────────────────────────────────────
   The page used to end three times: an orange call to action, a dark footer,
   then a signature bar. One dark close, with the mark bleeding in from the
   left as a mirror of the track record, and the signature still has the last
   word. */
.close {
  position: relative; isolation: isolate; overflow: hidden;
  clip-path: polygon(0 var(--wedge), 100% 0, 100% 100%, 0 100%);
  margin-top: calc(var(--wedge) * -0.5);
  padding-block: calc(var(--section-gap) + var(--wedge)) var(--space-12);
}
.close__bg { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.close__mark {
  --mw: clamp(360px, 48vw, 800px);
  position: absolute; left: -15%; bottom: calc(var(--mw) * -0.34);
  width: var(--mw); opacity: .2;
}
.close__mark .wing__edge { display: none; }
.close__in, .close__foot { position: relative; z-index: 1; }

.close__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.9rem, 8.6vw, 8rem);
  line-height: .86; letter-spacing: .004em;
  margin-top: var(--space-6);
}
.close__title > span { display: block; }
.close__lead {
  margin-top: clamp(var(--space-8), 3vw, var(--space-12));
  font-size: var(--text-body); color: var(--fg-soft);
  max-width: 46ch; text-wrap: pretty;
}
/* The address is the button. Nothing else on this page is set this large in
   the accent, so there is never a question about what to do here. */
.close__mail {
  display: inline-flex; align-items: baseline; gap: .22em;
  margin-top: clamp(var(--space-12), 6vw, var(--space-20));
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.75rem, 5vw, 4.25rem);
  line-height: 1; letter-spacing: .006em;
  color: var(--accent); position: relative; padding-bottom: .08em;
}
.close__mail::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 2px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform 320ms var(--ease);
}
.close__mail:hover::after { transform: scaleX(1); }
.close__arrow { transition: transform var(--t-std) var(--ease); }
.close__mail:hover .close__arrow { transform: translateX(.16em); }

.close__facts {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--grid-gap);
  margin-top: clamp(var(--space-12), 6vw, var(--space-20));
  max-width: 58rem;
}
.close__facts li {
  border-top: 1px solid var(--rule); padding-top: var(--space-4);
  font-size: var(--text-small); color: var(--fg-soft);
}
.close__facts span {
  display: block; margin-bottom: var(--space-2);
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em; color: var(--fg-muted);
}

.close__foot {
  margin-top: clamp(var(--space-20), 11vw, var(--space-32));
  padding-top: var(--space-8); border-top: 1px solid var(--rule);
}
.close__grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); }
.close__brand { grid-column: span 8; }
.close__brand .logo { margin-bottom: var(--space-4); }
.close__col { grid-column: span 4; }
.close__col h4 {
  font-size: var(--text-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--fg-muted); margin-bottom: var(--space-5);
}
.close__col li + li { margin-top: var(--space-3); }
.close__col a { font-size: var(--text-small); position: relative; }
.close__col a::after {
  content: ""; position: absolute; left: 0; bottom: -2px; width: 100%; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 220ms var(--ease);
}
.close__col a:hover::after { transform: scaleX(1); }
.close__bottom {
  margin-top: clamp(var(--space-12), 6vw, var(--space-16));
  display: flex; justify-content: space-between; gap: var(--space-6); flex-wrap: wrap;
}

/* The signature bar. Last thing on the page. */
.sig {
  background: var(--color-ox-deep); color: var(--color-bone);
  padding-block: var(--space-6); position: relative;
}
.sig__in { display: flex; justify-content: center; align-items: center; }
.sig span { font-size: var(--text-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .5em; text-indent: .5em; }
.sig .mark-sq { position: absolute; right: var(--gutter); top: 50%; margin-top: -5px; }

/* ── Reveals ───────────────────────────────────────────────────── */
.wipe { display: block; }
.wipe > * {
  display: block;
  padding-block: .18em .14em; margin-block: -.18em -.14em;
  /* Every coordinate is calc(percentage ± length), including the ones that
     are simply zero. Safari interpolates two polygons point by point and
     needs the two calc trees to match · a bare `0` against
     calc(100% + 2.1em) is where the sweep stalled and then jumped. */
  clip-path: polygon(
    calc(0% + 0px) calc(0% + 0px),
    calc(0% + 0px) calc(0% + 0px),
    calc(0% - var(--wipe-x)) calc(100% + 0px),
    calc(0% - var(--wipe-x)) calc(100% + 0px));
  transition: clip-path var(--t-wipe) var(--ease);
  transition-delay: var(--d, 0ms);
  /* No will-change here. It promotes the line to its own layer, and WebKit
     animates clip-path on a promoted layer by rasterising at the start and
     the end · which is the stall-then-snap. The sweep is short enough that
     it does not need the hint. */
}
.wipe.is-in > * {
  clip-path: polygon(
    calc(0% + 0px) calc(0% + 0px),
    calc(100% + var(--wipe-x)) calc(0% + 0px),
    calc(100% + 0px) calc(100% + 0px),
    calc(0% - var(--wipe-x)) calc(100% + 0px));
}

.rise { opacity: 0; transform: translateY(14px); transition: opacity 640ms var(--ease), transform 640ms var(--ease); transition-delay: var(--d, 0ms); }
.rise.is-in { opacity: 1; transform: none; }
.draw { transform: scaleX(0); transform-origin: left; transition: transform 700ms var(--ease); transition-delay: var(--d, 0ms); }
.draw.is-in { transform: scaleX(1); }

/* ── Intro ─────────────────────────────────────────────────────── */
.intro { position: fixed; inset: 0; z-index: 8000; background: var(--color-black); display: grid; place-items: center; }
.intro__in { width: clamp(160px, 22vw, 280px); }
.intro.is-out { animation: introOut 820ms var(--ease) forwards; pointer-events: none; }
@keyframes introOut {
  from { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }
  to   { clip-path: polygon(0% -70%, 100% -130%, 100% 0%, 0% 70%); }
}

/* ── Responsive ────────────────────────────────────────────────── */
@media (max-width: 1180px) {
  /* Sized by width and centred once the copy goes full-bleed underneath. */
  .field { top: 0; right: auto; left: 50%; transform: translateX(-50%); width: 132%; height: auto; }

  /* The wide veil runs left-to-right because the copy sits in the left
     third. Once the column goes full-width that same gradient buries the
     whole wall, so darken from the bottom instead and leave the top open. */
  .field__veil {
    background:
      linear-gradient(to top,
        color-mix(in srgb, var(--bg) 97%, transparent) 0%,
        color-mix(in srgb, var(--bg) 88%, transparent) 46%,
        color-mix(in srgb, var(--bg) 26%, transparent) 74%,
        transparent 92%),
      radial-gradient(120% 80% at 50% 30%,
        transparent 40%,
        color-mix(in srgb, var(--bg) 45%, transparent) 100%);
  }

  .hero__title { max-width: none; }
  .manifesto__copy { grid-column: 1 / 13; }
  .manifesto__wing { grid-column: 1 / 13; margin-top: var(--space-12); }
  .manifesto__wing .wing { width: min(70%, 460px); }
  .shead__l, .shead__r { grid-column: 1 / 13; }
  .shead__r { margin-top: var(--space-6); }
  .prop { grid-column: span 6; }
}

@media (max-width: 860px) {
  /* ── Phone ────────────────────────────────────────────────────────
     Not the desktop shrunk. Different composition, different type scale,
     and states that a finger can actually reach. */

  /* Section headings were 28px against a 52px headline · they read as
     captions rather than as the start of a section. */
  :root {
    --text-h2: clamp(2.3rem, 9.6vw, 3rem);
    --text-micro: .75rem;
  }
  .eyebrow, .micro { letter-spacing: .12em; }

  /* ── Hero ─────────────────────────────────────────────────────────
     Centred, because everything else on a phone is. The desktop headline
     hangs a hair left and indents its second line; both are deliberate
     there and read as a mistake in a narrow centred column. */
  .hero { padding-top: clamp(4.5rem, 9vh, 6rem); }
  .hero__inner { text-align: center; }
  .hero__eyebrow { justify-items: center; margin-bottom: var(--space-6); }
  .hero__eyebrow .eyebrow { justify-content: center; color: var(--fg-soft); }
  .hero__title { margin-left: 0; max-width: none; }
  .hero__title .line--2 { padding-left: 0; }
  .hero__lead { margin-inline: auto; max-width: 32ch; }
  .hero__cta { flex-direction: column; align-items: stretch; gap: var(--space-4); }
  .hero__cta .btn { justify-content: center; }
  .hero__cta .alink { justify-content: center; }
  /* The cue belongs at the foot of the card, not tucked under the buttons:
     it is telling you there is more below, so it has to sit at the edge. */
  .hero__inner { flex: 0 0 auto; }
  .hero__foot {
    position: relative; z-index: 2; width: 100%;
    margin-top: auto; padding-top: var(--space-12);
    padding-inline: var(--gutter);
  }
  .br-sm { display: inline; }
  /* The vignette holds the centred copy; the foot sits below it, still on
     open mosaic, so the card gets its own base. */
  .hero::after {
    content: ""; position: absolute; inset: auto 0 0 0; height: 38%;
    z-index: 1; pointer-events: none;
    background: linear-gradient(to top,
      color-mix(in srgb, var(--color-black) 94%, transparent) 0%,
      color-mix(in srgb, var(--color-black) 70%, transparent) 46%,
      transparent 100%);
  }
  .hero__foot-in { flex-direction: column; align-items: center; text-align: center; gap: var(--space-4); }

  /* The wall, rebuilt rather than scaled: twice the size and centred on the
     card, so the shards read as fragments of something large that the card
     is cropping · at 132% they were just small tiles. */
  .field {
    top: 50%; left: 50%; right: auto;
    transform: translate(-50%, -50%);
    width: 208%; height: auto;
  }
  /* On the card itself, not on the copy block. As a box around the copy the
     gradient ran out of room: at its own top edge it was still darkening by
     about half, and a gradient cut off mid-fade by its own boundary is a hard
     line · which is what showed up between the bright shards at the top and
     the dimmed ones below. Across the whole card it reaches transparent well
     inside the frame. */
  .hero::before {
    content: ""; position: absolute; inset: 0;
    z-index: 1; pointer-events: none;
    background: radial-gradient(145% 58% at 50% 46%,
      color-mix(in srgb, var(--color-black) 90%, transparent) 0%,
      color-mix(in srgb, var(--color-black) 80%, transparent) 34%,
      color-mix(in srgb, var(--color-black) 46%, transparent) 62%,
      color-mix(in srgb, var(--color-black) 14%, transparent) 82%,
      transparent 100%);
  }
  .field__veil { background: none; }

  /* ── Nav ──────────────────────────────────────────────────────────── */
  .nav__links, .nav__right > .btn { display: none; }
  .nav__toggle { display: inline-flex; align-items: center; gap: var(--space-3); font-size: var(--text-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .16em; }
  .nav__toggle-bars { display: block; width: 22px; height: 9px; position: relative; }
  .nav__toggle-bars::before, .nav__toggle-bars::after { content: ""; position: absolute; left: 0; width: 100%; height: 1px; background: currentColor; transition: transform var(--t-std) var(--ease); }
  .nav__toggle-bars::before { top: 0; }
  .nav__toggle-bars::after { bottom: 0; }
  body.menu-open .nav__toggle-bars::before { transform: translateY(4px) rotate(32.7deg); }
  body.menu-open .nav__toggle-bars::after  { transform: translateY(-4px) rotate(-32.7deg); }

  /* ── Services ─────────────────────────────────────────────────────
     A card, not a listing. The work fills the frame and the service name
     sits on it · picture, heading, paragraph stacked in a column is the
     shape of a blog post whatever is in it. */
  /* One service on screen at a time. Stacked full-bleed cards were eating
     four screens of scroll for four sentences; swiping sideways costs none. */
  .rows {
    display: flex; gap: var(--space-4);
    overflow-x: auto; overflow-y: hidden;
    overscroll-behavior-x: contain; touch-action: pan-x;
    scroll-snap-type: x mandatory;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    border-top: 0;
    scrollbar-width: none;
  }
  .rows::-webkit-scrollbar { display: none; }
  .row {
    flex: 0 0 79vw; scroll-snap-align: center;
    display: block; padding-block: 0; border-bottom: 0; margin: 0;
  }

  .row__head {
    display: block; position: relative;
    aspect-ratio: 16 / 9;
    margin-inline: 0;
    overflow: hidden;
  }
  .row__peek {
    position: absolute; inset: 0;
    width: auto; height: auto; margin: 0;
    clip-path: none;
    /* Held back: at full strength the photograph shouted over the name. */
    filter: brightness(.78) saturate(.9);
    background-image:
      linear-gradient(to top,
        color-mix(in srgb, var(--peek-scrim) 88%, transparent) 0%,
        color-mix(in srgb, var(--peek-scrim) 42%, transparent) 34%,
        transparent 68%),
      var(--src);
    background-position: center, center;
    opacity: 0; transition: opacity 520ms var(--ease);
  }
  .row__peek.is-ready { opacity: 1; }
  .row__peek--anim { filter: brightness(.86); }
  .row__peek--anim::after {
    display: block; inset: 0;
    background: linear-gradient(to top,
      color-mix(in srgb, var(--peek-scrim) 88%, transparent) 0%,
      color-mix(in srgb, var(--peek-scrim) 42%, transparent) 34%,
      transparent 68%);
  }
  .row__title {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
    padding: var(--space-4);
    color: var(--color-bone);
    font-size: clamp(1.75rem, 8.4vw, 2.5rem);
    line-height: 1.02;
  }
  .row:hover .row__title { color: var(--color-bone); }

  /* Dots, built by the script · a swipe deck with no position marker is a
     guess. */
  .rows-nav {
    display: flex; align-items: center; gap: var(--space-6);
    margin-top: var(--space-8);
  }
  .rows-count {
    flex: none; font-size: var(--text-micro); font-weight: 600;
    letter-spacing: .12em; color: var(--fg-muted);
    font-variant-numeric: tabular-nums;
  }
  .rows-count b { color: var(--accent); font-weight: 600; }
  .rows-dots { display: flex; gap: var(--space-3); flex: 1 1 auto; }
  .rows-dots button {
    /* height 4 + padding 24 under border-box left the bar 0px tall · the
       padding ate the content box. Give the button the full hit height and
       let the pseudo draw the 4px rule inside it. */
    flex: 1 1 0; height: 28px; padding: 0; background: none;
    position: relative; cursor: pointer;
  }
  .rows-dots button::after {
    content: ""; position: absolute; inset: 12px 0;
    background: color-mix(in srgb, var(--fg) 34%, transparent);
    transition: background 260ms var(--ease);
  }
  .rows-dots button[aria-current="true"]::after { background: var(--accent); }

  .row__body { margin-top: var(--space-5); }
  .row__arrow { display: none; }

  /* ── Everything else ──────────────────────────────────────────────── */
  .why__head { grid-column: 1 / 13; position: static; }
  .props { grid-column: 1 / 13; margin-top: var(--space-10); }

  .showcase__frame, .showcase__meta, .showcase__list { grid-column: 1 / 13; grid-row: auto; }
  /* Same edge-to-edge treatment as the service cards, so the phone has one
     way of showing a picture rather than two. */
  .showcase__frame { margin-inline: calc(-1 * var(--gutter)); }
  /* A row of chips, not a column: the picture and the switch have to be on
     screen together, or changing it means scrolling away from the thing you
     are trying to look at. */
  .showcase__list {
    flex-direction: row; margin-top: var(--space-6);
    gap: var(--space-2); overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x proximity; overscroll-behavior-x: contain;
    margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter);
    scrollbar-width: none;
  }
  .showcase__list::-webkit-scrollbar { display: none; }
  .showcase__hint { display: block; grid-column: 1 / 13; margin-top: var(--space-8); }
  .showcase__item {
    flex: 0 0 auto; width: auto; display: block;
    scroll-snap-align: center;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
    color: var(--fg-muted);
  }
  .showcase__item:first-child { border-top: 1px solid var(--rule); }
  .showcase__sector { display: none; }
  .showcase__name { font-size: 1.05rem; }
  .showcase__item.is-active {
    background: var(--accent); border-color: var(--accent);
    color: var(--color-black);
  }
  .showcase__meta { display: block; }
  .showcase__scope { margin-top: var(--space-3); }
  .showcase__sector, .row__sub { letter-spacing: .08em; }

  .step__n { grid-column: 1 / 3; }
  .step__t { grid-column: 3 / 13; }
  .step { row-gap: 0; }
  .step__c { grid-column: 1 / 13; margin-top: var(--space-3); }

  .expect > .eyebrow { grid-column: 1 / 13; }
  .expect__list { grid-column: 1 / 13; grid-template-columns: 1fr; margin-top: var(--space-6); }

  .close__facts { grid-template-columns: 1fr; gap: var(--space-6); }
  .close__brand { grid-column: span 12; margin-bottom: var(--space-10); }
  .close__col { grid-column: span 12; }

  /* Touch targets. Micro type gives a 14px tall link, which is a miss on a
     phone whatever the label says. */
  .nav__toggle { padding-block: var(--space-3); margin-block: calc(var(--space-3) * -1); }
  .close__col a, .close__col li > span { display: inline-block; padding-block: 10px; }
  .close__col li + li { margin-top: 0; }
  .alink { padding-block: 10px; }
  .close__bottom .micro { padding-block: 6px; }

  /* Centred: it is an object, not a column of text, and hanging left it
     left a third of the screen empty beside it. */
  .why__fly { width: min(62%, 280px); margin: clamp(var(--space-16), 14vw, var(--space-24)) auto 0; }
  .why__fly { transform: translateY(calc(8px - var(--fly, .5) * 18px)) rotate(calc((var(--fly, .5) - .5) * 2deg)); }

  .sig span { letter-spacing: .3em; text-indent: .3em; font-size: 9px; }
  .sig .mark-sq { display: none; }
}

/* Mobile menu */
.menu {
  position: fixed; inset: 0; z-index: 450;
  background: var(--color-black); color: var(--color-bone);
  padding: var(--space-24) var(--gutter) var(--space-12);
  display: flex; flex-direction: column; justify-content: space-between;
  opacity: 0; visibility: hidden;
  transition: opacity 220ms var(--ease), visibility 220ms;
}
body.menu-open .menu { opacity: 1; visibility: visible; }
body.menu-open { overflow: hidden; }
.menu__links a {
  display: block; font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.25rem, 11vw, 3.75rem);
  letter-spacing: .006em; line-height: 1.02;
  padding-block: var(--space-4); border-bottom: 1px solid #2A2826;
}
.menu__foot { display: flex; justify-content: space-between; gap: var(--space-6); flex-wrap: wrap; align-items: end; }

/* ── Reduced motion ────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .wipe > * { clip-path: none !important; }
  .rise { opacity: 1 !important; transform: none !important; }
  .draw { transform: none !important; }
  .wing__s { opacity: 1 !important; animation: none !important; }
  .marquee__track { animation: none; }
  .intro { display: none; }
}
