/* ==========================================================================
   Concept 5 — "Portrait as a switch"
   Geometry locked to Figma node 41:135 "Hero 1440" (1440x1024):
   logo 112x56 at left 56 / top 0 · giant word Archivo Regular 120px on the
   vertical centre, white and #B90000 alternating, 38px gap · columns at
   58.33%+17px and 75%+10px, 14px uppercase, tracking 1.54px, 6px row gap.
   Word size and weight are signed off — do not thicken, enlarge or shrink.
   ========================================================================== */

:root {
  --black:  #000000;
  --white:  #ffffff;
  --red:    #B90000;

  --t-55: rgba(255, 255, 255, 0.55);
  /* 0.47, not 0.45: #737373 on black is 4.42:1 — a hair under the 4.5 the
     small labels need. Two points of alpha clear the bar, the eye can't tell. */
  --t-45: rgba(255, 255, 255, 0.47);

  /* 120 / 1440 = 8.3333vw — 120px at the design width, fluid either side */
  --word:     clamp(46px, 8.3333vw, 132px);
  --word-gap: calc(var(--word) * 0.31667);   /* 38 / 120 */

  --edge: 56px;
  --top:  42px;
  --bot:  53px;

  --col-a: calc(58.3333% + 17px);   /* 857px at 1440 */
  --col-b: calc(75% + 10px);        /* 1090px at 1440 */

  --label:   14px;
  --track:   1.54px;
  /* One baseline step for every column line — 15px text + 6px gap in the
     Figma frame. Wrapped lines and new paragraphs land on the same grid, so
     the left and right columns always read as one set of rules. */
  --step:    21px;

  --f: "Archivo", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  --ease: cubic-bezier(.33, 1, .68, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--black);
  color: var(--white);
  font-family: var(--f);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--red); color: var(--white); }
:focus-visible { outline: 2px solid var(--white); outline-offset: 4px; }

/* ── stage ─────────────────────────────────────────────────── */

/* The scene is the scroll budget; the hero is pinned inside it and breaks up
   in place instead of sliding away. */
.scene { position: relative; height: 260vh; background: var(--black); overflow: clip; }  /* uncapped tiles were poking into the next section; clip (unlike hidden) keeps sticky alive */

.hero {
  position: sticky;
  top: 0;
  /* svh, not vh: on a phone 100vh includes the strip under the browser bars,
     so the counter and the bottom of the discipline list rendered off-screen
     (Oleg, 30.08). svh is the height that is actually visible; the vh line
     stays as the fallback for engines without it. */
  height: 100vh;
  height: 100svh;
  min-height: 660px;
  overflow: hidden;
  background: var(--black);
}

/* ── logo, flush to the top edge ───────────────────────────── */

/* fixed, not absolute: the mark hangs off the top edge on every screen, not
   just the hero */
.logo {
  position: fixed;
  top: 0;
  left: var(--edge);
  /* above the phone menu panel (39): the mark stays on screen when the
     panel is open — the panel carries the words, the mark carries the brand */
  z-index: 41;
  display: block;
}
.logo .mark { display: block; width: 112px; height: auto; }
/* The mark stays white throughout. Only the two filled squares under O and F
   change: brand red on black, black on the red field — so they never dissolve
   into the background they sit on. */
.logo .frame { transition: stroke .45s var(--ease); }
.logo .ltr,
.logo .sq { transition: fill .45s var(--ease); }
.logo .sq { fill: var(--red); }

/* on the red field the two cells go black so they stay visible */
body.on-red .logo .sq { fill: var(--black); }

/* on the white screen the mark inverts: black frames, black letters, and the
   red cells keep their white ink — the identity file's light variant */
body.on-white .logo .frame { stroke: var(--black); }
body.on-white .logo .ltr { fill: var(--black); }
body.on-white .logo .ltr.on-sq { fill: var(--white); }
body.on-white .logo .sq { fill: var(--red); }

/* ── nav — the left of the two top columns ─────────────────── */

.nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.nav a {
  color: var(--white);
  transition: color .25s var(--ease);
}
.nav a:hover { color: var(--t-55); }

/* ── the giant word, drifting right to left ────────────────── */

.band {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  z-index: 1;
  transform: translateY(-50%);
  overflow: hidden;
}

/* The drift is driven in JS, in pixels — see app.js. A CSS percentage
   animation would jolt sideways every time the text length changes, and the
   interludes are three times longer than the discipline words. */
.band-row {
  display: flex;
  width: max-content;
  will-change: transform;
}

/* margin instead of gap so half the row is an exact loop */
.w {
  display: block;
  margin-right: var(--word-gap);
  font-size: var(--word);
  font-weight: 400;
  line-height: 1.1;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--white);
}
.w:nth-child(even) { color: var(--red); }

/* ── the figure, standing on the letters ───────────────────── */

.stage {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;
}
/* The stack IS the Photoshop canvas (01.psb, 1792×2076). The four portraits
   are sized against each other in that file — a hunched skate stance really is
   shorter than an upright one in a helmet — and each export keeps that intent
   only as its share of the canvas height. So: bind the box to height, give it
   the canvas ratio, and let every portrait claim its own share of it. */
.stack {
  position: relative;
  height: min(92vh, 943px, 88vw * 2076 / 1792);
  aspect-ratio: 1792 / 2076;
  width: auto;
}
/* Only the live role is ever seen, so only the live role stays in the
   compositor — four full-height cut-outs held as live textures is real memory
   on a phone, and the ones behind cannot be looked at anyway. visibility flips
   after the fade, so the cross-fade itself is untouched. */
.layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .7s var(--ease), visibility 0s .7s;
}
.layer.is-live {
  opacity: 1;
  visibility: visible;
  transition: opacity .7s var(--ease), visibility 0s;
}

/* The opening portrait is held back until the canvas has assembled it out of
   squares — see intro() in app.js. Scoped to .has-js and released the moment
   the hero is up, so a scriptless page never hides its own photograph. */
.has-js:not(.hero-ready) .stack .layer { opacity: 0; visibility: hidden; }

/* Height is the unit, never width — --share comes straight from the export's
   height against the canvas, so the roles keep their real difference in stature.
   Widths differ and are allowed to; they hang off the shared floor. */
