/* vantagecreativehouse.com · site styles.
   Every value here comes from design-system/ (tokens.css is generated from it verbatim).
   Components mirror design-system/components/**.jsx one for one. Do not add styles the system does not contain. */

@import "tokens.css";

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: auto; }
body { margin: 0; min-height: 100vh; position: relative; overflow-x: hidden; }
body.vch { background: var(--ground); color: var(--ink); font-family: var(--font-body); font-weight: var(--weight-body); font-size: var(--text-body); line-height: var(--lh-body); -webkit-font-smoothing: antialiased; }
.vch-grain::before { z-index: 0; }
body > * { position: relative; z-index: 1; }
p { margin: 0; }
img, video { display: block; max-width: 100%; }
h1, h2, h3 { margin: 0; font-weight: 400; }
ul { list-style: none; margin: 0; padding: 0; }
a { color: inherit; }
button { font: inherit; }
:focus-visible { outline: var(--focus) solid var(--section, var(--ink)); outline-offset: var(--focus-offset); }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---------- type ---------- */
.display { font-family: var(--font-display); font-weight: 400; text-transform: uppercase; line-height: var(--lh-display); letter-spacing: var(--ls-display); text-wrap: balance; margin: 0; }
.display--wall { font-size: var(--text-wall); line-height: var(--lh-wall); letter-spacing: var(--ls-wall); }
.display--one { font-size: var(--text-display-1); max-width: 14ch; }
.display--two { font-size: var(--text-display-2); max-width: 18ch; }
.label { font-family: var(--font-body); font-weight: var(--weight-label); font-size: var(--text-label); line-height: var(--lh-label); letter-spacing: var(--ls-label); text-transform: uppercase; text-wrap: balance; }
.muted { color: var(--muted); }
.small { font-size: var(--text-small); }
.measure { max-width: var(--measure); }
.square { display: inline-block; width: .16em; height: .16em; background: var(--section); vertical-align: baseline; margin-left: .06em; }
.eyebrow { display: flex; gap: 12px; color: var(--muted); margin: 0; }
.eyebrow__n { color: var(--eyebrow-number, var(--ink)); }
.link { color: var(--link); text-decoration: underline; text-decoration-color: var(--muted); text-decoration-thickness: 1px; text-underline-offset: 3px; transition: color var(--t-ui), text-decoration-color var(--t-ui); }
.link:hover { color: var(--link-hover); text-decoration-color: var(--link-hover); }
.lines p { margin: 0; }
.lines p + p.gap { margin-top: var(--s-4); }

/* ---------- button ---------- */
.btn { display: inline-block; padding: 12px 24px; border-radius: var(--radius); text-decoration: none; cursor: pointer; line-height: 1; white-space: nowrap; box-sizing: border-box; transition: opacity var(--t-ui) var(--ease-out), background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out); }
.btn--primary { background: var(--ink); color: var(--ground); border: 1px solid var(--ink); }
.btn--primary:hover { opacity: var(--hover-ink); }
.btn--secondary { background: transparent; color: var(--ink); border: 1px solid var(--ink); }
.btn--secondary:hover { background: var(--ink); color: var(--ground); }
.btn--house { background: var(--house); color: var(--ink); border: 1px solid var(--house); }
.btn--film { background: var(--film-ink); color: var(--ink); border: 1px solid var(--film-ink); }
.btn[disabled] { background: var(--surface); color: var(--muted); border: 1px solid var(--surface); cursor: default; }

/* ---------- deadline bar ----------
   The bar is the one piece of chrome that has to be read rather than glanced at: it carries the
   competition deadline and the only link to it, at --text-label, 12px. The nav is a set of words a
   reader already knows the shape of, so --nav-over-film (Erin's token, .2) is right for the nav and
   stays exactly where it is. It is not right for 12px of live information, and measured over the
   beds it is what made the line unreadable: 1.05:1 on founders, 1.00:1 on the studios Kinetic plate,
   1.23:1 on Home's hero, against 4.5:1 for AA at this size.

   So the bar carries its own strength (--deadline-over-film) and its own scrim: one thin band drawn
   by the bar's own ::before, sized to the bar and fading out just below it, so there is no hard edge
   and the nav underneath is untouched. Ink under the ivory ink; ivory under the ink of the light
   variant. Nothing else on the page sees it. */
:root {
  --deadline-over-film: 1;            /* the bar's own strength over film; the nav keeps --nav-over-film */
  --deadline-scrim: rgba(20, 20, 20, .82);      /* under ivory chrome */
  --deadline-scrim-out: rgba(20, 20, 20, 0);    /* the same colour at zero: a gradient to the keyword
                                                   `transparent` fades through transparent black and
                                                   leaves a fringe under the pale band */
  --deadline-scrim-light: rgba(246, 245, 242, .86); /* under ink chrome, over a bright bed */
  --deadline-scrim-light-out: rgba(246, 245, 242, 0);
  --deadline-scrim-fade: 14px;        /* how far below the bar the band fades to nothing */
  /* The bar's own height, and the nav's sticky offset. site.js overwrites it from layout on load, on
     resize and when the fonts land — but site.js is the one thing on the page that is allowed not to
     run, and with the property unset the old `var(--deadline-h, 0px)` fallback pinned the nav at viewport
     top UNDER the bar (z-index 11 against the nav's 10): the wordmark sliced in half and the Start here
     CTA behind the bar, measured at 36px of occlusion at 1440 and 52px at 390 (QA F3). These are the
     measured heights, so the no-JS layout is the layout, and site.js's value lands on top of an already
     correct one rather than in place of a wrong one. */
  --deadline-h: 36px;
}
@media (max-width: 720px) { :root { --deadline-h: 52px; } }
.deadline { position: sticky; top: 0; z-index: 11; background: var(--surface); border-bottom: var(--line) solid var(--rule); padding: 10px var(--margin); color: var(--ink); }
/* Phase 66 · the target. Measured at 39x13px, the smallest interactive element on the site and one
   of the five entries the conversion audit walked: the whole competition is entered through it, on
   nine pages, at every scroll position. The padding takes it past 44px tall; the equal negative
   margin gives the space straight back to the line box, so the bar's own height is unchanged at
   both widths and every measurement taken against --deadline-h still holds. Padding, not a taller
   bar: the bar already eats 200px of a 667px frame while [date] wraps it to three lines. */
.deadline a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px;
  display: inline-block; padding-block: 16px; margin: -16px 0 -16px var(--s-3); }
/* over the hero film the bar loses its fill and goes ivory, on its own band; the surface returns after the hero */
.deadline { transition: opacity var(--t-ui) var(--ease-out), background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out); }
.deadline--film { background: transparent; border-bottom-color: transparent; color: var(--film-ink); opacity: var(--deadline-over-film); }
.deadline--film::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: calc(100% + var(--deadline-scrim-fade));
  background: linear-gradient(to bottom, var(--deadline-scrim) 0%, var(--deadline-scrim) 70%, var(--deadline-scrim-out) 100%);
  pointer-events: none; z-index: -1;
}
.deadline--film a { color: var(--film-ink); }
.deadline--film.deadline--film-light { color: var(--ink); opacity: var(--deadline-over-film); }
.deadline--film.deadline--film-light::before { background: linear-gradient(to bottom, var(--deadline-scrim-light) 0%, var(--deadline-scrim-light) 70%, var(--deadline-scrim-light-out) 100%); }
.deadline--film-light a { color: var(--ink); }

/* ---------- nav ---------- */
.nav { position: sticky; top: var(--deadline-h); z-index: 10; display: flex; align-items: center; gap: var(--s-6); height: var(--nav-h); padding: 0 var(--margin); background: var(--ground); opacity: 1; transform: none; transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out), background var(--t-ui) var(--ease-out); }
.nav__mark { display: flex; }
.nav__mark img { height: 29px; width: auto; }
.nav__mark .is-film, .nav--film .nav__mark .is-ink { display: none; }
.nav--film .nav__mark .is-film { display: block; }
.nav__items { display: flex; gap: var(--s-5); margin-left: auto; flex-wrap: wrap; font-size: calc(var(--text-label) * 1.3); }
.nav__items a { text-decoration: none; color: var(--muted); padding-bottom: 4px; border-bottom: 2px solid transparent; transition: color var(--t-ui); }
.nav__items a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--section); }
[data-section="founders"] .nav__items a[aria-current="page"] { border-bottom-color: transparent; }
/* ---------- the nav's plate over film · Phase 66 ------------------------------------------------
   Measured on The First Cut's first frame at 1440x900, the type hidden for the sample and each nav
   item scored against the composite under its own box: +Founders 2.88, +Studios 3.25, +Creative
   2.79 and +Solutions 1.15 to 1.17 — four of four under 4.5:1, one of them by a factor of four,
   because +Solutions sits over the dark camera body. At 390 +Solutions still failed at 3.55 and the
   wordmark dropped to 3.51. The same failure is on every page whose beat 00 is a bed.

   The answer is the one already on the page: the deadline bar reads 12.4 to 12.9:1 on that exact
   frame BECAUSE IT DRAWS ITS OWN PLATE and carries its own strength (--deadline-over-film: 1)
   rather than the nav's fade. This is that device, applied to the nav band: one thin band drawn by
   the nav's own ::before, sized to the nav and fading out just below it, so there is no hard edge;
   ink under ivory chrome, ivory under the ink of the light variant; and, exactly as the bar did,
   its own strength token so the type over the plate is legible type.

   --nav-over-film (Erin's token, .2) IS NOT CHANGED — its value is where she set it in tokens.css.
   What changed is that the nav over film now reads --nav-over-film-plated, the way the bar reads
   --deadline-over-film, because a plate under 20-percent type buys nothing: the arithmetic is that
   0.2-strength ivory over an 0.8-alpha ink plate still lands at about 1.5:1. Set
   --nav-over-film-plated: var(--nav-over-film) and the nav is back to exactly where it was, plate
   and all. Recorded in PROGRESS Phase 66 and named for Erin.

   The opacity moved from the element to its three children so the plate is NOT inside the fade
   group — a plate that fades with the type it sits under is not a plate. Nothing else moves: the
   wordmark, the items and the CTA do not overlap, so per-child opacity and element opacity paint
   the same picture. */
:root {
  --nav-over-film-plated: 1;                        /* the nav's own strength over its plate */
  --nav-scrim: rgba(20, 20, 20, .80);               /* under ivory chrome */
  --nav-scrim-out: rgba(20, 20, 20, 0);
  --nav-scrim-light: rgba(246, 245, 242, .86);      /* under ink chrome, over a bright bed */
  --nav-scrim-light-out: rgba(246, 245, 242, 0);
  --nav-scrim-fade: 16px;                           /* how far below the nav the band fades to nothing */
}
.nav--film { background: transparent; }
.nav--film > * { opacity: var(--nav-over-film-plated); transition: opacity var(--t-ui) var(--ease-out); }
.nav--film:hover > *, .nav--film:focus-within > * { opacity: 1; }
.nav--film::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: calc(100% + var(--nav-scrim-fade));
  background: linear-gradient(to bottom, var(--nav-scrim) 0%, var(--nav-scrim) 70%, var(--nav-scrim-out) 100%);
  pointer-events: none; z-index: -1;
}
.nav--film.nav--film-light::before { background: linear-gradient(to bottom, var(--nav-scrim-light) 0%, var(--nav-scrim-light) 70%, var(--nav-scrim-light-out) 100%); }
.nav--film .nav__items a { color: rgba(239, 236, 230, .7); }
.nav--film .nav__items a[aria-current="page"] { color: var(--film-ink); border-bottom-color: transparent; }
.nav--film .btn--house { background: var(--film-ink); border-color: var(--film-ink); }
/* over a bright bed the ivory chrome disappears, so it goes ink; the plate above is ivory there and
   the strength is the plated one, for the same reason. */
.nav--film.nav--film-light > * { opacity: var(--nav-over-film-plated); }
.nav--film-light .nav__mark .is-film { display: none; }
.nav--film-light .nav__mark .is-ink { display: block; }
.nav--film-light .nav__items a { color: rgba(32, 32, 32, .75); }
.nav--film-light .nav__items a[aria-current="page"] { color: var(--ink); }
.nav--film-light .btn--house { background: var(--ink); border-color: var(--ink); color: var(--film-ink); }
.nav--hidden { transform: translateY(-100%); }
@media (max-width: 720px) {
  /* stacks: wordmark and button on one row, the four items under */
  .nav { display: grid; grid-template-columns: 1fr auto; gap: var(--s-3) var(--s-4); height: auto; padding: var(--s-4) var(--margin); align-items: center; }
  .nav__mark img { height: 26px; }
  .nav__items { margin-left: 0; grid-column: 1 / -1; }
  .nav__cta { grid-row: 1; grid-column: 2; }
}

/* ---------- track · pinned film, the copy animates over it ----------
   The Scout Motors mechanics (reference/scout/scroll-notes.md), on real film that plays:
   A  the stage pins for (beats + 1) viewports; the beats cross-dissolve in place, the film changes under them
   B  .track--wall: giant type scrolls at scroll speed over the pinned film
   C  the block that follows (.ground, .footer) slides up over the pinned stage and covers it
   Type on film is ivory. Copy on film is short: a headline, a line, one action. */
/* --close is the closing allowance: how many beat-heights the LAST beat holds for while the ground
   slides over it. At 1 (the default, and what every track shipped with) the ground starts covering the
   moment the last beat arrives, so it reads for a third of the scroll the others get. A track that
   wants its last beat to read like the others sets --close: 2. site.js reads this property, so the
   beat index arithmetic follows the height rather than being hardcoded beside it. */
.track { position: relative; --beats: 1; --close: 1; --beat-h: 100vh; height: calc((var(--beats) + var(--close)) * var(--beat-h)); margin-bottom: -100vh; color: var(--film-ink); }
.track--top { margin-top: calc(-1 * (var(--chrome-h, var(--nav-h)) + var(--deadline-h))); }
.track__stage { position: sticky; top: 0; height: 100vh; min-height: 560px; overflow: hidden; background: #1a1917; }
.track__poster, .track__film { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.track__film { opacity: 0; transition: opacity var(--t-dissolve) var(--ease-out); }
.track__film.is-on { opacity: 1; }
/* a flat quarter of ink so ivory reads on any frame, and more at the foot where the copy sits */
.track__scrim { position: absolute; inset: 0; background: linear-gradient(to top, rgba(32,32,32,.55), rgba(32,32,32,.25) 45%, rgba(32,32,32,.2)); pointer-events: none; }
/* a bright bed takes ink type over a pale scrim; a dark bed keeps ivory over ink. data-tone rides on each
   film and on the stage (the poster's own tone, which is what reduced motion and first paint show). */
.track__stage[data-tone="light"] .track__scrim { background: linear-gradient(to top, rgba(239,236,230,.62), rgba(239,236,230,.3) 45%, rgba(239,236,230,.24)); }
.track__stage[data-tone="light"] .display,
.track__stage[data-tone="light"] .lines,
.track__stage[data-tone="light"] .label,
.track__stage[data-tone="light"] p { color: var(--ink); }
.track__stage[data-tone="light"] .track__q { color: rgba(32,32,32,.72) !important; }
.track__stage[data-tone="light"] .btn--film { background: var(--ink); color: var(--film-ink); border-color: var(--ink); }
/* every beat's first line sits on the same y (Scout: a third of the way down); the hold line sits just above it */
.track__copy { position: absolute; inset: 0; --copy-top: 36vh; }
.track__beat { position: absolute; left: var(--margin); right: var(--margin); top: var(--copy-top); bottom: var(--s-7); display: grid; align-content: start; gap: var(--s-5); transform: translateY(var(--reveal-rise)); transition: opacity var(--t-dissolve) var(--ease-out), transform var(--t-dissolve) var(--ease-out), visibility 0s var(--t-dissolve); }
/* Hiding a beat is JavaScript's job, so only a page with JavaScript alive is allowed to hide one.
   Without the .js class every beat renders — see the flat-track release further down. */
.js .track__beat { opacity: 0; visibility: hidden; }
/* Erin, 16 Sep: the outgoing block does not slide away, it fades where it stands, and the next one fades
   into the same spot after it so the eye never has to travel. The incoming beat waits half a dissolve
   before it starts, so the two are never both fully on; the small rise stays on the way in only. */
.track__beat.is-on { opacity: 1; visibility: visible; transform: none; transition-delay: calc(var(--t-dissolve) * .5); }
.track__beat.is-past { transform: none; }
/* C · the story beats sit in the MIDDLE of the frame. Home's opening film composes its rooms around a
   bright rectangle that ends up dead centre — a seamless, a laptop screen, a whiteboard — so the centre is
   where the open space is, and where the type belongs. Vertical centring too: these beats are one line, not
   a stack, so they read as placed rather than parked at the top. */
.track__beat--centre { top: 0; bottom: 0; align-content: center; justify-items: center; text-align: center; }
.track__beat--centre .display { max-width: 22ch; }
/* the holding question sits just above the centred block, clear of the tallest of them (--beat-max, which
   the script measures) rather than at a guessed offset */
.track__hold--centre { top: 50%; text-align: center;
  transform: translateY(calc(-50% - var(--beat-max, 0px) / 2 - var(--s-5))); }

/* a line that holds through several beats: data-from / data-to, toggled by the script */
.track__hold { position: absolute; left: var(--margin); right: var(--margin); top: var(--copy-top); transform: translateY(calc(-100% - var(--s-5))); margin: 0; opacity: 0; transition: opacity var(--t-dissolve) var(--ease-out); pointer-events: none; }
.track__hold.is-on { opacity: 1; }
.track .display, .track .lines, .track .label, .track p { color: var(--film-ink); }
.track .btn--film { color: var(--ink); }
/* 12ch at 96px set the hero sentence as four ragged lines of 10 / 9 / 13 / 11 characters, which reads
   as type that overflowed rather than type that was set. 18ch gives two even lines. */
.track .display--one { font-size: var(--text-display-1); max-width: 18ch; }
.track .display--two { max-width: 18ch; }
.track__line { max-width: 34ch; }
.track__q { color: rgba(239, 236, 230, .72) !important; }
.track__beat .lines { max-width: 45ch; }
.track__beat > .btn { justify-self: start; }
/* the first beat on Home keeps the hero layout: the beat spans the stage, the H1 on the copy line, the foot on the fold */
.track__beat--hero { display: block; top: 0; }
/* One column. "Find your vantage." used to sit in a second 5fr column with align-items: end, which
   landed it mid-frame at x 840 / y 678, attached to nothing. It now sits in .hero__foot above the
   button, where the eye is already going. */
.track__beat--hero .hero__top { position: absolute; top: var(--copy-top); left: 0; right: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-6); align-items: end; }
/* The hero beat never cross-dissolves with another beat — it is the only beat on its track — so it
   carries no transition, and site.js is free to drive its opacity per scroll frame to clear the
   ground's edge before it reaches the letterforms. A transition here would lag the scrub by 800ms. */
.track__beat--hero { transition: none; }
.track__beat--hero .hero__foot { position: absolute; left: 0; bottom: var(--s-7); display: grid; gap: var(--s-4); }
.hero__place { white-space: nowrap; }
/* the scale beat: the headline grows from 1.6x to 1 across its own beat; the script drives the transform */
.track__scale { transform-origin: left center; transform: scale(var(--hero-scale-from)); max-width: 18ch; }
@media (max-width: 720px) { .track__scale { max-width: calc((100vw - 2 * var(--margin)) / var(--hero-scale-from)); } }
/* the five answers on a phone take the answer size */
/* Home · the vantage beat. Big Anton statement, then a supporting capability list in the label face,
   each item revealed on scroll by grow.js (--g -> --on, one line at a time). */
.lines--vantage { --g: 0; list-style: none; margin: var(--s-5) 0 0; padding: 0; max-width: 34ch; }
.lines--vantage li { --on: clamp(0, calc((var(--g, 0) - var(--at)) * 11), 1);
  font-size: clamp(14px, 1.5vw, 18px); line-height: 1.4; letter-spacing: var(--ls-label);
  opacity: var(--on); transform: translateY(calc(12px * (1 - var(--on))));
  transition: opacity .35s ease, transform .35s cubic-bezier(.2,.7,.2,1); }
.lines--vantage li + li { margin-top: var(--s-3); }
.lines--vantage li:nth-child(1) { --at: 0.04; }
.lines--vantage li:nth-child(2) { --at: 0.24; }
.lines--vantage li:nth-child(3) { --at: 0.44; }
.lines--vantage li:nth-child(4) { --at: 0.64; }
@media (min-width: 721px) { .lines--vantage { max-width: none; } }  /* one line each on desktop */
@media (prefers-reduced-motion: reduce) { .lines--vantage li { transition: none; opacity: 1; transform: none; } }
@media (max-width: 720px) { .track__beat .display--two.track__answer { font-size: var(--text-answer); letter-spacing: var(--ls-answer); } }
@media (min-width: 721px) { .track__answer { letter-spacing: var(--ls-answer); max-width: 22ch; } }
/* the three looks (Studios): each name in its own type, one size up, on its own film */
.track .look__name { font-size: var(--text-display-1); }
/* B · the wall: page tops. 2.5 viewports; the type sits low, scrolls up over the film, the ground covers */
.track--wall { height: calc(var(--beat-h) * 1.5 + 100vh); }
.track--wall .track__flow { position: absolute; top: 62vh; left: var(--margin); right: var(--margin); z-index: 1; max-width: var(--wide); overflow-wrap: anywhere; }
.track--wall .pagetop__bar { background: var(--film-ink); }
.track--wall .eyebrow + .display--wall { margin-top: var(--s-4); }
.track--wall .eyebrow { --eyebrow-number: var(--film-ink); }
.track--wall .pagetop__line, .track--wall .pagetop__more { max-width: var(--measure); }
/* a closing track: one viewport, no pin, the footer follows it */
.track--close { height: 100vh; margin-bottom: 0; }
/* C · what follows a track slides over it */
.ground, .footer { position: relative; z-index: 2; background: var(--ground); }
.ground { display: flow-root; padding-bottom: var(--section-gap); }
.ground--tight { padding-bottom: 0; }
.track + .footer { margin-top: 0; }
@media (max-width: 720px) {
  .track__copy { --copy-top: 30vh; }
  .track__beat--hero .hero__top { grid-template-columns: 1fr; gap: var(--s-4); }
  .track__beat--hero .display--one { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
  /* a face fills a phone frame, so the beats sit on the foot and the hold line above the tallest of them
     (--beat-max, measured by the script) */
  .track__beat:not(.track__beat--hero):not(.track__beat--centre) { top: auto; bottom: var(--s-8); }
  .track__hold:not(.track__hold--centre) { top: auto; bottom: calc(var(--s-8) + var(--beat-max, 0px) + var(--s-5)); transform: none; }
  /* the film's white space is central on a phone too — the frame is a centre crop of the same rooms */
  .track__beat--centre { top: 0; bottom: 0; }
  .track--wall .track__flow { top: 50vh; }
  .ground { padding-bottom: var(--section-gap-mobile); }
  .ground--tight { padding-bottom: 0; }
}

/* ---------- sections ---------- */
.section { padding: 0 var(--margin); margin-top: var(--section-gap); background: transparent; transition: background var(--t-ground) var(--ease-out), color var(--t-ground) var(--ease-out); }
.section__in { max-width: var(--content); }
.section--wide .section__in { max-width: var(--wide); }
.section--shift { padding: var(--s-9) var(--margin); }
.section--shift.is-on { background: var(--ground-shift); }
@media (max-width: 720px) {
  .section { margin-top: var(--section-gap-mobile); }
  .section--shift { padding: var(--s-8) var(--margin); }
}
/* The reveal is opt-in on JavaScript. site.js is what supplies .is-on, so CSS may only hide a section
   once site.js has proved it is alive (it sets data-js on <html> when it finishes; the inline head
   script drops the .js class again if that never happens). Without this, a 404 or a blocked extension
   turned six of eleven sections on Home into opacity 0 and nothing reported it. */
.js .reveal { opacity: 0; transform: translateY(var(--reveal-rise)); transition: opacity var(--t-reveal) var(--ease-out), transform var(--t-reveal) var(--ease-out); }
.js .reveal.is-on { opacity: 1; transform: none; }

/* statement · eyebrow, display two, lines, one still */
.statement .display--two { margin-top: var(--s-4); }
.statement .lines { margin-top: var(--s-5); }
.statement .still { margin-top: var(--s-6); }
.statement .steps { margin-top: var(--s-6); }
.statement .after { margin-top: var(--s-5); }

/* still · a frame inside the column */
.still { margin: 0; }
.still__frame { position: relative; aspect-ratio: 16 / 9; background: #2a2926; overflow: hidden; }
.still__frame--tint { background: var(--section); }
.still__frame--tint::after { content: ""; position: absolute; inset: 0; opacity: .22; mix-blend-mode: multiply; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E"); }
.still__frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* a generated still, per the design system: desaturated, dimmed, 45% grain, labelled Generated */
.still__frame--generated img { filter: var(--generated-filter); }
.still__frame--generated::after { content: ""; position: absolute; inset: 0; opacity: var(--generated-grain); mix-blend-mode: soft-light; pointer-events: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E"); }
.still__generated { position: absolute; top: var(--s-4); left: var(--s-4); color: var(--film-ink); z-index: 1; }
.still__caption { color: var(--muted); margin-top: var(--s-3); }

/* rule list · rows on 1px rules */
.rules { max-width: var(--content); }
.rules li { display: grid; grid-template-columns: 200px 1fr; gap: var(--s-5); padding: var(--s-4) 0; border-top: var(--line) solid var(--rule); }
.rules li:last-child { border-bottom: var(--line) solid var(--rule); }
.rules__l { color: var(--ink); line-height: 1.6; }
.rules--bold .rules__l { font-weight: var(--weight-bold); font-family: var(--font-body); font-size: var(--text-body); letter-spacing: 0; text-transform: none; line-height: var(--lh-body); }
@media (max-width: 720px) { .rules li { grid-template-columns: 1fr; gap: var(--s-1); } }

/* Phase 58 · .answers-wrap / .answers__static / .answers__closing are gone. They were the five
   answers set down again as a ground section after the opening track on founders.html, which was
   the only page left using them (Home's resolve became a beat in Phase 55). The resolve is a beat
   on the stage now — .track__beat--resolve, at the end of this file — so the five set down where
   they stood instead of in a section under the film. Grepped across every html, css, js and md
   before removal: nothing else referenced them. */

/* work strip · three pieces, poster and play, plays in place. Film goes edge to edge here and in the hero only. */
.work { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); width: 100vw; max-width: none; margin-left: calc(-1 * var(--margin)); }
.work > :first-child { grid-column: 1 / -1; }
.piece { position: relative; aspect-ratio: 16 / 9; background: #2a2926; overflow: hidden; }
.piece--play { cursor: pointer; }
.piece__poster, .piece video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.piece__cap { position: absolute; left: var(--s-4); bottom: var(--s-4); display: flex; gap: var(--s-4); align-items: center; color: var(--film-ink); background: none; border: 0; padding: 0; cursor: inherit; text-align: left; }
.piece__play { width: var(--play); height: var(--play); border: 1px solid var(--film-ink); border-radius: 50%; display: grid; place-items: center; flex-shrink: 0; }
.piece__play::before { content: ""; width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; border-left: 9px solid var(--film-ink); margin-left: 2px; }
.piece__title { display: grid; gap: 2px; }
.piece__title strong { font-weight: var(--weight-bold); }
.piece.is-playing .piece__cap, .piece.is-playing .piece__poster { display: none; }
@media (max-width: 720px) { .work { grid-template-columns: 1fr; } }
.work + .proof { margin-top: var(--s-6); }

/* proof line */
.proof { color: var(--muted); max-width: var(--measure); }
.work + .proof { margin-top: var(--s-6); }
.section .display--two + .work { margin-top: var(--s-6); }

/* start here · ends every page */
.start .lines { margin-top: var(--s-5); }
.start .btn { margin-top: var(--s-6); }

/* ---------- footer ---------- */
.footer { border-top: var(--line) solid var(--rule); padding: var(--s-7) var(--margin); margin-top: var(--section-gap); display: grid; grid-template-columns: auto 1fr auto auto; gap: var(--s-6); align-items: start; }
.footer__mark { display: grid; gap: 8px; }
.footer__mark img { height: 22px; width: auto; }
.footer__mark span { color: var(--ink); }
.footer__line { font-size: var(--text-small); }
.footer__links { display: grid; gap: 8px; }
.footer__links a { color: var(--ink); text-decoration: none; }
.footer__place { color: var(--muted); white-space: nowrap; }
@media (max-width: 720px) { .footer { grid-template-columns: 1fr 1fr; gap: var(--s-6) var(--s-5); margin-top: var(--section-gap-mobile); } .footer__place { white-space: normal; } }

/* ---------- reduced motion ---------- */
/* tracks do not pin: one poster, the beats stacked under it in reading order, no film */
@media (prefers-reduced-motion: reduce) {
  .track { height: auto; margin-bottom: 0; }
  .track__stage { position: relative; height: auto; min-height: 0; }
  .track__film { display: none; }
  .track__poster { position: relative; display: block; aspect-ratio: 16 / 9; height: auto; }
  .track__scrim { display: none; }
  .track__copy { position: relative; inset: auto; padding: var(--s-8) var(--margin); display: grid; gap: var(--s-7); }
  /* .js-scoped, to out-specify the .js .track__beat hide rule above */
  .track__beat, .track__hold, .js .track__beat, .js .track__hold { position: static; opacity: 1; visibility: visible; transform: none; }
  .track__beat--hero .hero__top, .track__beat--hero .hero__foot { position: static; }
  .track__beat--hero .hero__foot { margin-top: var(--s-6); }
  .track__scale { transform: none; }
  .track--wall .track__flow { position: relative; top: auto; left: auto; right: auto; padding: var(--s-8) var(--margin) 0; }
  .track--wall .track__stage { min-height: 0; }
  .track--wall .track__copy { display: none; }
  .reveal, .js .reveal { transform: none; }
  .nav, .section, .btn, .link { transition: none; }
}

/* ======================================================================
   Components for the section pages, the Founder Page, the forms, the roadmap.
   Values from design-system/design-system.html and components/**.jsx.
   ====================================================================== */

/* page top · wall headline with the square, 40vh down on desktop */
.pagetop { padding: calc(40vh - var(--nav-h)) var(--margin) 0; }
.pagetop__in { max-width: var(--wide); overflow-wrap: anywhere; }
.pagetop__bar { width: 48px; height: 2px; background: var(--section); margin-bottom: var(--s-4); }
.pagetop .eyebrow { --eyebrow-number: var(--section); }
.pagetop .display--wall { margin-top: var(--s-4); }
.pagetop--plain .display--wall { margin-top: 0; }
.pagetop__line { margin-top: var(--s-5); max-width: var(--measure); }
.pagetop__more { margin-top: var(--s-4); max-width: var(--measure); }
.pagetop .display--one { max-width: 14ch; }
@media (max-width: 720px) { .pagetop { padding-top: calc(var(--nav-h) + var(--s-8)); } }

/* headline then lines */
.block .lines, .block .rules, .block .faq, .block .looks, .block .ledger, .block .roster, .block .gallery, .block .beats { margin-top: var(--s-5); }
.block .rules, .block .faq, .block .looks { margin-top: var(--s-6); }
.block .after { margin-top: var(--s-5); }
.block .btn { margin-top: var(--s-6); }

/* beats · one line, middots in ink */
.beats { display: flex; flex-wrap: wrap; gap: 0 .5em; }
.beats span { white-space: nowrap; }
.beats span + span::before { content: "·"; color: var(--ink); margin-right: .5em; }
.beats--after { margin-top: var(--s-6); }

/* looks · three columns, each name in its own voice, a slot for the still */
.looks { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-6); }
.look__slot { position: relative; aspect-ratio: 16 / 9; background: var(--section); margin-bottom: var(--s-4); overflow: hidden; }
.look__slot::after { content: ""; position: absolute; inset: 0; opacity: .22; mix-blend-mode: multiply; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E"); }
.look__name { font-size: var(--text-display-2); line-height: 1; }
.look__name--cinema { font-family: var(--font-body); font-weight: 300; font-style: italic; }
.look__name--kinetic { font-family: var(--font-display); text-transform: uppercase; letter-spacing: -.01em; }
.look__name--field { font-family: var(--font-body); font-weight: 400; }
.look p { margin-top: var(--s-3); }

/* faq · plain list on rules, no accordion */
.faq li { padding: var(--s-4) 0; border-top: var(--line) solid var(--rule); }
.faq li:last-child { border-bottom: var(--line) solid var(--rule); }
.faq__q { font-weight: var(--weight-bold); font-size: var(--text-question); }
.faq__a { margin-top: var(--s-2); color: var(--muted); max-width: 60ch; }

/* ledger · rows on rules, bold title left, lines right */
.ledger li { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-5); padding: var(--s-5) 0; border-top: var(--line) solid var(--rule); }
.ledger li:last-child { border-bottom: var(--line) solid var(--rule); }
.ledger__t { font-weight: var(--weight-bold); }

/* profile row · portrait left if supplied, text right */
.profile { display: grid; grid-template-columns: 1fr; gap: var(--s-6); padding: var(--s-6) 0; border-top: var(--line) solid var(--rule); }
.profile--portrait { grid-template-columns: var(--portrait) 1fr; }
.profile:last-child { border-bottom: var(--line) solid var(--rule); }
.profile__img { width: var(--portrait); height: var(--portrait); object-fit: cover; }
.profile__role { color: var(--muted); }
.profile .display--two { margin-top: var(--s-2); }
.profile__city { margin-top: var(--s-2); color: var(--muted); }
.profile__lines { margin-top: var(--s-4); max-width: var(--measure); }
.profile__lines p + p { margin-top: var(--s-2); }
@media (max-width: 720px) { .profile--portrait { grid-template-columns: 1fr; } }

/* roster · quiet two-column list */
.roster { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-2) var(--s-6); margin-top: var(--s-6); }
.roster li span { color: var(--muted); }

/* gallery · 4:5 tiles, three across, one on a phone, rules between; empty state */
/* .gallery__empty is gone (Phase 59). It set `The gallery is empty.` in the display face at 48px, which
   made a negative the loudest statement in the released half of the page and broke one copy.md paragraph
   into a headline and an orphan. The sentence is the first sentence of its paragraph and it is set as
   one, at body size, in .gallery__say. */
.gallery__name { color: var(--muted); margin: 0 0 var(--s-4); }
.gallery__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--rule); }  /* three across on desktop, one on a phone */
.gallery__grid > * { background: var(--ground); }
.gallery__frame { aspect-ratio: 4 / 5; display: flex; align-items: flex-end; padding: var(--s-4); color: var(--muted); white-space: nowrap; }
.tile { display: block; position: relative; aspect-ratio: 4 / 5; background: #2a2926; overflow: hidden; text-decoration: none; color: var(--film-ink); }
.tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: filter var(--t-ui) var(--ease-out); }
.tile:hover img { filter: brightness(var(--hover-brighten)); }
.tile__look { position: absolute; top: var(--s-4); left: var(--s-4); color: var(--film-ink); }
.tile__scrim { position: absolute; left: 0; right: 0; bottom: 0; height: 40%; background: linear-gradient(to top, rgba(32, 32, 32, .85), transparent); }
.tile__cap { position: absolute; left: var(--s-4); bottom: var(--s-4); display: grid; gap: 2px; }
.tile__cap strong { font-weight: var(--weight-bold); }
@media (max-width: 720px) { .gallery__grid { grid-template-columns: 1fr; } }

/* founder page */
.founder__top { padding: var(--s-5) var(--margin) 0; }  /* tight above the name so the cut clears the fold */
.founder__top-in { max-width: var(--wide); }
.founder__top .display--two { margin-top: var(--s-3); }
.founder__line { margin-top: var(--s-3); max-width: var(--measure); }
.founder__cut { margin-top: var(--s-5); }
.founder__cut .still__frame { background: #2a2926; }
.cut__cap { position: absolute; left: var(--s-4); bottom: var(--s-4); color: var(--film-ink); display: grid; gap: 2px; }
.cut__cap strong { font-weight: var(--weight-bold); }
.cut__video { position: absolute; inset: 0; width: 100%; height: 100%; }
.stills { display: grid; grid-template-columns: repeat(5, minmax(120px, 1fr)); gap: var(--s-4); }
.stills:focus-visible { outline: var(--focus) solid var(--section, var(--ink)); outline-offset: var(--focus-offset); }
.shorts { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--s-4); margin-top: var(--s-6); }
.shorts .still__frame { aspect-ratio: 9 / 16; }
.story { margin-top: var(--s-5); }
.story p + p { margin-top: var(--s-4); }
.embedrow { margin-top: var(--s-6); display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-7); align-items: start; }
.embedrow pre { margin: var(--s-4) 0 0; padding: var(--s-4); background: var(--surface); font-family: var(--font-body); font-size: var(--text-small); white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 720px) {
  .stills { grid-template-columns: repeat(5, 200px); overflow-x: auto; padding-bottom: var(--s-2); }
  .embedrow { grid-template-columns: 1fr; }
}

