/* ══════════════════════════════════════════════════════════════
   King’s Development and AI
   Set as a document, not a landing page: one paper stock, ink,
   and a single blue reserved for figures and links.
   Grotesk for headings, serif for prose, mono for data.
   ══════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Archivo';
  src: url('fonts/archivo-var-latin.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
}
@font-face {
  font-family: 'Source Serif';
  src: url('fonts/sourceserif-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Source Serif';
  src: url('fonts/sourceserif-600-latin.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Plex Mono';
  src: url('fonts/plexmono-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Plex Mono';
  src: url('fonts/plexmono-600-latin.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}

/* ── tokens ─────────────────────────────────────────────────── */
:root {
  --stone:    #E6E4DE;
  --stone-2:  #DDDAD2;
  --ink:      #17171B;
  --ink-soft: rgba(23,23,27,.74);
  --ink-mute: rgba(23,23,27,.66);
  --rule:     rgba(23,23,27,.20);
  --rule-2:   rgba(23,23,27,.34);
  --blue:     #22389B;

  --display: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --serif:   'Source Serif', Georgia, 'Times New Roman', serif;
  --mono:    'Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --f-h1:   clamp(2.7rem, 6.2vw, 5.1rem);
  --f-h2:   clamp(1.9rem, 3.4vw, 2.9rem);
  --f-h3:   clamp(1.2rem, 1vw + .95rem, 1.45rem);
  --f-body: clamp(1.03rem, .3vw + .96rem, 1.15rem);
  --f-mono: clamp(.68rem, .16vw + .64rem, .74rem);

  --gut:  clamp(1.25rem, 5vw, 4.5rem);
  --part: clamp(4rem, 7vw, 7rem);
  --ease: cubic-bezier(.2,.7,.3,1);
}

/* ── reset ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--stone);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--f-body);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }
a { color: inherit; }
button, input, textarea { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

.skip { position: absolute; left: -9999px; background: var(--ink); color: var(--stone); padding: .8rem 1.2rem; z-index: 100; }
.skip:focus { left: 1rem; top: 1rem; }

.shell { width: 100%; max-width: 1180px; margin-inline: auto; padding-inline: var(--gut); }

/* ── masthead ───────────────────────────────────────────────── */
.masthead {
  position: sticky; top: 0; z-index: 40;
  background: var(--stone);
  border-bottom: 1px solid var(--rule);
}
.masthead__in {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 2rem; padding-block: 1.1rem;
}
.mark {
  display: inline-flex; align-items: center; gap: .62rem;
  text-decoration: none; color: var(--ink);
}
.kmark { width: 1.42rem; height: auto; flex: none; overflow: visible; }
.lockup__type { display: grid; gap: .1rem; }
.lockup__type b {
  font-family: var(--display);
  font-weight: 800; font-stretch: 104%;
  font-size: .93rem; line-height: 1; letter-spacing: -.045em;
}
.lockup__type i {
  font-family: var(--mono); font-style: normal;
  font-size: .5rem; line-height: 1;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-mute);
}
@media (max-width: 480px) { .lockup__type b { font-size: .82rem; } }

.colophon__mark {
  display: inline-flex; align-items: center; gap: .62rem;
  color: var(--stone);
}
.colophon__mark .kmark { width: 1.3rem; }
.colophon__mark span {
  font-family: var(--display); font-weight: 800; font-stretch: 104%;
  font-size: .95rem; letter-spacing: -.03em;
}

.masthead__nav { display: flex; gap: 1.6rem; }
.masthead__nav a {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mute); text-decoration: none;
  padding-bottom: .15rem; border-bottom: 1px solid transparent;
  transition: color .18s, border-color .18s;
}
.masthead__nav a:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* Narrow screens used to hide the nav outright, which left a bar with nothing
   in it but the logo and no route to the work or the contact form. Drop the
   middle items instead and keep the two that matter, rather than adding a
   menu button and a panel to a site with eight sections. Contact stays inked
   so the one thing a visitor might want is the one thing that stands out. */
@media (max-width: 820px) {
  .masthead__nav { gap: 1.15rem; }
  /* $= not =, so these also match the gallery page's index.html#work form */
  .masthead__nav a[href$="#process"],
  .masthead__nav a[href$="#stack"],
  .masthead__nav a[href$="#answers"] { display: none; }
  .masthead__nav a[href$="#contact"] { color: var(--ink); border-bottom-color: var(--rule-2); }
}
@media (max-width: 400px) {
  .masthead__nav { gap: .9rem; }
  .masthead__nav a[href$="#build"] { display: none; }
}

/* ── shared type roles ──────────────────────────────────────── */
.kicker {
  font-family: var(--mono); font-size: var(--f-mono);
  font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-mute);
}
.kicker .num { color: var(--blue); margin-right: .5rem; }

.h2 {
  font-family: var(--display);
  font-size: var(--f-h2); font-weight: 700; font-stretch: 104%;
  line-height: 1.06; letter-spacing: -.02em;
  margin-top: .85rem; max-width: 22ch; text-wrap: balance;
}

/* The masthead is sticky, so an anchor jump would otherwise land the section's
   top edge underneath it, hiding the number and label you just clicked on.
   4rem sits just under the bar's own height, which tucks the section's top
   rule behind it: clearing the bar instead left that rule and the masthead's
   stacked a few pixels apart, reading as a mistake. */
main [id] { scroll-margin-top: 4rem; }
/* #main is the exception: it wants to land exactly at its top edge, which is
   the scroll position where the masthead pins and the intro has finished. */

.part { padding-block: var(--part); }
.part--rule { border-top: 1px solid var(--rule); }
.part__head { margin-bottom: clamp(2.5rem, 4.5vw, 4rem); }
.part__note { margin-top: 1.2rem; color: var(--ink-soft); max-width: 52ch; text-wrap: pretty; }

/* ── buttons and links ──────────────────────────────────────── */
.btn {
  display: inline-block;
  font-family: var(--display); font-weight: 600; font-size: .96rem;
  line-height: 1; padding: 1.05em 1.7em;
  background: var(--ink); color: var(--stone);
  border: 1px solid var(--ink);
  text-decoration: none; cursor: pointer;
  transition: background .18s, color .18s;
}
.btn:hover { background: transparent; color: var(--ink); }