.layer picture {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.layer img {
  display: block;
  height: var(--share, 100%);
  width: auto;
  max-width: none;
}

/* placeholder until the four shots exist */
.ph {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  line-height: var(--step);
  text-align: center;
  /* Opaque on purpose: during a cross-fade with a real portrait a
     translucent card let the photograph bleed through and its caption
     floated over the face — read as flicker. A solid card fades in as one
     object. */
  background: #050505;
  border: 1px dashed rgba(255, 255, 255, 0.22);
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
.ph b { font-weight: 500; font-size: var(--label); color: var(--white); }
.ph i { font-style: normal; }

/* ── the four mono-ish columns ─────────────────────────────── */

.col {
  position: absolute;
  z-index: 5;
  max-width: 216px;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
}
/* no paragraph gap — the line-height IS the rhythm */
.col p + p { margin-top: 0; }

/* The identity block became the page's h1. A heading brings a UA font-size,
   weight and margins with it, so they are unset here — the block looks exactly
   as it did, it is simply now announced as the top of the document. */
/* A heading arrives bold with margins of its own, so those are unset. The
   size is deliberately NOT touched: .col already sets var(--label), and an
   `inherit` here beat it on source order and pulled 16px off the body — which
   made the identity block a size larger than the menu beside it. */
.col-id { margin: 0; font-weight: inherit; }
.col-id span { display: block; }
/* the practice line speaks in the same voice as the fact lines beside the
   portrait: sentence case against the caps of the wordmark above it */
.col-id-sub {
  margin-top: var(--step);
  text-transform: none;
  letter-spacing: 0.2px;
  color: var(--t-55);
}
/* The range leads the column now — white, in the register the wordmark used
   to hold; the practice line sits under it in its quieter voice. */
.col-id-range { margin-top: 0; color: var(--white); }
.col-a { left: var(--col-a); }
/* the right column runs to the page edge, so its lines don't break early */
.col-b { left: var(--col-b); max-width: 262px; }
.col-top    { top: var(--top); }
/* The bottom zone is a fixed 7-line box: every block in it — disciplines, NOW
   and the counter — starts on ONE shared line, and nothing shifts when the
   role changes and the fact gets longer or shorter. */
.col-bottom {
  bottom: var(--bot);
  height: calc(var(--step) * 7);
}

/* disciplines — the switch.
   The marker sits OUTSIDE the text box, so every column on the page starts on
   the same vertical line: text aligns to text, never to the red square. */
.disc-rows { display: flex; flex-direction: column; align-items: flex-start; }
.ix {
  position: relative;
  padding-left: 0;
  background: none;
  border: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--t-45);
  cursor: pointer;
  transition: color .3s var(--ease);
}
.ix::before {
  content: "";
  position: absolute;
  left: -16px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -3px;
  background: var(--red);
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.ix:hover { color: var(--t-55); }
.ix.is-live { color: var(--white); }
.ix.is-live::before { opacity: 1; }
/* "Products & tools" wraps to two lines on a phone; there it answers to the
   one-word name instead (Oleg, 30.08). Everywhere else the pair stays. */
.ix .ix-short { display: none; }
@media (max-width: 780px) {
  .ix .ix-full { display: none; }
  .ix .ix-short { display: inline; }
}

/* now — the line and the fact for the live role.
   Caps are for labels: menu, identity, disciplines, counter. The fact is a
   whole sentence, so it drops to sentence case — word shapes and ascenders
   come back and it reads at a glance. Tracking eases off to match. */
.now-line { color: var(--white); transition: opacity .3s var(--ease); }
.now-fact {
  color: var(--t-55);
  text-transform: none;
  letter-spacing: 0.2px;
  transition: opacity .3s var(--ease);
}
.now.is-swap .now-line,
.now.is-swap .now-fact { opacity: 0; }

/* ── counter, bottom left ──────────────────────────────────── */

.cnt {
  position: absolute;
  left: var(--edge);
  /* top line of the bottom zone — same line the columns start on */
  bottom: calc(var(--bot) + var(--step) * 6);
  z-index: 6;
  font-size: var(--label);
  line-height: var(--step);          /* same baseline as the columns */
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
.cnt-n { color: var(--white); }

/* ══ THE BREAK-UP ════════════════════════════════════════════
   Squares land on top of the pinned hero and eat it, until the screen is one
   red field and the lead can stand on it. Tiles drift past at their own
   speeds: mostly behind the headline, two above it. */

/* the squares sit OVER the hero content — that is what makes it break up
   rather than fade */
.mosaic {
  position: absolute;
  inset: 0;
  z-index: 10;
  /* Decorative overlay stretched across the whole hero. Without this it
     swallowed the mouse: every click meant for the discipline buttons died
     here (or on .tiles / .lead-copy above), so the hero only LOOKED
     clickable — the cursor said pointer, the click went nowhere. */
  pointer-events: none;
  display: grid;
  grid-template-columns: repeat(var(--cols, 14), 1fr);
  grid-auto-rows: 1fr;
}
.mosaic i {
  display: block;
  background: var(--red);
  opacity: 0;
  transform: scale(.86);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
/* the 1px shadow closes the sub-pixel seams between cells, so a filled
   screen is one flat red field and not a visible grid */
.mosaic i.on { opacity: 1; transform: none; box-shadow: 0 0 0 1px var(--red); }

/* drifting square slots — the photographs don't exist yet */
.tiles {
  position: absolute;
  inset: 0;
  z-index: 12;
  pointer-events: none;   /* same story as .mosaic */
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.tiles.on { opacity: 1; }
.tile {
  position: absolute;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.30);
  border: 1px solid rgba(0, 0, 0, 0.45);
  will-change: transform;
}
/* the project frame inside a tile: monochrome and dimmed, so it reads as a
   trace of work drifting by, not as a gallery fighting the About copy */
.tile picture { position: absolute; inset: 0; display: block; }
.tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* half strength: full colour DID end up fighting the red field — the neon
     previews shouted over the About copy (Oleg, 30.08, second call). The
     colour stays, the volume drops. */
  opacity: 0.5;
}
/* Front tiles used to ride ABOVE the copy (z 14) — as empty black squares
   that read as depth, but with project frames inside one of them landed on
   the headline's last word. The copy wins: fronts now sit just under it,
   keeping their brightness step so the depth cue survives. */
.tile.front { z-index: 12; background: rgba(0, 0, 0, 0.55); }

/* the lead */
.lead-copy {
  position: absolute;
  inset: 0;
  z-index: 13;
  /* no padding here: every child positions itself, and a padded box would
     make the % in --col-b resolve against 1328 instead of the page */
  padding: 0;
  opacity: 0;
  /* invisible must also mean intangible — this layer spans the entire hero */
  pointer-events: none;
  transition: opacity .6s var(--ease);
}
.lead-copy.on { opacity: 1; pointer-events: auto; }

/* everything rises into place, staggered — headline lines first, then the
   paragraph, then the metrics */
.lead-h span,
.lead-text,
.lead-aside > * {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.lead-copy.on .lead-h span,
.lead-copy.on .lead-text,
.lead-copy.on .lead-aside > * { opacity: 1; transform: none; }

.lead-copy.on .lead-h span:nth-child(1) { transition-delay: .02s; }
.lead-copy.on .lead-h span:nth-child(2) { transition-delay: .10s; }
.lead-copy.on .lead-h span:nth-child(3) { transition-delay: .18s; }
.lead-copy.on .lead-text              { transition-delay: .30s; }
.lead-copy.on .lead-aside > *:nth-child(1) { transition-delay: .38s; }
.lead-copy.on .lead-aside > *:nth-child(2) { transition-delay: .44s; }
.lead-copy.on .lead-aside > *:nth-child(3) { transition-delay: .50s; }

/* the headline sits on the vertical centre and runs the full width;
   the offset is on the FIRST line, the last one squares up to the left edge */
/* eyebrow and headline travel as one block, centred together — the rubric
   keeps its distance instead of being nudged by a percentage guess */
.lead-head {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-58%);
}
.lead-head { position: absolute; }
.lead-h {
  padding: 0 var(--edge);
  display: flex;
  flex-direction: column;
  /* sized so the longest line runs edge to edge without wrapping */
  font-size: clamp(28px, 6.55vw, 100px);
  font-weight: 400;
  line-height: 0.94;
  letter-spacing: -0.015em;
  white-space: nowrap;
  color: var(--white);
}
/* "The" starts exactly under "person" of the line below. In em, not %, so the
   two stay locked together at any viewport width: this is the measured width
   of "is the " at the headline's own size. */
.lead-h-in { padding-left: 2.46em; }

/* The left block starts under "The" of the headline (2.46em is the measured
   width of "is the "), the right one begins on the --col-b axis — the same
   vertical that carries "Most of Design / Independent practice" in the hero. */
.lead-cols {
  position: absolute;
  /* full width, so --col-b resolves against the page exactly like the hero
     columns; the page margins come back as padding on the children */
  left: 0;
  right: 0;
  bottom: var(--bot);
  display: grid;
  grid-template-columns: var(--col-b) minmax(0, 1fr);
  gap: 0;
  /* both blocks hang from the same top line */
  align-items: start;
}
/* the copy starts under "The": page margin plus the measured width of
   "is the " at the headline's size */
/* The indent is a MARGIN, not padding: max-width is measured on the border
   box, so padding plus a narrow max-width squeezed the line to one character
   per row. Margin sits outside that measurement. */
.lead-body {
  margin-left: calc(var(--edge) + 2.46 * clamp(28px, 6.55vw, 100px));
  max-width: 58ch;
}
.lead-text {
  font-size: var(--label);
  line-height: var(--step);
  color: rgba(255, 255, 255, 0.88);
}
/* the history and the way of working are two thoughts, so they get two
   paragraphs — one block of eight lines read as a wall */
.lead-text + .lead-text { margin-top: var(--step); }
.lead-aside { justify-self: start; max-width: 34ch; padding-right: var(--edge); }
.lead-aside .btn { margin-top: calc(var(--step) * 2); }
/* description reads as one block, not as columns */
.lead-text {
  font-size: var(--label);
  line-height: var(--step);
  color: rgba(255, 255, 255, 0.88);
}

/* eyebrow: names the screen before the claim lands */
/* the rubric hangs beside the headline, its cap line level with the T of
   "The" — not floating above the block */
.lead-eyebrow {
  position: absolute;
  left: var(--edge);
  /* Pinned to the cap line of the T in "The", 4px below it. The number is
     measured, not guessed: at top:0 the rubric's own cap sat 7px lower than
     the headline's, because each has its own half-leading. */
  top: 9px;
  display: flex;
  align-items: center;
  margin: 0;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--white);
}
.lead-eyebrow i {
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 10px;
  background: var(--black);
  vertical-align: middle;
}

/* right column: a short block, the button, a quieter fallback */
.lead-aside-p {
  font-size: var(--label);
  line-height: var(--step);
  color: rgba(255, 255, 255, 0.88);
}
.lead-alt {
  margin-top: 12px;
  font-size: 12px;
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
}
.lead-alt a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); }