/* embed · the cut, one line, the word Vantage */
.embed { width: 100%; max-width: 100%; min-width: 320px; font-family: var(--font-body); font-weight: 300; font-size: var(--text-small); color: var(--ink); }
.embed__frame { position: relative; aspect-ratio: 16 / 9; background: #2a2926; }
.embed__frame video, .embed__frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* the line strip is width/16 tall, so frame (16:9) plus line make exactly 16:10, the ratio the iframe snippet declares */
.embed__line { display: flex; justify-content: space-between; align-items: center; gap: var(--s-4); width: 100%; aspect-ratio: 16 / 1; min-height: 28px; overflow: hidden; }
.embed__line > :first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Phase 66 · the line is an <h1> now (the embed is a top-level document too, and best-practice axe
   wants one). It inherits the line's own type, so nothing about the picture changes. */
.embed__who { margin: 0; font: inherit; letter-spacing: inherit; text-transform: none; }
.embed__line strong { font-weight: var(--weight-bold); }
.embed__line .label { color: var(--muted); }

/* forms · label above, 1px box on the surface, plain sentences for errors */
.form { display: grid; gap: var(--s-5); max-width: var(--content); }
.field { display: grid; gap: 8px; }
.field__label { color: var(--ink); }
.field input, .field textarea { width: 100%; box-sizing: border-box; min-height: var(--field-h); background: var(--surface); border: var(--line) solid var(--rule); border-radius: var(--radius); padding: 0 12px; font-family: var(--font-body); font-weight: 300; font-size: var(--text-body); color: var(--ink); outline: none; transition: border-color var(--t-ui); }
.field textarea { padding: 12px; resize: vertical; line-height: var(--lh-body); }
.field input:focus, .field textarea:focus { border-color: var(--ink); box-shadow: none; }
.field.is-error input, .field.is-error textarea { border-color: var(--error); }
.field__error { font-size: var(--text-small); color: var(--error); }
.field__error:empty { display: none; }
.form .btn { justify-self: start; }
.form__note { font-size: var(--text-small); color: var(--error); }
.form__note:empty { display: none; }

/* toggle · two words, a 2px section-colour underline slides between them */
.toggle { display: inline-grid; grid-template-columns: auto auto; gap: var(--s-6); position: relative; }
.toggle button { background: none; border: 0; padding: 0 0 6px; font-family: var(--font-body); font-weight: 300; font-size: var(--text-body); color: var(--muted); cursor: pointer; transition: color var(--t-ui); position: relative; }
.toggle button[aria-selected="true"] { color: var(--ink); }
.toggle button::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: var(--underline-toggle); background: var(--section); transform: scaleX(0); transform-origin: right; transition: transform var(--t-ui) var(--ease-out); }
.toggle button[aria-selected="true"]::after { transform: scaleX(1); }
.toggle button[aria-selected="true"] ~ button::after { transform-origin: left; }
[data-section="founders"] .toggle button::after { background: var(--ink); }

/* confirmation · Got it. */
.confirm { max-width: var(--content); }
.confirm .lines { margin-top: var(--s-5); }
.confirm .lines p + p { margin-top: var(--s-2); }

/* status token · label face in a 1px box */
.token { display: inline-block; padding: 6px 8px; border: var(--line) solid var(--ink); background: transparent; color: var(--ink); line-height: 1; white-space: nowrap; }
.token--now { border-color: var(--house); background: var(--house); }
.token--done { border-color: var(--rule); color: var(--muted); }

/* roadmap · a document read on a phone, and presented off a laptop. The 40vh page-top band is right for
   a section page arriving out of a film; on a document it is half a screen of nothing before the first
   line, so this page starts near the top. */
[data-page="roadmap"] .pagetop { padding-top: calc(var(--nav-h) + var(--s-7)); }
.roadmap { max-width: var(--wide); }
.roadmap__head { display: flex; align-items: baseline; gap: var(--s-5); flex-wrap: wrap; }
.roadmap__head .label { color: var(--muted); }
.roadmap__section { margin-top: var(--s-8); }
.roadmap__rows { margin-top: var(--s-5); }
/* the timeline: one spine down the page, each row a stop on it. The rail is a pseudo-element on the
   list rather than a border on each row, so it reads as a continuous line and an empty section still
   sits ON the line instead of leaving a hole in it. */
.roadmap__rows { position: relative; list-style: none; padding: 0 0 0 var(--s-6); margin-top: var(--s-5); }
.roadmap__rows::before { content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: var(--line);
  background: var(--rule); }
.roadmap__stop { position: relative; padding: 0; border-top: var(--line) solid var(--rule); }
.roadmap__row { position: relative; display: grid; grid-template-columns: 1fr auto; gap: var(--s-4);
  align-items: baseline; padding: var(--s-3) 0; }
.roadmap__tail { display: inline-flex; align-items: center; gap: var(--s-3); white-space: nowrap; }
.roadmap__stop:first-child { border-top: 0; }
.roadmap__dot { position: absolute; left: calc(-1 * var(--s-6) + 1px); top: calc(var(--s-3) + .55em);
  width: 9px; height: 9px; background: var(--ground); border: 2px solid var(--muted); border-radius: 50%; }
.roadmap__stop.is-now .roadmap__dot { background: var(--ink); border-color: var(--ink); }
.roadmap__stop.is-done .roadmap__dot { background: var(--muted); border-color: var(--muted); }
.roadmap__stop.is-next .roadmap__dot { background: var(--ground); border-color: var(--rule); }
.roadmap__body { display: grid; gap: 3px; }
.roadmap__text { display: block; }
.roadmap__when { color: var(--muted); }
.roadmap__stop.is-done .roadmap__text { text-decoration: line-through; color: var(--muted); }
.roadmap__stop.is-now .roadmap__text { font-weight: var(--weight-bold); }
.roadmap__leg { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; }
.roadmap__count { color: var(--muted); }
.roadmap__note { flex-basis: 100%; margin: var(--s-3) 0 0; max-width: var(--measure); color: var(--muted); }
@media print {
  .nav, .gate { display: none !important; }
  .roadmap { max-width: none; }
  .roadmap__section { break-inside: avoid; }
}
.gate { display: grid; gap: var(--s-5); max-width: 320px; }
.gate .btn { justify-self: start; }

/* start · the-first-cut · the hidden attribute must win over a component's own display (.field and .form are grids);
   the toggle sits at the start of the form column like the button does, so its underline is word-wide */
[hidden] { display: none !important; }
.form .toggle { justify-self: start; }

/* founder page · the cut: play circle beside the caption when there is film; caption alone on the poster */
.cut.piece--play .cut__cap { left: calc(var(--s-4) + var(--play) + var(--s-4)); }
.cut.is-playing .cut__cap, .cut.is-playing .cut__play, .cut.is-playing > img { display: none; }

/* ---- the founder graphics layer -------------------------------------------
   The edit room, drawn over the film: you watch the minute get cut and reframed as you scroll.
   Real text and inline SVG over a scrubbed clip, never generated into it — so it stays crisp and
   screen-readable, stays editable without a re-render, and never asks the model for the one thing
   it is worst at, legible type. --p is the clip's scroll progress (0..1) and --cut the point of the
   hard cut, both set by the track; .is-tight fires once the cut has landed. --p counts BEATS, not
   film: the rule, the playhead and the timecode all describe the minute being built, and the film
   runs on its own progress underneath.
   Every word in this layer is copy.md verbatim or a design-system allowance — a numeral, a timecode,
   the Generated label. The five beat names stay in .track__copy, because copy.md is the copy.
   Everything lives at the frame edges: the founder keeps the middle, the copy keeps the left third. */
.track__gfx { position: absolute; inset: 0; pointer-events: none; --p: 0; --cut: .5; color: var(--film-ink);
  /* position WITHIN each angle: --a across the wide, --b across the tight. Every element can now
     know where it sits inside its own shot, which is the whole point of two angles. */
  --a: max(0, min(1, var(--p) / var(--cut)));
  --b: max(0, min(1, (var(--p) - var(--cut)) / (1 - var(--cut)))); }
.track__stage[data-tone="light"] .track__gfx { color: var(--ink); }

/* the beat number, top left; the honesty label, top right. They balance, and neither touches the copy. */
.gfx__n { position: absolute; left: var(--margin); top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-6)); margin: 0;
  display: flex; align-items: center; gap: var(--s-3); width: 10ch; opacity: .85;
  font-variant-numeric: tabular-nums; }
.gfx__n::after { content: ""; flex: 1; height: 1px; background: currentColor; opacity: .5; }

/* the honesty label: this founder is generated, and the frame says so on its face. BRIEF §1. */
.gfx__made { position: absolute; right: var(--margin); top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-6)); margin: 0;
  display: inline-flex; align-items: center; gap: var(--s-2); opacity: .7; }
.gfx__made::before { content: ""; width: 7px; height: 7px; background: currentColor; }

/* the readout: the rule of the minute on the left, the running timecode on the right */
.gfx__readout { position: absolute; left: var(--margin); right: var(--margin); bottom: calc(var(--s-7) + var(--s-4));
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-5); }
.gfx__label, .gfx__tc { margin: 0; }
.gfx__label { opacity: .8; }
.gfx__tc { font-variant-numeric: tabular-nums; letter-spacing: .1em; opacity: .9; }

/* the minute, as five twelve-second segments filling with the scroll */
.gfx__time { position: absolute; left: var(--margin); right: var(--margin); bottom: var(--s-7); height: 8px;
  transition: opacity var(--t-ui) var(--ease-out); }
.gfx__segs { position: absolute; left: 0; right: 0; top: 3px;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.gfx__seg { position: relative; height: 2px; background: currentColor; opacity: .25; overflow: hidden; }
.gfx__seg::after { content: ""; position: absolute; inset: 0; background: currentColor;
  transform-origin: left center; transform: scaleX(var(--fill, 0)); }
.gfx__seg.is-on { opacity: 1; }

/* the square — Studios' one accent mark — doing the work of a playhead */
.gfx__head { position: absolute; top: 1px; left: calc(var(--p) * 100%); width: 7px; height: 7px;
  margin-left: -3.5px; background: var(--section); }

/* the in point: where the hard cut lands, marked on the rule once it has been passed */
.gfx__cut { position: absolute; top: 0; left: calc(var(--cut) * 100%); width: 1px; height: 8px;
  background: currentColor; opacity: 0; transition: opacity var(--t-ui) var(--ease-out); }
.track__gfx.is-tight .gfx__cut { opacity: .9; }

/* wide and vertical: once the cut has landed, the 9:16 the founder also gets is drawn over the wide
   frame, and closes in to it. copy.md, What is in the box — "The Founder Cut, wide and vertical."
   Two hairlines and a stamp, no mask: a mask would darken the very third the copy sits in. */
.gfx__vert { position: absolute; inset: 0; opacity: 0; transition: opacity var(--t-dissolve) var(--ease-out); }
.track__gfx.is-tight .gfx__vert { opacity: .75; }
.gfx__vert-frame { position: absolute; top: 0; bottom: 0; left: 50%; aspect-ratio: 9 / 16;
  border-left: 1px solid currentColor; border-right: 1px solid currentColor;
  transform: translateX(-50%) scaleX(1.16); transition: transform var(--t-dissolve) var(--ease-out); }
.track__gfx.is-tight .gfx__vert-frame { transform: translateX(-50%) scaleX(1); }
.gfx__vert-tag { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; white-space: nowrap;
  bottom: calc(var(--s-7) + var(--s-4) + var(--s-6)); font-variant-numeric: tabular-nums; }

/* at the cut the rule recedes. Phase 60: the copy no longer moves at the cut. .is-tight lives on the
   GRAPHICS LAYER now, not on the stage, because four layers share one stage on Studios, and the copy
   layer is the layer's sibling rather than its child, so it cannot be reached from it. Nothing is
   lost: .track--film.track--beats anchors every beat to --copy-foot and never reads --copy-top. */
.track__gfx.is-tight .gfx__time { opacity: .5; }

@media (max-width: 720px) {
  /* phones already anchor the beats to the foot of the stage, so the readout would land on the copy;
     the number, the label and the rule carry it alone. The stage is nearly 9:16 already, so the
     vertical guide would sit on the bezel and say nothing. */
  .gfx__readout, .gfx__vert { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  /* There is no pin, no film and no scroll to read, and the stage flows in reading order — so an
     absolutely positioned layer lands straight on top of the five beats. The measurements go.
     The honesty label stays and joins the flow under the poster, because that poster is still a
     generated frame and BRIEF §1 does not bend for a media query. */
  .track__gfx { position: static; inset: auto; display: block; padding: var(--s-5) var(--margin) 0; }
  .track__gfx > * { display: none; }
  .track__gfx .gfx__made { display: inline-flex; position: static; opacity: 1; }
}

/* ---- beds that are still photographs ---------------------------------------
   The generated rooms are stills; their light-move has not been generated yet. Until it is, each bed
   moves with the scroll across its own run of beats — a slow push, pull or drift, never a loop on a
   clock, so it obeys the rule the rest of the page obeys: the scroll is the transport, and stopping
   scrolling stops the picture. Scale never drops below 1, so object-fit: cover can never show an edge. */
.track__film[data-move] { --m: 0; will-change: transform; }
.track__film[data-move="in"]    { transform: scale(calc(1 + .055 * var(--m))); }
.track__film[data-move="out"]   { transform: scale(calc(1.055 - .055 * var(--m))); }
.track__film[data-move="left"]  { transform: scale(1.06) translateX(calc(-1.1% * var(--m))); }
.track__film[data-move="right"] { transform: scale(1.06) translateX(calc(1.1% * var(--m))); }
@media (prefers-reduced-motion: reduce) { .track__film[data-move] { transform: none; } }

/* ---- three looks, three graphics grammars ----------------------------------
   copy.md sells three looks, so Studios shows three — same film language, three different films.
   The invariants that keep them one system: one signal (--p and .is-tight on the layer);
   exactly one 9px square per grammar, always marking a position in time; every line one device pixel;
   all text at .label except Kinetic's count; and one opacity ladder (1 the square, .8 live text,
   .7 the Generated label, .28 a rail, .22-.18 a hairline).
   What differs is how each reads the same clock: Cinema takes --p raw and continuous, Field
   smoothsteps it into --q so it lags and settles, Kinetic floors it to fifths and cuts.
   THE RULE THAT KEEPS IT HONEST: --p drives geometry with no transition, classes drive state with
   one. Never both on a property, never both on an element — a transition on a scrubbed value lags
   the film and the two visibly disagree. The scrub already is the animation. */

/* ---- Cinema · "Film grade, slow, one light." -------------------------------
   A camera report, not an interface: one instrument held steady, measuring one continuous take, so
   the only thing that really moves is the light. Five elements, four of them hairlines or numerals. */
.gfx--cinema .gfx__take { position: absolute; right: var(--margin); margin: 0; display: grid; justify-items: end;
  top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-6) + var(--s-5)); opacity: .8; }
.gfx--cinema .gfx__take span { grid-area: 1 / 1; opacity: 0; }
.gfx--cinema .gfx__take span:first-child { opacity: 1; }
/* the one place the design system's "film cuts at 0ms" rule is applied to type — because the type
   is describing a cut */
.gfx--cinema.is-tight .gfx__take span:first-child { opacity: 0; }
.gfx--cinema.is-tight .gfx__take span:last-child { opacity: 1; }

.gfx--cinema .gfx__tc { position: absolute; right: var(--margin); bottom: var(--s-7); margin: 0;
  font-variant-numeric: tabular-nums; letter-spacing: .1em; opacity: .8; }

/* the rail: six ticks are the five twelve-second segments drawn as boundaries. Cinema never counts. */
.gfx--cinema .gfx__rail { position: absolute; right: var(--margin); width: 12px;
  top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-6) + var(--s-8)); bottom: var(--s-9); }
/* the ticks carry .28 on their own <g> so the run-line can sit above them at .55 in the same svg */
.gfx--cinema .gfx__rail svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.gfx--cinema .gfx__rail--h { display: none; }
.gfx--cinema .gfx__mark { position: absolute; right: -2px; width: 9px; height: 9px; background: currentColor;
  top: calc(var(--p) * (100% - 9px)); }
/* class-driven, so this one is allowed its ease: the eyeline in a close-up sits higher */
.gfx--cinema .gfx__eyeline { position: absolute; left: 62%; right: var(--margin); top: 50vh; height: 1px;
  background: currentColor; opacity: .18; transition: top var(--t-dissolve) var(--ease-in-out); }
.gfx--cinema.is-tight .gfx__eyeline { top: 42vh; }

/* ---- Kinetic · "Motion graphics forward, fast type, energy." ---------------
   The one look where the graphics are the film, so the layer is edited rather than animated: it cuts,
   at zero milliseconds, four things at once, five times across the section — and between cuts nothing
   whatsoever moves. Not busier than Cinema; one twelfth as many events, and far louder. */
.gfx--kinetic .gfx__count { position: absolute; right: var(--margin); top: 26vh; left: auto; width: auto; margin: 0;
  display: flex; justify-content: flex-end; align-items: baseline; gap: var(--s-3); opacity: 1;
  font-family: var(--font-display); font-size: var(--text-display-1); line-height: 1;
  letter-spacing: var(--ls-display); text-transform: none; }
.gfx--kinetic .gfx__count::after { content: none; }

.gfx--kinetic .gfx__where { position: absolute; right: var(--margin); margin: 0; display: grid; justify-items: end;
  top: calc(26vh + var(--text-display-1) + var(--s-4)); opacity: .8; }
.gfx--kinetic .gfx__where span { grid-area: 1 / 1; opacity: 0; }

/* the rule stops being a bar that fills and becomes a cut list with a jumping playhead */
.gfx--kinetic .gfx__seg { opacity: .25; }
.gfx--kinetic .gfx__seg::after { transform: scaleX(1); }
.gfx--kinetic .gfx__seg::before { content: ""; position: absolute; right: 0; top: -13px;
  width: 9px; height: 9px; background: currentColor; opacity: 0; }
.gfx--kinetic[data-step="0"] .gfx__where span:nth-child(1),
.gfx--kinetic[data-step="1"] .gfx__where span:nth-child(2),
.gfx--kinetic[data-step="2"] .gfx__where span:nth-child(3),
.gfx--kinetic[data-step="3"] .gfx__where span:nth-child(4),
.gfx--kinetic[data-step="4"] .gfx__where span:nth-child(5) { opacity: 1; }
.gfx--kinetic[data-step="0"] .gfx__seg:nth-child(1),
.gfx--kinetic[data-step="1"] .gfx__seg:nth-child(2),
.gfx--kinetic[data-step="2"] .gfx__seg:nth-child(3),
.gfx--kinetic[data-step="3"] .gfx__seg:nth-child(4),
.gfx--kinetic[data-step="4"] .gfx__seg:nth-child(5) { opacity: 1; }
.gfx--kinetic[data-step="0"] .gfx__seg:nth-child(1)::before,
.gfx--kinetic[data-step="1"] .gfx__seg:nth-child(2)::before,
.gfx--kinetic[data-step="2"] .gfx__seg:nth-child(3)::before,
.gfx--kinetic[data-step="3"] .gfx__seg:nth-child(4)::before,
.gfx--kinetic[data-step="4"] .gfx__seg:nth-child(5)::before { opacity: 1; }

/* the only appearing-and-vanishing element in any of the three. It runs once each time .is-tight is
   ADDED — that is, on every downward crossing of the cut. Scrolling back up removes the class and the
   slash simply goes; it does not play in reverse. An earlier version of this comment claimed both
   directions, which the code has never done: a CSS animation fires on class add, not on class remove.
   Firing on the way back up needs a real signal held across the crossing, not a state class. */
.gfx--kinetic .gfx__slash { position: absolute; left: var(--margin); right: var(--margin); top: 50vh;
  height: 1px; background: currentColor; opacity: .55; clip-path: inset(0 100% 0 0); }
.gfx--kinetic.is-tight .gfx__slash { animation: gfx-slash var(--t-hold) var(--ease-in-out) 1 forwards; }
@keyframes gfx-slash {
  0%   { clip-path: inset(0 100% 0 0); }
  45%  { clip-path: inset(0 0 0 0); }
  55%  { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 0 0 100%); }
}

/* ---- Field · "On location, natural light, real rooms." ---------------------
   Notes taken on the day rather than measurements of it. The only grammar with no counter of any
   kind — no number, no timecode, no segments — and that refusal is the design. Nothing appears,
   disappears, steps or ticks: one line tilts by just over a degree across the whole section while one
   square crosses it. Field is the section you could scroll past without noticing a graphics layer,
   which is what "nothing staged" ought to feel like. */
.gfx--field { --q: calc(var(--p) * var(--p) * (3 - 2 * var(--p))); }
.gfx--field .gfx__place { position: absolute; right: var(--margin); bottom: var(--s-7); margin: 0;
  opacity: calc(max(0, min(1, (var(--p) - .08) * 12)) * .75); }
/* three nested boxes because each has exactly one job: the wrapper is class-driven and may ease,
   the tilt box is --p-driven and never may, and the SVG holds a true one-pixel stroke through both
   a non-uniform scale and a rotation, which a rotated 1px div cannot */
.gfx--field .gfx__horizon-wrap { position: absolute; left: 50%; right: var(--margin); top: 0; height: 8px;
  transition: transform var(--t-dissolve) var(--ease-out); }
.gfx--field.is-tight .gfx__horizon-wrap { transform: translateY(-4vh) rotate(.35deg); }
.gfx--field .gfx__horizon-tilt { position: absolute; inset: 0; transform-origin: 100% 50%;
  transform: translateY(calc(54vh - var(--q) * 8vh)) rotate(calc(-.6deg + var(--q) * 1.2deg)); }
.gfx--field .gfx__horizon { position: absolute; inset: 0; width: 100%; height: 100%; opacity: .22; }
.gfx--field .gfx__sun { position: absolute; top: 50%; margin-top: -4.5px; width: 9px; height: 9px;
  background: currentColor; left: calc(var(--q) * (100% - 9px)); }

@media (max-width: 720px) {
  /* The beats own the foot and the founder's face owns the middle, so every grammar collapses into
     the top band. Cinema's vertical rail would read as a scrollbar at this width, so it lies down. */
  .gfx--cinema .gfx__rail { left: var(--margin); right: var(--margin); width: auto; height: 12px; bottom: auto;
    top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-7)); }
  .gfx--cinema .gfx__rail--v, .gfx--cinema .gfx__eyeline, .gfx--cinema .gfx__take { display: none; }
  .gfx--cinema .gfx__rail--h { display: block; }
  .gfx--cinema .gfx__mark { right: auto; top: 1px; left: calc(var(--p) * (100% - 9px)); }
  .gfx--cinema .gfx__tc { left: var(--margin); right: auto; bottom: auto;
    top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-5)); }

  /* five stepping labels on a 375px frame is noise, and the same five phrases are readable further
     down the page in real text */
  .gfx--kinetic .gfx__where { display: none; }
  .gfx--kinetic .gfx__count { font-size: var(--text-display-2); left: var(--margin); right: auto;
    justify-content: flex-start; top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-5)); }
  .gfx--kinetic .gfx__time { top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-8)); bottom: auto; }
  .gfx--kinetic .gfx__seg::before { top: auto; bottom: -13px; }

  /* a phone frame is nearly vertical, so a tilted full-width line reads as a broken layout rather
     than a horizon: the rise goes entirely and the tilt halves */
  .gfx--field .gfx__horizon-wrap { left: var(--margin); top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-7)); }
  .gfx--field.is-tight .gfx__horizon-wrap { transform: rotate(.18deg); }
  .gfx--field .gfx__horizon-tilt { transform: rotate(calc(-.3deg + var(--q) * .6deg)); }
  .gfx--field .gfx__place { left: var(--margin); right: auto; bottom: auto;
    top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-5)); }
}


/* ---- Cinema, extensive -----------------------------------------------------
   One instrument held steady in the right margin, measuring a single continuous take. Everything
   drifts; the only thing that changes state instantly is the take number, which cuts at 0ms while the
   frame spends the next 800ms re-finding the subject. A camera report, not an interface. */

/* the angle clock, under the take number: 00:00 to 00:30, twice, summing to the minute */
.gfx--cinema .gfx__take-tc { position: absolute; right: var(--margin); margin: 0;
  top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-6) + var(--s-5) + var(--s-4));
  font-variant-numeric: tabular-nums; letter-spacing: .1em; opacity: .85; }

/* the take drawn as it is exposed. pathLength normalises the spine to 1 so the dash registers exactly
   through the non-uniform scale. */
.gfx--cinema .gfx__rail-run { stroke-dasharray: var(--p) 1; }
/* the in point, appearing on the rail once the cut has been passed */
.gfx--cinema .gfx__rail-cut { position: absolute; right: 0; width: 12px; height: 1px;
  background: currentColor; top: calc(var(--cut) * (100% - 1px)); opacity: 0;
  transition: opacity var(--t-ui) var(--ease-out); }
.gfx--cinema.is-tight .gfx__rail-cut { opacity: .9; }

/* the eyeline gains a left cap: a bare 1px div cannot carry a rule and a cross-tick at the same weight */
.gfx--cinema svg.gfx__eyeline { position: absolute; left: 62%; right: var(--margin); height: 12px;
  top: 50vh; background: none; opacity: .18; transition: top var(--t-dissolve) var(--ease-in-out); }
.gfx--cinema.is-tight svg.gfx__eyeline { top: 42vh; }

/* the reframe as four corners rather than two hairlines, travelling in from the full frame to the 9:16.
   copy.md, What is in the box: "The Founder Cut, wide and vertical."
   The bracket is 100vh - 12vh - 12vh = 76vh tall; a 9:16 crop of that is 42.75vh wide, so half is
   21.375vh and the tight edges sit at calc(50% +/- 21.375vh).
   left/right rather than transform: scaleX, because a scaled parent stretches a 12px corner arm to 16px
   and a corner mark cannot survive that. contain: layout keeps the reflow inside this subtree, and it
   runs once per crossing, not per frame. */
.gfx--cinema .gfx__vert { position: absolute; inset: 0; contain: layout; opacity: .28;
  transition: opacity var(--t-ui) var(--ease-out); }
