/* ==========================================================================
   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);
  --t-45: rgba(255, 255, 255, 0.45);

  /* 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;
  height: 100vh;
  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 sits a line below the identity, dimmer than the name: it explains,
   it does not compete. The bullet is the only punctuation the hero uses. */
.col-id-range { margin-top: var(--step); color: var(--t-55); }
.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; }

/* 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;
  /* full strength and full colour — Oleg's call (30.08): no dimming, no
     desaturation. If colour ends up fighting the red field, the grayscale
     filter that used to live here is one line away. */
}
/* 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);
}
.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 ──────────────────────────────────────────────── */

.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);
  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-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, behind the chest rather than the waist */
  .band { top: 38%; }

  .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). */
  .col-bottom { bottom: 110px; height: auto; }
  .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; }

  .stack { height: min(64vh, 88vw * 2076 / 1792); }
  /* the figure stands on the lower edge of the screen — the waist crop
     leaves through the bottom under the gradient instead of hanging
     mid-air (Oleg, 30.08) */
  .stage { align-items: flex-end; }

  .cnt { bottom: 24px; }

  /* 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 desktop standfirst indent is a column device; on a phone it reads
     as a hole in the layout */
  .serve .sec-sub { margin-left: 0; max-width: 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 CTA always fits: full width between equal margins (Oleg, 30.08) */
  .foot-lead { padding-right: var(--edge); }
  .foot-cta,
  .drawer-send { display: flex; width: 100%; }
  .foot-cta { 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);
  }
  .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; }
  .foot-fine { flex-direction: column; gap: 6px; }

  .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 */
  .stack { height: min(82vh, 124vw * 2076 / 1792); margin-bottom: 6vh; }
  /* 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) {
  /* the whole column — standfirst, titles, copy — pulls in towards the
     numbers; the desktop 96px gutter read as a gulf here (Oleg, 30.08) */
  .serve .sec-sub { margin-left: 58px; max-width: 62ch; }
  .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-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);
  }
  .clock:nth-child(1), .clock:nth-child(2), .clock:nth-child(3), .clock:nth-child(4) { grid-column: auto; }
}
@media (max-width: 780px) {
  .serve .sec-sub { margin-left: 0; max-width: none; }
  /* 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);
}

.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 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) {
  .stack { height: min(74vh, 100vw * 2076 / 1792); margin-bottom: 5vh; }
  .stage { align-items: flex-end; }
  .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; }
}