/* ══ THE BREAK-UP ════════════════════════════════════════════
   Squares land on top of the pinned hero and eat it, until the screen is one
   red field and the lead can stand on it. Tiles drift past at their own
   speeds: mostly behind the headline, two above it. */










/* the headline sits on the vertical centre and runs the full width;
   the offset is on the FIRST line, the last one squares up to the left edge */


/* ── the split button ────────────────────────────────────────
   Two rectangles, one control: the label and a square that holds the arrow.
   On hover both deepen and the arrow flies out to the right while its twin
   comes in from the left; leaving reverses the pair. */

.btn {
  display: inline-flex;
  gap: 4px;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.btn-label,
.btn-arw {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--red);
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--white);
  transition: background-color .3s var(--ease);
}
.btn-label { padding: 18px 26px; white-space: nowrap; }  /* a CTA never wraps */
.btn-arw {
  position: relative;
  width: 56px;
  overflow: hidden;
}
/* a light touch: enough to register, not a colour change */
.btn:hover .btn-label,
.btn:hover .btn-arw,
.btn:focus-visible .btn-label,
.btn:focus-visible .btn-arw { background: #a81212; }

/* each arrow fills the square and is centred in it, so a translate of one
   full width parks it completely outside — the incoming twin is invisible
   until the hover starts */
.btn-arw i {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-style: normal;
  font-size: 18px;
  transition: transform .38s var(--ease);
}
.btn-arw i:first-child  { transform: translateX(0); }
.btn-arw i:last-child   { transform: translateX(-100%); }
.btn:hover .btn-arw i:first-child,
.btn:focus-visible .btn-arw i:first-child { transform: translateX(100%); }
.btn:hover .btn-arw i:last-child,
.btn:focus-visible .btn-arw i:last-child  { transform: translateX(0); }

/* on the red field the same control inverts to black */
.btn--onred .btn-label,
.btn--onred .btn-arw { background: var(--black); }
.btn--onred:hover .btn-label,
.btn--onred:hover .btn-arw,
.btn--onred:focus-visible .btn-label,
.btn--onred:focus-visible .btn-arw { background: #161616; }

@media (prefers-reduced-motion: reduce) {
  .btn-arw i { transition: none; }
}

/* ══ SECTIONS BELOW THE FOLD ═════════════════════════════════
   Back on black after the red flood. Same tokens throughout: 14px caps at
   1.54 tracking, 21px step, red only as a marker. */

.serve { position: relative; padding: 14vh var(--edge) 12vh; background: var(--black); }
.work  { position: relative; padding: 14vh 0 12vh; background: var(--black); }

.sec-head { margin-bottom: 7vh; }
.sec-k {
  display: flex;
  align-items: center;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-55);
}
.sec-k i {
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 10px;
  background: var(--red);
  vertical-align: middle;
}
.sec-h {
  margin-top: var(--step);
  font-size: clamp(34px, 5.4vw, 78px);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--white);
}
.sec-sub {
  margin-top: var(--step);
  max-width: 46ch;
  /* one notch above the label scale — this is reading copy, not a tag */
  font-size: 17px;
  line-height: 26px;
  color: var(--t-55);
}

/* ── work: copy left, reel running off the right edge ────
   The strip is not padded on the right, so a card is always cut by the
   viewport — the reader can see there is more without being told. */

/* The scene is the scroll budget the strip plays across; the section itself
   is pinned inside it, so the page stops and the cards travel sideways
   instead. Nothing moves on its own — the hand on the wheel drives it. */
.work-scene { position: relative; height: 300vh; background: var(--black); }

.work {
  position: sticky;
  top: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "nav"
    "reel";
  align-content: center;
  padding-right: 0;
}
.reel { grid-area: reel; }

/* the heading rides inside the strip as its first panel: scroll sideways and
   it leaves with the cards, exactly as in the reference */
.reel-lead {
  flex: 0 0 clamp(240px, 26vw, 380px);
  padding-right: 4%;
}
.reel-lead .sec-h { margin-top: var(--step); }
.reel-lead .sec-sub { max-width: none; }

.reel-nav {
  grid-area: nav;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 20px;
  margin: 0 var(--edge) 3vh var(--edge);
}
/* the hint shares the counter's line and its grey — one voice, not two */
/* hint and counter share one baseline: they are siblings in their own box,
   so the arrow buttons between them cannot drag the alignment around */
.reel-meta { display: flex; align-items: baseline; gap: 20px; }
.reel-hint {
  font-size: 12px;
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
.reel-count {
  white-space: nowrap;
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
.reel-n { color: var(--white); }
.reel-arrows { display: flex; gap: 8px; }
.reel-arw {
  width: 46px; height: 46px;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.22);
  font: inherit;
  font-size: 16px;
  color: var(--t-55);
  cursor: pointer;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.reel-arw:hover { color: var(--white); border-color: var(--white); }
.reel-arw[disabled] { opacity: .3; cursor: default; }

/* the one place that drops below the 14px floor: a hint under the controls,
   deliberately quieter than everything around it */
.reel-hint {
  flex-basis: 100%;
  font-size: 12px;
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}

.reel {
  display: flex;
  gap: 32px;
  padding-left: var(--edge);
  padding-right: var(--edge);
  overflow-x: auto;
  overflow-y: hidden;          /* a stray vertical bar inside the strip is a bug */
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  /* No snapping at all. The drift moves the strip by fractions of a pixel per
     frame, and any snap — mandatory or proximity — rounds that straight back
     to the nearest point, so the reel never actually moved. */
  scroll-snap-type: none;
  scrollbar-width: none;
  cursor: grab;
  touch-action: pan-x pan-y;
}
.reel::-webkit-scrollbar { display: none; }
.reel.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.reel:focus-visible { outline: 2px solid var(--white); outline-offset: 8px; }

/* Frame proportion 16:9 — Oleg's call (27.08): case previews are exported as
   wide screen frames (1920×1080-family), and a 3:2 slot would crop them or
   letterbox them. Width stays at Trionn's 670; the height simply follows the
   ratio. The third term is a height guard — on a short laptop the card
   shrinks rather than pushing the arrows and the heading off the screen. */
.card { flex: 0 0 min(40vw, 670px, 52vh * 16 / 9); }

.card-shot {
  position: relative;
  display: block;
  -webkit-user-drag: none;
  user-select: none;
}
.card-slot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 9;
  background: #0b0b0b;
  border: 1px dashed rgba(255, 255, 255, 0.20);
  overflow: hidden;
  transition: border-color .3s var(--ease);
}
/* Real previews live as .frame <picture>s stacked inside the slot. Two or
   three frames rotate on a slow beat (app.js); a single frame simply stands.
   The dashed border and the caption belong to the empty placeholder only. */
.card-slot.has-frames { border-style: solid; border-color: rgba(255, 255, 255, 0.14); }
.card-slot.has-frames > span { display: none; }
.card-slot .frame {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.card-slot .frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.card-slot .frame.is-on { opacity: 1; }

.card-slot > span {
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
/* a motion preview drops in here as a muted looping video. It must never take
   the pointer, or dragging across a card would stall on it. */
.card-slot video,
.card-slot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
}

/* The "Open the site" plate is gone (Oleg, 30.08): the card is visibly
   interactive and the domain link sits right under it. The whole card stays
   one big link opening in a new tab. */
.card-meta { margin-top: var(--step); }
.card-k {
  display: flex;
  gap: 16px;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}
/* each token is atomic: on a phone the long discipline run squeezed its
   neighbours and "[ 001 ]" broke across two lines (Oleg, 30.08). The last
   span may wrap inside itself; the index and year never do. */
.card-k span { white-space: nowrap; }
.card-k span:last-child { white-space: normal; }
.card-meta h3 {
  margin-top: 6px;
  font-size: clamp(20px, 2.1vw, 32px);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--white);
}
.card-note {
  margin-top: 6px;
  max-width: 40ch;
  font-size: var(--label);
  line-height: var(--step);
  color: var(--t-55);
}
.card-link {
  display: inline-block;
  margin-top: var(--step);
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--white);
  border-bottom: 1px solid rgba(255, 255, 255, 0.35);
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.card-link:hover { color: var(--red); border-color: var(--red); }

/* ── practice accordion ──────────────────────────────────── */

.acc-item { border-top: 1px solid rgba(255, 255, 255, 0.12); }
.acc-item:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }

.acc-head { list-style: none; cursor: pointer; }
.acc-head::-webkit-details-marker { display: none; }
.acc-head {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) 28px;
  align-items: center;
  gap: 0 24px;
  width: 100%;
  padding: 26px 0;
  background: none;
  border: 0;
  text-align: left;
  font: inherit;
  color: var(--t-55);
  cursor: pointer;
  transition: color .3s var(--ease);
}
.acc-head:hover { color: var(--white); }
.acc-item[open] .acc-head { color: var(--white); }

.acc-n {
  font-size: var(--label);
  letter-spacing: var(--track);
  color: var(--t-45);
}
/* brand red goes muddy at 14px on black — the live number gets a brighter
   cut of the same hue */
.acc-item[open] .acc-n { color: #ff2a1f; }
.acc-t {
  font-size: clamp(26px, 3.4vw, 52px);
  line-height: 1.05;
  letter-spacing: -0.015em;
}

/* plus that becomes a minus, drawn from two rules */
.acc-sign { position: relative; width: 20px; height: 20px; justify-self: end; }
.acc-sign::before,
.acc-sign::after {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 20px; height: 1px;
  background: currentColor;
  /* one gesture, one duration: the upright bar lying down IS the plus
     becoming a minus. A second, longer spin on the parent used to run over
     the top of it and the two read as a stutter. */
  transition: transform .38s cubic-bezier(.34, 1.4, .64, 1);
}
.acc-sign::after { transform: rotate(90deg); }
.acc-item[open] .acc-sign::after { transform: rotate(0deg); }

/* Height in pixels, set from JS. The fr-based version looked cleaner but did
   not animate reliably here: an opened panel stayed at 0 while the one being
   closed kept its full height. An explicit height always resolves. */
/* Native <details>: the panel opens even if scripts fail. The motion is a
   choreography, not a size tween: the open discipline draws a red rule under
   itself, the copy arrives line by line from below, the sign turns over with
   a small spring. The base path animates only transform and opacity. */
.acc-item > .acc-body { overflow: hidden; }
/* belt and braces: some engines keep the closed panel in flow, so say it */
.acc-item:not([open]) > .acc-body { display: none; }

/* the copy cascades — each block rises on its own beat, so the panel reads
   as unfolding rather than popping in as one slab */
.acc-item[open] .acc-inner > * {
  animation: acc-line .34s var(--ease) both;
}
.acc-item[open] .acc-inner > *:nth-child(1) { animation-delay: .02s; }
.acc-item[open] .acc-inner > *:nth-child(2) { animation-delay: .07s; }
.acc-item[open] .acc-inner > *:nth-child(3) { animation-delay: .12s; }
.acc-item[open] .acc-inner > *:nth-child(4) { animation-delay: .17s; }
@keyframes acc-line {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Height glide where the platform can do it properly: Chromium 131+ knows
   how to interpolate a <details> panel to auto height (interpolate-size +
   ::details-content). Everyone else keeps the instant, reliable open — the
   cascade above carries the motion there. */
@supports (interpolate-size: allow-keywords) {
  .serve { interpolate-size: allow-keywords; }
  .acc-item:not([open]) > .acc-body { display: block; }
  .acc-item::details-content {
    height: 0;
    overflow: clip;
    transition: height .3s var(--ease), content-visibility .3s allow-discrete;
  }
  .acc-item[open]::details-content { height: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .acc-item[open] .acc-inner > * { animation: none; }
  .acc-sign::before,
  .acc-sign::after { transition: none; }
  .acc-item::details-content { transition: none; }
}
/* Horizontal indent only. A vertical padding here keeps its height even when
   the row is collapsed to 0fr, which left a sliver of text showing under
   every closed panel; the bottom spacing lives on the last child instead. */
.acc-inner { padding: 0 0 0 96px; }
.acc-inner > *:last-child { margin-bottom: 30px; }
.acc-inner p + p { margin-top: 14px; }
/* the claim the rest of the panel then backs up — white, so the eye lands on
   it first when the panel opens */
.acc-inner p.acc-lead { color: var(--white); max-width: 62ch; }
.acc-inner p.acc-lead + p { margin-top: var(--step); }
.acc-inner p {
  max-width: 82ch;
  font-size: var(--label);
  line-height: var(--step);
  color: var(--t-55);
}
/* tags hold one line across the full width — no orphan chip on a second row */
/* a touch more air before the chips than between paragraphs.
   The selector carries p as well, otherwise the generic "p + p" rule below
   wins on source order and pulls the chips back up. */
.acc-inner p.acc-tags { display: flex; flex-wrap: nowrap; gap: 8px; margin-top: 32px; max-width: none; }
.acc-tags i { white-space: nowrap; }
.acc-tags i {
  font-style: normal;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-55);
}

/* ── footer ──────────────────────────────────────────────── */

/* the footer's own mark exists only on the phone, where the top one has
   scrolled away with the hero */
.foot-logo { display: none; }

.foot {
  position: relative;
  padding: 11vh 0 0;
  background: var(--black);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  overflow: hidden;
}
/* The two footer columns sit on the hero's own axes: 58.33%+17 and 75%+10.
   Column one carries the headline, the clocks take the second row under
   Direct so they land on the baseline of "you're building". */
.foot-grid {
  display: grid;
  /* no padding on the grid itself: the percentages have to be read off the
     full viewport, exactly like the hero columns, or the axes drift inwards
     by the size of the page margin */
  grid-template-columns: var(--col-a) calc(var(--col-b) - var(--col-a)) minmax(0, 1fr);
  /* row one is only as tall as the columns need; the headline spans both and
     the leftover height falls into row two, where the clocks hang from the
     bottom edge */
  grid-template-rows: min-content 1fr;
  gap: 3vh 0;
  padding: 0;
  align-items: start;
}
/* the headline block spans both rows, so the clocks can hang off its
   bottom edge: their times line up with the base of the button */
.foot-lead { grid-column: 1; grid-row: 1 / span 2; padding-left: var(--edge); }
.foot-col-a { grid-column: 2; grid-row: 1; }
.foot-col-b { grid-column: 3; grid-row: 1; padding-right: var(--edge); }
.clocks { grid-column: 1 / -1; grid-row: 2; align-self: end; }
.foot-sub {
  margin-top: var(--step);
  /* rides the button's own line — the second headline line (Oleg, 30.08) */
  margin-left: calc(1.6 * clamp(34px, 5.4vw, 78px));
  max-width: 42ch;
  font-size: var(--label);
  line-height: var(--step);
  color: var(--t-55);
}
.foot-h {
  display: flex;
  flex-direction: column;
  margin-top: var(--step);
  font-size: clamp(34px, 5.4vw, 78px);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--white);
}
.foot-h-in { padding-left: 1.6em; }

/* the footer button is the split control; only its spacing lives here */
.foot-cta {
  /* pulled up: the button sets the footer's lower line, and the clocks hang
     from it — a taller gap here pushes the faces away from Direct.
     Indented to the second headline line, so it starts under "you're". */
  margin: 3vh 0 0 calc(1.6 * clamp(34px, 5.4vw, 78px));
}




.foot-col { display: flex; flex-direction: column; align-items: flex-start; }
/* body here reads like the hero's fact line: 14/21, sentence case, quiet */
.foot-col a, .foot-col p {
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: 0.2px;
  color: var(--t-55);
}
.foot-col a { transition: color .25s var(--ease); }
.foot-col a:hover { color: var(--white); }
.foot-k {
  display: flex;
  align-items: center;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--white);
  margin-bottom: 6px;
}

/* Four faces: a circle and two hands, nothing else. Full width, so the
   columns resolve against the page and each face lands on a footer axis —
   Madrid on the Where axis at the left, then one step of the same rhythm per
   city outward. Placement is positional, so the running order lives in the
   markup and this stays true whichever cities are listed. */