.gfx--cinema.is-tight .gfx__vert { opacity: .45; }
/* The five-beats layer's own .gfx__vert-frame sets aspect-ratio and translateX(-50%) to centre a
   9:16 box off a single `left: 50%`. Cinema sizes the box with left AND right instead, so both of
   those have to be reset or the box lands half its own width to the left — measured at 335px against
   a computed left of 527.6, a difference of exactly half the 385px width. A cascade collision, not a
   maths error, and only visible by measuring the rendered rect. */
.gfx--cinema .gfx__vert-frame { position: absolute; top: 12vh; bottom: 12vh;
  left: var(--margin); right: var(--margin); border: 0;
  aspect-ratio: auto; transform: none;
  transition: left var(--t-dissolve) var(--ease-out), right var(--t-dissolve) var(--ease-out); }
.gfx--cinema.is-tight .gfx__vert-frame { left: calc(50% - 21.375vh); right: calc(50% - 21.375vh);
  aspect-ratio: auto; transform: none; }
.gfx--cinema .gfx__corner { position: absolute; width: 12px; height: 12px; }
.gfx--cinema .gfx__corner:nth-child(1) { top: 0; left: 0; border-top: 1px solid currentColor; border-left: 1px solid currentColor; }
.gfx--cinema .gfx__corner:nth-child(2) { top: 0; right: 0; border-top: 1px solid currentColor; border-right: 1px solid currentColor; }
.gfx--cinema .gfx__corner:nth-child(3) { bottom: 0; left: 0; border-bottom: 1px solid currentColor; border-left: 1px solid currentColor; }
.gfx--cinema .gfx__corner:nth-child(4) { bottom: 0; right: 0; border-bottom: 1px solid currentColor; border-right: 1px solid currentColor; }
.gfx--cinema .gfx__vert-tag { position: absolute; left: 50%; transform: translateX(-50%); margin: 0;
  bottom: calc(12vh - var(--s-5)); white-space: nowrap; font-variant-numeric: tabular-nums;
  opacity: 0; transition: opacity var(--t-ui) var(--ease-out) 200ms; }
.gfx--cinema.is-tight .gfx__vert-tag { opacity: .8; }

/* the running minute stays bottom right */
.gfx--cinema .gfx__tc { position: absolute; right: var(--margin); bottom: var(--s-7); margin: 0;
  font-variant-numeric: tabular-nums; letter-spacing: .1em; opacity: .8; }

@media (max-width: 720px) {
  /* the beats own the foot and the face owns the middle, so the reframe bracket and the eyeline go */
  .gfx--cinema .gfx__vert, .gfx--cinema svg.gfx__eyeline, .gfx--cinema .gfx__take-tc { display: none; }
}


/* ---- 01 · What we make: the deliverables at their true shapes ----------------------------------------
   The copy names a minute delivered wide AND vertical, plus short cuts, stills and a page. Showing those
   five things at their real aspect ratios is what turns the section from a paragraph into a rack of what
   arrives. Each copy line sits against the artefacts it names, so the layout carries information. */
.make { max-width: none; }
.make__lede { max-width: var(--measure); }
.make__minute { margin: var(--s-7) 0 0; }
.make__shapes { display: grid; grid-template-columns: minmax(0, 1fr) minmax(140px, 240px);
  gap: var(--s-5); align-items: end; }
.make__wide { position: relative; }
.make__wide video { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; background: #1a1917; }
.make__vert video { width: 100%; aspect-ratio: 9 / 16; object-fit: cover; display: block; background: #1a1917; }
.make__minute figcaption, .make__rest figcaption { margin-top: var(--s-4); max-width: var(--measure); }
.make__rest { margin: var(--s-7) 0 0; }
/* the supporting pieces are quiet and small: two short cuts, two stills, and the page standing taller */
.make__row { display: grid; gap: var(--s-4); align-items: stretch;
  grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(150px, .72fr); }
.make__row img { width: 100%; display: block; min-width: 0; }
.make__cut, .make__still { aspect-ratio: 16 / 9; object-fit: cover; align-self: end; }
.make__page { border: var(--line) solid var(--rule); background: var(--surface); overflow: hidden;
  min-width: 0; max-width: 100%; }
.make__page img { height: 100%; object-fit: cover; object-position: top center; }
/* the page stands taller than the cuts beside it, because it is a page and they are frames — but only
   while it sits BESIDE them; on a phone it spans the row and must not reach past it */
@media (min-width: 901px) { .make__page { align-self: end; height: calc(100% + var(--s-6)); } }
.make__close { margin-top: var(--s-6); max-width: var(--measure); }
@media (max-width: 900px) {
  .make__shapes { grid-template-columns: minmax(0, 1fr) minmax(110px, 150px); }
  .make__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .make__page { grid-column: span 2; aspect-ratio: 16 / 10; height: auto; }
}

/* the month band: the dated milestones side by side, so the shape of the next six months reads in one
   glance. A grid of --cols months; each stop sits in its month's column and stacks with any others there.
   Scrolls sideways inside its own box on a phone rather than pushing the page wide. */
.band__scroll { overflow-x: auto; margin-top: var(--s-5); padding-bottom: var(--s-3); }
.band__scroll:focus-visible { outline: var(--focus) solid var(--ink); outline-offset: var(--focus-offset); }
.band { display: grid; grid-template-columns: repeat(var(--cols, 6), minmax(150px, 1fr)); gap: 0 var(--s-4); min-width: 100%; }
.band__months { display: contents; }
.band__months .label { grid-row: 1; padding-bottom: var(--s-3); border-bottom: var(--line) solid var(--ink); color: var(--ink); }
.band__stops { display: contents; list-style: none; margin: 0; padding: 0; }
.band__stop { grid-column: var(--col); position: relative; padding: var(--s-4) 0 var(--s-3) var(--s-5); border-top: var(--line) solid var(--rule); display: grid; gap: 3px; align-content: start; }
.band__stop .roadmap__dot { position: absolute; left: 0; top: calc(var(--s-4) + .5em); }
.band__stop.is-now .roadmap__dot { background: var(--ink); border-color: var(--ink); }
.band__stop.is-done .roadmap__dot { background: var(--muted); border-color: var(--muted); }
.band__stop.is-next .roadmap__dot { background: var(--ground); border-color: var(--ink); }
.band__text { font-weight: var(--weight-bold); line-height: 1.35; }
.band__when { color: var(--muted); }
.roadmap__section--band .roadmap__leg { flex-wrap: wrap; }
@media print { .band__scroll { overflow: visible; } }


/* ---- owners: a bubble of initials per person, one colour each, and a legend with the load ---------- */
.owner { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  background: var(--stone); color: var(--ink); font-family: var(--font-body); font-weight: var(--weight-bold);
  font-size: 11px; letter-spacing: .02em; flex: none; }
.owner__i { line-height: 1; }
.owner--erin  { background: var(--ink); color: var(--ground); }
.owner--kutay { background: var(--house); }
.owner--yurek { background: var(--vch-muted-teal, #80A7AF); color: var(--ground); }
.owner--emily { background: var(--vch-burnt-orange, #C35421); color: var(--ground); }
.owners { display: inline-flex; gap: 4px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.roadmap__people { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-6);
  margin-top: var(--s-6); padding: var(--s-4) 0; border-top: var(--line) solid var(--rule); border-bottom: var(--line) solid var(--rule); }
.person { display: inline-flex; align-items: center; gap: var(--s-3); }
.person__name { font-weight: var(--weight-bold); }
.person__n { color: var(--muted); font-variant-numeric: tabular-nums; }
.roadmap__expand { margin-left: auto; }

/* ---- subtasks: fold under their row in a native <details>; the summary IS the row ------------------ */
.subs > summary { list-style: none; cursor: pointer; }
.subs > summary::-webkit-details-marker { display: none; }
.subs > summary:focus-visible { outline: var(--focus) solid var(--ink); outline-offset: var(--focus-offset); }
.subs__n { color: var(--muted); }
.subs[open] > summary .subs__n { color: var(--ink); }
.roadmap__subs { list-style: none; margin: 0 0 var(--s-3); padding: 0 0 0 var(--s-6); position: relative; }
.roadmap__subs::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: var(--line); background: var(--rule); }
.roadmap__sub { position: relative; display: grid; grid-template-columns: 1fr auto; gap: var(--s-4);
  align-items: baseline; padding: var(--s-2, 8px) 0; font-size: 15px; }
.roadmap__sub .roadmap__dot { top: calc(var(--s-2, 8px) + .5em); width: 7px; height: 7px; }
.roadmap__sub.is-now .roadmap__dot { background: var(--ink); border-color: var(--ink); }
.roadmap__sub.is-done .roadmap__dot { background: var(--muted); border-color: var(--muted); }
.roadmap__sub.is-next .roadmap__dot { background: var(--ground); border-color: var(--rule); }
.roadmap__sub.is-done .roadmap__text { text-decoration: line-through; color: var(--muted); }
.roadmap__sub .token { font-size: 10px; padding: 3px 7px; }
.band__meta { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
/* the stacked month list only exists on a phone; the grid only exists off one */
.band__list { display: none; list-style: none; margin: var(--s-5) 0 0; padding: 0; }
@media (max-width: 720px) {
  .band__scroll { display: none; }
  .band__list { display: block; }
  .band__month { padding: var(--s-4) 0; border-top: var(--line) solid var(--ink); }
  .band__month-name { display: block; margin-bottom: var(--s-3); }
  .band__month-stops { list-style: none; margin: 0; padding: 0; }
  .band__list .band__stop { grid-column: auto; padding: var(--s-3) 0 var(--s-3) var(--s-5); border-top: var(--line) solid var(--rule); }
  .band__list .band__stop:first-child { border-top: 0; }
  .band__list .band__stop .roadmap__dot { top: calc(var(--s-3) + .5em); }
  /* a row on a phone: the text on its own line, then one compact meta line — date on the left, steps,
     owners and status on the right — instead of four stacked lines of air. display: contents lifts
     the text and the date out of the body so they can share the row's flex line with the tail. */
  .roadmap__row, .roadmap__sub { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--s-3); padding: var(--s-3) 0; }
  .roadmap__row .roadmap__body, .roadmap__sub .roadmap__body { display: contents; }
  .roadmap__row .roadmap__text, .roadmap__sub .roadmap__text { flex: 1 1 100%; }
  .roadmap__row .roadmap__when, .roadmap__sub .roadmap__when { flex: 0 1 auto; margin-right: auto; }
  .roadmap__tail { flex: 0 0 auto; justify-content: flex-end; gap: var(--s-2, 8px); }
  .roadmap__sub { padding: var(--s-2, 8px) 0; }
  .roadmap__dot { top: calc(var(--s-3) + .5em); }
  .roadmap__sub .roadmap__dot { top: calc(var(--s-2, 8px) + .5em); }
  /* the legend: two people per line, the button full width beneath rather than stranded on the right */
  .roadmap__people { gap: var(--s-3) var(--s-5); }
  .person { flex: 0 0 calc(50% - var(--s-5) / 2); }
  .roadmap__expand { flex-basis: 100%; margin-left: 0; }
}
@media print { .subs { open: open; } .roadmap__expand { display: none; } }

/* ---------- Home · Three parts of one house ----------------------------------------------------
   Four entries, four up on desktop and one column on a phone. On rules, not cards: BRIEF §3.5 and §5
   (no cards, no borders around blocks, radius 0, no shadow). The rule is the top edge of each part,
   so the four read as one ledger rather than four boxes. */
.house { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-6); }
.statement .house { margin-top: var(--s-6); }
.house__part { border-top: var(--line) solid var(--rule); padding-top: var(--s-4); }
.statement .house__part .lines { margin-top: 0; }
.house__part strong { font-weight: var(--weight-bold); }
@media (max-width: 720px) { .house { grid-template-columns: 1fr; gap: var(--s-5); } }

/* Home · the work strip carries a line under each title (copy.md v7), so the caption is two lines
   rather than one and wants the body measure, not a nowrap title row. */
.piece__title .lines { max-width: 34ch; }

/* The square on film. It stands in for the full stop of a wall headline, so it takes the colour of the
   glyph it replaces rather than the section colour: over the hero and the Founders wall the section
   colour is ink, which is the one colour that cannot be seen there. currentColor follows the stage's
   own tone, so a bright bed flips the square to ink with the type. Home and Founders both. */
.track .display .square { background: currentColor; }
/* Phase 63. Except where the stage has dissolved to solid ivory: on a ground beat there is no film
   under the glyph, the type is ink, and currentColor makes the one accent mark on the page ink as
   well — which is the reduced-motion and no-JS path already disagreeing with the live one about the
   same square (both of those render it in --section, two rules below). On the ground the section
   colour is legible and is where BRIEF §5 says the one accent lives. */
.track__stage[data-tone="ground"] .display .square { background: var(--section); }

/* Reduced motion, a bright bed. The stage keeps its dark background when the pin is released, and a
   light-tone stage puts INK type on it — which is the one combination that cannot be read. The poster
   renders as its own block up there, so the copy block below it belongs on the ground. Home's quick
   scroll found this; the same fault was already sitting under any light-tone track. */
@media (prefers-reduced-motion: reduce) {
  .track__stage[data-tone="light"] { background: var(--ground); }
}

/* ---------- Home v8 --------------------------------------------------------------------------- */

/* The vantage used to take --section-gap plus one step at both ends, so the page's opening ground
   section was 48px taller top and bottom than every other one. Phase 49 removed both rules and the
   two classes with them: one vertical rhythm for the ground, --section-gap everywhere, and the thesis
   is carried by its type size. See "one rhythm" at the end of this file for the measurement. */

/* The generated bed under "Authority content." The design system's treatment for generated imagery:
   desaturated and dimmed. The visible "Generated demonstration" label rides in the beat's copy, so it
   appears and leaves with the bed it describes, and BRIEF §1 is satisfied on the frame itself. */
.track__film--generated { filter: var(--generated-filter); }
.track__generated { color: inherit; opacity: .78; margin: 0; }

/* A rule list of names only: the lines under them already ran on the track's beats, so the resolve
   carries the names and nothing else, and the second column would be an empty gutter. */
.rules--names li { grid-template-columns: 1fr; }

/* A rule list that is a sequence: the numeral, the bold lead, the sentence. The numerals are earned
   here (it is the order a project runs), which is why they appear nowhere else on Home. */
.rules--steps li { grid-template-columns: 3ch minmax(0, 190px) 1fr; }
.rules--steps .rules__n { color: var(--muted); padding-top: .45em; }
@media (max-width: 720px) {
  .rules--steps li { grid-template-columns: 3ch 1fr; }
  .rules--steps .rules__n { grid-row: span 2; }
}

/* ---------- Home v8 · how it adds up ----------------------------------------------------------
   Four framed stills at their true ratios, laid apart across a 16:9 stage, that translate inward and
   scale until they lock into one wide 16:9 frame. One custom property, --a, runs 0 to 1 across the
   section; every frame's geometry is a calc between its scattered box (--x0 --y0 --w0 --h0, all in
   percent of the stage) and its quadrant of the assembled frame (--x1 --y1, always 50 by 50). The
   script eases --a out and scrubs it to scroll; nothing here animates on a clock.
   Quiet by construction: radius 0, no shadow, 1px stone rules as the only frame edge. */
.adds__lede { margin-top: var(--s-5); max-width: var(--measure); }
.adds__stage { position: relative; aspect-ratio: 16 / 9; margin-top: var(--s-8); }
.adds__box { position: absolute; inset: 0; overflow: hidden; border: var(--line) solid var(--rule); background: var(--ground-shift); }
.adds__box img { width: 100%; height: 100%; object-fit: cover; }
/* Phase 49: no --generated-filter here. These four are crops of the same four plates that fill the
   screen unfiltered a section above, and one page cannot grade the same room two ways. The word
   Generated on an ink plate is what carries BRIEF §3.13's honesty; the grade was styling. */
/* the four labels sit under their own frame while the frames are apart, and are gone by the time the
   frames meet, because by then they would be lying across each other's pictures */
.adds__label { position: absolute; top: 100%; left: 0; margin: var(--s-3) 0 0; color: var(--muted); white-space: nowrap;
  opacity: calc((0.6 - var(--a)) / 0.35); transition: none; }
/* the one label the assembly is for, in last */
.adds__final { margin-top: var(--s-4); color: var(--ink); opacity: calc((var(--a) - 0.8) / 0.2); }

/* Assembled, statically: under prefers-reduced-motion, and on a phone, where a 16:9 stage is 200px
   tall and four scattered labels would lie on top of one another. The four tile two up inside the
   frame they make, each keeping its own label, and all five labels are visible. */
@media (max-width: 720px) {
  .adds__stage { position: static; aspect-ratio: auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5) 0; margin-top: var(--s-6); }
  .adds__frame { position: static; width: auto; height: auto; }
  .adds__box { position: relative; inset: auto; aspect-ratio: 16 / 9; }
  .adds__label { position: static; white-space: normal; opacity: 1; }
  .adds__final { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .adds__stage { position: static; aspect-ratio: auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5) 0; }
  .adds__frame { position: static; width: auto; height: auto; }
  .adds__box { position: relative; inset: auto; aspect-ratio: 16 / 9; }
  .adds__label { position: static; white-space: normal; opacity: 1; }
  .adds__final { opacity: 1; }
}

/* The house track resolves to one row: Founders, the part of the house that was not a beat. */
.house--one { grid-template-columns: minmax(0, var(--content)); }

/* The company's door. Two buttons side by side, wrapping on a phone rather than shrinking. */
.doors { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-6); }

/* The phone hero clamps the H1 at three lines so the fold cannot be pushed off the screen. v8's
   headline is a longer sentence and three lines cut it mid-word with an ellipsis, which is not a
   headline any more. Five lines at the phone display size is 192px, and the foot sits on the fold
   regardless, so nothing below it moves. */
@media (max-width: 720px) { .track__beat--hero .display--one { -webkit-line-clamp: 5; } }

/* ======================================================================================
   Home v8 · the craft-and-audit pass (Phase 47)
   Every rule below answers a numbered finding in the two reports. Each one says which.
   ====================================================================================== */

/* ---- the scrim ------------------------------------------------------------------------
   Craft 5 / audit: the shipped scrim was a global wash — 20 to 55 percent of ink over the WHOLE
   frame — so every bed on the page read hazy and desaturated. BRIEF §3.4.1 does not ask for that:
   it asks for "a scrim from the bottom in ink at 60 percent over the bottom 40 percent". That is
   what the hero gets now, exactly. The beat tracks get the same idea sized to their own copy: a
   band that protects the area the type actually occupies and nothing above it, so the top of every
   frame runs at full contrast (taste.md: light must have an opinion; if it has no texture it is slop). */
.track--top .track__scrim {
  background: linear-gradient(to top, rgba(32,32,32,.6) 0%, rgba(32,32,32,0) 40%); }
.track--top .track__stage[data-tone="light"] .track__scrim {
  background: linear-gradient(to top, rgba(239,236,230,.72) 0%, rgba(239,236,230,0) 40%); }

/* Home's two beat tracks. The copy drops to 54vh so it sits inside the band (Scout places its hero
   line low in the frame for the same reason), and the band reaches 58 percent up so the hold line
   clears too. Above that: the picture, untouched. */
.track--beats .track__copy { --copy-top: 54vh; }
.track--beats .track__scrim {
  background: linear-gradient(to top, rgba(32,32,32,.78) 0%, rgba(32,32,32,.58) 26%, rgba(32,32,32,0) 58%); }
/* 60 percent is the brief's own number for the hero scrim (§3.4.1) and it is the ceiling here too: at
   .86 the ivory band was bleaching the Brooklyn skyline out of the Studios bed, which is the wash
   taste.md calls the cardinal sin, 26 points above anything the brief asks for. The ramp keeps its
   shape — the band still reaches 58 percent so the hold line clears — and only its strength comes
   down. Measured ink contrast on the beat headlines after the change is in PROGRESS. */
.track--beats .track__stage[data-tone="light"] .track__scrim {
  background: linear-gradient(to top, rgba(239,236,230,.6) 0%, rgba(239,236,230,.44) 26%, rgba(239,236,230,0) 58%); }
@media (max-width: 720px) {
  /* the phone anchors beats to the foot already, so the band only has to be a little taller and a
     little stronger — a 390 frame puts more picture behind fewer words */
  .track--beats .track__copy { --copy-top: 40vh; }
  .track--beats .track__scrim {
    background: linear-gradient(to top, rgba(32,32,32,.82) 0%, rgba(32,32,32,.64) 34%, rgba(32,32,32,0) 66%); }
  .track--beats .track__stage[data-tone="light"] .track__scrim {
    background: linear-gradient(to top, rgba(239,236,230,.6) 0%, rgba(239,236,230,.48) 34%, rgba(239,236,230,0) 66%); }
}

/* ---- the honesty label on the generated bed (audit 8, craft 15) -------------------------
   Two faults in one element. It sat in the beat's grid flow, which pushed that beat's headline 42px
   below every other beat's so the cross-dissolve jumped; and it was ivory with no plate, which on a
   bright bed is invisible. It now sits top-right of the stage on its own — the same place and the
   same job as .gfx__made on the Founder track — on a small ink plate, so it is legible on any bed in
   any tone, which is what BRIEF §1 asks for. */
.track__generated { position: absolute; right: 0; left: auto; margin: 0; z-index: 2;
  top: calc(-1 * var(--copy-top) + var(--chrome-h, 64px) + var(--s-6));
  background: rgba(32,32,32,.62); padding: 3px 8px; opacity: 1; }
.track__generated,
.track__stage[data-tone="light"] .track__generated { color: var(--film-ink); }
@media (max-width: 720px) {
  /* On a phone the beats anchor to the FOOT of the stage and the hold line sits directly above the
     tallest of them, so a label pinned above the beat lands on top of "What we make." The reason for
     lifting it out of the flow does not apply here either — a bottom-anchored beat grows upward, so
     an extra line cannot push the headline off the common baseline. It rejoins the flow. */
  .track--beats .track__generated { position: static; top: auto; right: auto; justify-self: start; }
  /* ...but it goes LAST in the beat, not first. The beats now share a box height (see --beat-max
     below), so a label above the headline is the one thing left that can push one beat's headline
     off the line the other two sit on — measured at 45px before this rule. Last in the grid, it
     still sits inside the beat it belongs to and still labels the bed it is over. */
  .track--beats .track__generated { order: 1; }
}

/* ---- the routes out of a track (craft 14) -----------------------------------------------
   "See the studio", "Meet the bench", "See how it works" were 17px underlined body text with a grey
   underline, over film — on the light Studios bed the underline was all but gone, and three of the
   site's four sections are reached through them. They take the label face at the design system's
   button padding, which also makes them a 44px target. */
/* color: inherit, because .link's own colour is --link, which is INK — on a dark bed that made
   "See how it works" ink-on-ink and effectively invisible, and it is the only route to /solutions on
   the page. Inheriting means it follows the stage's tone with the rest of the copy. */
.track__beat .link { color: inherit; display: inline-block; font-family: var(--font-body); font-weight: var(--weight-label);
  font-size: var(--text-label); line-height: var(--lh-label); letter-spacing: var(--ls-label);
  text-transform: uppercase; text-decoration: none; padding: 14px 24px;
  border: var(--line) solid currentColor; transition: background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out); }
.track__beat .link:hover { background: var(--film-ink); color: var(--ink); }
.track__stage[data-tone="light"] .track__beat .link:hover { background: var(--ink); color: var(--film-ink); }

/* ---- flat tracks: no GSAP, or no site.js at all (audit 5.1, 5.2) ------------------------
   Blocking the two vendor files left four of seven beats permanently invisible behind 3,060px of
   scroll each, and blocking site.js blanked six of eleven sections. A track that cannot be animated
   has to read as a stacked section instead, which is the release the reduced-motion path already
   does — so this is that same release under two more conditions. */
.track--flat, html:not(.js) .track { height: auto; margin-bottom: 0; }
.track--flat .track__stage, html:not(.js) .track__stage { position: relative; height: auto; min-height: 0; }
.track--flat .track__film, html:not(.js) .track__film { display: none; }
.track--flat .track__poster, html:not(.js) .track__poster { position: relative; display: block; aspect-ratio: 16 / 9; height: auto; }
.track--flat .track__scrim, html:not(.js) .track__scrim { display: none; }
.track--flat .track__copy, html:not(.js) .track__copy { position: relative; inset: auto; padding: var(--s-8) var(--margin); display: grid; gap: var(--s-7); }
.track--flat .track__beat, .track--flat .track__hold,
html:not(.js) .track__beat, html:not(.js) .track__hold { position: static; opacity: 1; visibility: visible; transform: none; }
.track--flat .track__generated, html:not(.js) .track__generated { position: static; justify-self: start; }
.track--flat .track__beat--hero .hero__top, .track--flat .track__beat--hero .hero__foot,
html:not(.js) .track__beat--hero .hero__top, html:not(.js) .track__beat--hero .hero__foot { position: static; }
.track--flat .track__beat--hero .hero__foot, html:not(.js) .track__beat--hero .hero__foot { margin-top: var(--s-6); }
.track--flat .track__scale, html:not(.js) .track__scale { transform: none; }
.track--flat .track__stage[data-tone="light"], html:not(.js) .track__stage[data-tone="light"] { background: var(--ground); }
.track--flat.track--wall .track__copy, html:not(.js) .track--wall .track__copy { display: none; }
.track--flat.track--wall .track__flow, html:not(.js) .track--wall .track__flow { position: relative; top: auto; left: auto; right: auto; padding: var(--s-8) var(--margin) 0; }
.track--flat.track--wall .track__stage, html:not(.js) .track--wall .track__stage { min-height: 0; }

/* ---- the work strip captions (craft 6) --------------------------------------------------
   Three ivory captions over a sunlit crowd, sunlit foliage and a pale sunset: under 2:1 on two of
   the three, and this is the proof section. The same bottom band the tiles already use. */
.piece::after { content: ""; position: absolute; inset: auto 0 0 0; height: 48%;
  background: linear-gradient(to top, rgba(32,32,32,.78), rgba(32,32,32,0)); pointer-events: none; }
.piece.is-playing::after { display: none; }
.piece__cap { z-index: 1; }

/* ---- the page raises its voice twice (craft 11, ruling 12) -------------------------------
   --text-display-2 (48px) was the ceiling on all nine ground sections while --text-display-1
   (clamp 40-96px) was used only in the hero and --text-wall not at all. The two lines the page
   actually turns on — the thesis, and the claim the assembly is built to prove — take display-1.
   Both are existing tokens; nothing new was invented. Everything else stays at display-2 so the
   jump still means something. */
.display--lift { font-size: var(--text-display-1); max-width: 16ch; }

/* ---- unfilled placeholders (craft 8, audit 4.5, ruling 3) --------------------------------
   copy.md's square brackets stay in square brackets — BRIEF §1 — and check.sh still lists them as
   FILL. What stops is the 404: an anchor whose href is still a placeholder is inert, out of the tab
   order, and says so. Five of these shipped, one of them the page's primary conversion point. */
a[data-placeholder] { cursor: default; color: var(--muted); text-decoration: none; }
.btn[data-placeholder] { background: var(--surface); color: var(--muted); border-color: var(--rule); }
.btn[data-placeholder]:hover { background: var(--surface); color: var(--muted); opacity: 1; }

/* ---- skip link (audit 4.6) --------------------------------------------------------------
   Seven focusable items sit before <main> on every page. */
.visually-hidden:focus { position: fixed; top: var(--s-3); left: var(--s-3); width: auto; height: auto;
  clip: auto; overflow: visible; z-index: 20; background: var(--ink); color: var(--ground);
  padding: 10px 16px; text-decoration: none; }

/* ---- rule rows read as editorial, not administrative (craft 20) ------------------------- */
.rules--steps li, .rules--names li { padding: var(--s-5) 0; }

/* ---- how it adds up, rebuilt (craft 1, audit 7 and 12, ruling 5) -------------------------
   What was wrong: the four frames ended at the four 50x50 quadrants of the stage, so the "assembled"
   state was a 2x2 contact sheet — four unrelated crops touching at the seams — under a headline that
   promises one film. The geometry was animated on left/top/width/height, the only per-frame relayout
   on the page. And --a was written on .adds__stage while .adds__final, its sibling, read it: custom
   properties inherit downward, not sideways, so the payoff line was at opacity 1 from first paint.

   What it does now: every frame converges on the CENTRE of the stage and scales until it fills the
   stage's height, so the four end up stacked on one another in DOM order and the last to arrive —
   the history wide, the only true 16:9 in the set — lands exactly on the stage and covers the rest.
   One frame, which is what the sentence says.

   Two properties carry it, both composited: translate and scale, uniform, so no picture is ever
   stretched and object-fit never has to re-crop. --dx / --dy are the move to centre expressed in the
   frame's OWN width and height (that is what a percentage translate means), --k the scale that takes
   its height to the stage's; all three are arithmetic on the scattered box and are written once in
   the markup beside it. --a is declared on .adds and written there by site.js, so the stage, the
   frames and .adds__final all inherit the one number. */
.adds { --a: 0; }
.adds__frame { position: absolute; z-index: var(--z, 1);
  left: calc(var(--x0) * 1%); top: calc(var(--y0) * 1%);
  width: calc(var(--w0) * 1%); height: calc(var(--h0) * 1%); }
/* --ai is this frame's own share of --a: it waits for --d, then runs its 0.82 of the scrub. The four
   therefore land one after another rather than all at once, and the stack order runs with the delay,
   so the last to arrive is the one on top. */
.adds__move { position: absolute; inset: 0; will-change: transform;
  --ai: max(0, min(1, (var(--a) - var(--d, 0)) / 0.82));
  transform: translate(calc(var(--dx) * var(--ai) * 1%), calc(var(--dy) * var(--ai) * 1%))
             scale(calc(1 + (var(--k) - 1) * var(--ai))); }
.adds__label { top: 100%; }
/* the payoff line, in the display face at the section's H2 size. It was 11px grey, which is the
   smallest type on the page announcing the climax of the one component built to carry the argument. */
.adds__final { font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: var(--text-display-2); line-height: var(--lh-display); letter-spacing: var(--ls-display);
  color: var(--ink); margin-top: var(--s-6); }
@media (max-width: 720px) {
  /* craft 17: the phone tile had a 24px vertical gutter and a 0px horizontal one, so the frames
     touched in pairs and read as a layout bug */
  .adds__stage { gap: var(--s-5); }
  .adds__frame { position: static; left: auto; top: auto; width: auto; height: auto; }
  .adds__move { position: static; transform: none; will-change: auto; }
  .adds__final { margin-top: var(--s-5); }
}
@media (prefers-reduced-motion: reduce) {
  .adds__frame { position: static; left: auto; top: auto; width: auto; height: auto; }
  .adds__move { position: static; transform: none; will-change: auto; }
}

/* ---- the generated tile's label (audit 6) -----------------------------------------------
   nowrap inside a 13-percent-wide box with overflow hidden clipped it mid-word while the frames were
   apart, and ivory-on-cream made it invisible once they met. Plate, wrap, always legible. */
.adds__box--generated .still__generated { max-width: calc(100% - 2 * var(--s-4)); white-space: normal;
  line-height: 1.25; background: rgba(32,32,32,.62); padding: 3px 8px; }
/* The interviews plate is 9:16 and every static state (phone, reduced motion, no GSAP) crops it to
   16:9. Cover from the top third instead, which keeps the window and the two chairs; in the scattered
   state the box is already 9:16 so this changes nothing there. Scoped to the tall tile in Phase 49,
   when the other three became generated too and would otherwise have taken a crop bias meant for a
   portrait frame. */
.adds__box--tall img { object-position: center 18%; }

/* ---- the Founders row carries weight (craft 13) -----------------------------------------
   It was the one orphan rule row on the page: a 17px line and a 17px underlined phrase for the
   gallery, which is the platform. The name takes the display face and the route takes a button. */
.house--one .house__part { padding-top: var(--s-5); }
.house--one .house__name { display: block; margin-bottom: var(--s-3); }
.house--one .lines { max-width: var(--measure); }

/* ---- the footer place line (craft 20) ---------------------------------------------------
   It sat in muted grey directly above three ink links and read as their column heading. */
.footer__where { display: grid; gap: 4px; }

/* the hero poster is wrapped in a <picture> so the phone takes one poster file instead of two;
   display:contents keeps the absolutely positioned <img> behaving exactly as it did bare */
picture { display: contents; }

/* ---- how it adds up, when the scrub cannot run --------------------------------------------
   Without GSAP (or without site.js at all) --a never moves, so the four frames stayed scattered at
   their absolute coordinates with no layout under them and "Your brand film" sat at opacity 0 — the
   payoff line invisible and the tile a pile. The phone and reduced-motion paths already render the
   same four as a static two-up; this is that state under two more conditions. */
.adds--static .adds__stage, html:not(.js) .adds__stage {
  position: static; aspect-ratio: auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.adds--static .adds__frame, html:not(.js) .adds__frame {
  position: static; left: auto; top: auto; width: auto; height: auto; }
.adds--static .adds__move, html:not(.js) .adds__move { position: static; transform: none; will-change: auto; }
.adds--static .adds__box, html:not(.js) .adds__box { position: relative; inset: auto; aspect-ratio: 16 / 9; }
.adds--static .adds__label, html:not(.js) .adds__label { position: static; white-space: normal; opacity: 1; }
.adds--static .adds__final, html:not(.js) .adds__final { opacity: 1; }

/* Reduced motion releases the pin and stacks the beats, so an absolutely positioned honesty label
   stays pinned to the stage and ends up sitting on the arena poster — labelling a frame that is not
   generated, which is the BRIEF §1 problem in reverse. It rejoins its own beat. */
@media (prefers-reduced-motion: reduce) {
  .track__generated { position: static; top: auto; right: auto; justify-self: start; }
}

/* ---- Phase 48 · Home, second pass ------------------------------------------------------------ */

/* ---- the hero H1 holds (BRIEF §3.4.1) ---------------------------------------------------------
   "The H1 holds. Only the line under it is allowed to dissolve." Phase 47 stopped the ground's edge
   slicing through the letterforms by fading the whole hero beat out in front of it — which cleared
   the slice by breaking the one rule the brief states about the H1, and left 140px of wordless film
   behind. The copy layer is now a SIBLING of the sticky stage (see index.html), so it occupies the
   track's first viewport and travels up and off at scroll speed while the film holds behind it. The
   type leaves the frame the way type leaves a frame in a film; it never dissolves, and site.js
   writes nothing on it. --close: 2 then puts the ground's first pixel where the copy's last one
   goes: three viewports of track, the copy in the first, the cover sweeping the third. */
/* Erin, 16 Sep: the hero copy is pinned with the film like every other block and fades out in place
   (site.js writes its opacity across the second viewport of the track), so no text ever scrolls away
   under the nav. This overrides BRIEF §3.4.1 "the H1 holds" on Erin's instruction. */
.track--top .track__copy { z-index: 1; }
.track--top .track__beat--hero { transition: none; }
/* released states put the copy back in the flow under the poster, like every other track */
@media (prefers-reduced-motion: reduce) { .track--top .track__copy { position: relative; top: auto; height: auto; min-height: 0; } }
.track--flat.track--top .track__copy, html:not(.js) .track--top .track__copy {
  position: relative; top: auto; height: auto; min-height: 0; }

/* ---- reduced motion is not a dark twin (BRIEF §5) ---------------------------------------------
   "Light is the only palette. There is no dark twin." With the pin released the stage is no longer a
   film frame — it is a poster block with a copy block under it — but it kept .track__stage's
   near-black fill, so the make track shipped as ~800px of ink with ivory type while the house track,
   which carries data-tone="light", shipped ivory. One page, two palettes, and one of them forbidden.
   Released, every track is a light ground section with ink type, which is what the light-tone track
   already was. The same release is the no-JS and no-GSAP path, so it takes the same rules. */
@media (prefers-reduced-motion: reduce) {
  .track__stage { background: var(--ground); }
  .track .display, .track .lines, .track .label, .track p,
  .track__beat .link { color: var(--ink); }
  .track__q, .track__stage[data-tone="light"] .track__q { color: rgba(32,32,32,.72) !important; }
  .track .btn--film { background: var(--ink); color: var(--film-ink); border-color: var(--ink); }
  .track__beat .link:hover { background: var(--ink); color: var(--film-ink); }
  .track .display .square { background: var(--section); }
}
.track--flat .track__stage, html:not(.js) .track__stage { background: var(--ground); }
.track--flat .display, .track--flat .lines, .track--flat .label, .track--flat p, .track--flat .track__beat .link,
html:not(.js) .track .display, html:not(.js) .track .lines, html:not(.js) .track .label,
html:not(.js) .track p, html:not(.js) .track__beat .link { color: var(--ink); }
.track--flat .track__q, html:not(.js) .track__q { color: rgba(32,32,32,.72) !important; }
.track--flat .btn--film, html:not(.js) .track .btn--film {
  background: var(--ink); color: var(--film-ink); border-color: var(--ink); }
.track--flat .display .square, html:not(.js) .track .display .square { background: var(--section); }

/* ---- the three beats read for the same length of time -----------------------------------------
   A beat's share of the track is --dwell (site.js reads it, falling back to data-h). All three were
   1, which is equal SCROLL, not equal READING: the first beat spends part of its share climbing into
   the frame and the last spends part of its share under the ground's edge, and the two corrections
   do not cancel. Measured (10px sweep, per-beat content box against the cover edge, whole page), the
   three were 570 / 550 / 740 on the make track at 1440 and 540 / 520 / 470 at 390 — and note that
   they are imbalanced in OPPOSITE directions at the two widths, because a desktop beat hangs from
   the top of the stage and a phone beat stands on its foot. One number cannot fix both, which is why
   the share is a custom property and not an attribute. Solved from those measurements; the shares on
   each track sum to 3, so the track's height and every other number on it are unchanged. */
/* Phase 49 re-measured and re-solved these. Moving the copy to the hero's foot band moved it ~200px
   closer to the floor of the stage, which is where the ground's cover edge comes in, so the last beat
   of each track lost that much clean reading and the desktop spread went back out to 1.48:1 on the
   make track. Same sweep, same definition, re-solved: shares still sum to 3, so no track's height
   changes. The phone was inside 6 percent already and is untouched. */
.track--beats .track__beat[data-film="event"]     { --dwell: 0.976; }
.track--beats .track__beat[data-film="interview"] { --dwell: 0.985; }
.track--beats .track__beat[data-film="brand"]     { --dwell: 1.039; }
.track--beats .track__beat[data-film="studios"]   { --dwell: 0.994; }
.track--beats .track__beat[data-film="creative"]  { --dwell: 1.008; }
.track--beats .track__beat[data-film="solutions"] { --dwell: 0.998; }
@media (max-width: 720px) {
  .track--beats .track__beat[data-film="event"]     { --dwell: 1.004; }
  .track--beats .track__beat[data-film="interview"] { --dwell: 0.987; }
  .track--beats .track__beat[data-film="brand"]     { --dwell: 1.010; }
  .track--beats .track__beat[data-film="studios"]   { --dwell: 0.966; }
  .track--beats .track__beat[data-film="creative"]  { --dwell: 0.986; }
  .track--beats .track__beat[data-film="solutions"] { --dwell: 1.047; }
}

/* ---- the phone beat headlines share a line ----------------------------------------------------
   On a phone a beat is anchored to the foot of the stage, so a beat with an extra line of copy grows
   UPWARD and its headline starts 28px higher than its neighbours' — the cross-dissolve hopped. Every
   beat takes the height of the tallest, which site.js already measures as --beat-max for the hold
   line, so the tops line up without guessing at a number. */
@media (max-width: 720px) {
  .track--beats .track__beat:not(.track__beat--hero):not(.track__beat--centre) { min-height: var(--beat-max, 0px); }
}

/* ---- the assembly, held still (phone · reduced motion · no GSAP) ------------------------------
   The one original component on the page resolved to a 2x2 contact sheet in every state that cannot
   run the scrub — roughly half the traffic — under a headline that promises one film. It now shows
   the result the way the scrub ends: the event frame full width, "Your brand film" under it, and the
   four sources as a labelled row beneath. No script, three rules. */
/* position: relative was missing and did not matter while the figure carried only a picture. Phase 49
   put the honesty caption on it, and .still__generated is absolute: without a positioned parent it
   hung off the section instead of off the frame it labels. */
.adds__assembled { display: none; position: relative; margin: var(--s-7) 0 0; }
.adds__assembled img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
/* .adds--static is the class site.js already puts on the section whenever the scrub cannot run —
   a phone, prefers-reduced-motion, or GSAP missing — and html:not(.js) is the same state with no
   script at all. One condition, one set of rules. */
.adds--static .adds__assembled, html:not(.js) .adds__assembled { display: block; }
.adds--static .section__in, html:not(.js) .adds .section__in { display: flex; flex-direction: column; }
.adds--static .adds__final, html:not(.js) .adds__final { order: 1; margin-top: var(--s-5); opacity: 1; }
.adds--static .adds__stage, html:not(.js) .adds__stage { order: 2; margin-top: var(--s-7);
  position: static; aspect-ratio: auto; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-5); }
.adds--static .adds__frame, html:not(.js) .adds__frame { position: static; left: auto; top: auto; width: auto; height: auto; }
.adds--static .adds__move, html:not(.js) .adds__move { position: static; transform: none; will-change: auto; }
.adds--static .adds__box, html:not(.js) .adds__box { position: relative; inset: auto; aspect-ratio: 16 / 9; }
.adds--static .adds__label, html:not(.js) .adds__label { position: static; white-space: normal; opacity: 1; margin-top: var(--s-2); }

/* ======================================================================================
   Home · Phase 49 · the cohesion pass
   Erin: "the home page does not feel cohesive." The contact sheet said why in four places —
   four visual worlds below the hero, copy landing in a different place in every full-bleed
   section, six headlines at three sizes, and four section shapes on a page that has three.
   The beds and the order are in index.html. The three rules the page now holds to are here.
   ====================================================================================== */

/* ---- 1 · one copy anchor -----------------------------------------------------------------
   Every full-bleed section put its words somewhere else. The hero's foot block sits at the left
   margin in the bottom band, above the button; the beat tracks hung their copy from --copy-top,
   54vh on a desktop and 40vh on a phone — mid-frame, a third of the way down, attached to nothing.
   Scrolling the page, the type jumped up and down the frame at every section.

   One anchor now, and it is the hero's: --copy-foot, the distance from the floor of a stage to the
   bottom of the copy block. That number was already in the page twice over — .track__beat--hero
   ends at --s-7 inside a hero beat that itself ends at --s-7 — so it is written once here and both
   the hero foot and every beat read it. Beats are bottom-anchored at all widths, which is what the
   phone already did; the desktop's top-anchoring was the odd one out and was the reason one
   mechanism could not serve both. Every beat takes --beat-max (site.js measures the tallest), so
   the three headlines on a track also share one line.

   Consequence worth naming: the copy sits LOWER on the beat tracks than it did, which is the point —
   it sits where the hero put it — and it moves the type down into the scrim's strongest band, so the
   band itself can be shorter and give the picture back. See "the scrim follows the copy" below. */
:root { --copy-foot: calc(var(--s-7) * 2); }
/* :not(--hero) because Home's one track (Phase 55) carries .track--beats AND the hero beat on the
   same element, and the hero is a full-stage block with its own top and foot, not a copy block
   standing on the anchor. On every other page the two classes never met, so nothing there moves. */
.track--beats .track__beat:not(.track__beat--centre):not(.track__beat--hero) {
  top: auto; bottom: var(--copy-foot); min-height: var(--beat-max, 0px); }
/* the eyebrow hold sits directly above the headline, in the same block, at both widths */
.track--beats .track__hold:not(.track__hold--centre) {
  top: auto; bottom: calc(var(--copy-foot) + var(--beat-max, 0px) + var(--s-5)); transform: none; }
/* The hero's own foot is re-expressed in the same token rather than left as the two stacked --s-7s
   that happened to add up to it, so the anchor is one number in one place and cannot drift. Same
   pixel as before: the beat used to stop --s-7 above the stage floor with the foot --s-7 above that. */
.track--top .track__beat--hero { bottom: 0; }
.track--top .track__beat--hero .hero__foot { bottom: var(--copy-foot); }
@media (max-width: 720px) {
  /* the phone rules above this point set --s-8; --copy-foot is the one number now */
  .track--beats .track__beat:not(.track__beat--hero):not(.track__beat--centre) { bottom: var(--copy-foot); }
  .track--beats .track__hold:not(.track__hold--centre) {
    bottom: calc(var(--copy-foot) + var(--beat-max, 0px) + var(--s-5)); }
}

/* ---- the scrim follows the copy ----------------------------------------------------------
   The band was built in Phase 48 for copy that started at 54vh, so it ran to 58 percent of the
   frame. The copy now finishes at --copy-foot and starts around 75 percent down, so two fifths of
   every bed was being washed to protect type that is nowhere near it. The strength at the floor is
   unchanged (the brief's 60 percent); only the reach comes in, which hands the top of every frame
   back to the picture. taste.md: light must have an opinion. */
.track--beats .track__scrim {
  background: linear-gradient(to top, rgba(32,32,32,.78) 0%, rgba(32,32,32,.58) 18%, rgba(32,32,32,0) 44%); }
.track--beats .track__stage[data-tone="light"] .track__scrim {
  background: linear-gradient(to top, rgba(239,236,230,.6) 0%, rgba(239,236,230,.46) 18%, rgba(239,236,230,0) 44%); }
@media (max-width: 720px) {
  .track--beats .track__scrim {
    background: linear-gradient(to top, rgba(32,32,32,.82) 0%, rgba(32,32,32,.64) 26%, rgba(32,32,32,0) 56%); }
  .track--beats .track__stage[data-tone="light"] .track__scrim {
    background: linear-gradient(to top, rgba(239,236,230,.6) 0%, rgba(239,236,230,.5) 26%, rgba(239,236,230,0) 56%); }
}

/* ---- 2 · one measure on the ground -------------------------------------------------------
   Ground sections already shared one left edge (--margin) but not one measure: a statement's lines
   ran to the 720px column while .adds__lede, .proof and the Founders row ran to --measure, so the
   right edge of the text moved section to section. --measure is the token for a line of body copy,
   so every line on the ground takes it. The rule lists keep the 720px column: they are a grid of two
   and three columns, not a line of prose, and they are the page's third section shape. */
.ground .lines { max-width: var(--measure); }

/* ---- 3 · one rhythm ----------------------------------------------------------------------
   --section-gap opens every ground section and closes every ground run. The one number that was not
   it — the thesis section's gap plus a step — is gone (see above). A run that opens on a track's
   resolve carries the same token. */

/* ======================================================================================
   Home · Phase 54 · the seams dissolve
   Erin, 16 Sep: "these sections are still scrolling up." Inside a track the beats already
   faded in place. What still slid was the SEAM — the ground scrolling up to uncover the
   next film, and the next ground scrolling up over the pinned film. The rule for the whole
   page is that nothing slides over anything: a block fades out where it stands, the next
   fades into the same spot, and the background changes behind it.

   A .track--seam does both ends of that.
     · film -> ground. No cover sweep at all: --close: 0 and margin-bottom: 0, so the ground
       element below starts where the stage stops rather than 100vh early. The last beat is a
       GROUND BEAT: the bed cross-dissolves to a solid ivory field, the scrim goes off, the
       type goes ink, and the next section's opening block fades into the same copy anchor.
       The stage releases on ivory with that heading at the anchor and the rest of the section
       runs in flow directly under it, on the same colour. There is no edge to see.
     · ground -> film. .track__open is an ivory panel over the WHOLE stage, copy included, at
       full opacity when the track pins — so the stage entering from below is the same ivory
       the section above ended on — dissolving to nothing across the first quarter of the
       first beat, which brings the bed and that beat's words up together out of one field.
   Everything here is opt-in on .track--seam. founders.html and studios.html keep their cover.
   ====================================================================================== */

/* no cover sweep: the next block begins where the stage ends, not a viewport early */
.track--seam { margin-bottom: 0; }

/* the ground bed. A div rather than a video: site.js already treats a non-VIDEO .track__film as
   a bed with nothing to load and nothing to play, so it inherits the one cross-dissolve. */
.track__film--ground { background: var(--ground); }

/* data-tone="ground" · the stage IS the ground. No scrim, ink type, ivory field. */
.track__stage[data-tone="ground"] { background: var(--ground); }
.track__stage[data-tone="ground"] .track__scrim { background: none; }
.track__stage[data-tone="ground"] .display,
.track__stage[data-tone="ground"] .lines,
.track__stage[data-tone="ground"] .label,
.track__stage[data-tone="ground"] p { color: var(--ink); }
.track__stage[data-tone="ground"] .track__q { color: rgba(32,32,32,.72) !important; }
.track__stage[data-tone="ground"] .btn--film { background: var(--ink); color: var(--film-ink); border-color: var(--ink); }
.track__stage[data-tone="ground"] .link { color: var(--ink); }

/* the ground beat sits on the page's one copy anchor, like every other beat */
.track__beat--ground { top: auto; bottom: var(--copy-foot); min-height: var(--beat-max, 0px); }
.track__beat--ground .display--lift { max-width: 18ch; }

/* the opening dissolve. Opacity is driven by site.js (--open); the CSS default is fully opaque, so
   the stage is ivory from first paint until the scroll says otherwise. Above the copy layer, so bed
   and words arrive through one dissolve. */
.track__open { position: absolute; inset: 0; z-index: 2; background: var(--ground);
  opacity: var(--open, 1); pointer-events: none; }

/* On a seam track the hero beat is beat 0 of a track that ends on the ground, so it fades out on the
   ordinary beat dissolve when the ground beat arrives instead of on a ramp of its own. site.js writes
   nothing on its opacity there. This is still the Phase 53 behaviour Erin asked for — the hero copy is
   pinned and never scrolls away — on the page's one mechanism rather than a second one. */
.track--seam .track__beat--hero { transition: opacity var(--t-dissolve) var(--ease-out), visibility 0s var(--t-dissolve); }

/* the flow content joins the released stage with no gap of its own: the distance from the heading to
   the content under it is --copy-foot, the same anchor everything else on the page measures from */
.ground--joined > :first-child { margin-top: 0; padding-top: 0; }

/* released states — reduced motion, no GSAP, no JavaScript. BRIEF §3.3: the ground beats render in
   flow as the plain section they replace, and nothing is hidden. The overlay and the ivory bed have
   no work to do once the pin is gone. */
.track--flat .track__open, html:not(.js) .track__open { display: none; }
.track--flat .track__film--ground, html:not(.js) .track__film--ground { display: none; }
@media (prefers-reduced-motion: reduce) { .track__open, .track__film--ground { display: none; } } /* unchanged */

/* ---- dwell parity, now that there is nothing to be covered by --------------------------------
   Phases 48 and 49 solved per-beat --dwell values to compensate for the cover sweep eating the last
   beat's reading. There is no cover sweep on a seam track, so equal scroll is equal reading again and
   the compensations are wrong rather than merely unnecessary. Every beat is 1, at both widths. The
   first beat of a track that opens on the ivory panel takes 1.12, because the first quarter of its
   share is spent dissolving up out of that panel. */
.track--beats.track--seam .track__beat[data-film="interview"],
.track--beats.track--seam .track__beat[data-film="brand"],
.track--beats.track--seam .track__beat[data-film="creative"],
.track--beats.track--seam .track__beat[data-film="solutions"],
.track--seam .track__beat--ground,
.track--seam .track__beat { --dwell: 1; }
.track--beats.track--seam .track__beat[data-film="event"],
.track--beats.track--seam .track__beat[data-film="studios"] { --dwell: 1.12; }
@media (max-width: 720px) {
  .track--beats.track--seam .track__beat[data-film="interview"],
  .track--beats.track--seam .track__beat[data-film="brand"],
  .track--beats.track--seam .track__beat[data-film="creative"],
  .track--beats.track--seam .track__beat[data-film="solutions"],
  .track--seam .track__beat--ground,
  .track--seam .track__beat { --dwell: 1; }
  .track--beats.track--seam .track__beat[data-film="event"],
  .track--beats.track--seam .track__beat[data-film="studios"] { --dwell: 1.12; }
}

/* ======================================================================================
   The film track on a phone · the copy block is centred and is its own composition
   Erin, 16 Sep. At 390 a bed is a 9:16 centre crop and the copy is one short block, so it is
   centred in the frame — horizontally and vertically — rather than hung off the left margin
   at the foot. Desktop composition is untouched: left, foot-anchored, the Phase 49 anchor.

   Phase 58: re-scoped from [data-page="home"] to .track--film, so Founders — the second page
   built on one pinned stage — gets the same composition rather than a copy of it. A no-op for
   Home, which was the only .track--film on the site when the block was written; every selector
   here also names .track--film, .track--beats, .track--top or a beat class. The rules for
   ground sections in FLOW stay on [data-page="home"]: Home has no .ground element left, and a
   released section on another page is left-weighted (BRIEF §5), not centred.
   ====================================================================================== */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--centre),
  .track--film.track--seam .track__beat--ground {
    top: 0; bottom: 0; align-content: center; justify-items: center;
    text-align: center; gap: var(--s-5); min-height: 0; }
  .track--film.track--beats .track__beat .display { text-wrap: balance; max-width: 15ch; }
  .track--film.track--beats .track__beat .lines { max-width: 32ch; }
  .track--film.track--beats .track__beat .btn { justify-self: center; }
  /* the holding eyebrow sits directly above the centred block, clear of the tallest beat */
  .track--film.track--beats .track__hold:not(.track__hold--centre) {
    top: 50%; bottom: auto; text-align: center;
    transform: translateY(calc(-50% - var(--beat-max, 0px) / 2 - var(--s-5))); }
  /* the hero: the same composition. The H1, the line and the button become one centred stack in the
     middle of the frame instead of a top block and a foot block. */
  .track--film.track--top .track__beat--hero {
    display: grid; top: 0; bottom: 0; align-content: center; justify-items: center;
    text-align: center; gap: var(--s-6); }
  .track--film.track--top .track__beat--hero .hero__top,
  .track--film.track--top .track__beat--hero .hero__foot {
    position: static; left: auto; right: auto; top: auto; bottom: auto;
    display: grid; gap: var(--s-4); justify-items: center; }
  .track--film.track--top .track__beat--hero .display--one { max-width: 12ch; }
  /* ground sections in flow take the same centring and the same measure */
  [data-page="home"] .ground .section__in { text-align: center; }
  [data-page="home"] .ground .lines,
  [data-page="home"] .ground .display { margin-left: auto; margin-right: auto; max-width: 32ch; }
  [data-page="home"] .ground .display { max-width: 15ch; }
  [data-page="home"] .ground .rules li { text-align: center; }
  [data-page="home"] .ground .doors { justify-content: center; }
  [data-page="home"] .ground .piece__cap { text-align: left; }

  /* ---- the scrim as a soft full-height centre band ------------------------------------------
     The Phase 49 band was built for copy standing on the floor of the stage. Centred copy needs the
     protection in the middle of the frame, so the band peaks at the centre at the brief's 60 percent
     and falls to nothing at both edges, handing the top and the bottom of every 9:16 crop back to the
     picture. Ink under the hero's ivory type, ivory under the beds' ink type. */
  .track--film.track--beats .track__scrim {
    background: linear-gradient(to bottom, rgba(32,32,32,0) 0%, rgba(32,32,32,.34) 16%,
      rgba(32,32,32,.6) 50%, rgba(32,32,32,.34) 84%, rgba(32,32,32,0) 100%); }
  .track--film.track--beats .track__stage[data-tone="light"] .track__scrim {
    background: linear-gradient(to bottom, rgba(239,236,230,0) 0%, rgba(239,236,230,.36) 16%,
      rgba(239,236,230,.62) 50%, rgba(239,236,230,.36) 84%, rgba(239,236,230,0) 100%); }
  .track--film.track--beats .track__stage[data-tone="ground"] .track__scrim { background: none; }
}