.lnk {
  font-family: var(--display); font-weight: 600; font-size: .96rem;
  text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--rule-2); padding-bottom: .2rem;
  transition: border-color .18s;
}
.lnk:hover { border-bottom-color: var(--ink); }

.copy {
  padding: .32em .7em .28em;
  background: none; border: 1px solid var(--rule-2); border-radius: 2px;
  font-family: var(--mono); font-size: .68rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-mute); cursor: pointer;
  transition: color .18s, border-color .18s;
}
.copy:hover { color: var(--ink); border-color: var(--ink); }
.copy[data-done] { color: var(--blue); border-color: var(--blue); }

/* ── opening ────────────────────────────────────────────────── */
.opening { padding-block: clamp(3rem, 7vw, 6.5rem) clamp(3.5rem, 7vw, 6.5rem); }
.opening__in {
  display: grid;
  grid-template-columns: minmax(0,1fr) clamp(280px, 34vw, 440px);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: end;
}
@media (max-width: 900px) { .opening__in { grid-template-columns: 1fr; align-items: start; } }

.opening__h1 {
  font-family: var(--display);
  font-size: var(--f-h1); font-weight: 700; font-stretch: 104%;
  line-height: .98; letter-spacing: -.03em;
  margin-top: 1.1rem;
}
.standfirst {
  margin-top: 1.6rem; max-width: 44ch;
  font-size: clamp(1.12rem, .5vw + 1rem, 1.35rem);
  line-height: 1.5; color: var(--ink-soft); text-wrap: pretty;
}
.opening__cta { display: flex; flex-wrap: wrap; align-items: center; gap: 1.6rem; margin-top: 2.2rem; }

/* ── the opening's 3D moment: nine tiles finding their place ──
   This scene starts in view, so its --p can never begin near 0: you cannot
   scroll above the top of the page. --q remaps the reachable range onto
   0 to 1, so the tiles start scattered rather than half-assembled. */
/* The scroll intro now sits above the opening, so this scene gets the full
   0 to 1 range like every other one and no longer needs the top-of-page
   remap it had before. --q still shapes the window: scattered until 0.18,
   assembled by 0.60, which is well before it leaves the screen. */
.fit { align-self: end; --q: clamp(0, (var(--p, .5) - .18) / .42, 1); }
.fit__stage {
  overflow: hidden;
  perspective: 1200px; perspective-origin: 50% 50%;
  height: clamp(200px, 21vw, 280px);
  position: relative;
}
.fit__in {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(54deg) rotateZ(calc(-40deg + var(--q) * 6deg));
}
.tile {
  --unit: clamp(32px, 4vw, 58px);
  position: absolute; left: 50%; top: 50%;
  width: var(--unit); height: var(--unit);
  margin: calc(var(--unit) * -1.5) 0 0 calc(var(--unit) * -1.5);
  border: 1px solid var(--ink);
  background: rgba(230,228,222,.72);
  /* scattered and tilted at rest, locked into the grid as you scroll */
  transform:
    translate3d(
      calc(var(--c) * var(--unit) + (1 - var(--q)) * var(--x) * 1px),
      calc(var(--r) * var(--unit) + (1 - var(--q)) * var(--y) * 1px),
      calc((1 - var(--q)) * 46px))
    rotateZ(calc((1 - var(--q)) * var(--rz) * 1deg));
  opacity: calc(.45 + var(--q) * .55);
}
.tile--key {
  border: 2px solid var(--blue);
  background: rgba(34,56,155,.14);
  opacity: 1;
}
.fit__cap {
  margin-top: 1.2rem; max-width: 34ch;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mute); line-height: 1.9;
}
.fit__cap b { color: var(--ink); font-weight: 600; }

/* ── offer list (what we build / why) ───────────────────────── */
.offer { border-top: 1px solid var(--rule); }
.offer__row {
  display: grid; grid-template-columns: 15rem minmax(0,1fr);
  gap: 1rem clamp(1.5rem, 4vw, 4rem);
  padding-block: clamp(1.6rem, 2.6vw, 2.4rem);
  border-bottom: 1px solid var(--rule);
}
@media (max-width: 780px) { .offer__row { grid-template-columns: 1fr; gap: .6rem; } }

.offer__row dt {
  font-family: var(--display);
  font-size: var(--f-h3); font-weight: 700; font-stretch: 104%;
  letter-spacing: -.015em; line-height: 1.15;
}
/* Price sits under the service name, in the blue kept for figures. A buyer
   who cannot see a number cannot work out whether to call. */
.offer__row dt em {
  display: block; margin-top: .5rem;
  font-family: var(--mono); font-style: normal;
  font-size: var(--f-mono); font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--blue);
}
.offer__row dd p { color: var(--ink-soft); max-width: 62ch; text-wrap: pretty; }
.offer__tags {
  margin-top: .9rem;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-mute);
}
.offer--tight .offer__row { padding-block: clamp(1.3rem, 2vw, 1.9rem); }

/* ── work ───────────────────────────────────────────────────── */
.work { list-style: none; padding: 0; border-top: 1px solid var(--rule); }
.job { padding-block: clamp(2.2rem, 3.4vw, 3.2rem); border-bottom: 1px solid var(--rule); }

.job__meta {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .7rem 1.2rem;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-mute);
}
.job__fig { color: var(--blue); font-weight: 600; }
/* A filled chip, not an outline. This is the one label on the page that
   explains an absence (no client name, no screenshots), so it has to be
   read, not skimmed past. */
.nda {
  background: var(--ink); color: var(--stone);
  padding: .34em .7em .3em;
  font-weight: 600; letter-spacing: .16em;
  border-radius: 2px;
}