.clocks {
  /* The row spans the whole footer, so it lay across the button in the column
     to its left and swallowed the pointer — hover only survived on the arrow
     square sticking out past it. Faces are not interactive; the box lets
     clicks through and only the clocks themselves take the pointer. */
  pointer-events: none;
  display: grid;
  grid-template-columns:
    var(--col-a)
    repeat(3, calc((var(--col-b) - var(--col-a)) / 2))
    minmax(0, 1fr);
  align-items: end;
}
.clock { pointer-events: auto; }
.clock:nth-child(1) { grid-column: 2; }   /* on the Where axis */
.clock:nth-child(2) { grid-column: 3; }   /* midpoint */
.clock:nth-child(3) { grid-column: 4; }   /* on the Direct axis */
.clock:nth-child(4) { grid-column: 5; }   /* one more step of the same rhythm */

.clock svg { display: block; width: 88px; height: 88px; overflow: visible; }
.clock svg circle { fill: none; stroke: rgba(255, 255, 255, 0.35); stroke-width: 0.75; }
.clock svg .pin { fill: var(--red); stroke: none; }
.clock svg line {
  stroke: var(--white);
  stroke-width: 1;
  stroke-linecap: round;
  transform-origin: 22px 22px;
}
.clock svg .hh { stroke-width: 1.4; }
/* the second hand sweeps rather than ticks: one continuous turn a minute,
   started at the right offset, so nothing jumps four times a second across
   the footer */
.clock svg .sh {
  stroke: rgba(255, 255, 255, 0.32);
  stroke-width: 0.6;
  animation: sweep 60s linear infinite;
}
@keyframes sweep {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .clock svg .sh { animation: none; }
}
.clock-city {
  margin-top: var(--step);
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--white);
}
.clock-time {
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: 0.2px;
  color: var(--t-55);
  font-variant-numeric: tabular-nums;
}
.clock-time i {
  font-style: normal;
  margin-left: 6px;
  font-size: 12px;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}

/* ── machine-readable proof ─────────────────────────────────
   A footnote, not a banner: it sits on the hairline that closes the practice
   section and stays at label scale. Claim left, evidence right. */
.proof {
  display: flex;
  flex-wrap: wrap;
  /* baseline, not stretch: the tap-target padding on the llms.txt link makes
     its line taller, and the right half visibly slipped off the left's line */
  align-items: baseline;
  gap: 8px 24px;
  padding: calc(var(--step) * 2) var(--edge);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 12px;
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
}
.proof-h { color: var(--t-55); }
.proof-t { color: var(--t-45); }
.proof-t a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.proof-t a:hover { color: var(--white); }

.foot-band { margin-top: 7vh; overflow: hidden; }
.foot-band-row {
  display: flex;
  width: max-content;
  animation: foot-drift 46s linear infinite;
}
.foot-band-row span {
  font-size: clamp(56px, 11vw, 176px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.11);
  margin-right: 5vw;
}
@keyframes foot-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* the fine print reads as fine print: 12px, like the reel hint */
.foot-fine {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding: calc(var(--step) * 1.5) var(--edge) 34px;
  font-size: 12px;
  line-height: var(--step);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-45);
}

/* ── drawer ──────────────────────────────────────────────── */

.drawer { position: fixed; inset: 0; z-index: 60; }
.drawer[hidden] { display: none; }
.drawer-veil {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.drawer.on .drawer-veil { opacity: 1; }

.drawer-panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(560px, 100%);
  padding: 40px var(--edge) 48px;
  background: #0a0a0a;
  border-left: 1px solid rgba(255, 255, 255, 0.14);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform .45s var(--ease);
}
.drawer.on .drawer-panel { transform: none; }

.drawer-head { display: flex; align-items: center; justify-content: space-between; }
.drawer-x {
  background: none; border: 0; font: inherit; font-size: 18px;
  color: var(--t-55); cursor: pointer; padding: 6px;
  transition: color .25s var(--ease);
}
.drawer-x:hover { color: var(--white); }

.drawer-h {
  margin-top: 6vh;
  font-size: clamp(26px, 3.2vw, 40px);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.015em;
  color: var(--white);
}
.drawer-note {
  margin-top: var(--step);
  font-size: var(--label);
  line-height: var(--step);
  color: var(--t-55);
}

.drawer-form { margin-top: 5vh; display: flex; flex-direction: column; gap: var(--step); }
.fld { display: flex; flex-direction: column; gap: 8px; }
.fld > span {
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-55);
}
.fld > span i { font-style: normal; color: var(--t-45); }
.fld input, .fld textarea {
  padding: 14px 16px;
  background: #000;
  border: 1px solid rgba(255, 255, 255, 0.18);
  font: inherit;
  font-size: var(--label);
  line-height: var(--step);
  color: var(--white);
  resize: vertical;
  transition: border-color .25s var(--ease);
}
.fld input:focus, .fld textarea:focus { outline: none; border-color: var(--red); }
.fld textarea::placeholder { color: var(--t-45); }

/* the drawer's submit is the same control as everywhere else */
.drawer-send { margin-top: var(--step); }
.drawer-send:disabled { opacity: 0.55; cursor: default; }

/* the bot field — off-canvas, not display:none, so autofillers ignore it
   while form parsers still find it */
.fld-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* a missed required field: the site's own voice, not the browser bubble */
.fld input.fld-miss, .fld textarea.fld-miss { border-color: var(--red); }

.drawer-done {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-top: 5vh;
  max-width: 36ch;
  font-size: 17px;
  line-height: 26px;
  color: var(--white);
}
/* the one green thing on the site — a confirmation is allowed to say "done"
   in the colour everyone reads as done (Oleg, 30.08) */
.drawer-done-check {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  background: #1e8a44;
  color: #fff;
  font-style: normal;
  font-size: 15px;
}
.drawer-alt {
  font-size: var(--label);
  line-height: var(--step);
  color: var(--t-55);
}
.drawer-alt a { border-bottom: 1px solid rgba(255, 255, 255, 0.4); }

body.drawer-open { overflow: hidden; }

/* ── narrow screens ────────────────────────────────────────── */

@media (max-width: 1100px) {
  :root { --col-a: 52%; --col-b: 76%; }
  .col { max-width: 22vw; }
}

/* The headline is nowrap on desktop so the lines stay authored. Below this
   width that clips it off-screen, so wrapping comes back and the offset goes
   away. The breakpoint is 1030, not 900 — Oleg's call (30.08): under ~1020
   the aside column squeezed to ~200px and the copy read one word per line.
   From here down the whole block stacks on the left axis. */
/* ── phone navigation: burger + full-screen panel ───────────────
   Desktop never sees these; under 780px the burger replaces the top nav.
   This block sits BEFORE the 780 media block on purpose: the media rule
   turns the burger on, and source order must let it win. */
.burger {
  position: fixed;
  top: 18px;
  right: 16px;
  z-index: 40;
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  width: 48px;
  height: 48px;
  padding: 0 10px;
  background: none;
  border: 0;
  cursor: pointer;
}
.burger i {
  display: block;
  height: 2px;
  background: var(--white);
  transition: transform .3s var(--ease);
}
.burger[aria-expanded="true"] i:nth-child(1) { transform: translateY(4.5px) rotate(45deg); }
.burger[aria-expanded="true"] i:nth-child(2) { transform: translateY(-4.5px) rotate(-45deg); }

/* The panel: menu items huge and centred — the same Archivo caps voice as
   the giant hero word — socials in a small column at the bottom. */
.menu-panel[hidden] { display: none; }  /* author display:flex would beat the attribute — and an invisible full-screen layer ate every tap on the page */
.menu-panel {
  position: fixed;
  inset: 0;
  z-index: 39;
  display: flex;
  flex-direction: column;
  background: #050505;
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.menu-panel.open { opacity: 1; }
.menu-id {
  margin: 108px var(--edge) 0;
  font-size: var(--label);
  line-height: var(--step);
  letter-spacing: 0.2px;
  text-transform: none;
  color: var(--t-55);
}
.menu-panel nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
}
.menu-panel nav a {
  font-size: clamp(48px, 15vw, 64px);
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: var(--white);
}
.menu-soc {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding-bottom: 44px;
}
.menu-soc a {
  padding: 6px 10px;
  font-size: var(--label);
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--t-55);
}
@media (prefers-reduced-motion: reduce) {
  .menu-panel, .burger i { transition: none; }
}