/* ======================================================================================
   Home · Phase 55 · one pinned stage, and nothing on the page scrolls
   Erin, for the third time: no block ever scrolls up. A block fades out where it stands,
   the next fades into the same spot, the background changes behind it. Phase 52 did that
   inside a track and Phase 54 dissolved the seams between them, but the released stages
   and the flow content under them still scrolled as ordinary page content.

   So Home is ONE track — .track--film — whose stage is sticky from the top of the page to
   the footer and whose beats are every section of copy.md HOME in order. Everything below
   is opt-in on that class or on .track__beat--panel, which only Home uses. founders.html
   and studios.html keep their tracks, their cover sweeps and their compositions.
   ====================================================================================== */

/* ---- the beats all read for the same length ------------------------------------------------
   There is no cover sweep and no opening panel on this track (nothing precedes it: the page
   opens on the film), so equal scroll is equal reading and every beat is 1. The two that are
   not are inline in the markup, where the reason is next to the thing: the assembly and the
   work strip take 1.5 so a viewer can watch one happen and start the other. These have to
   out-specify the Phase 54 seam values, which are (0,4,0). */
.track--film.track--beats.track--seam .track__beat,
.track--film.track--beats.track--seam .track__beat[data-film] { --dwell: 1; }

/* ---- the panel beat ------------------------------------------------------------------------
   A beat carrying more than a headline and its lines: the assembly, a rule list, the work
   strip, a statement of five lines, the two doors. It stands on the same copy anchor as every
   other beat and takes whatever height it needs above it — it is not held to --beat-max, which
   is the line the six service and house beats share under their two eyebrows. site.js excludes
   it from that measurement for the same reason. */
.track--beats .track__beat.track__beat--panel:not(.track__beat--centre) {
  top: auto; bottom: var(--copy-foot); min-height: 0; }
.track__beat--panel .display--lift { max-width: 18ch; }
/* the Founders row used to sit in .house--one on the ground; the name is still its own line */
.track__beat--panel .house__name { display: block; margin-bottom: var(--s-3); }
.track__beat--panel .after { margin-top: var(--s-5); }
.track__beat--panel .proof { max-width: var(--measure); }

/* ---- ink on the ground, for everything, not for a list of elements -------------------------
   A track is `color: var(--film-ink)` and every tone flip repaints an ELEMENT LIST — .display,
   .lines, .label, p. That was enough while the only things on a track were headlines and lines.
   The rule list's third cell is a bare <span>, so it matched nothing, inherited the track's ivory
   and vanished on the ivory ground: "One event." with no "We film your team in action." beside
   it, in the live page and in the released fallback both. The copy layer takes the colour. */
.track--film .track__stage[data-tone="ground"] .track__copy { color: var(--ink); }
.track--film.track--flat .track__copy,
html:not(.js) .track--film .track__copy { color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .track--film .track__copy { color: var(--ink); } }

/* ---- the assembly, on a beat ---------------------------------------------------------------
   A 16:9 stage the full width of the frame is 745px tall at 1440, so on a beat it is sized from
   the height it is allowed rather than the width it could have: the frame, less the copy anchor,
   less room for "Your brand film" under it. The four converge inside that box exactly as they
   did in the flow — every --x0/--dx/--k in the markup is a percentage of this stage, so the
   geometry is untouched. */
.track__beat--figure .adds { width: 100%; }
.track__beat--figure .adds__stage {
  margin-top: 0;
  width: min(100%, calc((100vh - var(--chrome-h, var(--nav-h)) - var(--deadline-h)
    - var(--copy-foot) - var(--s-9)) * 16 / 9)); }
.track__beat--figure .adds__final { margin-top: var(--s-5); }
/* the static arrangement — a phone, prefers-reduced-motion, no GSAP — used to hang its column
   off .section__in, which a beat does not have. .adds--static is on .adds itself. */
.adds--static, html:not(.js) .adds { display: flex; flex-direction: column; }
.track__beat--figure .adds--static .adds__assembled { margin-top: 0; }

/* ---- the work strip, on a beat -------------------------------------------------------------
   .work breaks out to 100vw inside a .section__in; a beat already runs margin to margin, so the
   breakout would hang off the right edge. Three across in the frame instead, and the first piece
   stops spanning the row. */
.track__beat--work .work {
  width: auto; max-width: none; margin-left: 0;
  grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.track__beat--work .work > :first-child { grid-column: auto; }
/* the caption gets a right edge, so its line wraps inside the poster instead of running off it */
.track__beat--work .piece__cap { left: var(--s-3); right: var(--s-3); bottom: var(--s-3); gap: var(--s-3); }
/* and it stays ivory. A caption sits on FILM, not on the ground the beat's tone describes, so the
   ground-tone repaint above has to stop at the poster's edge — otherwise the second line of every
   caption goes ink and disappears into a sunlit frame. */
.track__beat--work .piece__cap,
.track__beat--work .piece__cap strong,
.track__beat--work .piece__cap .lines { color: var(--film-ink); }

/* The page ends on the footer. The stage releases on the ivory of the last beat and the footer —
   the only flow content on the page — arrives on the same ivory a --section-gap below it, which is
   the rest every other run on the site ends with. Nothing is uncovered and there is no edge. */

@media (max-width: 720px) {
  /* ---- centred in the SAFE area, not in the frame -----------------------------------------
     Phase 54 centred a beat in the whole 844px stage, which was right for a headline and a line
     but puts a rule list or three posters under the deadline bar and the nav. Every beat except
     the hero — which is meant to sit in the film, under a 20-percent bar — centres in what is
     left below the chrome. --safe is the one number, read from the two heights site.js measures. */
  .track--film.track--beats .track__copy {
    --safe: calc(var(--chrome-h, var(--nav-h)) + var(--deadline-h)); }
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--centre) {
    top: var(--safe); bottom: 0; }
  /* the hold's own position on a phone is Phase 56's: it is measured off the block it belongs to,
     not off the middle of the frame. See "the eyebrow is part of the block" at the end of this file. */
  /* A beat is centred (Phase 54), so `justify-items: center` sizes each block to its content and
     centres it — which is what a headline and its lines want. The blocks that are a FIGURE rather
     than a line of prose want the whole width instead: a rule list, the work grid, the assembly.
     Only those are stretched; stretching a headline as well would pin its 15ch measure to the
     left edge while the lines under it stayed centred. */
  .track--film.track--beats .track__beat--panel > .rules,
  .track--film.track--beats .track__beat--panel > .work,
  .track--film.track--beats .track__beat--panel > .adds,
  .track--film.track--beats .track__beat--panel .adds > * { justify-self: stretch; width: 100%; }
  .track--film.track--beats .track__beat--panel .doors { justify-content: center; }
  .track--film.track--beats .track__beat--panel .after { text-align: center; }
  /* the rule list: the numeral spans the two rows, so the rows close up with the gap token */
  .track--film.track--beats .track__beat--panel .rules--steps li { padding: var(--s-3) 0; }
  /* three 16:9 posters, the headline above them and the proof line under them in one frame: the
     poster width comes from the height three of them are allowed inside the safe area, not from
     the width of the phone. The reserve is the headline, the proof line and the gaps between. */
  .track--film.track--beats .track__beat--work .work {
    grid-template-columns: 1fr; gap: var(--s-2); justify-items: center; }
  .track--film.track--beats .track__beat--work .piece {
    width: min(100%, calc((100vh - var(--chrome-h, var(--nav-h)) - var(--deadline-h)
      - var(--s-10) - var(--s-8) - var(--s-4)) / 3 * 16 / 9)); }
  /* the caption has to live inside a poster a third of the width it had in the flow, so its line
     takes --text-small — the design system's caption size — instead of body. The title keeps body
     weight and size: it is the name of the piece. */
  .track--film.track--beats .track__beat--work .piece__title .lines {
    font-size: var(--text-small); line-height: var(--lh-label); max-width: none; }
  /* and the caption band reaches as far up the poster as the caption does. At 48 percent of a
     160px poster the band stopped under the title, which put "Cabinet Health" on sunlit foliage. */
  .track--film.track--beats .track__beat--work .piece::after { height: 72%;
    background: linear-gradient(to top, rgba(32,32,32,.86), rgba(32,32,32,.62) 46%, rgba(32,32,32,0)); }
  /* the assembly is already assembled here: the frame, its payoff line, then the four sources */
  .track--film.track--beats .track__beat--figure .adds__stage { margin-top: var(--s-5); gap: var(--s-4) var(--s-4); }
  .track--film.track--beats .track__beat--figure .adds__assembled { margin-top: 0; }
}