.job__h {
  font-family: var(--display);
  font-size: clamp(1.45rem, 2vw + .9rem, 2.15rem);
  font-weight: 700; font-stretch: 104%;
  line-height: 1.08; letter-spacing: -.024em;
  margin-top: .8rem; max-width: 24ch; text-wrap: balance;
}
.job__body {
  display: grid; gap: 1.2rem; margin-top: 1.2rem;
  max-width: 68ch;
}
.job__body > p { color: var(--ink-soft); text-wrap: pretty; }

.who { border-top: 1px solid var(--rule); }
.who > div {
  display: grid; grid-template-columns: 8.5rem minmax(0,1fr);
  gap: .3rem 1.5rem; padding-block: .65rem;
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}
@media (max-width: 620px) { .who > div { grid-template-columns: 1fr; } }
.who dt {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-mute);
}
.who dd { font-size: .97rem; color: var(--ink-soft); text-wrap: pretty; }

.pull {
  border-left: 2px solid var(--blue);
  padding-left: 1.3rem;
  font-size: clamp(1.06rem, .4vw + 1rem, 1.2rem);
  line-height: 1.5;
  text-wrap: pretty;
}

.facts { list-style: none; padding: 0; display: grid; gap: .35rem; }
.facts li {
  font-family: var(--mono); font-size: .79rem; line-height: 1.5;
  letter-spacing: .02em; color: var(--ink-soft);
  padding-left: 1.1rem; position: relative;
}
.facts li::before { content: ''; position: absolute; left: 0; top: .62em; width: .5rem; height: 1px; background: var(--rule-2); }

/* two links on one entry, so they need to sit apart rather than run together */
.job__see { margin-top: .2rem; display: flex; flex-wrap: wrap; gap: .5rem 1.6rem; }
.job__see a {
  font-family: var(--display); font-weight: 600; font-size: .93rem;
  text-decoration: none; border-bottom: 1px solid var(--rule-2);
  padding-bottom: .18rem; transition: border-color .18s;
}
.job__see a::after { content: ' →'; }
.job__see a:hover { border-bottom-color: var(--ink); }

.work__cta { margin-top: 2.5rem; color: var(--ink-soft); max-width: 60ch; }
.work__cta a { color: var(--blue); text-underline-offset: 4px; }

/* placeholder marker, impossible to publish by accident */
.fill { color: #9B2C10; border-bottom: 2px dashed rgba(155,44,16,.5); padding-inline: .1em; }

/* ── process ────────────────────────────────────────────────── */
.steps {
  list-style: none; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
@media (max-width: 920px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }
.step { padding-top: 1.4rem; border-top: 2px solid var(--ink); }
.step__n {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .12em; text-transform: uppercase; color: var(--blue);
}
.step__h {
  font-family: var(--display); font-size: var(--f-h3);
  font-weight: 700; font-stretch: 104%; letter-spacing: -.015em;
  margin-top: .55rem;
}
.step p { margin-top: .7rem; font-size: .97rem; color: var(--ink-soft); text-wrap: pretty; }

/* ── stack ──────────────────────────────────────────────────── */
.stack { border-top: 1px solid var(--rule); }
.stack > div {
  display: grid; grid-template-columns: 11rem minmax(0,1fr);
  gap: .3rem clamp(1.5rem, 4vw, 4rem);
  padding-block: 1rem;
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}
@media (max-width: 620px) { .stack > div { grid-template-columns: 1fr; } }
.stack dt {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-mute);
}
.stack dd { color: var(--ink-soft); text-wrap: pretty; }

/* ── contact ────────────────────────────────────────────────── */
.contact {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  gap: clamp(2.5rem, 6vw, 5.5rem); align-items: start;
}
@media (max-width: 880px) { .contact { grid-template-columns: 1fr; } }

.contact__p { margin-top: 1.4rem; color: var(--ink-soft); max-width: 44ch; text-wrap: pretty; }
.contact__alt { margin-top: 1.4rem; color: var(--ink-soft);
  /* flex so the copy button sits with the address, and lands flush left
     rather than half-indented when the line has to wrap */
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .55rem;
}
.contact__alt a { color: var(--blue); text-underline-offset: 4px; }

.assure { list-style: none; padding: 0; margin-top: 2rem; border-top: 1px solid var(--rule); max-width: 36ch; }
.assure li {
  padding-block: .7rem; border-bottom: 1px solid var(--rule);
  font-family: var(--mono); font-size: .78rem;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-mute);
}

.form { display: grid; gap: 1.4rem; border-top: 2px solid var(--ink); padding-top: 1.8rem; }
.field { display: grid; gap: .45rem; }
.field label {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-mute);
}
.field input, .field textarea {
  background: transparent; border: 0; border-bottom: 1px solid var(--rule-2);
  padding: .6rem 0; font-family: var(--serif); font-size: 1.05rem;
  transition: border-color .18s; resize: vertical;
}
.field input:focus, .field textarea:focus { outline: 0; border-bottom-color: var(--ink); }
.field input:focus-visible, .field textarea:focus-visible { outline: 0; }
.form .btn { justify-self: start; }
.form__note {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase; color: #9B2C10; min-height: 1.2em;
}

/* ── colophon ───────────────────────────────────────────────── */
.colophon { background: var(--ink); color: var(--stone); padding-block: clamp(2.8rem, 5vw, 4rem); }
.colophon__in { display: grid; gap: .6rem; }
.colophon__mark {
  font-family: var(--display); font-weight: 700; font-stretch: 108%;
  font-size: .95rem; letter-spacing: .16em; text-transform: uppercase;
}
.colophon__line { color: rgba(230,228,222,.78); font-size: .95rem; }
.colophon__line a { color: var(--stone); text-underline-offset: 4px; }
.colophon__note {
  margin-top: 1.4rem; max-width: 68ch;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .06em; line-height: 2.1;
  color: rgba(230,228,222,.68);
}
.colophon__note b { color: var(--stone); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── the gallery page ───────────────────────────────────────── */
.gal-top { padding-block: clamp(2.5rem, 5vw, 4.5rem) clamp(2.5rem, 5vw, 4rem); }
.gal-h1 {
  font-family: var(--display);
  font-size: clamp(2.3rem, 5vw, 3.8rem); font-weight: 700; font-stretch: 104%;
  line-height: 1.02; letter-spacing: -.028em; margin-top: .9rem;
}
.gal-lede { margin-top: 1.4rem; max-width: 54ch; color: var(--ink-soft); text-wrap: pretty; }
.gal-back { margin-top: 1.4rem; }
.gal-back a {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--blue); text-underline-offset: 5px;
}