@media (max-width: 1030px) {
  /* display:block, not the desktop flex-column — the flex container laid
     every span on its own row no matter what the spans said */
  .lead-h { display: block; white-space: normal; font-size: clamp(28px, 7.4vw, 52px); line-height: 1.04; }
  .lead-h-in { padding-left: 0; }
  /* the authored three-line break is a desktop device; here the sentences
     run as one flow — period, space, And — and the block loses a line of
     orphans (Oleg, 30.08) */
  .lead-h span { display: inline; }
  .lead-h span::after { content: " "; }

  /* One flow, not two layers. The headline used to sit on the vertical
     centre with the copy pinned to the bottom — two absolute blocks that
     never knew about each other. With the second About paragraph the copy
     grew tall enough to slide under the headline on a phone. Stacked in a
     column, they cannot meet. */
  .lead-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-bottom: 0;
  }
  .lead-head { position: static; transform: none; }
  .lead-cols { position: static; margin-top: calc(var(--step) * 2); grid-template-columns: minmax(0, 1fr); gap: var(--step) 0; }
  /* full-bleed lines read like a spreadsheet on a tablet — keep a measure */
  .lead-body { margin-left: var(--edge); margin-right: var(--edge); max-width: 66ch; }
  .lead-aside { justify-self: start; max-width: 66ch; padding-left: var(--edge); }
  .lead-eyebrow { position: static; margin: 0 0 var(--step) var(--edge); }
}

/* Phones AND portrait tablets: one grammar. The iPad-vertical fell into a
   gap — desktop columns with touch-sized targets broke the header and ran
   the discipline list off screen. The comma is an OR. */
@media (max-width: 780px), (max-width: 1030px) and (orientation: portrait) {
  /* label never drops below 14px — Oleg's floor */
  :root { --edge: 24px; --top: 28px; --bot: 28px; --track: 1.1px;
          /* the word behind the figure holds its scale on a small screen —
             at the desktop clamp it shrank to a whisper */
          --word: clamp(72px, 24vw, 110px); }
  /* and rides higher still — behind the jaw, not the chest: the raised
     figure pushed the composition up and 38% read low (Oleg, 30.08) */
  .band { top: 30%; }

  .col { max-width: none; width: calc(50% - 32px); }
  .col-a { left: var(--edge); }
  .col-b { left: 50%; }
  .col-top { top: 128px; }
  /* The bottom box is a fixed seven lines on desktop, but coarse-pointer
     tap targets make each discipline 45px tall — five of them overflow the
     box, ran over the counter and off the screen edge. Let the box size
     itself and stand higher, with the counter below on its own line.
     Type runs a step bigger here — 14px labels over a full-bleed photo
     read like fine print (Oleg, 30.08). */
  /* ── the phone hero bottom is a different machine (Oleg, 30.08) ──
     The five names run as ONE horizontal strip: the live name docks at the
     left edge with its red dot, the grey rest trail off and the screen edge
     cuts the next one — that cut is the "you can swipe" hint. app.js scrolls
     the strip when the rotation moves on. Under the strip the caption runs
     the full width — headline in one line, fact wrapping free — and the
     counter keeps the floor. */
  /* ONE state for the whole strip range — the stepped seats jumped at
     every breakpoint (Oleg, 30.08). The caption lives in a fixed-height
     box like the desktop's seven-line rule: the headline always sits on
     the same line, the strip always hangs the same 20px above it, and a
     long fact grows DOWN toward the floor instead of shoving the strip. */
  .col-bottom.disc { left: 0; right: 0; width: auto; max-width: none; bottom: 254px; height: auto; }
  .disc-rows {
    flex-direction: row;
    align-items: center;
    gap: 28px;
    overflow-x: auto;
    padding: 0 var(--edge);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-x;
  }
  .disc-rows::-webkit-scrollbar { display: none; }
  .disc .ix { white-space: nowrap; padding-left: 18px; }
  /* Every name owns its square: near-black at rest, red when live. An
     invisible dot left an 18px phantom indent and the first name read as
     misaligned — an inadmissible designer's sin (Oleg, 30.08). */
  .disc .ix::before {
    left: 0;
    opacity: 1;
    background: #292929;
    transition: background-color .3s var(--ease);
  }
  .disc .ix.is-live::before { background: var(--red); }

  /* full width on a phone, two thirds where there is room — one fluid
     formula, no breakpoint jump */
  .col-b.col-bottom.now {
    left: var(--edge); right: auto; text-align: left;
    width: auto;
    max-width: min(calc(100vw - 2 * var(--edge)), max(66vw, 44ch));
    /* headline pinned to the box top = one line at every width and every
       role; the WEB-sized air (28px) stands between it and the strip */
    bottom: 36px; height: 190px;
  }
  .now-line { white-space: nowrap; }

  /* the diver reads a size smaller than the others — the hood and snorkel
     eat into his share of the canvas. He alone gets a step up, standing on
     the same floor (Oleg, 30.08). */
  .layer[data-role="2"] { transform: scale(1.07); transform-origin: 50% 100%; }

  /* the mark lives at the top of the page, not on the glass: it scrolls
     away with the hero. The burger stays — white lines under a difference
     blend read dark on the light practice section by themselves. */
  .logo { position: absolute; }
  .burger { mix-blend-mode: difference; }

  /* the second mark: bolted tight under the footer's top line, with real
     air before CONTACT — the negative margin swallows the footer's own
     11vh headroom (Oleg, 30.08 ×2) */
  .foot-logo { display: block; margin-top: calc(10px - 11vh); padding: 0 var(--edge) 40px; }
  .foot-logo .mark { display: block; width: 112px; height: auto; }
  /* the red cells: the header mark is painted by `.logo .sq` — this copy
     lives outside `.logo` and went colourless without its own rule */
  .foot-logo .sq { fill: var(--red); }

  /* a 660px floor on a phone whose visible height is ~550-660 recreated the
     exact clipping svh removes — the floor steps down to the smallest real
     phone screen (Oleg, 30.08) */
  .hero { min-height: 552px; }
  .col-bottom .ix,
  .now-line { font-size: 16px; }
  .ix { text-align: left; }   /* buttons centre by default — a wrapped name staircased */
  .now-fact { font-size: 15px; line-height: 22px; }

  /* Pinned to the TOP on phones, not the bottom (Oleg, 30.08, third pass).
     Bottom-anchoring made the head's height depend on each browser's
     viewport math — the same build sat differently in Safari, Chrome and
     the emulator. Now the head is a fixed ~110px under the mark everywhere.
     Size stays moderate — 142vw cropped the shoulders hard ("не больше,
     а выше") — and the figure fades out through its own lower quarter, so
     it has already gone dark by the time the discipline text starts. */
  .stage { align-items: flex-start; }
  .stack {
    margin-top: 90px;
    height: min(82svh, 126vw * 2076 / 1792);
    -webkit-mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, 0) 96%);
    mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, 0) 96%);
  }

  /* the strip's five squares already say "where we are" — a numeric counter
     next to them was double bookkeeping (Oleg, 30.08). Desktop keeps it:
     there the counter heads the vertical list. */
  .cnt { display: none; }

  /* the range line lives behind the portrait's head at this width — it
     stays in the footer and the meta, the hero keeps the name readable.
     The selector needs the span: `.col-id span { display:block }` outweighs
     a bare class and kept it visible. */
  .col-id span.col-id-range { display: none; }

  /* the closing block keeps the button's width, give or take — a paragraph
     running the full screen above a narrow button read as two designs */
  .lead-aside { max-width: 32ch; }

  /* the send button fills the drawer; the footer CTA hugs its label on
     anything wider than a phone — full-bleed red read as a slab on a
     tablet (Oleg, 30.08). The ≤719 block below widens it back for phones. */
  .foot-lead { padding-right: var(--edge); }
  .drawer-send { display: flex; width: 100%; }
  .foot-cta { display: flex; width: fit-content; }
  .foot-cta { margin-left: 0; }
  .foot-sub { margin-left: 0; }
  .foot-cta .btn-label,
  .drawer-send .btn-label { flex: 1; padding: 16px 18px; }

  /* faces read small under a thumb — a step up (Oleg, 30.08) */
  .clock svg { width: 104px; height: 104px; }

  .work { padding: 10vh 0 9vh; }
  .serve { padding: 10vh var(--edge) 9vh; }

  .reel { gap: 20px; }
  /* the heading stands above the reel (app.js moves the node); the PIN
     STAYS — the vertical scroll keeps driving the strip, that is the
     gesture (Oleg, 30.08). Pagination sits right, with more air before
     the cards. */
  .work { display: block; padding-top: 10vh; }
  .reel-lead { flex-basis: auto; width: auto; margin: 0 var(--edge) 4vh; padding: 0; }
  .reel-nav { justify-content: flex-end; margin: 0 var(--edge) 5vh; }
  .card { flex-basis: 86vw; }
  /* Always visible on touch (no hover), but quieter than the hover version:
     seven cards in a row each carrying a full-size red plate drowned the
     previews. */
  .reel-nav { justify-content: flex-start; margin: 0 var(--edge) 2vh var(--edge); }
  .reel-nav .reel-hint { text-align: left; }
  /* "Scroll, drag or ←→" broke across two lines and left the arrow orphaned on
     the second. On touch the peeking card already says swipe, and the arrows
     are right there, so the sentence goes rather than wraps. */
  .reel-hint { display: none; }
  .sec-sub { font-size: 16px; line-height: 24px; }

  .acc-head { grid-template-columns: 44px minmax(0, 1fr) 20px; gap: 0 14px; padding: 20px 0; }
  .acc-inner { padding-left: 58px; padding-right: 4px; }
  /* This wrap was being lost: the desktop rule is `.acc-inner p.acc-tags`
     (0-2-1) and this one was a bare class (0-1-0), so nowrap kept winning and
     the third chip ran off the right edge of the phone. Same specificity now. */
  .acc-inner p.acc-tags { flex-wrap: wrap; }

  .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--step) 6%; }
  .foot-lead { grid-column: 1 / -1; grid-row: 1; padding-left: var(--edge); }
  .foot-col-a { grid-column: 1; grid-row: 2; padding-left: var(--edge); }
  .foot-col-b { grid-column: 2; grid-row: 2; padding-right: var(--edge); }
  /* Four faces in a wrapping flex row came out three-plus-one and left Zurich
     stranded on a line of its own. Two by two divides evenly. */
  /* one row of four faces — it fits from ~520px up; only the narrowest
     phones fall back to 2×2 (Oleg, 30.08) */
  .clocks {
    grid-column: 1 / -1;
    grid-row: 3;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--step) 20px;
    padding: 0 var(--edge);
    /* the base grid bottom-aligns; here a two-line "New York" shoved its
       face above the row (Oleg, 30.08). Top-align and keep every city on
       one line — the faces stay level whatever the label does. */
    align-items: start;
  }
  .clock-city { white-space: nowrap; }
  .clock:nth-child(1), .clock:nth-child(2), .clock:nth-child(3), .clock:nth-child(4) { grid-column: auto; }
  .clock svg { width: 60px; height: 60px; }
  .foot-h-in { padding-left: 0; }
  /* the gap under © 2026 equals the gap to its left edge — same air on
     both sides, no more (Oleg, 30.08) */
  .foot-fine { flex-direction: column; gap: 6px;
               padding-bottom: var(--edge); }

  .drawer-panel { width: 100%; border-left: 0; }

  /* the mark keeps its full desktop size — Oleg's call (30.08): the burger
     panel now owns the top-right, so nothing competes with it */

  /* phone nav: the burger takes over, the hero sheds the anchor list AND
     the identity column — the words moved into the panel, the portrait
     gets the screen (Oleg, 30.08) */
  .nav { display: none; }
  .col-id { display: none; }
  .burger { display: flex; }

  /* The portrait is the screen on a phone (Oleg, 30.08): taller than the
     old waist-crop — width runs slightly past the viewport, so shoulders
     bleed off the edges instead of the figure floating small in the middle.
     A soft gradient settles the lower edge into the field so the discipline
     block reads over it. */
  /* big but not wall-to-wall (138vw drowned the giant word), and lifted
     off the bottom edge; the fade catches the crop either way */
  /* sizing lives in the top-pinned rule earlier in this block; nothing to
     override here any more */
  /* the fade always ends in SOLID black: the waist crop and the band must
     never compete with the discipline block reading over them */
  .stage::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 38vh;
    background: linear-gradient(180deg, rgba(5,5,5,0) 0%, rgba(5,5,5,0.78) 62%, #050505 92%);
    pointer-events: none;
  }
}