/* ======================================================================================
   Home · Phase 56 · the beat headings at wall size, and the eyebrow back on the block
   Erin, 16 Sep: "make these headings a lot bigger", with the Creative page's wall as the
   reference — a short rule, the eyebrow, then the headline at wall size, left on the margin.

   That is a composition, not a font size, and the font size on its own broke it: the hold
   was positioned off the middle of the frame while the beat was centred by its own height,
   so on a phone the eyebrow and its rule landed on top of the body lines. A hold is one
   element covering three beats, so it cannot live in a beat's grid — but it can be measured
   off the same box the beats are, and that is what it does here. The six film beats share
   --beat-max (site.js measures the tallest of them), the box is placed by one piece of
   arithmetic, and the hold is placed by the same arithmetic one line higher.

   Film beats only — the six service and house beats. The ground panels and the rule lists
   keep display-2 and their centred phone composition, so the page still has a hierarchy.
   ====================================================================================== */

/* the hold is a block: a short rule, the eyebrow under it */
.track--film.track--beats .track__hold::before {
  content: ""; display: block; width: 96px; height: 2px; background: currentColor;
  margin-bottom: var(--s-4); }

/* desktop · display-1, and air under 96px of type. --s-5 was the gap for a 48px headline;
   at display-1 the lines were sitting in the descenders. */
/* Phase 58: .track--beats is compounded on so the re-scope keeps this rule's specificity ABOVE
   Phase 54's phone `gap: var(--s-5)`, which it had by a point while both carried
   [data-page="home"]. Dropping the prefix from both made them equal and Phase 54 won on a phone,
   which took --beat-max at 390 from 256.78px to 240.78px on Home and moved the six film beats.
   Caught on the regression sheet, not by reading the file. */
.track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) {
  gap: var(--s-6); }
.track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) .display--two {
  font-size: var(--text-display-1); max-width: 12ch; }

@media (max-width: 720px) {
  /* --text-wall, left on the margin. This supersedes Phase 54's centred phone composition for
     the film beats and for the eyebrow above them; the hero and the ground panels keep it. */
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel),
  .track--film.track--beats .track__hold:not(.track__hold--centre) {
    justify-items: start; text-align: left; }
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) .display {
    font-size: var(--text-wall); line-height: var(--lh-wall); letter-spacing: var(--ls-wall);
    max-width: 8ch; text-wrap: balance; }
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) .lines,
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) .display {
    margin-left: 0; margin-right: 0; }
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel) .lines {
    max-width: 100%; }

  /* ---- the eyebrow is part of the block ----------------------------------------------------
     The six film beats share one box, --beat-max tall, centred in the safe area below the
     chrome. One box for all six is what makes the eyebrow a HOLD: it does not move when the
     beat under it changes. The beats fill that box from the top; a beat shorter than the
     tallest simply ends higher, and its headline still starts where every other one does. */
  .track--film.track--beats .track__beat:not(.track__beat--hero):not(.track__beat--panel):not(.track__beat--centre) {
    top: calc(var(--safe) + (100% - var(--safe) - var(--beat-max, 0px)) / 2);
    bottom: auto; min-height: var(--beat-max, 0px); align-content: start; }
  /* and the hold sits directly above that box, by the same arithmetic, one --s-5 clear of it */
  .track--film.track--beats .track__hold:not(.track__hold--centre) {
    top: calc(var(--safe) + (100% - var(--safe) - var(--beat-max, 0px)) / 2);
    bottom: auto; left: var(--margin); right: var(--margin);
    transform: translateY(calc(-100% - var(--s-5))); }
}

/* ---- and the scrim follows the copy, again -------------------------------------------------
   Phase 49 pulled the desktop band in to 44 percent of the frame because the copy finished at
   --copy-foot and started around 75 percent down. Wall-size headings with an eyebrow above them
   start at 51 percent instead, so the eyebrow and the top of the headline were standing on bare
   window. The reach goes out to 58 percent; the strength at the floor is unchanged, which is
   still the brief's 60 percent. The phone keeps its own full-height centre band (Phase 54). */
@media (min-width: 721px) {
  .track--film.track--beats .track__scrim {
    background: linear-gradient(to top, rgba(32,32,32,.78) 0%, rgba(32,32,32,.6) 24%, rgba(32,32,32,0) 58%); }
  .track--film.track--beats .track__stage[data-tone="light"] .track__scrim {
    background: linear-gradient(to top, rgba(239,236,230,.64) 0%, rgba(239,236,230,.52) 24%, rgba(239,236,230,0) 58%); }
  .track--film.track--beats .track__stage[data-tone="ground"] .track__scrim { background: none; }
}

/* The held eyebrow is structure now, not a whisper: it carries a rule and it names the group the
   headline belongs to, the way the Creative page's wall does (.pagetop__bar is the section colour
   at full strength). At .72 on a sunlit seamless it was barely there. */
.track--film.track--beats .track__stage[data-tone="light"] .track__q,
.track--film.track--beats .track__stage[data-tone="ground"] .track__q { color: var(--ink) !important; }


/* ======================================================================================
   Founders · Phase 58 · the wall, the five answers, the resolve, and the empty gallery

   The page is Home's grammar with three additions, and nothing else: a WALL beat (the page
   name at wall size over the scrubbed skyline), a RESOLVE beat (the five answers setting
   down as a list while the bed dissolves to ivory) and the two-column empty state under the
   seam. Everything else on the page is a class that already ships and is verified on Home.
   ====================================================================================== */

/* ---- the three dwells ----------------------------------------------------------------
   Phase 54's seam parity put every beat at 1 and the first beat of a track that opens on the
   ivory panel at 1.12, because the first quarter of its share is spent dissolving up out of
   that panel. That is beat 00 here. The five answers take 1.1 — they are one line each and go
   past quickly — and the resolve takes 1.5, which is a beat and a half to watch seven lines
   write themselves in. Total 8.12. These have to out-specify Phase 55's (0,4,0) blanket. */
.track--film.track--beats.track--seam .track__beat--wall,
.track--film.track--beats.track--seam .track__beat--wall[data-film] { --dwell: 1.12; }
.track--film.track--beats.track--seam .track__beat--answer,
.track--film.track--beats.track--seam .track__beat--answer[data-film] { --dwell: 1.1; }
.track--film.track--beats.track--seam .track__beat--resolve,
.track--film.track--beats.track--seam .track__beat--resolve[data-film] { --dwell: 1.5; }

/* ---- beat 00 · the wall ---------------------------------------------------------------
   The page name is the one thing on the page at wall size, and it fades where it stands: it
   never travels, scales, or scrolls under the chrome. It carries --panel as well as --wall,
   which is what keeps it out of site.js's --beat-max measurement — that box belongs to the
   five answers and their held question, and a 200px headline in it would hang the eyebrow
   half a screen above the frame. */
.track__beat--wall .display--wall { max-width: none; }

/* One measure, not two. `One minute each.` took .track__line's 34ch (355px at 1440) and the three
   lines under it took .track__beat .lines' 45ch (470px) — same size, same leading, same left edge,
   two right edges 115px apart, stacked, which reads as two components that happen to be adjacent
   rather than as the one paragraph copy.md wrote. The 45ch measure also wrapped the paragraph to five
   lines with `history.` alone on the last one. One value for both, wide enough to break the three
   sentences without an orphan: checked at both widths, not reasoned about. */
.track__beat--wall .track__line,
.track__beat--wall .lines { max-width: var(--measure-wall); }
:root { --measure-wall: 50ch; }

/* ---- beat 06 · the resolve ------------------------------------------------------------
   Seven lines and one bed, and the page's one new move. The bed cross-dissolves to the ivory
   ground on the ordinary --t-dissolve; the seven set down where they stand, in copy.md order,
   with a 16px rise on the way in.

   Phase 59: they arrive on SCROLL, not on a clock. Phase 58 fired a transition-delay ladder off
   .is-on, and measured, 612 of the beat's 941px were a frame that did not change: the ladder
   completed in 2.4s of its own time wherever the reader was, so a reader at trackpad speed
   crossed the whole beat with the list still writing itself and a reader at reading speed sat on
   a still frame for four seconds. The page's own principle is that nothing below the chrome runs
   on a clock — every other frame on it is scrubbed — and the resolve was the only thing on the
   page not built the page's way.

   --r is the beat's local progress, published by site.js from the same per-beat number the
   assembly reads. Line n rises out of nothing across a quarter of the beat, starting a
   fifteenth of a beat after the one above it, so the seventh completes at 0.70 and the last
   thirty percent of the dwell is the hold: the list is complete and still while the seam
   arrives. The reader's own hand writes it, which is the sentence the page is making.

   There is no transition on the two properties --r drives: a scrub with a transition on it is a
   scrub the reader's hand cannot feel. BRIEF §5's 400ms floor is about animation; this is
   position, like every bed on the page. Going back up the seven lift off in reverse, which is
   the honest consequence of the hand being the transport and is what the scrubbed beds already
   do. */
.track__beat--resolve { --r: 1; gap: var(--s-6); }
.track__beat--resolve .lines p {
  --arrive: clamp(0, calc((var(--r) - var(--s)) * 4), 1);
  opacity: var(--arrive);
  transform: translateY(calc(var(--reveal-rise) * (1 - var(--arrive)))); }
.track__beat--resolve .resolve__list p:nth-child(1) { --s: 0; }
.track__beat--resolve .resolve__list p:nth-child(2) { --s: .0667; }
.track__beat--resolve .resolve__list p:nth-child(3) { --s: .1333; }
.track__beat--resolve .resolve__list p:nth-child(4) { --s: .2; }
.track__beat--resolve .resolve__list p:nth-child(5) { --s: .2667; }
.track__beat--resolve .resolve__close p:nth-child(1) { --s: .3333; }
.track__beat--resolve .resolve__close p:nth-child(2) { --s: .45; }

/* The payoff at the size the payoff deserves. Every ground and panel beat on Home leads with a
   display line — 96px for a named block, 48px for a panel — with 17px lines under it; this beat
   was the only one on the site whose entire content was 17px body, and copy.md v9's own stage
   direction for it reads "This is the payoff of the run, not a footnote." The five answers stay
   at body size: they are a list and they should look like one, and they are the run-up. The two
   closing lines take .display--two, which is Home's panel grammar exactly — the composition of
   WHO IT IS FOR. and GET IN TOUCH. — arrived at by promoting copy that is already there, in the
   order it is already in. No new copy, no new token, and no second square: beat 00 has the
   page's one. The measure is wider than .display--two's 18ch because these are sentences and not
   names, and 18ch set the first of them as five lines. */
.resolve__close { margin-top: var(--s-4); }
.resolve__close p.display { max-width: 26ch; }
.resolve__close p + p { margin-top: var(--s-4); }
/* On a phone the display drops to 28px and the beat is the tallest thing on the track, so the close
   takes the whole column rather than a measure: 26ch of 28px type inside a 358px column set the first
   sentence as five lines and pushed the beat 3px past the fold on an iPhone SE, which is the tightest
   frame in the matrix. Full width, and it fits at 375, 390 and 430 with room. */
@media (max-width: 720px) {
  .resolve__close p.display { max-width: 100%; text-wrap: pretty; }
  .track__beat--resolve { gap: var(--s-4); }
  .resolve__close { margin-top: 0; }
  .resolve__close p + p { margin-top: var(--s-2); }
}

/* The five must not read twice. In the flat, no-JavaScript and reduced-motion states every
   beat renders, so the answers appear on beats 01 to 05 AND in the resolve. The resolve's
   list is hidden in all three; the two closing lines are the payoff and stay. This is the job
   .answers__static { display: none } used to do, moved to the beat that replaced it.
   And the seven have to be VISIBLE in those states: their opacity is 0 by default above, and
   nothing there ever adds .is-on. */
.track--flat .track__beat--resolve .resolve__list,
html:not(.js) .track__beat--resolve .resolve__list { display: none; }
.track--flat .track__beat--resolve .lines p,
html:not(.js) .track__beat--resolve .lines p { opacity: 1; transform: none; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .track__beat--resolve .resolve__list { display: none; }
  .track__beat--resolve .lines p { opacity: 1; transform: none; transition: none; }
}

/* ---- the phone · the wall and the resolve stay left on the margin ---------------------
   The re-scoped Phase 54 block centres a panel beat on a phone, which is Erin's standing
   decision and right for a headline and its lines. Two beats here depart from it, and both
   are named in FOUNDERS-PLAN 1.4 and 2.7 rather than drifting:
     · the wall, because a page name is a wall and a wall is left on the margin — the same
       composition Phase 56 gives every film beat;
     · the resolve, because a five-line list centred in a 390 frame reads as a poem.
   They keep the vertical centring in the safe area; only the horizontal alignment changes. */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat--wall:not(.track__beat--hero):not(.track__beat--centre),
  .track--film.track--beats .track__beat--resolve:not(.track__beat--hero):not(.track__beat--centre) {
    justify-items: start; text-align: left; gap: var(--s-6); }
  .track--film.track--beats .track__beat--wall .display,
  .track--film.track--beats .track__beat--wall .lines,
  .track--film.track--beats .track__beat--wall .track__line,
  .track--film.track--beats .track__beat--resolve .lines {
    max-width: 100%; margin-left: 0; margin-right: 0; }
}

/* ---- the empty gallery ----------------------------------------------------------------
   1,200 x 400px of nothing with 671px of dead frame beside it was the audit's measurement of
   this block. The fix is layout, not imagery: at 721 and up the words take one column and the
   three 4:5 frames take the other, so the right-hand half is closed without inventing a pixel
   of picture. Below that, one column: headline, lines, then the frames stacked.

   The state is a CLASS the generator writes (tools/build-founders.js), not a :has() here —
   the orchestrator's ruling. The day the first real tiles land the class is simply not
   written, .gallery__state is a plain block, and the grid inside it is the three-across
   gallery BRIEF §3.6 asks for, with no further edit anywhere.

   The three frames keep their aspect ratio, their hairlines and their Cut 001 / 002 / 003
   labels, and take no image, no tint, no animation and no hover: they are not links and an
   empty frame that lights up under the pointer is a promise the page cannot keep. */
.gallery__state { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-7); }
.gallery__say .lines { max-width: var(--measure); }

/* ---- the three empty frames (Phase 59) ------------------------------------------------------
   Phase 58 put the words in one column and the frames in the other at 721 and up, which closed the
   dead right-hand half and cost the frames their size: each cell measured 188 x 235px, and because
   the grid draws its rules as 1px GAPS over a --rule background the visible artefact was one
   568 x 255 box divided by two hairlines — a 2.2:1 rectangle where BRIEF §3.6 asks for three 4:5
   frames. Measured, not inferred.

   So the hairline moves from the wrapper to the cell, and the frames take the whole content column
   at the width the real gallery will use — three across, ≈ 420px each at 1440 — which is the
   geometry the first three real posters occupy. The day inGallery flips, the generator stops writing
   the state class, the grid reverts to its own seam rules and nothing on the page moves.
   The words go above them: one paragraph at body size, at the body measure, which is copy.md's
   shape for this block. */
.gallery__state--empty { gap: var(--s-6); }
.gallery__state--empty .gallery__grid { background: none; gap: var(--s-4); }
.gallery__state--empty .gallery__frame { border: var(--line) solid var(--rule); }

/* ---- and three across on a phone, not three stacked ------------------------------------------
   A STANDING DEVIATION from BRIEF §3.6's "one across on a phone", on the orchestrator's ruling.
   §3.6's one-across governs the gallery — posters you need to see. There is nothing to see: three
   stacked 4:5 frames at 358 x 447 are 1,341px of empty ivory, 16 percent of the phone document,
   and one of them is an entire 844px screen containing an empty rectangle and the words CUT 002.
   One row of three keeps the brief's letter — three numbered empty frames, 4:5, on a phone — at
   about 128px instead of 1,341. It applies only while the state is empty; a real poster is
   one-across on a phone exactly as §3.6 says. */
@media (max-width: 720px) {
  .gallery__state--empty .gallery__grid { grid-template-columns: repeat(3, 1fr); gap: var(--s-3); }
  .gallery__state--empty .gallery__frame { padding: var(--s-2); font-size: 10px; letter-spacing: .06em; }
}


/* ======================================================================================
   Studios · Phase 60 · one seam track, four graphics layers on one stage, and the box

   copy.md v9 STUDIOS in order, on Home's and Founders' grammar. Three things are new and
   nothing else is: the graphics layer is bound to a RUN OF BEATS instead of to a whole
   track, the honesty plate comes out of the aria-hidden layer so the disclosure reaches
   the accessibility tree, and .adds runs backwards.
   ====================================================================================== */

/* ---- the graphics layer, per run of beats ---------------------------------------------
   Four layers share one stage here, so a layer fades in and out on the run it covers, on
   the same .is-on mechanism a .track__hold has. Until now a layer was always on, because
   the track it lived in contained only itself. .is-tight and .is-cutting move from the
   stage to the layer for the same reason: the cut is a fact about one film. */
.track__gfx { opacity: 0; transition: opacity var(--t-dissolve) var(--ease-out); }
.track__gfx.is-on { opacity: 1; }
/* released states: the pin is gone and every bed is display:none, so an absolutely
   positioned layer would land on the poster and on the stacked beats. */
.track--flat .track__gfx, html:not(.js) .track__gfx { display: none; }

/* ---- the honesty plate, out of the aria-hidden layer -----------------------------------
   BRIEF §1, and the site-wide rule recorded in this phase: a generated PERSON is disclosed
   in the accessibility tree as well as on the frame; a generated empty room is scenery.
   The label used to sit inside .track__gfx, which is aria-hidden="true", so it was drawn
   for a sighted reader and hidden from everyone else. It is a sibling of the layer now,
   keeping .gfx__made's position and styling, with its own run of beats and no aria-hidden.
   In the released states every .track__film is display:none and the only picture left is
   the stage poster, which is an empty room — so the plate goes with the beds it labels,
   rather than labelling a frame that has no person in it. */
/* Phase 63: opacity alone hides an element from SIGHT and leaves it in the accessibility tree, so
   all four disclosures were announced at every scroll position — four "Generated demonstration" in
   a row, in DOM order, three of them naming a frame that is not on screen, including over beat 03
   whose bed is an empty room that carries no plate at all. The visibility pair is .track__beat /
   .track__beat.is-on verbatim (site.css:157, :164), so the plate fades on the same dissolve it
   already did and enters and leaves the tree with the run it labels. */
.plate-label { z-index: 2; opacity: 0; visibility: hidden; color: var(--film-ink);
  transition: opacity var(--t-dissolve) var(--ease-out), visibility 0s var(--t-dissolve);
  pointer-events: none; }
.plate-label.is-on { opacity: .7; visibility: visible;
  transition-delay: calc(var(--t-dissolve) * .5); }
.track__stage[data-tone="light"] .plate-label { color: var(--ink); }
.track--flat .plate-label, html:not(.js) .plate-label { display: none; }
@media (prefers-reduced-motion: reduce) { .plate-label { display: none; } }

/* ---- the box opens · .adds with --a read backwards -------------------------------------
   Home assembles; Studios unpacks. The same component, the same two composited properties,
   the same easing, the same eight inline custom properties beside each frame in the markup
   — and one modifier that inverts the progress. Zero JavaScript: site.js already writes an
   eased 0..1 across the beat's own scroll onto .adds when the assembly lives on a beat.

   At --a 0 every part is at --ai 1, which is its transform onto the centre of the stage at
   full height: assembled, one wide frame filling the stage. At --a 1 every part is at --ai
   0, which is its own box at its own true ratio in its own place: apart. --d runs DOWN the
   list because --ai reaches 0 at --a equal to 1 - --d, so a larger delay settles earlier:
   the wide frame is the first to move and the Founder Page the last to settle, and the four
   labels take ink in copy.md order, top to bottom.

   --ai is declared on the FRAME as well as on the move, because custom properties inherit
   downward and not sideways and the label is the move's sibling, not its child. That is the
   same bug that had Home's payoff line at opacity 1 from first paint. */
.adds--unbox .adds__frame,
.adds--unbox .adds__move { --ai: max(0, min(1, ((1 - var(--a)) - var(--d, 0)) / 0.82)); }
/* Home's labels are two words and nowrap; these are sentences, so each one wraps inside the
   width of the GROUP it names, written beside it as --lw in percent of its own frame. Zero
   until the part is four tenths of the way to its place, full when it settles. No transition
   property anywhere: this is a scrub, and a transition on a scrubbed value lags it by 800ms. */
.adds--unbox .adds__label { white-space: normal; max-width: calc(var(--lw, 100) * 1%);
  opacity: calc((0.4 - var(--ai)) / 0.4); }
/* the fifth line of the box is not a part: it is the line under the picture and it arrives
   last, at --a past 0.8, which is what .adds__final already does. It is a sentence and not a
   payoff headline, so it takes body type rather than the display face. */
.adds--unbox .adds__final { font-family: var(--font-body); font-weight: var(--weight-body);
  text-transform: none; font-size: var(--text-body); line-height: var(--lh-body);
  letter-spacing: normal; max-width: var(--measure); margin-top: var(--s-6); }

/* Every static state — a phone, no JavaScript, reduced motion, no GSAP — shows four frames,
   one per label, in the two-up grid .adds--static already produces. The four second frames
   of the three picture groups carry .unbox__extra and go; every word of copy.md is still on
   the page, which is what copy-test and the reduced-motion screenshots check. */
.adds--static .unbox__extra, html:not(.js) .unbox__extra { display: none; }
@media (max-width: 720px) { .unbox__extra { display: none; } }
@media (prefers-reduced-motion: reduce) { .unbox__extra { display: none; } }
/* and the labels are always on in those states: --a never moves there, so the scrub's own
   opacity calc would hold every one of them at zero */
.adds--unbox.adds--static .adds__label,
html:not(.js) .adds--unbox .adds__label { opacity: 1; max-width: none; }
@media (max-width: 720px) { .adds--unbox .adds__label { opacity: 1; max-width: none; } }
@media (prefers-reduced-motion: reduce) { .adds--unbox .adds__label { opacity: 1; max-width: none; } }
/* the closing line reads UNDER the picture here, not above it: .adds--static puts Home's
   payoff line first because on Home it is the conclusion of an assembly. */
.adds--unbox.adds--static .adds__final,
html:not(.js) .adds--unbox .adds__final { order: 3; margin-top: var(--s-5); }

/* ---- the wall ---------------------------------------------------------------------------
   The page keeps its own eyebrow numbering, which is the one thing on this page that Home
   and Founders do not have. The numeral follows the stage's tone with everything else on the
   beat rather than staying ink on a bed that may not be light. */
.track__beat--wall .eyebrow { --eyebrow-number: currentColor; }

/* ---- the six things we make -------------------------------------------------------------
   They are film beats: wall size, left on the margin at both widths, and inside --beat-max,
   which is what puts every film beat's headline on one line and what makes the two holds on
   this page stand still. But each of them carries a name, two lines and a route, so Phase 54
   would centre them on a phone as a panel — these re-point Phase 56's wall composition at the
   piece class at the specificity Phase 54 uses, exactly as Phase 58 did for the wall and the
   resolve on Founders. */
.track--film.track--beats .track__beat--piece { gap: var(--s-6); }
.track--film.track--beats .track__beat--piece .display--two {
  font-size: var(--text-display-1); max-width: 12ch; }
@media (max-width: 720px) {
  .track--film.track--beats .track__beat--piece:not(.track__beat--hero):not(.track__beat--centre) {
    justify-items: start; text-align: left; gap: var(--s-5); }
  .track--film.track--beats .track__beat--piece .display {
    font-size: var(--text-wall); line-height: var(--lh-wall); letter-spacing: var(--ls-wall);
    max-width: 8ch; text-wrap: balance; margin-left: 0; margin-right: 0; }
  .track--film.track--beats .track__beat--piece .lines {
    max-width: 100%; margin-left: 0; margin-right: 0; }
}

/* ---- the dwells ------------------------------------------------------------------------
   In CSS beside the height formula, keyed off the beat classes, so the arithmetic cannot
   drift from the geometry — and doubled with [data-film] so each one out-specifies Phase 55's
   (0,4,0) blanket AND Phase 48's per-bed values, which still name event, interview and brand.
   Twenty-three beats, total 26.55. --beat-h is 58vh on this page and the reason is in
   studios.html beside the track. */
.track--film.track--beats.track--seam .track__beat--studios-wall,
.track--film.track--beats.track--seam .track__beat--studios-wall[data-film] { --dwell: 1.25; }
.track--film.track--beats.track--seam .track__beat--studios-room,
.track--film.track--beats.track--seam .track__beat--studios-room[data-film] { --dwell: 1.4; }
.track--film.track--beats.track--seam .track__beat--studios-intro,
.track--film.track--beats.track--seam .track__beat--studios-intro[data-film] { --dwell: 1.1; }
.track--film.track--beats.track--seam .track__beat--piece,
.track--film.track--beats.track--seam .track__beat--piece[data-film] { --dwell: 1.15; }
.track--film.track--beats.track--seam .track__beat--look,
.track--film.track--beats.track--seam .track__beat--look[data-film] { --dwell: 1.45; }
.track--film.track--beats.track--seam .track__beat--day-a,
.track--film.track--beats.track--seam .track__beat--day-a[data-film] { --dwell: 1.4; }
.track--film.track--beats.track--seam .track__beat--day-b,
.track--film.track--beats.track--seam .track__beat--day-b[data-film] { --dwell: 1.2; }
.track--film.track--beats.track--seam .track__beat--cut,
.track--film.track--beats.track--seam .track__beat--cut[data-film] { --dwell: 1.25; }
.track--film.track--beats.track--seam .track__beat--name,
.track--film.track--beats.track--seam .track__beat--name[data-film] { --dwell: 0.8; }
.track--film.track--beats.track--seam .track__beat--claim,
.track--film.track--beats.track--seam .track__beat--claim[data-film] { --dwell: 0.8; }
.track--film.track--beats.track--seam .track__beat--box,
.track--film.track--beats.track--seam .track__beat--box[data-film] { --dwell: 1.7; }
.track--film.track--beats.track--seam .track__beat--first-cut,
.track--film.track--beats.track--seam .track__beat--first-cut[data-film] { --dwell: 1.2; }

/* ---- the box, sized from the height it is allowed ---------------------------------------
   .track__beat--figure reserves --s-9 above the 16:9 stage for Home's one payoff line. This
   beat carries a headline ABOVE the picture as well as a line under it, and measured at
   1440x900 that put the block 31px above the chrome. The reserve is the headline and its gap
   plus the closing line and its gap, taken from the tokens rather than guessed: measured
   again after the change the block is 659px inside 704px of frame. */
.track__beat--box .adds__stage {
  width: min(100%, calc((100vh - var(--chrome-h, var(--nav-h)) - var(--deadline-h)
    - var(--copy-foot) - var(--s-10) - var(--s-7)) * 16 / 9)); }

/* ======================================================================================
   CREATIVE · Phase 61 · two short seam tracks, and the working day drawn
   ====================================================================================== */

/* ---- the dwells --------------------------------------------------------------------------
   Keyed off the beat classes beside the height formula, doubled with [data-film] so each one
   out-specifies Phase 55's (0,4,0) blanket, the seam block's (0,4,0)+[data-film] and — for the
   wall — Phase 58's 1.12. Three beats across two stages: 2.0 pinned, then 1.5 pinned.

   Beat 00 takes 1, not the stylesheet's 1.12. That 0.12 pays for the ivory panel dissolving off
   across the first quarter of the beat, and this page has no .track__open: it arrives from
   Home's Creative beat, which is bed-set, and opens on bed-set. No panel, no 0.12. Recorded
   here rather than set inline so the two pages do not look inconsistent to a reviewer reading
   the stylesheet, and so the arithmetic cannot drift from the geometry. */
.track--film.track--beats.track--seam .track__beat--creative-wall,
.track--film.track--beats.track--seam .track__beat--creative-wall[data-film] { --dwell: 1; }
.track--film.track--beats.track--seam .track__beat--creative-who,
.track--film.track--beats.track--seam .track__beat--creative-who[data-film] { --dwell: 1; }
.track--film.track--beats.track--seam .track__beat--day,
.track--film.track--beats.track--seam .track__beat--day[data-film] { --dwell: 1.5; }

/* ---- the working day -----------------------------------------------------------------------
   One horizontal axis and it is ONE NEW YORK DAY: 00:00 at the left edge, 24:00 at the right.
   Two rails on that one axis, one per city. Each rail is a hairline in --rule running the full
   measure, with its working hours drawn on top at --underline-toggle, so a working hour is twice
   the weight of an hour that is not.

   --r is the beat's own local progress, published by site.js on the beat (see the note at
   resolveEl). --d IS that progress, read as a position on the axis, and each segment fills from
   its own left edge at the moment the day reaches it: the reader's hand is the sun. The last
   segment ends at 0.75 (18:00), so the drawing COMPLETES at three quarters of the dwell and the
   last quarter is the hold — finished and still while the second seam arrives, which is the shape
   Phase 59 tuned on the Founders resolve and for the same reason: a reader at trackpad speed must
   not cross the beat with the picture half drawn. (CREATIVE-PLAN 1.4 wrote --d as --r / 0.75,
   which maps the beat onto a full 24 hours and so finishes the drawing at 0.5625 of the dwell,
   with 44 percent of the beat dead. The axis is a day, not the beat; the beat is the working
   part of it.)

   There is no transition on the two properties --r drives. A scrub with a transition on it is a
   scrub the reader's hand cannot feel; BRIEF §5's 400ms floor is about animation, and this is
   position, like every bed on the site. Going back up the segments retract to their own left
   edges in reverse order and the orange eight is the last thing on the screen, which is the
   honest consequence of the hand being the transport and is what every scrubbed bed already does.

   --r defaults to 1 on the beat, so with no JavaScript, no GSAP or under prefers-reduced-motion
   the FINISHED day is what renders, every segment full and nothing having moved. That costs no
   suppression rule of its own, which is the point of defaulting to 1. */
.track__beat--day { --r: 1; }
.day { --d: clamp(0, var(--r), 1);
  margin-top: var(--s-7); width: 100%; max-width: var(--measure);
  display: grid; gap: var(--s-5); }