.gal { padding-block: var(--part); border-top: 1px solid var(--rule); }
.gal__head { max-width: 60ch; margin-bottom: clamp(2rem, 3.5vw, 3rem); }
.gal__note { margin-top: 1.1rem; color: var(--ink-soft); text-wrap: pretty; }

.gal__grid {
  list-style: none; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
.gal__grid img { width: 100%; height: auto; border: 1px solid var(--rule-2); }
/* Landscape captures of a website, not portrait captures of an app. The phone
   view sits in the same grid, capped so one tall portrait cell cannot set the
   height of the whole row. */
.gal__grid--wide { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.gal__grid--wide .is-phone img { max-width: 240px; }
.gal__grid p { margin-top: .9rem; font-size: .9rem; line-height: 1.5; color: var(--ink-soft); text-wrap: pretty; }

.gal-cta { padding-block: var(--part); border-top: 1px solid var(--rule); }
.gal-cta__p { margin-top: 1.1rem; margin-bottom: 1.8rem; max-width: 44ch; color: var(--ink-soft); }

/* ── motion ─────────────────────────────────────────────────── */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

[data-rise] { animation: rise .7s var(--ease) both; }
[data-rise="1"] { animation-delay: .04s }
[data-rise="2"] { animation-delay: .10s }
[data-rise="3"] { animation-delay: .18s }
[data-rise="4"] { animation-delay: .26s }
[data-rise="5"] { animation-delay: .32s }

.js [data-reveal] { opacity: 0; transform: translateY(12px); }
[data-reveal] { transition: opacity .6s var(--ease), transform .6s var(--ease); }
.js [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
}

/* ══════════════════════════════════════════════════════════════
   SCENES
   Axonometric line-work in CSS 3D, driven by --p (0 to 1) which
   main.js writes as each [data-scene] crosses the viewport.
   Drawn as a technical illustration, not a product render: ink
   line-work on paper, no gloss, no gradients, no lighting.
   ══════════════════════════════════════════════════════════════ */

.exploded {
  padding-block: clamp(3.5rem, 7vw, 7rem);
  border-top: 1px solid var(--rule);
  overflow: hidden;
}
.exploded__in {
  display: grid;
  grid-template-columns: minmax(0, 27rem) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (max-width: 900px) { .exploded__in { grid-template-columns: 1fr; } }
.exploded__note { margin-top: 1.2rem; color: var(--ink-soft); text-wrap: pretty; }

/* the stage: one perspective, one rotated world inside it */
.stage {
  max-width: 100%;
  overflow: hidden;      /* the 3D world can reach past its box on narrow screens */
  perspective: 1500px;
  perspective-origin: 50% 45%;
  height: clamp(300px, 34vw, 440px);
  position: relative;
}
.stage__in {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  /* settles from a steep plan view into a low axonometric as you scroll */
  transform:
    rotateX(calc(64deg - var(--p, .5) * 16deg))
    rotateZ(calc(-44deg + var(--p, .5) * 12deg));
  transition: transform .1s linear;
}

.pl {
  position: absolute;
  left: 50%; top: 50%;
  width: clamp(190px, 21vw, 280px);
  aspect-ratio: 1 / 1;
  margin: 0 0 0 clamp(-140px, -10.5vw, -95px);
  translate: 0 -50%;
  transform-style: preserve-3d;
  /* stacked flat at rest, pulled apart as --p climbs */
  transform: translateZ(calc((1.5 - var(--i)) * var(--p, .5) * 108px));
  border: 1px solid var(--ink);
  background:
    repeating-linear-gradient(to right, rgba(23,23,27,.07) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(to bottom, rgba(23,23,27,.07) 0 1px, transparent 1px 26px),
    rgba(230,228,222,.82);
}
.pl b, .pl i {
  position: absolute; left: .75rem;
  font-family: var(--mono); font-style: normal;
  letter-spacing: .13em; text-transform: uppercase;
  white-space: nowrap;
}
.pl b { bottom: 1.55rem; font-size: .72rem; font-weight: 600; }
.pl i { bottom: .7rem; font-size: .63rem; color: var(--ink-mute); }

/* the deepest layer is the one everyone else skips: mark it */
.pl:last-child { border-color: var(--blue); border-width: 2px; }
.pl:last-child b { color: var(--blue); }

/* ── project scenes ─────────────────────────────────────────── */
.job {
  display: grid;
  grid-template-columns: minmax(0,1fr) clamp(300px, 30vw, 430px);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: start;
}
@media (max-width: 900px) { .job { grid-template-columns: 1fr; } }

.scene { position: sticky; top: 14vh; overflow: hidden; }
@media (max-width: 900px) {
  .scene { position: static; margin-top: 1.5rem; }
  .scene .stage { height: clamp(230px, 58vw, 300px); }
}
.scene__cap {
  margin-top: .9rem;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-mute); text-align: center;
}

.scene .stage { height: clamp(250px, 26vw, 340px); }

/* real screens, fanned in perspective rather than laid flat */
.stage--fan { perspective: 1100px; perspective-origin: 50% 50%; }
.stage--fan .stage__in {
  display: flex; align-items: center; justify-content: center;
  transform: rotateY(calc(-16deg + var(--p, .5) * 22deg));
}
.dev {
  position: absolute;
  width: clamp(108px, 11.5vw, 148px);
  transform:
    translateX(calc(var(--i) * (52px + var(--p, .5) * 92px)))
    translateZ(calc(var(--i) * var(--i) * -78px))
    rotateY(calc(var(--i) * (6deg + var(--p, .5) * 16deg)));
  transition: transform .1s linear;
}
/* A website is shown in landscape, at the shape it is really used in, rather
   than squeezed into a phone frame. It rides the same fan stage as the device
   scenes so it turns with them and belongs to the same world. */
.site {
  width: clamp(240px, 27vw, 360px);
  margin: 0;
  border: 1px solid var(--ink);
  background: var(--stone);
  box-shadow: 0 18px 34px -22px rgba(23,23,27,.55);
}
.site figcaption {
  padding: .4em .7em .34em;
  border-bottom: 1px solid var(--ink);
  font-family: var(--mono); font-size: .58rem; font-weight: 600;
  letter-spacing: .12em; color: var(--ink-mute);
}
.site img { width: 100%; height: auto; display: block; }

.dev img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--ink);
  box-shadow: 0 18px 34px -22px rgba(23,23,27,.55);
}

/* ══════════════════════════════════════════════════════════════
   DATA GRAPHICS
   Three different languages on purpose: a bar chart for durations,
   a gantt for where the process falls, a unit chart for test counts.
   All drawn from figures verified in the repositories, all authored
   in CSS so they cost nothing and stay sharp at any size.
   ══════════════════════════════════════════════════════════════ */

.chart { margin: 0 0 clamp(2.5rem, 5vw, 4rem); max-width: 46rem; }
.chart__cap, .chart__foot {
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .11em; text-transform: uppercase; color: var(--ink-mute);
}
.chart__rows { margin-block: 1.1rem; border-top: 1px solid var(--rule); }
.chart__foot { text-transform: none; letter-spacing: .04em; line-height: 1.7; }

.bar {
  display: grid;
  grid-template-columns: 12.5rem minmax(0,1fr) 4.2rem;
  gap: 1rem; align-items: center;
  padding-block: .6rem;
  border-bottom: 1px solid var(--rule);
}
@media (max-width: 620px) { .bar { grid-template-columns: 6.6rem minmax(0,1fr) 3.6rem; gap: .55rem; } }
.bar__k { font-size: .92rem; color: var(--ink-soft); }
/* The price rides in the label column rather than taking a fourth one, which
   would have squeezed the bar itself to nothing on a phone. */
.bar__k em {
  display: block; margin-top: .15rem;
  font-family: var(--mono); font-style: normal;
  font-size: .72rem; font-weight: 600; letter-spacing: .02em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.bar__t { display: block; height: 10px; background: rgba(23,23,27,.07); }
.bar__t i {
  display: block; height: 100%;
  width: calc(var(--b) * 1% * min(1, var(--p, .5) * 2));
  background:
    linear-gradient(to right,
      var(--ink) 0 calc(var(--a) / var(--b) * 100%),
      rgba(23,23,27,.30) calc(var(--a) / var(--b) * 100%) 100%);
}
.bar--wide .bar__t i {
  background:
    linear-gradient(to right,
      var(--blue) 0 calc(var(--a) / var(--b) * 100%),
      rgba(34,56,155,.30) calc(var(--a) / var(--b) * 100%) 100%);
}
.chart__scale {
  display: flex; justify-content: space-between;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-mute);
  padding-top: .9rem;
}
.bar__v {
  font-family: var(--mono); font-size: .82rem; font-weight: 600;
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* gantt: where the four steps actually fall. The bars carry numbers only:
   the names live once, in the cards below, and the numbers tie the two. */
.gantt { margin: 0 0 clamp(2.5rem, 5vw, 4rem); max-width: 52rem; }
.gantt__scale {
  display: flex; justify-content: space-between;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .11em; text-transform: uppercase; color: var(--ink-mute);
  padding-bottom: .5rem; border-bottom: 1px solid var(--rule);
}
.gantt__rows { display: grid; gap: .5rem; margin-top: .9rem; }
.gbar { position: relative; height: 1.7rem; }
.gbar i {
  position: absolute; top: 0; bottom: 0;
  left: calc(var(--s) * 1%);
  width: calc((var(--e) - var(--s)) * 1% * min(1, var(--p, .5) * 2));
  min-width: 3px;
  background: rgba(23,23,27,.13);
  border-left: 2px solid var(--ink);
}
.gbar--long i { background: rgba(34,56,155,.13); border-left-color: var(--blue); }
.gbar b {
  position: absolute; top: 50%; translate: 0 -50%;
  left: calc(var(--s) * 1% + .55rem);
  font-family: var(--mono); font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; color: var(--ink-mute);
}
/* the closing step sits hard right, so its number hangs to the left of it */
.gbar:last-child b { left: auto; right: calc(100% - var(--s) * 1% + .55rem); }
@media (max-width: 620px) { .gantt__scale { font-size: .58rem; letter-spacing: .04em; } }

/* ── unit chart: one mark per automated check ────────────────
   25 marks to a row, so the three columns read as comparable bars while
   every mark stays countable. --rows is the full rows, --rem the exact
   number left over in the partial top row. Nothing is rounded. */
.checks { margin: 0 0 clamp(2.5rem, 5vw, 4rem); }
.checks__lede {
  font-size: clamp(1.1rem, .6vw + 1rem, 1.35rem);
  max-width: 40ch; text-wrap: pretty;
}
.checks__lede b {
  font-family: var(--mono); font-weight: 600;
  color: var(--blue); font-variant-numeric: tabular-nums;
}
.checks__foot {
  margin-top: 1.6rem; max-width: 58ch;
  color: var(--ink-soft); text-wrap: pretty;
}

.tally {
  display: flex; align-items: flex-end;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin-top: clamp(1.6rem, 3vw, 2.4rem);
  flex-wrap: wrap;
}
.tally__col { --cell: clamp(4px, .48vw, 7px); }

.tally__marks {
  display: flex; flex-direction: column; align-items: flex-start;
  width: calc(25 * var(--cell));
  /* fills from the bottom as the section comes past, like a suite running */
  clip-path: inset(calc(100% - min(100%, var(--p, .5) * 170%)) 0 0 0);
}
/* the partial top row: exactly --rem marks wide */
.tally__marks u {
  display: block; order: -1;
  width: calc(var(--rem) * var(--cell));
  height: calc(var(--rem) * 1px * 999);
  max-height: var(--cell);
  background-image: radial-gradient(circle at 50% 50%, var(--ink) 0 26%, transparent 27%);
  background-size: var(--cell) var(--cell);
}
.tally__marks i {
  display: block;
  width: 100%;
  height: calc(var(--rows) * var(--cell));
  background-image: radial-gradient(circle at 50% 50%, var(--ink) 0 26%, transparent 27%);
  background-size: var(--cell) var(--cell);
}
.tally__col:nth-child(2) .tally__marks i,
.tally__col:nth-child(2) .tally__marks u {
  background-image: radial-gradient(circle at 50% 50%, var(--blue) 0 26%, transparent 27%);
}

.tally__k {
  margin-top: .9rem;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mute); line-height: 1.8;
  max-width: calc(25 * var(--cell) + 2rem);
}
.tally__k b {
  display: block; margin-bottom: .15rem;
  font-size: 1.35rem; letter-spacing: -.02em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.tally__col:nth-child(2) .tally__k b { color: var(--blue); }
.tally__k i { display: block; font-style: normal; opacity: .8; }

/* ── hub and spoke: four products off one core ─────────────── */
.stage--tilt .stage__in {
  transform:
    rotateX(calc(36deg - var(--p, .5) * 20deg))
    rotateZ(calc(-15deg + var(--p, .5) * 12deg));
}
.hub { position: absolute; inset: 0; transform-style: preserve-3d; }
.hub__core {
  position: absolute; left: 50%; top: 50%;
  width: 5.4rem; height: 5.4rem; margin: -2.7rem 0 0 -2.7rem;
  display: grid; place-items: center;
  border: 2px solid var(--blue);
  background: rgba(230,228,222,.9);
}
.hub__core b {
  font-family: var(--mono); font-size: .62rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--blue); text-align: center; line-height: 1.5;
}
.spoke {
  position: absolute; left: 50%; top: 50%;
  width: calc(88px + var(--p, .5) * 104px); height: 0;
  transform: rotate(calc(var(--a) * 1deg));
  transform-origin: 0 0;
}
.spoke i { display: block; height: 1px; width: 100%; background: var(--rule-2); }
.spoke b {
  position: absolute; left: 100%; top: 0;
  padding-left: .7rem;
  transform-origin: 0 50%;
  transform: translateY(-50%) rotate(calc(var(--a) * -1deg));
  font-family: var(--mono); font-size: .6rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap; color: var(--ink-soft);
}
/* Left-pointing spokes keep their label at the far end of the arm, then slide
   it back by its own width. The shift happens after the counter-rotation, so
   it travels along the world horizontal rather than along the arm. */
.spoke--l b {
  padding-left: 0; padding-right: .7rem;
  transform: translateY(-50%) rotate(calc(var(--a) * -1deg)) translateX(-100%);
}
.spoke b::before {
  content: ''; position: absolute; left: 0; top: 50%;
  width: 5px; height: 5px; translate: -2.5px -50%;
  background: var(--ink);
}
/* must follow the base rule: same specificity, so order decides */
.spoke--l b::before { left: auto; right: 0; translate: 2.5px -50%; }

/* ── concentric: clean architecture drawn as rings ─────────── */
.rings { position: absolute; inset: 0; transform-style: preserve-3d; }
.ring {
  position: absolute; left: 50%; top: 50%;
  width: calc(4.6rem + var(--i) * 4rem);
  aspect-ratio: 1;
  margin: 0; translate: -50% -50%;
  border: 1px solid var(--ink);
  transform: translateZ(calc(var(--i) * var(--p, .5) * 96px));
  background: rgba(230,228,222,.55);
}
.ring b {
  position: absolute; left: .45rem; top: .35rem;
  font-family: var(--mono); font-size: .62rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap; color: var(--ink-mute);
}
.ring--core { border: 2px solid var(--blue); background: rgba(34,56,155,.16); }
.ring--core b { color: var(--blue); font-weight: 600; }

/* ── flow: a path receding into depth, one node per stage ──── */
.stage--depth { perspective: 900px; }
.stage--depth .stage__in {
  transform: rotateY(calc(28deg - var(--p, .5) * 16deg)) rotateX(6deg);
}
.flow { position: absolute; inset: 0; transform-style: preserve-3d; }
.node {
  position: absolute; left: 50%; top: 50%;
  transform:
    translateX(calc((var(--i) - 2) * (48px + var(--p, .5) * 40px) - 30px))
    translateY(calc((var(--i) - 2) * (26px + var(--p, .5) * 14px)))
    translateZ(calc((2 - var(--i)) * var(--p, .5) * 82px));
}
.node i {
  display: block; width: 13px; height: 13px;
  border: 1px solid var(--ink); background: var(--stone);
  translate: -50% -50%;
}
.node:first-child i { border-width: 2px; border-color: var(--blue); }
.node b {
  position: absolute; left: 0; top: 0;
  padding-left: .85rem; translate: 0 -50%;
  font-family: var(--mono); font-size: .6rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap; color: var(--ink-soft);
}
.node::after {
  content: ''; position: absolute; left: 0; top: 0;
  width: calc(48px + var(--p, .5) * 40px); height: 1px;
  background: var(--rule-2);
  transform-origin: 0 50%;
  transform: rotate(28deg);
}
.node:last-child::after { display: none; }
/* The node transforms are centred on --i 2, which is the middle of a five-step
   flow. A three-step flow centred the same way hangs up and to the left with
   its last node in the middle of the stage. Re-centre on --i 1 rather than
   respacing with --i 0/2/4, which would double the gap and leave the
   connectors reaching halfway to the next node. */
.flow--3 .node {
  transform:
    translateX(calc((var(--i) - 1) * (48px + var(--p, .5) * 40px) - 30px))
    translateY(calc((var(--i) - 1) * (26px + var(--p, .5) * 14px)))
    translateZ(calc((1 - var(--i)) * var(--p, .5) * 82px));
}

/* ── parallax: process and stack ─────────────────────────────
   Differential drift, not a slideshow. Each column and row moves
   at a slightly different rate as its section crosses the viewport,
   so the grid shears gently and settles level at the midpoint.
   Transform only, so it costs nothing and shifts no layout. */
.steps[data-scene] .step {
  transform: translateY(calc((var(--n, 0) - 1.5) * (var(--p, .5) - .5) * -30px));
  will-change: transform;
}
.stack[data-scene] > div {
  transform: translateY(calc((var(--n, 0) - 3) * (var(--p, .5) - .5) * -14px));
}
/* the label column lags its values a touch, which is what makes it read
   as depth rather than as the whole row sliding */
.stack[data-scene] > div dt {
  display: block;
  transform: translateX(calc((var(--p, .5) - .5) * -10px));
}

@media (prefers-reduced-motion: reduce) {
  .steps[data-scene] .step,
  .stack[data-scene] > div,
  .stack[data-scene] > div dt { transform: none; }
}

/* parallax on the offer rows and the work entries, same differential drift */
.offer[data-scene] .offer__row {
  transform: translateY(calc((var(--n, 0) - 1.5) * (var(--p, .5) - .5) * -18px));
}
.work[data-scene] .job {
  transform: translateY(calc((var(--n, 0) - 2) * (var(--p, .5) - .5) * -12px));
}
@media (prefers-reduced-motion: reduce) {
  .offer[data-scene] .offer__row,
  .work[data-scene] .job { transform: none; }
}

/* On a narrow screen three columns will not sit side by side without
   shrinking the marks past counting. Stack them instead, marks beside
   their label, so the blocks stay the same width and the heights still
   compare down the page. */
@media (max-width: 760px) {
  .tally { flex-direction: column; align-items: stretch; gap: 1.4rem; }
  .tally__col { --cell: 5px; display: flex; align-items: flex-end; gap: 1.1rem; }
  .tally__marks { flex: none; }
  .tally__k { margin-top: 0; max-width: none; }
  .tally__k b { font-size: 1.25rem; }
}

/* ══════════════════════════════════════════════════════════════
   SCROLL INTRO
   Built to the spec in the logo pack: a pinned dark stage, the mark
   drawn stroke by stroke, then docked into the real masthead slot.
   The stage is display:none until intro.js adds .has-intro, so a
   JavaScript failure costs a visitor nothing.
   ══════════════════════════════════════════════════════════════ */
.intro { display: none; }
/* The spec's 480vh left a blank screen once the mark had docked, and a pinned
   intro always ends with the pinned screen: the site is a whole viewport below.
   So the stage is pulled up by a screen and stacked above the page. Progress
   reaches 1 exactly when the site's own masthead is at the top of the viewport,
   directly behind the frame, and the frame is hidden at that instant. The
   docked mark and the real one are the same pixels, so the swap is invisible. */
.has-intro .intro {
  display: block; position: relative; z-index: 30;
  height: 200vh; margin-bottom: -100vh;
  /* The stage is 200vh of nothing stacked above the masthead, so without this
     it silently swallows every click on the nav for the first two screens:
     the links looked fine and did nothing. It is aria-hidden with nothing
     interactive inside, so it should never be a click target at any point.
     This lives here rather than in the frame loop on purpose. It was set per
     frame in JS, and a throw inside that loop's try/catch meant it never
     actually applied. Presentation that a page depends on to stay usable
     belongs in the stylesheet, where no exception can skip it. */
  pointer-events: none;
}
.has-intro .masthead { z-index: 20; }
.has-intro main { position: relative; z-index: 5; background: var(--stone); }

.intro__frame {
  position: sticky; top: 0;
  height: 100vh; overflow: hidden;
  background: var(--ink); color: var(--stone);
  --hair: #3A3A36;
  --mute: #8A8A82;
}
/* a hard cut, never a colour lerp: a mid-state grey makes the mark vanish */
.intro__frame.is-light {
  background: var(--stone); color: var(--ink);
  --hair: #DCDCD6;
  --mute: var(--ink-mute);
}
/* the rule the real masthead carries, so the join is invisible */
.intro__frame::after {
  content: ''; position: absolute; left: 0; right: 0;
  top: var(--rule-top, 68px); height: 1px;
  background: var(--rule);
  opacity: var(--rule, 0);
}

.intro__hero {
  /* the spec puts this at 6vh, but that was before a scroll cue sat under it */
  position: absolute; left: 0; right: 0; bottom: 15vh;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  transform-origin: 50% 100%;
  pointer-events: none;
}
.intro__hero b {
  font-family: var(--display);
  font-weight: 800; font-stretch: 104%;
  font-size: clamp(1.55rem, 4.1vw, 3.05rem);
  line-height: 1; letter-spacing: -.045em; white-space: nowrap;
}
.intro__hero i {
  font-family: var(--mono); font-style: normal;
  font-size: .7rem; letter-spacing: .26em;
  text-transform: uppercase; color: var(--mute);
}
/* The proposition, on the one screen that used to carry only a name. Set in
   the prose face and given its own space above the scroll cue, so somebody
   who lands from a search learns what we sell before deciding to leave. */
.intro__hero s {
  text-decoration: none;
  margin-top: 6px; max-width: 30ch; text-align: center;
  font-family: var(--serif);
  font-size: clamp(.92rem, .5vw + .8rem, 1.08rem);
  /* inherit, never a fixed colour: the frame inverts to paper part way
     through and a hardcoded light value would vanish into it */
  line-height: 1.45; color: inherit; opacity: .84;
  text-wrap: balance;
}

/* People do not always know a page wants scrolling, least of all when the
   first thing they meet is a mark being drawn. */
.intro__cue {
  position: absolute; left: 0; right: 0; bottom: 30px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  font-family: var(--mono); font-size: .68rem;
  letter-spacing: .26em; text-transform: uppercase; color: var(--mute);
}
.intro__cue i {
  display: block; width: 1px; height: 40px;
  background: var(--hair); position: relative; overflow: hidden;
}
.intro__cue i::after {
  content: ''; position: absolute; left: 0; width: 1px; height: 14px;
  background: currentColor;
  animation: cue 1.9s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes cue {
  0%   { top: -14px; opacity: 0; }
  30%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { top: 40px; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .intro__cue i::after { animation: none; top: 13px; } }

/* This lockup has to be typographically identical to the masthead's, not just
   close: the mark docks pixel-exact, so any difference in the gap or the type
   shows up as a doubled wordmark during the crossfade. The spec's 15px gap was
   wrong for this site. */
.intro__lockup {
  position: absolute; left: 0; top: 0;
  display: flex; align-items: center; gap: .62rem;
  will-change: transform;
}
.intro__mark {
  width: 30px; height: 36px; flex: none;
  transform-origin: 0 0; overflow: visible;
  /* first paint before the script runs, so nothing flashes in the corner */
  transform: translate(calc(50vw - 243px), calc(50vh - 256px)) scale(13);
}
.intro__cross { opacity: 0; }

.intro__type { display: grid; gap: .1rem; opacity: var(--lock, 0); }
.intro__type b {
  font-family: var(--display); font-weight: 800; font-stretch: 104%;
  font-size: .93rem; line-height: 1; letter-spacing: -.045em;
}
.intro__type i {
  font-family: var(--mono); font-style: normal;
  font-size: .5rem; line-height: 1;
  letter-spacing: .26em; text-transform: uppercase; color: var(--mute);
}

.intro__bracket {
  position: absolute; width: 24px; height: 24px;
  border: 0 solid var(--hair);
  opacity: var(--furniture, 1);
}
.intro__bracket--tl { top: 28px;  left: 28px;  border-top-width: 1px; border-left-width: 1px;
  translate: calc(var(--drift, 0px) * -1) calc(var(--drift, 0px) * -1); }
.intro__bracket--tr { top: 28px;  right: 28px; border-top-width: 1px; border-right-width: 1px;
  translate: var(--drift, 0px) calc(var(--drift, 0px) * -1); }
.intro__bracket--bl { bottom: 28px; left: 28px; border-bottom-width: 1px; border-left-width: 1px;
  translate: calc(var(--drift, 0px) * -1) var(--drift, 0px); }
.intro__bracket--br { bottom: 28px; right: 28px; border-bottom-width: 1px; border-right-width: 1px;
  translate: var(--drift, 0px) var(--drift, 0px); }

@media (max-width: 700px) {
  .intro__hero {
  /* the spec puts this at 6vh, but that was before a scroll cue sat under it */
  position: absolute; left: 0; right: 0; bottom: 15vh;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  transform-origin: 50% 100%;
  pointer-events: none;
}
.intro__hero b {
  font-family: var(--display);
  font-weight: 800; font-stretch: 104%;
  font-size: clamp(1.55rem, 4.1vw, 3.05rem);
  line-height: 1; letter-spacing: -.045em; white-space: nowrap;
}
.intro__hero i {
  font-family: var(--mono); font-style: normal;
  font-size: .7rem; letter-spacing: .26em;
  text-transform: uppercase; color: var(--mute);
}

/* People do not always know a page wants scrolling, least of all when the
   first thing they meet is a mark being drawn. */
.intro__cue {
  position: absolute; left: 0; right: 0; bottom: 30px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  font-family: var(--mono); font-size: .68rem;
  letter-spacing: .26em; text-transform: uppercase; color: var(--mute);
}
.intro__cue i {
  display: block; width: 1px; height: 40px;
  background: var(--hair); position: relative; overflow: hidden;
}
.intro__cue i::after {
  content: ''; position: absolute; left: 0; width: 1px; height: 14px;
  background: currentColor;
  animation: cue 1.9s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes cue {
  0%   { top: -14px; opacity: 0; }
  30%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { top: 40px; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .intro__cue i::after { animation: none; top: 13px; } }

  /* the lockup's position comes entirely from the tracking transform now */
  .intro__bracket { width: 16px; height: 16px; }
}

/* ── section hierarchy ───────────────────────────────────────
   What we build and Selected work are what a prospect came for.
   They get the larger heading and the room; process, why and
   stack are supporting evidence and are set down a level so they
   do not compete for the same attention. */
.part--major { padding-block: clamp(5rem, 9vw, 9rem); }
.part--major .h2 {
  font-size: clamp(2.4rem, 4.6vw, 4rem);
  letter-spacing: -.028em; max-width: 18ch;
}
.part--major .part__head { margin-bottom: clamp(3rem, 5.5vw, 5rem); }

.part--minor { padding-block: clamp(3.25rem, 5.5vw, 5.5rem); }
.part--minor .h2 { font-size: clamp(1.6rem, 2.4vw, 2.15rem); max-width: 26ch; }
.part--minor .part__head { margin-bottom: clamp(2rem, 3.5vw, 3rem); }

/* the offer is the shopfront: give each line real presence */
.part--major .offer__row { padding-block: clamp(2rem, 3.2vw, 3rem); }
.part--major .offer__row dt {
  font-size: clamp(1.45rem, 1.6vw + 1rem, 2.05rem);
  letter-spacing: -.022em;
}
.part--major .offer__row dd p { font-size: 1.06rem; }

/* and each project a headline that carries */
.part--major .job__h {
  font-size: clamp(1.6rem, 2.3vw + .9rem, 2.5rem);
  max-width: 22ch;
}
.part--major .job { padding-block: clamp(2.6rem, 4vw, 3.75rem); }

/* the chart now closes the section rather than delaying it */
.chart { margin: clamp(2.5rem, 5vw, 4rem) 0 0; }

/* who this is for: a reader who cannot place themselves assumes they are wrong */
.qualifier {
  margin-top: 1.5rem; max-width: 40ch;
  font-family: var(--mono); font-size: var(--f-mono);
  letter-spacing: .09em; text-transform: uppercase;
  line-height: 1.9; color: var(--ink-mute);
}
#answers .offer__row dt { font-size: var(--f-h3); }

/* a lone screen is not a fan: give it the room three would have shared */
.scene--devices .dev:only-child {
  width: clamp(150px, 16vw, 205px);
  transform: rotateY(-6deg);
}