/* ── the narrowest phones (320–420) ─────────────────────────────
   Two columns cannot hold a 22-character e-mail address, and the two-part
   red button ran past the right edge at 320. One column, full-width CTAs. */
@media (max-width: 420px) {
  /* two columns stay (Oleg: "влезет же") — only the e-mail needs a size
     step down to clear a 160px column, with anywhere-wrap as the net */
  .foot-col-b a { font-size: 13px; overflow-wrap: anywhere; }
  .clocks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .clock svg { width: 88px; height: 88px; }
}

/* ── practice + footer under 1030 — AFTER the base styles on purpose:
   the .serve and footer base rules live late in the file and would win the
   cascade over the main phone block (same lesson as the burger). */
@media (max-width: 1030px) {
  .acc-head { grid-template-columns: 44px minmax(0, 1fr) 24px; gap: 0 14px; }
  .acc-inner { padding-left: 58px; padding-right: 0; }

  /* footer stacks: contact block with a left-set button, then the two
     address columns, then the clocks across the full width (Oleg, 30.08) */
  .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(var(--step) * 2) 6%; }
  .foot-lead { grid-column: 1 / -1; grid-row: 1; padding-left: var(--edge); padding-right: var(--edge); }
  .foot-cta { margin-left: 0; }
  .foot-sub { margin-left: 0; }
  .foot-col-a { grid-column: 1; grid-row: 2; padding-left: var(--edge); }
  .foot-col-b { grid-column: 2; grid-row: 2; padding-right: var(--edge); }
  .clocks {
    grid-column: 1 / -1;
    grid-row: 3;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--step) 20px;
    padding: 0 var(--edge);
    /* the base grid bottom-aligns; here a two-line "New York" shoved its
       face above the row (Oleg, 30.08). Top-align and keep every city on
       one line — the faces stay level whatever the label does. */
    align-items: start;
  }
  .clock-city { white-space: nowrap; }
  .clock:nth-child(1), .clock:nth-child(2), .clock:nth-child(3), .clock:nth-child(4) { grid-column: auto; }
}
@media (max-width: 780px) {
  /* number gutter tightens further and the body runs the full measure */
  .acc-head { grid-template-columns: 34px minmax(0, 1fr) 20px; gap: 0 10px; }
  .acc-inner { padding-left: 0; padding-right: 0; }
}

/* ── rise on entry ───────────────────────────────────────────
   Everything below the flood starts 20px low and settles as it enters the
   viewport — the same small lift Trionn uses, staggered inside each group so
   a row of cards arrives one after another rather than as a slab. */

.rise {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.rise.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rise { opacity: 1; transform: none; transition: none; }
}


/* ── the strip's own entrance ────────────────────────────────
   Cards rise as they come into the visible part of the reel — horizontally
   as you swipe, and vertically when the section first arrives. */
/* position drives opacity and offset from JS; the transition only smooths
   the step the arrows make, not the continuous drift */
.reel .card,
.reel .reel-lead {
  transition: opacity .18s linear, transform .18s linear;
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
  .reel .card, .reel .reel-lead { opacity: 1; transform: none; transition: none; }
}


/* ══ THE WHITE SCREEN ════════════════════════════════════════
   Practice is the document of the site: what is on offer, in order. White
   gives the eye a rest between two black fields and reads as specification
   rather than atmosphere. Same grid, same type, inverted ink. */

.serve {
  background: var(--white);
  color: var(--black);
}
.serve .sec-k { color: rgba(0, 0, 0, 0.55); }
.serve .sec-k i { background: var(--red); }
.serve .sec-h { color: var(--black); }
/* the standfirst starts where the discipline names start, not at the page
   margin — one vertical for the whole section */
/* The standfirst sits under the heading on the disciplines' own vertical —
   same 96px indent as the names below it — and runs to roughly where the
   word "do" ends, so the block reads as one column of copy. */
.serve .sec-head { display: block; }
.serve .sec-sub {
  margin: var(--step) 0 0 96px;
  max-width: min(62ch, 46vw);
  color: rgba(0, 0, 0, 0.6);
}