.day__rail { display: grid; grid-template-columns: 8ch 1fr; align-items: center; gap: var(--s-5); }
.day__city { color: var(--muted); }
/* the rail: the hairline is the element's own background, the segments sit on it */
.day__line { position: relative; height: var(--underline-toggle); }
.day__line::before { content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: var(--line); margin-top: calc(var(--line) / -2); background: var(--rule); }
.day__seg { position: absolute; top: 0; bottom: 0;
  left: calc(var(--from) * 100%); width: calc((var(--to) - var(--from)) * 100%);
  background: var(--ink); transform-origin: left;
  --fill: clamp(0, calc((var(--d) - var(--from)) / (var(--to) - var(--from))), 1);
  transform: scaleX(var(--fill)); }
/* the eight hours New York is asleep and Istanbul is working: the page's one accent, and the
   only thing on this screen in the burnt orange. */
.day__seg--eight { background: var(--section); }

/* On a phone the two rails stack with the city label ABOVE each instead of beside it, and the
   band takes the full column rather than --measure. At 375 the eight-hour segment is about 111px
   of a 345px column, which reads. */
@media (max-width: 720px) {
  .day { margin-top: var(--s-6); max-width: 100%; gap: var(--s-6); }
  .day__rail { grid-template-columns: 1fr; gap: var(--s-3); }
}


/* ======================================================================================
   START HERE · Phase 62 · the read writes itself down, and the form does not move

   One seam stage of three beats and one released form. Nothing here is a new token and
   nothing is re-scoped: four amendments to the form block above, one ladder, and five
   small classes. The four amendments are SHARED WITH THE FIRST CUT, which is why they are
   written as behaviour that page already has rather than as anything new.
   ====================================================================================== */

/* ---- the dwells ---------------------------------------------------------------------------
   Keyed off the beat classes beside the geometry, doubled with [data-film] so each one
   out-specifies Phase 55's (0,4,0) blanket and the seam block's blanket 1.

   Beat 00 is NOT here. .track__beat--wall already takes 1.12 from Phase 58, and START-PLAN 1.3
   says in as many words: do not write a dwell rule for it, the absence is the argument. Creative
   and Studios each had to override it DOWN because neither opens on an ivory panel; this page
   opens on ivory and stays on it, so the 0.12 is the arrival and no override is written.

   01 is 1.5 — a beat and a half to watch four lines write themselves, the shape Phase 59 tuned on
   the Founders resolve. 02 is 1.0: it is two lines and it is the last thing said on the stage. */
.track--film.track--beats.track--seam .track__beat--read,
.track--film.track--beats.track--seam .track__beat--read[data-film] { --dwell: 1.5; }
.track--film.track--beats.track--seam .track__beat--free,
.track--film.track--beats.track--seam .track__beat--free[data-film] { --dwell: 1; }

/* ---- the read arrives ----------------------------------------------------------------------
   --r is the beat's own local progress, published by site.js on any [data-beat-r] (site.js:497,
   Phase 61). Each line rises out of nothing across a fifth of the beat, starting an eighth of a
   beat after the one above it, so a reader sees a line finish before the next is more than half
   in: one line at a time, which is the blueprint's word. The fourth completes at 0.575 and the
   rest of the dwell is the hold — the read complete and still while the seam arrives.

   --r defaults to 1 ON THE BEAT, so with no JavaScript, no GSAP or under prefers-reduced-motion
   the FINISHED read is what renders, every line at full opacity and zero translate, and nothing
   has moved. That costs no suppression rule of its own, which is the point of defaulting to 1.

   There is no transition on either property --r drives. A scrub with a transition on it is a
   scrub the reader's hand cannot feel; BRIEF §5's 400ms floor is about animation, and this is
   position, like every bed on the site. Going back up the four lift off in reverse, last written
   first gone, which is the honest consequence of the hand being the transport. */
.track__beat--read { --r: 1; }
.track__beat--read .read li {
  --arrive: clamp(0, calc((var(--r) - var(--s)) * 5), 1);
  opacity: var(--arrive);
  transform: translateY(calc(var(--reveal-rise) * (1 - var(--arrive)))); }
.track__beat--read .read li:nth-child(1) { --s: 0;    }
.track__beat--read .read li:nth-child(2) { --s: .125; }
.track__beat--read .read li:nth-child(3) { --s: .25;  }
.track__beat--read .read li:nth-child(4) { --s: .375; }
/* the body measure, not .rules' 720px column: the blueprint asks for the read at the size and in
   the shape it will arrive in, which is a letter, not a table. */
.track__beat--read .read { max-width: var(--measure); }
.track__beat--read .read__lede { max-width: var(--measure); }

/* ---- the read on a phone · the lead-in runs into its sentence -------------------------------
   MEASURED FIRST, then changed. At 375x667 — the tightest frame in the matrix, and the one where
   the deadline bar wraps to 200px of chrome — the safe area is 467px and the beat came in at 583:
   a heading, a lede, and four rows each setting its bold lead-in on its own line above a two-line
   sentence. 116px over, at both ends of a centred box.

   The fix is the composition, not the type size, and it is what copy.md already does: the four
   parts are written "**What we heard.** The part of your story a stranger would remember" — one
   sentence with a bold opening, not a term and a definition. At 1440 the rule list's 200px term
   column is right and it stays. At 345px that column does not exist, the hairline above each row
   is separating a bold line from a line of the same size, and the bold is doing that work already.
   So on a phone the row is one paragraph: lead-in inline, sentence after it, two lines instead of
   three, and at the row padding a four-row list can afford in that frame. Four rows go from 447px
   to 288px and the beat comes in at 437 inside 467.

   Nothing is smaller. The read is the page's product and it stays at body size at every width. */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat--read:not(.track__beat--hero):not(.track__beat--centre) {
    gap: var(--s-4); }
  .track__beat--read .read li { display: block; padding: var(--s-2) 0; }
  .track__beat--read .read .rules__l { margin-right: .3em; }
  .track__beat--read .read__lede,
  .track__beat--read .read { max-width: 100%; }
}

/* ---- the released form ---------------------------------------------------------------------- */

/* the wait line, directly above the first field */
.form__wait { max-width: var(--measure); margin-bottom: var(--s-6); }
.form__after { margin-top: calc(-1 * var(--s-2)); max-width: var(--measure); font-size: var(--text-small); }

/* the help line, between the label and the input, at caption size in muted */
.field__help { font-size: var(--text-small); color: var(--muted); max-width: var(--measure); margin: 0; }

/* ---- 1 · the error slot is RESERVED, so seven errors move nothing --------------------------
   site.css's `.field__error:empty { display: none }` reserved nothing: when errors arrive at once
   the form grows by a line per field and everything under the first bad field jumps down under the
   reader's thumb. The slot is always there and only its opacity changes. About 20px a field, paid
   once, in exchange for a form that does not move. Shared with The First Cut.
   Appended rather than edited, so the stylesheet above is untouched: same specificity, later in
   the file, so this is the rule that applies. */
.field__error:empty { display: block; }
/* The reserved height and the rendered height have to be THE SAME NUMBER or the reservation does
   not reserve: at the inherited --lh-body the sentence set 22.7px inside a 19.6px slot and four
   errors still grew the document by 13px. Measured, then fixed. --lh-label is the design system's
   line height for a caption and .field__error is one. */
.field__error { line-height: var(--lh-label);
  min-height: calc(var(--text-small) * var(--lh-label)); opacity: 0;
  transition: opacity var(--t-ui) var(--ease-out); }
.field__error:not(:empty) { opacity: 1; }

/* ---- 2 · the confirmation replaces the form in place --------------------------------------
   Two children of one wrapper in one grid cell: the outgoing block fades out where it stands and
   the incoming one fades into the same spot after the half dissolve wait, which is the grammar the
   whole site runs on, applied to the one place where a block is genuinely replaced by another.
   [hidden] has to be overridden — and with !important, because the site-wide [hidden] rule carries
   it — so both children keep their cell for the cross dissolve. The element is taken out of the
   tree, the tab order and the pointer by visibility and pointer-events instead, so aria-hidden is
   not needed and must not be added.

   The OUTGOING block keeps `visibility 0s var(--t-dissolve)` so it stays on screen for the whole
   of its fade and only then leaves the tree. The INCOMING one must not: a transition on visibility
   delays it by the same 800ms, and forms.js moves focus to `Got it.` the instant it is shown —
   measured, focus went nowhere and landed on <body>, because the heading was still
   visibility: hidden and an invisible element cannot take focus. So .is-sent replaces the whole
   transition rather than adding a delay to it: visibility flips now, opacity fades in after the
   half dissolve wait, and the heading is focusable at the moment it is asked for. */
.formwrap { display: grid; }
.formwrap > .form, .formwrap > .confirm { grid-area: 1 / 1; align-self: start;
  transition: opacity var(--t-dissolve) var(--ease-out), visibility 0s var(--t-dissolve); }
.formwrap > .form { opacity: 1; }
.formwrap > .confirm { opacity: 0; visibility: hidden; pointer-events: none; }
.formwrap > .form[hidden], .formwrap > .confirm[hidden] { display: grid !important;
  opacity: 0; visibility: hidden; pointer-events: none; }
.formwrap.is-sent > .confirm { opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity var(--t-dissolve) var(--ease-out) calc(var(--t-dissolve) * .5); }

/* ---- 3 · an input does not react to the pointer --------------------------------------------
   A box that changes colour as the pointer crosses it on the way somewhere else is noise on a form
   with seven of them. Focus is the only state that means anything here, and it is the one the
   keyboard reader needs. Nothing to add: there is no hover rule on .field input and there must not
   be one. This comment is the rule.

   ---- 4 · the sending state --------------------------------------------------------------------
   copy.md has no word for it, so the button keeps its label and goes inert, and the form carries
   aria-busy. .btn[disabled] is the design system's own inert look and is already above. */


/* ======================================================================================
   PHASE 63 · the fix pass on Studios and Creative
   Written against critique-studios.md, qa-studios.md and review-creative.md. Every number
   in the comments below was measured on the running page, before and after.
   ====================================================================================== */

/* ---- 1 · an in-page route has to clear the chrome it lands under -------------------------
   There was no scroll-margin anywhere in this stylesheet. The deadline bar is always on
   screen and the nav is on screen whenever the reader has just scrolled up, so the margin is
   both of them plus a step. Without it a jump puts the target's own headline behind the bar:
   measured at 375x667, where the document is tall enough that the browser is not clamped by
   the foot of the page, `Get in touch.` landed at viewport 0 under a 52px bar. The three
   other widths passed only because the scroll was clamped.
   = 100 + 36 + 24 = 160px at 721 and up, 102 + 52 + 24 = 178px below it. */
:target,
#get-in-touch { scroll-margin-top: calc(var(--chrome-h, var(--nav-h)) + var(--deadline-h) + var(--s-5)); }

/* ---- 2 · a beat that is one frame on a desktop and two on a phone ------------------------
   The markup is the DESKTOP frame: one .track__beat--pair carrying two .pair__part blocks.
   Below 720px site.js promotes the parts to beats of their own before it counts anything, so
   the pair element is simply not there. Above it the parts are ordinary blocks inside one
   beat and everything below is their in-frame layout.

   .pair__part carries the beat classes it will need when it becomes a beat, and one of them —
   .track__beat--ground — sets min-height: var(--beat-max). On a phone that is what anchors a
   ground beat to the foot; on a desktop, on a static block, it is 276px of dead air inside a
   frame. :not(.track__beat) is exactly "while this is not a beat", so the reset needs no
   width query of its own and cannot drift from the promotion rule. */
.pair__part { display: grid; align-content: start; gap: var(--s-5); }
.pair__part:not(.track__beat) { min-height: 0; top: auto; bottom: auto; }

/* How a day works: all six rows measure 460px in a 704px safe area at 1440, so above 721px
   they are one list in one frame under one headline. The two parts sit flush — no gap, and
   the second list loses its first rule, or the seam between them draws two hairlines where
   the other five rows have one. */
.track__beat--day-pair { gap: 0; }
.track__beat--day-pair .pair__part + .pair__part { gap: 0; }
.track__beat--day-pair .pair__part + .pair__part .rules li:first-child { border-top: 0; }
.track--film.track--beats.track--seam .track__beat--day-pair,
.track--film.track--beats.track--seam .track__beat--day-pair[data-film] { --dwell: 1.4; }

/* ---- 3 · Studios · the reading rate ------------------------------------------------------
   --beat-h moves out of the inline style so a media query can say two numbers. Below 721px
   the page still carries twenty-three beats and 430x932 measures 15,990px of document against
   the blueprint's 16,000px ceiling, so 56vh is the number and it cannot rise. Above it the day
   merges and the page carries twenty-two, which buys the difference back as reading rate
   rather than as height. The value is measured against the ceiling at 1440x900, not reasoned
   about; the arithmetic is in PROGRESS Phase 63. */
.track--studios { --beat-h: 56vh; }
@media (min-width: 721px) { .track--studios { --beat-h: 61vh; } }

/* ---- 4 · Studios beat 00 · the H1 does not bury the room ---------------------------------
   Nine words at --text-wall measured a 1325x544 box at 1440 — 56% of the first frame, 31.2%
   of it solid glyph — over the one bed whose subject is the page's whole argument. The room
   was behind it. BRIEF §5 asks for forty percent whitespace above the fold on a non-hero page
   and SITE-BLUEPRINT §1 asks this frame for "a working room in daylight and a product name".
   Founders' wall carries one word, which is why the same size works there.
   The measure is not the lever — narrowing it adds lines and makes the slab taller — so the
   band comes down instead, and only on this page's wall. Measured after: in PROGRESS. */
.track__beat--studios-wall .display--wall {
  font-size: clamp(64px, 8.6vw, 132px); }

/* ---- 5 · the box · what is actually in it ------------------------------------------------
   The Founder Page tile, with no populated page to photograph. An ivory frame under its own
   copy.md label claims nothing; the screenshot of our own empty template claimed the platform
   arrives empty. One <img> the day a real page exists. */
.adds__box--blank { background: var(--ground); border-color: var(--rule); }

/* The disclosure BRIEF §1 makes mandatory was the least legible text on the page: ink type on
   an ink-at-62% plate measured ~2.0:1 in the rendered frame, 87px of a 171px thumbnail at 390,
   and clipped mid-word to GEN. It is a light band with ink on it now — 11.4:1 against the
   plate — and it is sized for the frame it sits on rather than for a full-bleed stage. */
.adds__box--generated .still__generated {
  background: rgba(246,245,242,.86); color: var(--ink);
  font-size: 9px; letter-spacing: .08em; padding: 2px 5px;
  top: 4px; left: 4px; max-width: calc(100% - 8px); }

/* ---- 6 · Studios · the phone pays for a still and gets a still ---------------------------
   data-move drives --m on every bed that carries it, at both widths. The drift belongs to the
   phone, where the bed IS the poster; on a desktop the same bed is a scrubbing sweep and does
   not want a second transform on top of it. */
@media (min-width: 721px) {
  .track__film[data-phone-still][data-move] { transform: none; }
}

/* ---- 7 · the phone, at 390 --------------------------------------------------------------- */
@media (max-width: 720px) {
  /* 7a · the three looks stop collapsing into one band.
     Phase 60 cleared all nine .gfx__* rules of the nav by moving them onto
     --chrome-h + --deadline-h, which worked and stacked every layer into the same 30px strip:
     three different grammars all drawing a hairline with a mark on it at y 207 under a readout
     at y 185. The section's whole argument is that the three behave differently.
     Three places, three grammars, all still clear of the chrome and all clear of the beats,
     which end at y 496 at 390 and leave the lower third of the frame free. */

  /* Cinema keeps its instrument in the right margin, because that is what it is: a vertical
     rail measuring one continuous take. It reads as a rail and not as a scrollbar now that it
     is the only thing in that margin. */
  .gfx--cinema .gfx__rail { left: auto; right: var(--margin); width: 12px; height: auto;
    top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-9));
    bottom: var(--s-8); }
  .gfx--cinema .gfx__rail--v { display: block; }
  .gfx--cinema .gfx__rail--h { display: none; }
  .gfx--cinema .gfx__mark { left: auto; right: -2px; top: calc(var(--p) * (100% - 9px)); }
  /* one row BELOW the honesty plate, which is also right-aligned at chrome + --s-6 and which the
     take card sat exactly on top of */
  .gfx--cinema .gfx__take { display: grid; right: var(--margin); left: auto; justify-items: end;
    top: calc(var(--chrome-h, 64px) + var(--deadline-h, 0px) + var(--s-7)); }

  /* Kinetic's five destinations are copy.md's own line performed, and they were not on the
     phone at all. They go where they sit at 1440 — along the foot — with the five segments
     under them, so the count is at the head of the frame and the cut list at its foot. */
  .gfx--kinetic .gfx__where { display: grid; justify-items: start;
    left: var(--margin); right: var(--margin); top: auto; bottom: var(--s-9); opacity: 1; }
  .gfx--kinetic .gfx__time { top: auto; bottom: var(--s-7); }
  .gfx--kinetic .gfx__seg::before { top: auto; bottom: -13px; }

  /* Field's horizon goes to the lower third, which is where a horizon is and which is also
     the answer to it being drawn through the subject's face. */
  .gfx--field .gfx__horizon-wrap { left: var(--margin); right: var(--margin);
    top: auto; bottom: 26vh; }
  .gfx--field .gfx__place { left: var(--margin); right: auto; top: auto; bottom: var(--s-7); }

  /* 7b · a rule list is a table, not a paragraph.
     The panels stay centred — that is the site's phone composition and it is Erin's standing
     decision — but a two-column term-and-description list centred inside one is ragged on both
     edges with the term floating over its own description. The row goes back to left. */
  .track--film.track--beats .track__beat .rules,
  .track--film.track--seam .track__beat--ground .rules { text-align: left; justify-self: stretch; }
  .track--film.track--beats .track__beat .rules li { text-align: left; }
}

/* ---- 8 · Creative · the day's axis is visible ---------------------------------------------
   --rule on --ground measures 1.21:1, below any perceptual floor, so the 24-hour frame of
   reference the whole drawing depends on was not on the screen: at --r 0 the reader met two
   city names beside nothing. The rail is --muted now (4.6:1 on ivory, against a 3:1 floor for
   a non-text mark) with a tick at each end — the rail's two ends ARE 00:00 and 24:00, and
   making the rail visible is the whole of the fix. No number, no counter, nothing ticks. */
.day__line::before { background: var(--muted); }
.day__line::after { content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: var(--underline-toggle); transform: translateY(-50%); pointer-events: none;
  background:
    linear-gradient(var(--muted), var(--muted)) left center / var(--line) 100% no-repeat,
    linear-gradient(var(--muted), var(--muted)) right center / var(--line) 100% no-repeat; }

/* On a phone the city label sits above its own rail, and a centred panel centred it: ISTANBUL
   landed at roughly 0.42 of the axis, directly over the point where the orange stops, and the
   drawing read as an underline under a word. Left over the rail's own left edge, with the two
   rails close enough that the ink segments' shared edge at 0.375 reads as one vertical line —
   and that alignment IS the overlap. */
@media (max-width: 720px) {
  .day { gap: var(--s-4); }
  .day__rail { justify-items: stretch; }
  .day__city { text-align: left; }
}

/* ---- 9 · Creative beat 01 · the claim lands where a reader is looking ---------------------
   `Who turns up.` is foot-anchored like every ground beat, and on a 900px frame that put 626px
   of empty ivory above 22 words with the nav hidden — the first scroll on the page. It takes
   the track's copy anchor less one --s-10 step instead, which is the line the page name above it
   occupied: measured, the h2 lands at 374 against the H1's 390 at 1440x900 and at 320 against 330
   at 1280x800. The markup's own comment for this beat has always said "the two lines fade into
   the same place the page name stood" and now the build does it. Ivory above: 626px → 358px.
   Below 721px the ground beats keep the foot: the phone frame is already 267px clear above and
   262px below, which is the centred composition working. */
@media (min-width: 721px) {
  /* the selector is Phase 55's own — .track--beats .track__beat.track__beat--panel:not(--centre),
     (0,4,0) — with the page's beat class in place of --panel, because that is the rule that puts
     every panel beat on the foot and nothing shorter out-specifies it. */
  .track--beats .track__beat.track__beat--creative-who:not(.track__beat--centre) {
    top: calc(var(--copy-top) - var(--s-10)); bottom: auto; min-height: 0; }
}

/* ---- 10 · Start here · the three boxes that are not required ------------------------------
   copy.md v9 names the required set in the line above the form and carries one word, "Optional",
   for the fields that are not in it. It is a suffix inside the <label>, so it is part of the
   label's own accessible name — a reader on a screen reader meets "How long you have run it,
   Optional" rather than having to hold the count from a sentence three fields back — and it is
   set in the label's face, one step quieter, so the labels still read as one column. */
.field__opt { margin-left: var(--s-3); color: var(--muted); }


/* ======================================================================================
   THE FIRST CUT · Phase 64 · the questions change

   Five beats on one seam stage and one released form, and one move that runs with no
   JavaScript at all. Nothing here is a new token. site.css was append-only for this
   phase; the backup is build/backups/site.css.pre-phase64.
   ====================================================================================== */

/* ---- the one site-wide rule this phase changes ---------------------------------------------
   The square is the design system's ONE accent mark, and on the live path it never appeared.
   :987 gives every square inside a track `currentColor` — the colour of the glyph it stands in
   for — which is right over a FILM-tone bed, where the type is ivory and --section on three of
   four sections is a colour that cannot be seen. It is wrong everywhere else: over a LIGHT-tone
   bed the type is ink, so currentColor draws the accent as ink, and Phase 63 measured the result
   — /studios' #B8D2D8 and /creative's #C35421 both rendered rgb(32,32,32), the accent nowhere on
   either page. Phase 63's fix scoped the section colour to ground stages only, and measured
   itself inert, because neither page has a square on a ground beat.

   So the rule is scoped the other way round, which is the critique's own alternative: currentColor
   on a film-tone stage, --section everywhere else. Home and Founders are unchanged — Home's square
   is on the hero, which is the only film-tone bed on the site that carries one, and Founders'
   --section IS --ink so both readings draw the same pixel. Studios and Creative gain the accent
   they were specified to have. This page's H1 sits on a light-tone bed in a --section: --ink
   section, so it draws ink either way. Proved by regression, per page, in PROGRESS Phase 64.
   Appended, not edited: (0,3,0), later in the file, so this is the rule that applies. */
.track__stage:not([data-tone="film"]) .display .square { background: var(--section); }

/* ---- the dwells ---------------------------------------------------------------------------
   Keyed off the beat classes beside the geometry, doubled with [data-film] so each one
   out-specifies Phase 55's (0,4,0) blanket and the seam block's blanket 1.

   Beat 00 takes 1, not the 1.12 .track__beat--wall carries from Phase 58. That 0.12 pays for an
   ivory .track__open panel dissolving off across the first quarter of the beat, and this page has
   no panel: the prize is a room and the room is in the first frame (the routes into this page do
   not agree with each other — the deadline bar is pressed from any page at any scroll over any
   bed — so there is nothing to be continuous with, and a quarter of a beat of blank ivory in
   front of the prize is the page's worst measured fault rebuilt in a nicer material). Creative
   wrote the same override for the same reason at :2222.

   --fc-read out-specifies the Phase 62 --read rule it sits beside: this read has no --lede line
   above four rows AND two closing lines, it is the last argument before the dates, and 1.3 is
   what it measured at. */
.track--film.track--beats.track--seam .track__beat--fc-prize,
.track--film.track--beats.track--seam .track__beat--fc-prize[data-film] { --dwell: 1; }
.track--film.track--beats.track--seam .track__beat--fc-gets,
.track--film.track--beats.track--seam .track__beat--fc-gets[data-film] { --dwell: 1.3; }
.track--film.track--beats.track--seam .track__beat--fc-how,
.track--film.track--beats.track--seam .track__beat--fc-how[data-film] { --dwell: 1.3; }
.track--film.track--beats.track--seam .track__beat--fc-watch,
.track--film.track--beats.track--seam .track__beat--fc-watch[data-film] { --dwell: 1; }
.track--film.track--beats.track--seam .track__beat--fc-read,
.track--film.track--beats.track--seam .track__beat--fc-read[data-film] { --dwell: 1.3; }
.track--film.track--beats.track--seam .track__beat--fc-close,
.track--film.track--beats.track--seam .track__beat--fc-close[data-film] { --dwell: 1; }

/* Phase 66 · the two pairs, and they exist ONLY above 720px: below it site.js promotes the parts
   back to the four beats above and these two elements are not in the document at all, so the
   phone's geometry is untouched. Each pair's dwell is its taller part plus a little, the same
   arithmetic Studios' --day-pair used: --fc-how-pair carries 418px of content at 1440 against a
   706px safe area, --fc-read-pair 565. Six beats and 6.9 dwell units become four and 5.2, which is
   about 1,090px less pinned scroll at 1440 for the same words. */
.track--film.track--beats.track--seam .track__beat--fc-how-pair,
.track--film.track--beats.track--seam .track__beat--fc-how-pair[data-film] { --dwell: 1.4; }
.track--film.track--beats.track--seam .track__beat--fc-read-pair,
.track--film.track--beats.track--seam .track__beat--fc-read-pair[data-film] { --dwell: 1.5; }
/* the gap between the two halves of a merged frame is the same distance the copy uses between a
   block and the block under it, so the seam reads as one composition and not as two beats stacked. */
.track__beat--fc-how-pair, .track__beat--fc-read-pair { gap: var(--s-6); }

/* ---- the two rule lists on a phone ----------------------------------------------------------
   MEASURED FIRST, then changed, in the tightest frame in the matrix. At 375x667 the deadline bar
   wraps and the chrome is 200px, so the safe area is 467px. `How it works.` came in at 600 and the
   read at 584 — and neither answer is a smaller type token.

   Both lists take the composition copy.md already writes them in: "Apply, or nominate. For
   yourself, or for a founder who would never put their own name forward." is ONE sentence with a
   bold opening, not a term and a definition. At 1440 the list's 200px term column is right and it
   stays. At 345px that column does not exist, the row's hairline is separating a bold line from a
   line of the same size, and the bold is doing that work already. Phase 62 made exactly this change
   to the read on Start here; this is the same change applied to the page the component came from.

   The read then takes one further step down, to --s-1 row padding, Start here's own --s-4 between the blocks,
   because it is four rows under a three-line heading and a three-line lede where Start here's is
   four rows under a one-line heading and a two-line lede. Nothing is smaller: the read is the
   page's product and it stays at body size at every width. */
@media (max-width: 720px) {
  .track__beat--fc-how .rules li { display: block; padding: var(--s-2) 0; }
  .track__beat--fc-how .rules .rules__l { margin-right: .3em; }
  .track__beat--fc-how .rules { max-width: 100%; }
  .track__beat--fc-read .read li { padding: var(--s-1) 0; }
  /* and the heading takes the frame instead of the track's 15ch measure. Thirty-two characters
     set to 15ch is three lines in a 345px frame and two lines at the frame's own width — a line
     of pure leading bought for nothing, on the beat with the least room on the page. Scoped to
     this beat: every other heading on the track keeps the measure. */
  .track--film.track--beats .track__beat--fc-read .display { max-width: 24ch; }
  .track--film.track--beats .track__beat--fc-read:not(.track__beat--hero):not(.track__beat--centre) {
    gap: var(--s-4); }
}

/* ---- beat 00 · the H1 does not bury the room ------------------------------------------------
   MEASURED, and it is the same number Phase 63 measured on Studios' wall: nine words at --text-wall
   put a 1325 x 544 slab over 55.6 percent of the first frame at 1440x900 — over the one bed whose
   subject is the prize itself, a set standing ready with nobody in it. The chair, the camera and
   the window were behind the headline. BRIEF §5 asks a non-hero page for forty percent whitespace
   above the fold and SITE-BLUEPRINT asks this frame for the room.

   The measure is not the lever: narrowing it adds lines and makes the slab taller. The band comes
   down instead, to the same clamp Studios' wall took, and only on this page's wall. Founders'
   wall carries ONE word, which is why the full --text-wall works there. Measured after: PROGRESS. */
.track__beat--fc-prize .display--wall { font-size: clamp(64px, 8.6vw, 132px); }

/* ---- the choice · a fieldset of two radios -------------------------------------------------
   Two equal halves, --field-h tall, across the content column, with the underline at the token.
   A <fieldset> so the browser gives us a radiogroup, an accessible name from the <legend>, ONE
   tab stop, arrow keys and a single checked value for nothing — role="radiogroup", role="radio",
   aria-checked and aria-controls are all forbidden here, because every one of them replaces
   something the browser already does correctly with something a builder has to maintain. The
   fieldset's own default border and padding are reset: BRIEF §5 allows no box here.

   The .toggle block at :478 styles `.toggle button`, which this page no longer has, and nothing
   there needs editing — these rules sit beside it and apply to the labels instead. */
.toggle--radio { display: block; justify-self: stretch; gap: 0;
  border: 0; padding: 0; margin: 0; min-width: 0; max-width: var(--content); }
/* the two halves are a grid INSIDE the fieldset, not the fieldset itself: a <legend> is laid out
   by the browser rather than by its parent's formatting context, and making the fieldset the grid
   puts the question in a column beside the first half in some engines and not in others. One div
   costs nothing and the layout is then the same everywhere. */
.toggle--radio .toggle__row { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.toggle--radio .toggle__r { position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; }
.toggle--radio .toggle__l { display: flex; align-items: center; justify-content: center;
  min-height: var(--field-h); position: relative; cursor: pointer;
  color: var(--muted); transition: color var(--t-ui) var(--ease-out); }
.toggle--radio .toggle__r:checked + .toggle__l { color: var(--ink); }
/* the underline. Two elements, one growing from the side the other shrank toward, which is the
   slide BRIEF §3.10 asks for with nothing travelling across a gap. */
.toggle--radio .toggle__l::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--underline-toggle); background: var(--section);
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--t-ui) var(--ease-out); }
.toggle--radio .toggle__r:checked + .toggle__l::after { transform: scaleX(1); }
.toggle--radio .toggle__r:checked ~ .toggle__l::after { transform-origin: left; }
/* the ring goes on the LABEL: an outline on a 1px collapsed input is an outline nobody can see. */
.toggle--radio .toggle__r:focus-visible + .toggle__l { outline: var(--focus) solid var(--section);
  outline-offset: var(--focus-offset); }
/* the question above the two halves. A visible <legend> is the radiogroup's accessible name and
   copy.md's own line, so there is no aria-label and no second heading. A legend does not take part
   in a grid, so it is taken out of the flow of one and set as an ordinary line above it. */
.toggle--radio .toggle__q { display: block; float: none; width: auto; padding: 0;
  margin: 0 0 var(--s-4); color: var(--ink); max-width: var(--measure); }

/* the wait line above the CHOICE keeps the measure and loses the margin: see the-first-cut.html */
.form__wait--lede { margin-bottom: 0; }

/* ---- the questions change ------------------------------------------------------------------
   The page's one move, and the only form on the site that could carry it: it is the only offer we
   have that can be given to somebody who did not ask for it, so it is the only form with two
   readers. Four labels change, one field appears, four hold — and the four that hold are the
   sentence: the thing that has never been put on the internet, and the email, are asked of both.

   The pair sits in ONE grid cell, so the label box is the height of the taller string at every
   width and the swap moves nothing. The state is read off the radio with :has(), not off a class
   written by script, so the move runs with no JavaScript, has no flash of the wrong mode at first
   paint, and costs forms.js a deletion rather than the blueprint's ten lines.

   The out-of-mode half is visibility: hidden and not merely transparent, so it leaves the
   accessibility tree and the label's accessible name is ONE sentence, never two concatenated —
   which is the same class of defect the audit found on the old error spans, and it would have been
   reintroduced here by the fix for something else.

   The ladder step is a FRACTION of --t-ui, not a number of milliseconds, so under
   prefers-reduced-motion (--t-ui: 0ms) the durations and the stagger collapse together and the
   labels change in one frame, which is what SITE-BLUEPRINT §3 asks for with no rule of its own.
   --t-ui, not --t-dissolve: the transport is a press, not a scroll, and the underline under the
   same press moves at 400ms. At 800ms with an 800ms ladder the last label would finish two seconds
   after the reader's thumb left the control, which reads as broken rather than as considered.

   Browsers with no :has() (before Safari 15.4 / Chrome 105 / Firefox 121) get the apply wording on
   all four labels, the nominate field visible, every field present and submittable, and the radio
   still posting mode=nominate so the handler applies the nominate rules whatever the labels said.
   Nobody is trapped. */
.swap { display: inline-grid; }
.swap[data-swap="1"] { --i: 1; }
.swap[data-swap="2"] { --i: 2; }
.swap[data-swap="3"] { --i: 3; }
.swap > span {
  --step: calc(var(--i, 0) * var(--t-ui) * .3);
  grid-area: 1 / 1;
  transition-property: opacity, visibility;
  transition-duration: var(--t-ui), 0s;
  transition-timing-function: var(--ease-out), linear; }
/* Phase 66 · the base hide, on its own, with no :has() anywhere in the selector.
   It was written as the first half of the paired rule below, which means a browser that cannot
   parse :has() drops the WHOLE rule — including .swap__b's default state. Measured with the :has()
   rules removed in flight at 1440 and 390: all eight spans visibility: visible, opacity: 1, in one
   15px grid cell, printing on top of each other, and the accessible name both sentences run
   together — the exact defect visibility: hidden was chosen to prevent. The paired rule below still
   carries the delays, so in a browser that has :has() nothing about the move changes. */
.swap__b { opacity: 0; visibility: hidden; }
/* the half that is going: it fades on its own step and leaves the tree when the fade is done */
.swap__b,
.form:has(#fc-nominate:checked) .swap__a {
  opacity: 0; visibility: hidden;
  transition-delay: var(--step), calc(var(--step) + var(--t-ui)); }
/* the half that is arriving: in the tree at once, fades in after the half dissolve wait */
.swap__a,
.form:has(#fc-nominate:checked) .swap__b {
  opacity: 1; visibility: visible;
  transition-delay: calc(var(--step) + var(--t-ui) * .5), 0s; }

/* ---- the nominate-only field ----------------------------------------------------------------
   Hidden by CSS in apply mode, and by the hidden attribute as well once forms.js is alive. The
   MARKUP ships it with neither, so a reader with no JavaScript and a browser with no :has() sees
   it and can still nominate: the audit's no-JS nominate trap, closed from both ends.

   Its arrival is a CUT, at 0ms, and the two alternatives are both forbidden. Reserving its space
   always is 200px of blank ivory in the middle of the form in the majority mode, paid for by every
   reader for a question most will never answer. Animating the height open is a slide, everything
   under it travels, and Erin has ruled against that three times. It is placed second of eight, one
   field under Your name, so it appears and disappears at the top of the form rather than in the
   middle of it and nothing re-lays out above the reader's thumb. */
.form:has(#fc-apply:checked) .field[data-only="nominate"] { display: none; }

/* ==========================================================================
   Phase 65 · the Founder Page template
   Appended, never edited above this line. Every rule is scoped to a class that exists only on this
   page (.founder__*, .stills, .index, .still__seek) or under [data-page="founder"], so Home,
   Founders and the rest of the site are untouched by a shared stylesheet.
   ========================================================================== */

/* The founder's name is the only display type on the page, and it is display one. No 14ch clamp:
   a name that breaks across two lines mid-name is worse than a long line, and .founder__top-in
   already holds it to --wide. */
.founder__top .display--one { margin-top: var(--s-3); max-width: none; }

/* "The story." takes the label face (SITE-BLUEPRINT §2.7 item 2). Ink, not muted, so it is quiet
   rather than faint. No rule under it: the design system permits exactly three hairlines on the site
   (list rows, the field box, the status token box) and a line under a header is none of them; the
   section gap is what separates it. The other headers keep Anton — measured at 1440 with all four
   demoted and the page lost every section boundary it had. */
.founder__head { color: var(--ink); }
.founder__head + .story { margin-top: var(--s-4); }

/* The vertical cut is the top of a phone page at 9:16, filling the frame width (SITE-BLUEPRINT §2.7).
   The attribute is written empty when the founder has no vertical file, so a founder between the
   shoot and the edit — and the sample — keeps the 16:9 frame and is never letterboxed into a lie. */
@media (max-width: 720px) {
  .founder__cut .cut:not([data-vertical=""]) { aspect-ratio: 9 / 16; }
}

/* The five stills go two-up on a phone, not sideways. This page is forwarded: the reader has four
   seconds and no reason to discover a horizontal gesture, and the beats are the page's proof that a
   minute has a shape. Two-up shows four of five with no gesture at all. The fifth sits alone at half
   width on the last row, left weighted, and reads as "and then one more", which is what it is.
   This overrides the sideways row above by source order. A deviation from SITE-BLUEPRINT §2.7. */
@media (max-width: 720px) {
  .stills { grid-template-columns: 1fr 1fr; overflow-x: visible; padding-bottom: 0; }
}

/* ---- the move · the film indexes itself ----------------------------------
   Studios' gfx grammar, in flow. Studios' own .gfx__* classes live inside .track__gfx, an absolutely
   positioned layer on a pinned stage, so the names are not reused: the arithmetic is, the positioning
   is not. Quieter than Studios' version — no section-coloured playhead square, no accent, 2px. */
.index { display: flex; align-items: center; gap: var(--s-4); margin-top: var(--s-4); color: var(--ink); }
.index__tc { margin: 0; font-variant-numeric: tabular-nums; letter-spacing: .1em; opacity: .55; }
.index__segs { flex: 1; display: grid; grid-template-columns: repeat(var(--n, 5), 1fr); gap: 6px; }
.index__seg { position: relative; height: 2px; background: currentColor; opacity: .18; overflow: hidden;
  transition: opacity var(--t-ui) var(--ease-out); }
.index__seg.is-on { opacity: .55; }
/* The fill is 0 under the scroll driver — nothing has played, so nothing is filled and the lit
   segment only means "you are here". Under playback it is the beat's own progress. */
.index__seg::after { content: ""; position: absolute; inset: 0; background: currentColor;
  transform-origin: left center; transform: scaleX(var(--fill, 0)); }

/* The beat name takes full ink only while the film plays (SITE-BLUEPRINT §3, verbatim). Colour only:
   nothing moves, nothing counts up. */
.stills .still.is-at .still__caption { color: var(--ink); }
.stills .still__caption { transition: color var(--t-dissolve) var(--ease-in-out); }

/* ---- the seek control ----------------------------------------------------
   Written by founder.js and only when there is a film to seek, so with no JavaScript there is no
   control here at all and nothing on the page is pressable that does nothing. The visible in-point
   is the affordance, and it is also true: this is where the beat starts in the film. */
.still__seek { position: absolute; inset: 0; appearance: none; background: none; border: 0; padding: 0;
  cursor: pointer; color: var(--film-ink); display: block; }
.still__seek::before { content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(32, 32, 32, .55), transparent 45%);
  opacity: 0; transition: opacity var(--t-ui) var(--ease-out); }
.still__seek:hover::before, .still__seek:focus-visible::before { opacity: 1; }
.still__seek:focus-visible { outline: var(--focus) solid var(--section, var(--ink)); outline-offset: var(--focus-offset); }
.still__at { position: absolute; left: var(--s-3); bottom: var(--s-3); z-index: 1;
  opacity: .8; transition: opacity var(--t-ui) var(--ease-out); }
.still__seek:hover .still__at, .still__seek:focus-visible .still__at { opacity: 1; }

/* Reduced motion: driver B does not run, so the five names sit at full ink and stay there — the
   state SITE-BLUEPRINT §3's reduced-motion column asks for. Seeking still works: a hard cut to a
   timecode is --t-cut, 0ms. */
@media (prefers-reduced-motion: reduce) {
  [data-page="founder"] .stills .still__caption { color: var(--ink); }
}

/* The short cuts go two-up on a phone for the same reason the beats do, and because the default
   auto-fill just misses: at 390 the column is 359px and `minmax(180px, 1fr)` needs 376px for two
   tracks, so it drops to one and three 9:16 frames become 1,800px of full-bleed black — a third of
   the page, on the page a founder forwards. Stated as two columns, it is 171px each. */
@media (max-width: 720px) {
  .shorts { grid-template-columns: 1fr 1fr; }
}

/* Phase 67 · Erin, 18 Sep: the hero headline ran over "Find your vantage." The headline was pinned from
   the top (--copy-top) and the line from the bottom (--copy-foot) as two absolute blocks, so at 96px three
   lines of H1 reached 763px while the line began at 724px, and a shorter laptop screen made it worse.
   Above the phone breakpoint the two are now ONE column anchored to the foot: the headline sits on the
   line with a fixed gap, so they cannot meet at any width or height. The type also yields to a short
   frame (11vh a line) so three lines, the line and the button always clear the chrome. */
@media (min-width: 721px) {
  [data-page="home"] .track--top .track__beat--hero {
    display: grid; align-content: end; row-gap: var(--s-6); padding-bottom: var(--copy-foot); box-sizing: border-box; }
  [data-page="home"] .track--top .track__beat--hero .hero__top,
  [data-page="home"] .track--top .track__beat--hero .hero__foot { position: static; }
  [data-page="home"] .track--top .track__beat--hero .display--one { font-size: min(var(--text-display-1), 11vh); }
}


/* Phase 69 · 20 Sep · Erin: "A typical project." is one frame, not two. Five rows in one beat: on a
   phone the rows close up and the type comes down one step so the list clears the safe area. */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat--five .display--two { font-size: clamp(22px, 6.4vw, 28px); margin-bottom: var(--s-2); }
  .track--film.track--beats .track__beat--five .rules--five li { padding: 6px 0; font-size: 13px; line-height: 1.35; }
  .track--film.track--beats .track__beat--five .rules--five .rules__l { font-size: 13px; line-height: 1.35; }
}


/* Phase 71 · the scroll cue on Home, second version. A round ink plate, bottom centre of the pinned stage.
   The arrow bobs while there is more below. The ring around it fills with the reader's progress down the
   whole stage (cue.js sets --p, 0 to 1). At the end the ring is full, the arrow stops and becomes a flat
   bar, and the plate fades: the page is telling the reader it is done. */
.scrollcue { --p: 0; position: absolute; left: 50%; bottom: max(20px, env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: 6; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; pointer-events: none;
  background: rgba(22, 21, 19, .62); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); color: #efece6;
  transition: opacity .6s ease .8s; }