/* The standfirst and the discipline copy share one left line at every width
   (Oleg, 30.08). These sit AFTER the `.serve .sec-sub` base rule above —
   earlier copies of these overrides lived before it and lost the cascade,
   which is exactly the drift they exist to prevent. */
@media (max-width: 1030px) {
  .serve .sec-sub { margin-left: 58px; max-width: 62ch; }
  /* the reel heading spans the viewport here — its standfirst holds to half
     the screen instead of running edge to edge (Oleg, 30.08) */
  .reel-lead .sec-sub { max-width: 50vw; }
}
@media (max-width: 780px) {
  .serve .sec-sub { margin-left: 0; max-width: none; }
  .reel-lead .sec-sub { max-width: none; }
}

.serve .acc-item { border-top-color: rgba(0, 0, 0, 0.16); }
.serve .acc-item:last-child { border-bottom-color: rgba(0, 0, 0, 0.16); }
.serve .acc-head { color: rgba(0, 0, 0, 0.55); }
.serve .acc-head:hover,
.serve .acc-item[open] .acc-head { color: var(--black); }
.serve .acc-n { color: rgba(0, 0, 0, 0.4); }
.serve .acc-item[open] .acc-n { color: var(--red); }
.serve .acc-inner p { color: rgba(0, 0, 0, 0.62); }
.serve .acc-tags i {
  background: var(--black);
  border-color: var(--black);
  color: var(--white);
}
.serve :focus-visible { outline-color: var(--black); }


/* ── hit areas ───────────────────────────────────────────────
   Every control here was a 21px line of text stacked flush against the next —
   centre to centre 21px, no gap at all. That fails WCAG 2.2 target size (24px)
   and is well under what any phone asks for.

   An invisible over-sized pad was the first idea and it is wrong: these lists
   have no gap to grow into, so each pad would cover half its neighbour and the
   tap would resolve to the wrong link. Padding is the honest fix — the targets
   tile instead of overlapping, so every point belongs to exactly one control.

   3px on a mouse buys AA and is invisible in the rhythm; a coarse pointer gets
   the full 44 and the extra height it needs. */
.nav a,
.ix,
.foot-col-a a,
.foot-col-b a,
.card-link,
.proof-t a,
.drawer-alt a {
  display: inline-block;
  padding-block: 2px;
  min-height: 24px;            /* WCAG 2.2 AA target size, incl. the 18px card link */
}

@media (pointer: coarse) {
  .nav a,
  .ix,
  .foot-col-a a,
  .foot-col-b a,
  .card-link,
  .proof-t a,
  .drawer-alt a { padding-block: 12px; min-height: 44px; }
  /* padding alone lands the 18px card link on 42; the floor makes it exact */

  /* The hero discipline rows are the exception: stacked 44px rows read as a
     hole-ridden list and pushed the block off a phone screen; even 34px still
     looked like gaps, not lines (Oleg, 30.08 ×2). 26px is plain text rhythm
     for 16px type and stays above the WCAG 2.2 AA floor of 24 — each row
     spans the full column width, so the width does the rest of the work. */
  .disc .ix { padding-block: 5px; min-height: 26px; }
}

/* the reel arrows are real buttons; give them the same floor */
.reel-btn { min-width: 44px; min-height: 44px; }


/* ── portrait shatter ────────────────────────────────────────
   The switch between portraits. A canvas over the stack runs one shared grid
   of square cells — the same cell language as the logo and the scroll mosaic.
   Each cell swaps its old content for its new content in place at its own
   random moment, so the change grows across the figure as chains of squares;
   nothing drifts and neither portrait covers the other. Painted by app.js;
   markup keeps the plain cross-fade as the fallback for reduced motion and
   not-yet-loaded slots. */
/* width/height are explicit on purpose: an absolutely-positioned REPLACED
   element with inset:0 but auto size falls back to its intrinsic size — the
   bitmap, which is 2x on retina. The canvas then rendered double-sized from
   the top-left corner and the portrait visibly jumped right-and-down for the
   duration of the flight. 100% pins the element to the stack box regardless
   of what the bitmap resolution is. */
.shatter {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* while the canvas owns the pixels, the real layers must snap, not fade —
   appended after .layer/.is-live so equal specificity resolves this way */
.layer.no-fade { transition: none; }


/* ── portrait tablets (iPad mini/Air vertical) ──────────────────
   The phone grammar, one size up (Oleg, 30.08): hero figure low and large
   with the fade to solid black, and Work stacked — heading on top, bigger
   cards below. Landscape tablets keep the desktop scheme. */
@media (min-width: 781px) and (max-width: 1030px) and (orientation: portrait) {
  /* same move as the phone (Oleg, 30.08): the figure keeps its size but
     rides up under the mark — top-pinned, the fade mask from the shared
     portrait rule swallows the crop before the discipline strip */
  .stack { margin-top: 90px; margin-bottom: 0; height: min(74vh, 100vw * 2076 / 1792); }
  .stage { align-items: flex-start; }
  .stage::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 36vh;
    background: linear-gradient(180deg, rgba(5,5,5,0) 0%, rgba(5,5,5,0.78) 62%, #050505 92%);
    pointer-events: none;
  }

  .work { display: block; padding-top: 10vh; }
  .reel-lead { flex-basis: auto; width: auto; margin: 0 var(--edge) 4vh; padding: 0; }
  .reel-nav { justify-content: flex-end; margin: 0 var(--edge) 5vh; }
  .card { flex: 0 0 56vw; }
}


/* ── landscape tablets with touch ───────────────────────────────
   Desktop columns, but coarse-pointer targets make the five disciplines
   45px each — they overran the fixed seven-line box and slid off the
   bottom of a 768px screen. Same cure as the phone: the box sizes itself
   and stands clear of the counter. */
@media (min-width: 781px) and (orientation: landscape) and (pointer: coarse) {
  .col-bottom { height: auto; bottom: 84px; }
  .cnt { bottom: 28px; }
}

/* ── under 680px the five names genuinely overflow ─────────────
   Only here the strip scrolls: the right tail lets the LAST name dock at
   the left edge, and app.js moves the strip only on real overflow — on
   anything wider the row stands still and just the dot moves. The footer
   CTA goes back to full width on the same screens: between phone margins
   the hugged button read lost, on a tablet the full-bleed slab did
   (Oleg, 30.08). */
@media (max-width: 679px) {
  .disc-rows { padding-right: 55vw; }
  .foot-cta { width: 100%; }
}
/* 680–1030 portrait: the row is a squeeze at 28px gaps (the five names need
   ~715px) — a tighter gap buys the fit, so nothing ever scrolls here */
@media (min-width: 680px) and (max-width: 780px),
       (min-width: 680px) and (max-width: 1030px) and (orientation: portrait) {
  .disc-rows { gap: 16px; }
}

/* ── desktop: the disciplines move to the LEFT axis ────────────
   On the wide layout the list sat on the 58% axis and landed on the broad
   portraits — grey names drowned in the diver's wetsuit (Oleg, 30.08).
   Name left, figure centre, meaning right: the counter takes the top line
   of the bottom box as the list's header, the five names follow under it.
   Portrait tablets keep the strip; this covers everything wider. */
@media (min-width: 1031px), (min-width: 781px) and (max-width: 1030px) and (orientation: landscape) {
  .col-bottom.disc { left: var(--edge); }
  .col-bottom.disc .disc-rows { margin-top: var(--step); }
  /* the counter stands a full line clear of the list, not on its shoulder;
     the caption headline drops onto BRANDING's own baseline — same size,
     same line, one horizontal read (Oleg, 30.08) */
  .cnt { bottom: calc(var(--bot) + var(--step) * 7); }
  .col-bottom.now .now-line { margin-top: var(--step); }
}

/* ── the small iPhone (short viewport) ─────────────────────────
   Four faces at 88px sat shoulder to shoulder on a 375px screen — smaller
   faces buy the air back without giving up the single row. The top-pinned
   figure steps another 40px up: on a 667px viewport the 90px seat pushed
   the caption block too close to the floor (Oleg, 30.08). */
@media (max-width: 679px) {
  .clock svg { width: 64px; height: 64px; }
  .clocks { gap: var(--step) 16px; }
}
@media (max-width: 679px) and (max-height: 700px) {
  .stack { margin-top: 50px; }
}