.scrollcue > * { grid-area: 1 / 1; }
.scrollcue__ring { transform: rotate(-90deg); }
.scrollcue__track, .scrollcue__fill { fill: none; stroke-width: 1.5; }
.scrollcue__track { stroke: rgba(239,236,230,.22); }
.scrollcue__fill { stroke: #efece6; stroke-dasharray: 100; stroke-dashoffset: calc(100 - var(--p) * 100); }
.scrollcue__arrow { animation: scrollcue-bob 1.5s cubic-bezier(.5,0,.3,1) infinite; }
@keyframes scrollcue-bob { 0%, 100% { transform: translateY(-3px); opacity: .55; } 55% { transform: translateY(4px); opacity: 1; } }
.scrollcue__end { width: 12px; height: 1.6px; background: currentColor; border-radius: 1px; opacity: 0; }
.scrollcue.is-end .scrollcue__arrow { animation: none; opacity: 0; }
.scrollcue.is-end .scrollcue__end { opacity: 1; }
.scrollcue.is-end { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .scrollcue__arrow { animation: none; } }

/* Phase 70 · Solutions opens on Home's Solutions section over bed-desk, which is a bright room: ink copy at
   Home's beat sizes, not the film wall's ivory. */
.track--opener-light .track__flow { color: var(--ink); }
.track--opener-light .track__flow .display--two { color: var(--ink); font-size: clamp(40px, 6vw, 84px); line-height: 1; margin-bottom: var(--s-4); }
.track--opener-light .track__flow .lines { color: var(--ink); font-size: var(--text-body); max-width: 46ch; }
.track--opener-light .track__flow .lines p { color: var(--ink); }
.track--opener-light .track__scrim { background: linear-gradient(to right, rgba(239,236,230,.72) 0%, rgba(239,236,230,.35) 45%, rgba(239,236,230,0) 75%); }
@media (max-width: 720px) { .track--opener-light .track__scrim { background: rgba(239,236,230,.6); } }

.house__routes { display: flex; flex-wrap: wrap; gap: 8px 24px; }
@media (max-width: 720px) { .house__routes { justify-content: center; } }

/* ======================================================================================
   Phase 73 · 20 Sep · Erin: "On the pages that are full white, put the text in the center.
   Some texture around it, some type of animations. It's so boring."
   Home's ivory beats only. Three things, all CSS:
   1. the copy is centred in the frame at desktop widths too (the phone already was);
   2. the ivory ground is no longer flat: slow bars of window light drift across it, the same
      light the rooms are lit by, held off the middle so the type sits on calm ivory, plus grain;
   3. four viewfinder corners frame the copy and close in when a beat arrives, and the headline
      and its lines come up one after another.
   Everything stops under prefers-reduced-motion.
   ====================================================================================== */
:is([data-page="home"], [data-style="home"]) .track__film--ground { overflow: hidden; }
:is([data-page="home"], [data-style="home"]) .track__film--ground::before {
  content: ""; position: absolute; inset: -40% -60%;
  background:
    repeating-linear-gradient(100deg,
      rgba(255,252,244,0) 0 7%, rgba(255,250,238,.62) 7% 15%, rgba(255,252,244,0) 15% 17%,
      rgba(120,96,60,.07) 17% 18.2%, rgba(255,252,244,0) 18.2% 22%);
  transform: translate3d(0,0,0) rotate(-8deg);
  animation: ground-light 46s linear infinite;
  -webkit-mask-image: radial-gradient(ellipse 46% 40% at 50% 52%, rgba(0,0,0,.08) 0%, rgba(0,0,0,.5) 60%, #000 100%);
          mask-image: radial-gradient(ellipse 46% 40% at 50% 52%, rgba(0,0,0,.08) 0%, rgba(0,0,0,.5) 60%, #000 100%);
}
@keyframes ground-light { from { transform: translate3d(-22%,0,0) rotate(-8deg); } to { transform: translate3d(0,0,0) rotate(-8deg); } }
:is([data-page="home"], [data-style="home"]) .track__film--ground::after {
  content: ""; position: absolute; inset: -10%; opacity: .16; mix-blend-mode: multiply; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 240px 240px; animation: ground-grain 1.1s steps(5) infinite;
}
@keyframes ground-grain { 0% { transform: translate(0,0); } 20% { transform: translate(-3%,2%); } 40% { transform: translate(2%,-3%); } 60% { transform: translate(-2%,-2%); } 80% { transform: translate(3%,3%); } 100% { transform: translate(0,0); } }

/* the viewfinder corners, on the beat itself so they arrive and leave with its copy */
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"]::before {
  content: ""; position: absolute; pointer-events: none; z-index: -1;
  left: 50%; top: 50%; width: min(1040px, 86vw); height: min(640px, 74%);
  transform: translate(-50%, -50%) scale(1.06); opacity: 0;
  --c: rgba(28,27,25,.55); --l: 26px; --w: 1.5px;
  background:
    linear-gradient(var(--c),var(--c)) left top / var(--l) var(--w) no-repeat, linear-gradient(var(--c),var(--c)) left top / var(--w) var(--l) no-repeat,
    linear-gradient(var(--c),var(--c)) right top / var(--l) var(--w) no-repeat, linear-gradient(var(--c),var(--c)) right top / var(--w) var(--l) no-repeat,
    linear-gradient(var(--c),var(--c)) left bottom / var(--l) var(--w) no-repeat, linear-gradient(var(--c),var(--c)) left bottom / var(--w) var(--l) no-repeat,
    linear-gradient(var(--c),var(--c)) right bottom / var(--l) var(--w) no-repeat, linear-gradient(var(--c),var(--c)) right bottom / var(--w) var(--l) no-repeat;
  transition: transform 1.1s cubic-bezier(.2,.7,.2,1) .15s, opacity .8s ease .15s;
}
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on::before { transform: translate(-50%, -50%) scale(1); opacity: 1; }

/* the copy comes up one piece after another */
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on > * { animation: ground-rise .8s cubic-bezier(.2,.7,.2,1) both; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on > *:nth-child(2) { animation-delay: .12s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on > *:nth-child(3) { animation-delay: .24s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on > *:nth-child(4) { animation-delay: .36s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p { animation: ground-rise .8s cubic-bezier(.2,.7,.2,1) both; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p:nth-child(1) { animation-delay: .18s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p:nth-child(2) { animation-delay: .28s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p:nth-child(3) { animation-delay: .38s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p:nth-child(4) { animation-delay: .48s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p:nth-child(n+5) { animation-delay: .58s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li { animation: ground-rise .8s cubic-bezier(.2,.7,.2,1) both; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li:nth-child(2) { animation-delay: .1s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li:nth-child(3) { animation-delay: .2s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li:nth-child(4) { animation-delay: .3s; }
:is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li:nth-child(5) { animation-delay: .4s; }
@keyframes ground-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* centred at desktop widths as well */
@media (min-width: 721px) {
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] {
    left: 0; right: 0; top: calc(var(--chrome-h, var(--nav-h, 72px)) + var(--deadline-h, 36px)); bottom: 0;
    display: grid; align-content: center; justify-items: center; text-align: center; gap: var(--s-5);
    margin: 0; padding: 0 var(--gutter, 40px); max-width: none; width: auto; min-height: 0; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .display { max-width: 20ch; text-wrap: balance; margin-inline: auto; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .lines { max-width: 56ch; margin-inline: auto; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] > .rules { width: min(820px, 100%); text-align: left; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] > .work { width: min(1100px, 100%); }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .doors,
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .house__routes,
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .after { justify-content: center; display: flex; gap: 12px 24px; flex-wrap: wrap; }
}
@media (prefers-reduced-motion: reduce) {
  :is([data-page="home"], [data-style="home"]) .track__film--ground::before, :is([data-page="home"], [data-style="home"]) .track__film--ground::after { animation: none; }
  :is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on > *,
  :is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .lines > p,
  :is([data-page="home"], [data-style="home"]) .track--film .track__beat--panel[data-film="ground"].is-on .rules > li { animation: none; }
}

/* Phase 73, after the first look: the lifted headlines were wall-sized once centred; the cue moves to the
   right on a phone so it never sits on the last line of a centred block. */
@media (min-width: 721px) {
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--panel[data-film="ground"] .display--lift { font-size: clamp(40px, 4.6vw, 72px); line-height: 1.02; max-width: 22ch; }
}
@media (max-width: 720px) { .scrollcue { left: auto; right: 14px; transform: none; bottom: max(14px, env(safe-area-inset-bottom)); } }
@media (max-width: 720px) { .scrollcue { width: 36px; height: 36px; right: 10px; bottom: max(10px, env(safe-area-inset-bottom)); } .scrollcue__ring { width: 36px; height: 36px; } }

/* Phase 74 · the spent field: ivory over the picture and its scrim, under the copy. */
.track__spent { position: absolute; inset: 0; background: var(--ground, #efece6); opacity: 0; pointer-events: none; }

/* Phase 74 · Previous work: ten tiles, five across at desktop, two across on a phone, all in one frame. */
.piece__loop { opacity: 0; transition: opacity .6s ease; pointer-events: none; }
.piece__loop.is-on { opacity: 1; }
.piece.is-playing .piece__loop { display: none; }
.piece .piece__cap { z-index: 1; }
:is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .work--many { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; width: min(1320px, 100%); margin: 0 auto; }
:is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece { aspect-ratio: 16 / 9; width: auto; height: auto; }
:is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__title strong { font-size: 13px; }
:is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__title .lines { font-size: 11px; line-height: 1.3; }
:is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__play { transform: scale(.7); transform-origin: left bottom; }
@media (max-width: 720px) {
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .work--many { grid-template-columns: 1fr 1fr; gap: 6px; width: 100%; }
  :is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__title .lines { display: none; }
  :is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__title strong { font-size: 11px; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .display--two { font-size: 22px; margin: 0; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .proof { font-size: 12px; margin-top: 6px; }
}
:is([data-page="home"], [data-style="home"]) .track__beat--work .work--many .piece__cap { text-align: left; }
@media (max-width: 720px) {
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .work--many { justify-items: stretch; }
  :is([data-page="home"], [data-style="home"]) .track--film.track--beats .track__beat--work .work--many .piece { width: 100%; }
}

/* Phase 75 · the film overlay */
.watchbox { position: fixed; inset: 0; z-index: 1000; background: rgba(12,12,11,.94); display: grid; place-items: center; padding: 56px 16px 24px; }
.watchbox video { width: min(1400px, 100%); max-height: 100%; background: #000; }
.watchbox__x { position: absolute; top: 14px; right: 16px; background: none; border: 1px solid rgba(239,236,230,.5); color: #efece6; padding: 8px 14px; cursor: pointer; font-size: 11px; letter-spacing: .16em; }
.watchbox__x:hover, .watchbox__x:focus-visible { background: #efece6; color: #161513; }
/* Phase 75 · a long client name must not run off a phone */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat--watch .display { font-size: clamp(30px, 9.4vw, 40px); max-width: 100%; overflow-wrap: anywhere; }
}

/* Phase 76 · Studios' headline sits centred on the ivory ground */
.track--film.track--beats .track__beat--centred {
  left: 0; right: 0; top: calc(var(--chrome-h, var(--nav-h, 72px)) + var(--deadline-h, 36px)); bottom: 0;
  display: grid; align-content: center; justify-items: center; text-align: center; gap: var(--s-5);
  margin: 0; padding: 0 var(--gutter, 40px); max-width: none; width: auto; }
.track--film.track--beats .track__beat--centred .display--wall { font-size: clamp(40px, 6.4vw, 104px); line-height: .98; max-width: 16ch; text-wrap: balance; margin-inline: auto; }
.track--film.track--beats .track__beat--centred .eyebrow { justify-content: center; }

/* Phase 77 · Erin: the Studios headline sits in the true centre of the frame, "Studios" is part of it and
   larger, the block rises into place as the scroll arrives, and it holds longer (--dwell 2.6 inline). */
.track--film.track--beats.track--seam .track__beat.track__beat--centred {
  top: 0 !important; bottom: 0 !important; left: 0 !important; right: 0 !important; height: auto !important; min-height: 0 !important;
  display: flex !important; flex-direction: column; align-items: center; justify-content: center; gap: clamp(14px, 2vw, 28px);
  padding: calc(var(--deadline-h, 36px) / 2) var(--gutter, 40px) 0 !important; margin: 0 !important; max-width: none !important; width: auto !important; text-align: center; }
.track--film.track--beats .track__beat--centred > * { margin: 0 !important; }
.track--film.track--beats .track__beat--centred .eyebrow { font-size: clamp(18px, 2vw, 30px); letter-spacing: .2em; gap: .6em; justify-content: center; }
.track--film.track--beats .track__beat--centred .eyebrow .eyebrow__n { font-size: inherit; }
.track--film.track--beats .track__beat--centred.is-on .eyebrow { animation: ground-rise .9s cubic-bezier(.2,.7,.2,1) both; }
.track--film.track--beats .track__beat--centred.is-on .display--wall { animation: centred-in 1.1s cubic-bezier(.2,.7,.2,1) .1s both; }
@keyframes centred-in { from { opacity: 0; transform: translateY(28px) scale(.97); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .track--film.track--beats .track__beat--centred.is-on .eyebrow, .track--film.track--beats .track__beat--centred.is-on .display--wall { animation: none; } }

/* Phase 78 · Studios' second frame, Erin's words, centred */
.track--film.track--beats .track__beat--centred-list .display--two { font-size: clamp(56px, 8vw, 128px); line-height: .95; }
.track--film.track--beats .track__beat--centred-list .centred__where { font-size: clamp(13px, 1.2vw, 18px); letter-spacing: .18em; }
.track--film.track--beats .track__beat--centred-list .centred__list { list-style: none; padding: 0; display: grid; gap: .35em; margin-top: clamp(10px, 1.6vw, 24px) !important;
  font-family: var(--font-body); font-size: clamp(22px, 2.6vw, 40px); line-height: 1.2; color: var(--ink); }
.track--film.track--beats .track__beat--centred-list.is-on > * { animation: ground-rise .9s cubic-bezier(.2,.7,.2,1) both; }
.track--film.track--beats .track__beat--centred-list.is-on > *:nth-child(2) { animation-delay: .12s; }
.track--film.track--beats .track__beat--centred-list.is-on .centred__list li { animation: ground-rise .9s cubic-bezier(.2,.7,.2,1) both; }
.track--film.track--beats .track__beat--centred-list.is-on .centred__list li:nth-child(1) { animation-delay: .24s; }
.track--film.track--beats .track__beat--centred-list.is-on .centred__list li:nth-child(2) { animation-delay: .36s; }
.track--film.track--beats .track__beat--centred-list.is-on .centred__list li:nth-child(3) { animation-delay: .48s; }
@media (prefers-reduced-motion: reduce) { .track--film.track--beats .track__beat--centred-list.is-on > *, .track--film.track--beats .track__beat--centred-list.is-on .centred__list li { animation: none; } }

/* Phase 80 · Vantage Studios frame: the subheading, and three lines that go bold as the reader scrolls (--r is
   this beat's own progress, from site.js). Chivo ships 400 and 700, so the weight snaps; the ink deepens with it. */
.track--film.track--beats .track__beat--centred-list .centred__sub { font-family: var(--font-body); font-size: clamp(17px, 1.7vw, 26px); line-height: 1.3; max-width: 30ch; color: var(--ink); }
.track--film.track--beats .track__beat--centred-list .centred__list li { --on: clamp(0, calc((var(--bold, 0) - var(--at)) * 14), 1);
  font-weight: calc(400 + 300 * var(--on)); opacity: calc(.5 + .5 * var(--on)); transition: opacity .3s ease; }
.track--film.track--beats .track__beat--centred-list .centred__list li:nth-child(1) { --at: .22; }
.track--film.track--beats .track__beat--centred-list .centred__list li:nth-child(2) { --at: .44; }
.track--film.track--beats .track__beat--centred-list .centred__list li:nth-child(3) { --at: .66; }

/* Phase 80 · the six things, one page */
.six__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 40px) clamp(16px, 2vw, 32px); margin-top: var(--s-7, 48px); }
.six__tile { display: grid; gap: 10px; align-content: start; }
.six__film { position: relative; aspect-ratio: 16 / 9; overflow: hidden; background: #2a2926; }
.six__film img, .six__film video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.six__film video { opacity: 0; transition: opacity .6s ease; } .six__film video.is-on { opacity: 1; }
.six__film .still__generated { position: absolute; left: 8px; bottom: 8px; z-index: 1; }
.six__name { font-family: var(--font-display); font-size: clamp(22px, 2.1vw, 32px); line-height: 1; text-transform: uppercase; margin: 6px 0 0; font-weight: 400; }
.six__tile p { margin: 0; font-size: 15px; line-height: 1.5; color: var(--ink); }
@media (max-width: 900px) { .six__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .six__grid { grid-template-columns: 1fr; } }
/* Phase 80 · the six grid uses the whole width so both rows sit in one screen at desktop */
.section.six { padding-top: clamp(24px, 4vh, 56px); padding-bottom: clamp(24px, 4vh, 56px); }
.section.six .section__in { max-width: none; width: auto; display: block; }
.section.six .reveal.block { max-width: none; display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 32px; }
.section.six .reveal.block .display--two { margin: 0; }
.section.six .reveal.block .lines { margin: 0; max-width: 60ch; }
.six__grid { width: 100%; margin-top: clamp(16px, 2.4vh, 32px); }
@media (min-width: 901px) { .six__film { aspect-ratio: 16 / 7.4; } .six__tile p { font-size: 14px; line-height: 1.45; } .six__tile { gap: 6px; } }

/* Phase 82 · Creative and Solutions in Home's manner */
.centred-block { display: flex; flex-direction: column; align-items: center; text-align: center; gap: clamp(12px, 1.6vw, 24px); padding: clamp(56px, 12vh, 140px) var(--gutter, 40px); }
.centred-block .display--two { font-size: clamp(56px, 8vw, 128px); line-height: .95; margin: 0; }
.centred-block .centred__where { font-size: clamp(13px, 1.2vw, 18px); letter-spacing: .18em; margin: 0; }
.centred-block .centred__sub { font-size: clamp(17px, 1.7vw, 26px); line-height: 1.3; max-width: 34ch; margin: 0; }
.centred-block .centred__list { list-style: none; padding: 0; margin: clamp(10px, 1.6vw, 24px) 0 0; display: grid; gap: .35em; font-size: clamp(22px, 2.6vw, 40px); line-height: 1.2; font-weight: 700; }
.sell { padding: clamp(40px, 8vh, 96px) var(--margin, 40px); }
.sell__in { max-width: 1200px; margin: 0 auto; display: grid; gap: clamp(20px, 3vw, 40px); }
.sell--centre .sell__in { justify-items: center; text-align: center; }
.sell h2 { margin: 0; }
.sell .lines { max-width: 56ch; } .sell--centre .lines { margin-inline: auto; }
.sell__three { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(16px, 2.4vw, 40px); text-align: left; width: 100%; }
.sell__card { border-top: 1.5px solid var(--ink); padding-top: 14px; display: grid; gap: 8px; align-content: start; }
.sell__n { font-size: 11px; letter-spacing: .18em; color: var(--muted); }
.sell__card h3 { font-family: var(--font-display); font-weight: 400; text-transform: uppercase; font-size: clamp(22px, 2.2vw, 34px); line-height: 1; margin: 0; }
.sell__card p { margin: 0; font-size: 16px; line-height: 1.5; }
.sell__big { font-family: var(--font-display); text-transform: uppercase; font-size: clamp(34px, 5vw, 76px); line-height: 1; display: grid; gap: .1em; margin: 0; }
.sell--stone { background: var(--stone, #e4dfd6); }
@media (max-width: 800px) { .sell__three { grid-template-columns: 1fr; } }
.people { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: clamp(24px, 3vw, 56px) clamp(24px, 4vw, 72px); }
@media (max-width: 800px) { .people { grid-template-columns: 1fr; } }


/* Phase 84 · 21 Sep · Erin: "The text should be on top of the scroll videos". The centred page-name frame
   sits over the scrubbed bed, not the ivory ground. A soft ivory wash behind the type keeps ink legible
   over the picture without hiding it. */
.track__beat--over-film { isolation: isolate; }
.track__beat--over-film::after {
  content: ""; position: absolute; z-index: -1; left: 50%; top: 50%;
  width: min(1500px, 130vw); height: min(900px, 110vh); transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, rgba(239,236,230,.86), rgba(239,236,230,.62) 55%, rgba(239,236,230,0));
  pointer-events: none;
}


/* Phase 85 · 21 Sep · Erin: the cue must be seen on a phone, be more obvious, and sit on every page that
   opens on a scroll. Fixed to the visible viewport, bottom centre, at every width. Ink plate, ivory mark,
   so it reads over film and over ivory alike. These rules come last and override the Phase 71 cue. */
.scrollcue { position: fixed; left: 50%; right: auto; bottom: calc(22px + env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: 40; width: auto; height: 56px; padding: 0 6px 0 22px; border-radius: 28px; display: flex; align-items: center; gap: 12px;
  background: rgba(22,21,19,.88); color: #efece6; box-shadow: 0 6px 24px rgba(22,21,19,.28), 0 0 0 1px rgba(239,236,230,.18);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); pointer-events: none;
  transition: opacity .5s ease, padding .5s ease, gap .5s ease; }
.scrollcue::before { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 1.5px solid rgba(22,21,19,.55);
  animation: scrollcue-pulse 2s ease-out infinite; }
@keyframes scrollcue-pulse { 0% { transform: scale(1); opacity: .9; } 100% { transform: scale(1.25, 1.55); opacity: 0; } }
.scrollcue__word { font: 600 12px/1 var(--sans, "Helvetica Neue", Arial, sans-serif); letter-spacing: .2em; text-transform: uppercase;
  max-width: 90px; overflow: hidden; white-space: nowrap; transition: max-width .5s ease, opacity .4s ease; }
.scrollcue__dial { position: relative; width: 44px; height: 44px; display: grid; place-items: center; flex: none; }
.scrollcue__dial > * { grid-area: 1 / 1; }
.scrollcue .scrollcue__ring { width: 44px; height: 44px; }
.scrollcue .scrollcue__arrow { width: 18px; height: 18px; }
.scrollcue .scrollcue__track { stroke: rgba(239,236,230,.28); stroke-width: 2; }
.scrollcue .scrollcue__fill { stroke-width: 2; }
.scrollcue.is-moving { padding: 0 6px; gap: 0; }
.scrollcue.is-moving .scrollcue__word { max-width: 0; opacity: 0; }
.scrollcue.is-moving::before { animation: none; opacity: 0; }
.scrollcue.is-end { opacity: 0; }
@media (max-width: 720px) { .scrollcue { left: 50%; right: auto; transform: translateX(-50%); width: auto; height: 56px; bottom: calc(18px + env(safe-area-inset-bottom)); } }
@media (prefers-reduced-motion: reduce) { .scrollcue::before { animation: none; opacity: 0; } }


/* Phase 86 · We are hiring, on Creative. */
.hiring__list { list-style: none; margin: 0; padding: 0; max-width: 62ch; }
.hiring__list li { padding: .7em 0; border-top: 1px solid rgba(22,21,19,.14); line-height: 1.5; }
.hiring__list li:last-child { border-bottom: 1px solid rgba(22,21,19,.14); }
.hiring__cta { margin-top: 2.4em; }


/* Phase 89 · the hiring block is three collapsible roles. <details> carries the open state, so the only
   job here is to make the closed row look like a row worth pressing and the open one read like the page. */
.jobs { margin: 2.6em 0 3em; border-top: 1px solid rgba(22,21,19,.18); }
.job { border-bottom: 1px solid rgba(22,21,19,.18); scroll-margin-top: 110px; }
.job__head { display: flex; align-items: baseline; gap: 16px; padding: 1.15em 0; cursor: pointer; list-style: none;
  transition: opacity .2s ease; }
.job__head::-webkit-details-marker { display: none; }
.job__head:hover { opacity: .62; }
.job__title { font-weight: 700; font-size: clamp(21px, 2.3vw, 29px); line-height: 1.15; }
.job__meta { margin-left: auto; opacity: .6; white-space: nowrap; }
.job__sign { position: relative; flex: none; width: 15px; height: 15px; align-self: center; }
.job__sign::before, .job__sign::after { content: ""; position: absolute; left: 0; top: 7px; width: 15px; height: 1.5px;
  background: currentColor; transition: transform .25s ease; }
.job__sign::after { transform: rotate(90deg); }
.job[open] .job__sign::after { transform: rotate(0deg); }
.job__body { padding: .2em 0 2.6em; max-width: 62ch; }
.job__head2 { display: block; margin: 2.2em 0 .9em; opacity: .7; }
.jobs + .job__head2 { margin-top: 0; }
@media (max-width: 720px) {
  .job__head { flex-wrap: wrap; gap: 6px 14px; }
  .job__meta { margin-left: 0; width: 100%; order: 3; }
  .job__sign { margin-left: auto; }
}


/* Phase 95, rebuilt in Phase 98, rebuilt again in Phase 100 · the three product categories on Studios.
   Erin, 21 Sep: "4:5 full bleed carousel that takes over the entire screen, next to each other on desktop and
   one by one on mobile, plays on arrival." So the rail is edge to edge with no gap, the films are portrait,
   and every film that is on screen is playing. The caption sits on the film, not under it.
   Native overflow-x with snap, so a thumb, a trackpad, the keyboard and a screen reader all work with no JS.
   The 4:5 frame is a centre crop of a 16:9 master: the sides are lost. Proper vertical cuts would be better
   and are Erin's call. */
.cat { margin-top: clamp(44px, 8vh, 110px); }
.cat__head { max-width: 62ch; }
.cat__name { font-family: var(--font-display); font-size: clamp(26px, 3vw, 44px); line-height: 1; text-transform: uppercase; font-weight: 400; margin: 0; }
.cat__line { margin: .7em 0 0; font-size: clamp(15px, 1.3vw, 18px); line-height: 1.5; }
.cat__count { display: block; margin: 1.1em 0 0; opacity: .55; }
.cat__drag { margin-left: 14px; padding-left: 14px; border-left: 1px solid rgba(22,21,19,.25); }

.cat__railwrap { margin-top: clamp(18px, 2.6vw, 36px); }
.cat__rail { display: flex; gap: 0; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  /* edge to edge, measured off the rail's own box rather than --gutter (see Phase 98) */
  margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); padding: 0; }
.cat__rail::-webkit-scrollbar { display: none; }
.cat__rail:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

.cat__tile { flex: 0 0 auto; width: 100vw; scroll-snap-align: center; display: block;
  padding: 0; border: 0; background: none; text-align: left; cursor: pointer; font: inherit; color: inherit; }
.cat__film { position: relative; display: block; aspect-ratio: 4 / 5; width: 100%; overflow: hidden; background: #2a2926; }
.cat__film img, .cat__film video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cat__film video { opacity: 0; transition: opacity .8s ease; }
.cat__film video.is-on { opacity: 1; }
/* the scrim under the caption, and the play mark that says this opens with sound */
.cat__film::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 42%;
  background: linear-gradient(to top, rgba(18,17,16,.72), rgba(18,17,16,0)); pointer-events: none; }
.cat__film::after { content: ""; position: absolute; right: 20px; bottom: 26px; width: 0; height: 0;
  border-left: 15px solid rgba(239,236,230,.95); border-top: 10px solid transparent; border-bottom: 10px solid transparent;
  opacity: .8; transition: opacity .3s ease; }
.cat__tile:hover .cat__film::after, .cat__tile:focus-visible .cat__film::after { opacity: 1; }
.cat__meta { position: absolute; left: clamp(18px, 3vw, 36px); bottom: clamp(18px, 2.6vw, 32px); z-index: 1;
  display: grid; gap: 3px; color: #efece6; text-shadow: 0 1px 14px rgba(18,17,16,.5); }
.cat__client { font-family: var(--font-display); font-size: clamp(22px, 2.6vw, 38px); line-height: 1; text-transform: uppercase; }
.cat__kind { opacity: .8; }

.cat__bar { height: 1px; margin: 14px var(--gutter, 40px) 0; background: rgba(22,21,19,.16); }
.cat__bar span { display: block; height: 100%; background: var(--ink, #161513); transform: scaleX(0); transform-origin: 0 50%; }

/* Desktop: the films stand side by side and the rail still runs off both edges. Height leads, the 4:5 frame
   sets the width, so two and a bit films sit across a laptop and three across a wide screen. */
@media (min-width: 900px) {
  .cat__rail { scroll-snap-type: x proximity; }
  .cat__tile { width: auto; scroll-snap-align: start; }
  .cat__film { height: min(84vh, 880px); width: auto; aspect-ratio: 4 / 5; }
  .cat__film img, .cat__film video { width: 100%; }
}
@media (prefers-reduced-motion: reduce) { .cat__film video { transition: none; } }


/* Phase 101 · one carousel, every film on the same line. The category rides on the film as a tag. */
.cat--one { margin-top: clamp(28px, 4vh, 56px); }
.cat--one .cat__count { margin-top: 0; }
.cat__tag { position: absolute; left: clamp(18px, 3vw, 36px); top: clamp(16px, 2.4vw, 28px); z-index: 1;
  color: #efece6; opacity: .82; text-shadow: 0 1px 14px rgba(18,17,16,.55); }
.cat__film .cat__tag + video, .cat__film video { pointer-events: none; }
/* a second scrim at the top, so the tag reads over a bright frame */
.cat__film .cat__tag::before { content: ""; position: absolute; left: calc(clamp(18px, 3vw, 36px) * -1); right: -100vw; top: calc(clamp(16px, 2.4vw, 28px) * -1);
  height: calc(clamp(16px, 2.4vw, 28px) * 2 + 2.4em); background: linear-gradient(to bottom, rgba(18,17,16,.5), rgba(18,17,16,0));
  z-index: -1; pointer-events: none; }
@media (min-width: 900px) { .cat--one { margin-top: clamp(32px, 5vh, 72px); } }


/* Phase 102 · 21 Sep · Erin: a big title on top of the line, and the films smaller so more of the shelf is
   in the frame at once. The order is mixed so no two films of the same kind, or the same client, sit
   side by side. */
.work__title { font-size: clamp(44px, 7.4vw, 124px) !important; line-height: .94 !important; max-width: 18ch; text-wrap: balance; }
.cat--one .cat__tile { width: 74vw; }
@media (min-width: 900px) {
  .cat--one .cat__tile { width: auto; }
  .cat--one .cat__film { height: min(58vh, 620px); }
  .cat--one .cat__client { font-size: clamp(18px, 1.7vw, 26px); }
  .cat--one .cat__meta { left: clamp(14px, 1.6vw, 24px); bottom: clamp(14px, 1.6vw, 22px); }
  .cat--one .cat__tag { left: clamp(14px, 1.6vw, 24px); top: clamp(12px, 1.4vw, 20px); }
}


/* Phase 103 · 21 Sep · Erin: the heading is "We make." and the three kinds sit under it as subheadings. */
.work__kinds { list-style: none; padding: 0; margin: clamp(14px, 2vw, 26px) 0 clamp(16px, 2vw, 26px); display: grid; gap: .12em; }
.work__kinds li { font-family: var(--font-display); font-size: clamp(24px, 3.2vw, 52px); line-height: 1.05; text-transform: uppercase; }
.work__kinds li + li { opacity: .82; }
.work__kinds li + li + li { opacity: .66; }


/* Phase 104 · the lines under the Home headline come up one at a time as the reader scrolls, and the beat
   holds longer, so nobody skates past the three things we actually do. grow.js sets --g, 0 to 1, across the
   beat; each line has its own --at and lights when the reader reaches it. */
/* Phase 123 · 21 Sep · Erin: "Each point should arrive as a separate subhead line. Each point on scroll."
   So a line is not there until the reader reaches it: it arrives from below, at subhead size, and stays.
   Each keeps its own --at, so they land one after another rather than together. The measure is wide enough
   that each point holds one line at desktop width. */
.lines--grow { --g: 0; max-width: 40ch; margin-inline: auto; }
.lines--grow p { --on: clamp(0, calc((var(--g, 0) - var(--at)) * 11), 1);
  font-size: clamp(17px, 1.9vw, 29px);
  line-height: 1.3;
  opacity: var(--on);
  transform: translateY(calc(14px * (1 - var(--on))));
  transition: opacity .35s ease, transform .35s cubic-bezier(.2,.7,.2,1); }
.lines--grow p + p { margin-top: .55em; }
/* the beat's own arrival animation carries opacity with fill both, which outranks the declared value and
   was lighting all three lines at once. The scroll drives these, so the animation is off here. */
.track__beat.is-on .lines--grow p, .lines--grow p { animation: none !important; }
/* Phase 127 · five points now, so the steps are closer together. */
.lines--grow p:nth-child(1) { --at: 0.02; }
.lines--grow p:nth-child(2) { --at: 0.21; }
.lines--grow p:nth-child(3) { --at: 0.40; }
.lines--grow p:nth-child(4) { --at: 0.59; }
.lines--grow p:nth-child(5) { --at: 0.78; }
@media (min-width: 900px) { .lines--grow { max-width: 46ch; } }
@media (prefers-reduced-motion: reduce) { .lines--grow p { transition: none; opacity: 1; transform: none; } }


/* Phase 105 · 21 Sep · Erin: the films were soft and some carried black bars. The loops and posters are now
   cut to 4:5 at the source (720x900 film, 900x1125 poster) with the letterbox cropped off first, so the
   browser scales down rather than up and no bands survive. The line is smaller again. */
@media (max-width: 899px) { .cat--one .cat__tile { width: 62vw; } }
@media (min-width: 900px) {
  .cat--one .cat__film { height: min(46vh, 500px); }
  .cat--one .cat__client { font-size: clamp(15px, 1.25vw, 20px); }
  .cat--one .cat__tag { font-size: 10px; }
}


/* Phase 105b · the heading block on Studios stacks: "We make." then the three kinds under it, then the line.
   The site's default .block is a two column grid, which put the kinds beside the heading. */
.section.six .reveal.block, .section.six .block { display: block; }
.section.six .block .lines { max-width: 62ch; }


/* Phase 107 · 21 Sep · Erin: back to 16:9, and the shelf moves sideways as the reader scrolls down.
   .cat--drive gets its height from rail.js, the rail sticks while the reader passes it, and the vertical
   distance is spent moving the films across. Snap is off while driven and the rail is not hand-scrolled,
   so a touch drag scrolls the page as usual and the films follow. */
.cat--one .cat__film, .cat--drive .cat__film { aspect-ratio: 16 / 9; }
@media (max-width: 899px) { .cat--one .cat__tile { width: 82vw; } .cat--one .cat__film { height: auto; width: 100%; } }
@media (min-width: 900px) {
  .cat--one .cat__film { height: min(56vh, 560px); width: auto; }
}
.cat--drive { position: relative; }
/* the stuck rail holds the whole screen and sits in the middle of it, so the reader is inside the shelf
   rather than looking at a band of films with a field of ivory under them. */
.cat--drive .cat__railwrap { position: sticky; top: 0; height: 100vh; margin-top: 0;
  display: flex; flex-direction: column; justify-content: center; }
.cat--drive .cat__bar { margin-top: 20px; }
.cat--drive .cat__rail { overflow-x: hidden; scroll-snap-type: none; touch-action: pan-y; }
@media (prefers-reduced-motion: reduce) {
  .cat--drive { height: auto !important; }
  .cat--drive .cat__railwrap { position: static; }
  .cat--drive .cat__rail { overflow-x: auto; }
}


/* Phase 109 · 21 Sep · Erin: the three facts under the page name were too faint to read over the film.
   Full strength ink, a heavier weight, and a soft ivory halo so they hold on a bright frame. */
.centred__facts li { color: var(--ink, #161513); opacity: 1; font-weight: 600;
  text-shadow: 0 0 12px rgba(239,236,230,.85), 0 0 3px rgba(239,236,230,.9); }
.centred__facts li + li::before { opacity: .45; }


/* Phase 112 · the Lighthouse block at the foot of Studios. .centred-block already centres and sizes; this
   only gives it the page's measure and puts the routes on one row. */
#lighthouse .lines { max-width: 46ch; margin-inline: auto; }
#lighthouse .lines p + p { margin-top: .7em; }
#lighthouse .house__routes { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: clamp(24px, 3vw, 40px); }

#who-we-make-it-for .lines { max-width: 46ch; margin-inline: auto; }
#who-we-make-it-for .lines p + p { margin-top: .5em; }
#who-we-make-it-for .lines .gap { margin-top: 1.5em; }


/* Phase 122 · 21 Sep · Erin: the email button was rendering as a black box with no label. The token was
   resolving, so this pins the two colours literally rather than through --ground, which is the only way a
   missing variable could empty the button, and the label is the address itself so the reader can copy it
   even if the mail client never opens. */
.doors .btn--primary { background: #161513; color: #efece6; border-color: #161513; text-transform: none; letter-spacing: .04em; }
.doors { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }


/* Phase 128 · 22 Sep · Erin: the email button was STILL black on black. The label was there all along;
   its colour was being won by another rule on .label, not by --ground being empty. Forced on the anchor
   and on every descendant, which is the only way to be sure whatever wins later cannot take it back. */
.doors .btn--primary, .doors .btn--primary * { color: #efece6 !important; }
.doors .btn--primary { background: #161513 !important; border-color: #161513 !important; }
.doors .btn--primary:hover, .doors .btn--primary:focus-visible { background: #2a2926 !important; }

/* 26 Sep · Erin: logo and nav about 30% larger (above). Client names that are too long for the phone
   wall size fit the screen width instead of breaking onto a third line. The selector has to out-rank
   the home beat rule (:not(--hero):not(--panel) .display), which is why Phase 75's clamp never applied. */
@media (max-width: 720px) {
  .track--film.track--beats .track__beat.track__beat--watch:not(.track__beat--hero):not(.track__beat--panel) .display--fit {
    font-size: min(var(--text-wall), 12vw); max-width: 100%; }
}
.track--film.track--beats .track__beat.track__beat--watch .display--fit { text-wrap: balance; }

/* 26 Sep · the scroll cue is a real button: it takes the reader to the next section. */
.scrollcue { pointer-events: auto; cursor: pointer; border: 0; font: inherit; -webkit-appearance: none; appearance: none; }
.scrollcue:focus-visible { outline: 2px solid #efece6; outline-offset: 3px; }
.scrollcue.is-end { pointer-events: none; }

/* 1 Oct · /join: the four-item sections sit two by two on a desktop, one column on a phone. */
.sell__three.sell__three--four { grid-template-columns: repeat(2, minmax(0,1fr)); }
@media (max-width: 800px) { .sell__three.sell__three--four { grid-template-columns: 1fr; } }
.join__asks { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: 10px; max-width: 56ch; }
.join__asks li { font-family: var(--font-display); text-transform: uppercase; font-size: clamp(20px, 2vw, 28px); line-height: 1.1; }

/* 1 Oct · light motion inside a revealed section. It all hangs off the existing .reveal.is-on (site.js,
   once, when the section comes into view) and the motion tokens, which are 0 under reduced motion.
   With JavaScript off, html has no .js class and none of this applies: everything simply shows. */
.js .reveal .sell__card, .js .reveal .rules > li, .js .reveal .join__asks > li {
  opacity: 0; transform: translateY(var(--reveal-rise));
  transition: opacity var(--t-reveal) var(--ease-out), transform var(--t-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms + 150ms); }
.js .reveal.is-on .sell__card, .js .reveal.is-on .rules > li, .js .reveal.is-on .join__asks > li { opacity: 1; transform: none; }
.reveal :is(.sell__card, .rules > li, .join__asks > li):nth-child(2) { --i: 1; }
.reveal :is(.sell__card, .rules > li, .join__asks > li):nth-child(3) { --i: 2; }
.reveal :is(.sell__card, .rules > li, .join__asks > li):nth-child(4) { --i: 3; }
.reveal :is(.sell__card, .rules > li, .join__asks > li):nth-child(5) { --i: 4; }
.reveal :is(.sell__card, .rules > li, .join__asks > li):nth-child(n+6) { --i: 5; }
/* the rule above each card draws in from the left, like a light coming on */
.js .reveal .sell__card { position: relative; border-top-color: transparent; }
.js .reveal .sell__card::before { content: ""; position: absolute; left: 0; right: 0; top: -1.5px; height: 1.5px; background: var(--ink);
  transform: scaleX(0); transform-origin: left center; transition: transform calc(var(--t-reveal) * 1.6) var(--ease-out); transition-delay: inherit; }
.js .reveal.is-on .sell__card::before { transform: scaleX(1); }
/* buttons answer the pointer */
@media (hover: hover) {
  .btn { transition: opacity var(--t-ui) var(--ease-out), background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out),
    transform var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out); }
  .btn:not([disabled]):not([data-placeholder]):hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(22, 21, 19, .14); }
}
/* /join only, for now: one soft band of light passes across each heading as its section arrives */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .js [data-page="join"] .sell .reveal h2.display {
    background-image: linear-gradient(100deg, var(--ink) 42%, #fff4d6 50%, var(--ink) 58%);
    background-size: 260% 100%; background-position: 100% 0;
    -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent;
    transition: background-position calc(var(--t-reveal) * 3) var(--ease-in-out) .25s; }
  .js [data-page="join"] .sell .reveal.is-on h2.display { background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .js .reveal :is(.sell__card, .rules > li, .join__asks > li), .js .reveal .sell__card::before { transition-delay: 0s; }
  .btn:not([disabled]):not([data-placeholder]):hover { transform: none; box-shadow: none; }
}


/* 1 Oct · Erin: on /join the three facts under the page name (Community, Education, Business building)
   should be larger and arrive in bold. Set in the display face, one word at a time, after the title. */
body[data-page="join"] .centred__facts { list-style: none; padding: 0; margin: clamp(16px, 2.2vw, 32px) 0 0 !important;
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: .1em; }
body[data-page="join"] .track__beat .centred__facts li { font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(30px, 3.8vw, 58px); line-height: 1; letter-spacing: .01em; color: var(--ink, #161513); opacity: 1;
  text-shadow: 0 0 14px rgba(239,236,230,.85), 0 0 3px rgba(239,236,230,.9); }
body[data-page="join"] .track__beat .centred__facts li + li::before { content: none; display: none; width: .14em; height: .14em;
  border-radius: 50%; background: currentColor; opacity: .45; margin-right: .5em; vertical-align: middle; transform: translateY(-.06em); }
@keyframes facts-in { from { opacity: 0; transform: translateY(.35em) scale(.96); letter-spacing: .14em; filter: blur(4px); }
  to { opacity: 1; transform: none; letter-spacing: .01em; filter: none; } }
body[data-page="join"] .track__beat.is-on .centred__facts li { animation: facts-in .85s cubic-bezier(.2,.7,.2,1) both; }
body[data-page="join"] .track__beat.is-on .centred__facts li:nth-child(1) { animation-delay: .45s; }
body[data-page="join"] .track__beat.is-on .centred__facts li:nth-child(2) { animation-delay: .62s; }
body[data-page="join"] .track__beat.is-on .centred__facts li:nth-child(3) { animation-delay: .79s; }
@media (max-width: 720px) {
  body[data-page="join"] .centred__facts { flex-direction: column; gap: .14em; }
  body[data-page="join"] .track__beat .centred__facts li { font-size: clamp(34px, 10.5vw, 52px); }
  body[data-page="join"] .track__beat .centred__facts li + li::before { display: none; }
}
@media (prefers-reduced-motion: reduce) { body[data-page="join"] .track__beat.is-on .centred__facts li { animation: none; } }

body[data-page="join"] .sell__in > .lines + .sell__three { margin-top: clamp(28px, 3vw, 44px); }

/* 1 Oct · /join: retainer cards and the one-year timeline. The year reuses .sell__card, so its rules draw
   left to right in order, which is the timeline. A dot marks where each phase starts. */
.sell__in > .sell__three + .lines--after { margin-top: clamp(28px, 3vw, 44px); }
.sell__in > .lines + .sell__three { margin-top: clamp(28px, 3vw, 44px); }
.sell__three.year { list-style: none; padding: 0; margin: clamp(28px, 3vw, 44px) 0 0; grid-template-columns: repeat(4, minmax(0,1fr)); }
.year > li { position: relative; }
.year > li::after { content: ""; position: absolute; left: 0; top: -5px; width: 9px; height: 9px; border-radius: 50%; background: var(--ink); }
@media (max-width: 1000px) { .sell__three.year { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 800px) { .sell__three.year { grid-template-columns: 1fr; } }

/* 1 Oct · /join reading layout. Erin: subheadings and a design built for reading.
   Desktop: the section name holds on the left while the reading runs on the right, one measure wide.
   Each section opens with a lede, then short titled blocks. Card grids span both columns below. */
.sell--read .sell__in.read { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: clamp(32px, 6vw, 112px); row-gap: clamp(28px, 3.4vw, 48px); align-items: start; text-align: left; }
.read__head { display: grid; gap: 14px; align-content: start; position: sticky; top: clamp(88px, 12vh, 120px); }
.read__n { font-size: 12px; letter-spacing: .2em; color: var(--muted); display: flex; align-items: center; gap: 12px; }
.read__n::after { content: ""; width: 40px; height: 1px; background: currentColor; opacity: .6; }
.sell--read .read__head .display--two { font-size: clamp(40px, 4.6vw, 76px); line-height: .95; max-width: 11ch; }
.read__body { display: grid; gap: clamp(22px, 2.4vw, 32px); max-width: 62ch; }
.read__lede { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: clamp(22px, 2.1vw, 30px); line-height: 1.32; color: var(--ink); text-wrap: pretty; }
.read__item { display: grid; gap: 8px; padding-top: 18px; border-top: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); }
.read__item h3 { margin: 0; font-family: var(--font-display); font-weight: 400; text-transform: uppercase; font-size: clamp(19px, 1.6vw, 24px); line-height: 1.05; letter-spacing: .01em; }
.read__item p { margin: 0; font-size: clamp(17px, 1.25vw, 19px); line-height: 1.62; text-wrap: pretty; }
.read__item p + p { margin-top: .5em; }
.read__wide { grid-column: 1 / -1; margin-top: clamp(8px, 1.6vw, 24px); }
.join__asks.read__asks { margin: 4px 0 0; max-width: none; gap: 8px; }
.join__asks.read__asks li { font-size: clamp(18px, 1.6vw, 22px); padding-left: 1.2em; position: relative; }
.join__asks.read__asks li::before { content: ""; position: absolute; left: 0; top: .42em; width: .5em; height: 2px; background: var(--ink); }
@media (max-width: 900px) {
  .sell--read .sell__in.read { grid-template-columns: 1fr; }
  .read__head { position: static; }
  .sell--read .read__head .display--two { max-width: 14ch; }
}
/* reading blocks arrive one after another, like the cards */
.js .reveal .read__item, .js .reveal .read__lede { opacity: 0; transform: translateY(var(--reveal-rise, 16px));
  transition: opacity var(--t-reveal) var(--ease-out), transform var(--t-reveal) var(--ease-out); transition-delay: calc(var(--i, 0) * 90ms + 150ms); }
.js .reveal.is-on .read__item, .js .reveal.is-on .read__lede { opacity: 1; transform: none; }
.read__body > :nth-child(2) { --i: 1; } .read__body > :nth-child(3) { --i: 2; } .read__body > :nth-child(4) { --i: 3; } .read__body > :nth-child(n+5) { --i: 4; }
@media (prefers-reduced-motion: reduce) { .js .reveal .read__item, .js .reveal .read__lede { transition: none; opacity: 1; transform: none; } }
/* the name only holds on the left in text-only sections; where cards span both columns it sits still */
.read:has(.read__wide) .read__head { position: static; }
@media (max-width: 720px) { body[data-page="join"] .track__beat .centred__facts li { font-size: clamp(28px, 8.4vw, 44px); white-space: nowrap; } }

/* 1 Oct · /apply, the Vantage Creative application. Same reading layout as /join, form fields in the house style. */
[data-page="apply"] .apply { padding-top: clamp(120px, 18vh, 180px); }
[data-page="apply"] .read__head .display--two { font-size: clamp(56px, 7vw, 112px); }
.apply__intro { margin: 0; font-size: 17px; line-height: 1.6; color: var(--muted); }
.apply__form { display: grid; gap: clamp(28px, 3vw, 40px); }
.apply__group { border: 0; margin: 0; padding: 18px 0 0; border-top: 1px solid color-mix(in srgb, var(--ink) 16%, transparent); display: grid; gap: 22px; min-width: 0; }
.apply__group > legend { padding: 0; display: flex; align-items: baseline; gap: 14px; float: left; width: 100%; margin-bottom: 4px; }
.apply__group > legend + * { clear: both; }
.apply__legend { font-family: var(--font-display); text-transform: uppercase; font-size: clamp(22px, 2vw, 30px); line-height: 1; }
.apply__field { display: grid; gap: 6px; border: 0; margin: 0; padding: 0; min-width: 0; }
.apply__field > label, .apply__radios > legend { font-size: 17px; line-height: 1.4; font-weight: 400; color: var(--ink); padding: 0; }
.apply__hint { font-size: 14px; color: var(--muted); line-height: 1.4; }
.apply__field input[type="text"], .apply__field input[type="email"], .apply__field input[type="url"], .apply__field textarea {
  font: inherit; font-size: 17px; color: var(--ink); background: color-mix(in srgb, var(--surface, #fff) 70%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); border-radius: var(--radius, 4px); padding: 12px 14px; width: 100%; box-sizing: border-box;
  transition: border-color var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out); }
.apply__field textarea { resize: vertical; min-height: 128px; line-height: 1.55; }
.apply__field input:focus, .apply__field textarea:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 12%, transparent); }
.apply__choices { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }
.apply__choice { display: inline-flex; align-items: center; gap: 8px; padding: 10px 14px; border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); border-radius: 999px; cursor: pointer; font-size: 16px; }
.apply__choice input { accent-color: var(--ink); margin: 0; }
.apply__choice:has(input:checked) { border-color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent); }
.apply__field.is-missing input, .apply__field.is-missing textarea, .apply__field.is-missing .apply__choice { border-color: #a23b2a; }
.apply__field.is-missing > label, .apply__field.is-missing > legend { color: #a23b2a; }
.apply__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.apply__send { display: grid; gap: 12px; justify-items: start; padding-top: 8px; }
.apply__send .btn[disabled] { opacity: .5; cursor: wait; }
.apply__status { margin: 0; font-size: 15px; min-height: 1.4em; color: var(--muted); }
.apply__status.is-bad { color: #a23b2a; }
.apply__done { display: grid; gap: 14px; outline: none; }
.apply__done p { margin: 0; font-size: 18px; line-height: 1.6; }
.apply__done[hidden] { display: none; }
.apply__lead { margin: 0; font-size: 16px; }
.apply__optional { color: var(--muted); font-size: 14px; }
.apply__consent { display: flex; gap: 12px; align-items: flex-start; font-size: 16px; line-height: 1.5; cursor: pointer; padding: 14px; border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); border-radius: var(--radius, 4px); }
.apply__consent input { accent-color: var(--ink); width: 18px; height: 18px; margin-top: 3px; flex: none; }
.apply__field.apply__consent.is-missing { border-color: #a23b2a; color: #a23b2a; }
[data-page="members"] .apply { padding-top: clamp(120px, 18vh, 180px); }
[data-page="members"] .members__form { max-width: 440px; }
/* 2 Oct · four nav items (+Creative added): keep them on one line on phones */
@media (max-width: 720px) { .nav__items { gap: 14px; font-size: calc(var(--text-label) * 1.05); flex-wrap: nowrap; } }
.apply__field input[type="password"] { font: inherit; font-size: 17px; color: var(--ink); background: color-mix(in srgb, var(--surface, #fff) 70%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); border-radius: var(--radius, 4px); padding: 12px 14px; width: 100%; box-sizing: border-box; }
.apply__field input[type="password"]:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 12%, transparent); }

/* Careers page (Oct 2026) · the hiring block moved off Creative onto its own page. Clear the fixed nav. */
[data-page="careers"] .hiring { padding-top: clamp(120px, 18vh, 180px); }
/* Careers · longer job copy needs air between paragraphs and around the perks list. */
.job__body .lines > p + p { margin-top: .85em; }
.job__body .lines + .hiring__list { margin-top: 1em; }
.job__body .hiring__list + .lines { margin-top: 1.6em; }
.job__body .hiring__cta + .lines { margin-top: 1.4em; }
