/* ============================== fonts ==============================
   Self-hosted, latin subset only, straight from the Google Fonts CDN files.
   No third-party connection on the critical path any more.

   THESE MUST STAY VARIABLE FONTS. The site sets font-stretch between 104%
   and 114% at fourteen call sites and animates font-variation-settings
   'wdth' 96 -> 114 in the settle keyframe. Swap in static weight instances
   and every headline silently flattens to one width and the accent word
   stops moving. The wdth axis is load-bearing, not decoration.

   Instrument Sans italic is deliberately absent — .it is the only italic on
   the site and it is always Archivo. Nothing renders in italic body text. */
@font-face{
  font-family:'Archivo';
  src:url('/assets/fonts/archivo-normal.woff2') format('woff2');
  font-weight:400 900;
  font-style:normal;
  font-stretch:75% 125%;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Archivo';
  src:url('/assets/fonts/archivo-italic.woff2') format('woff2');
  font-weight:400 900;
  font-style:italic;
  font-stretch:75% 125%;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Instrument Sans';
  src:url('/assets/fonts/instrument-sans-normal.woff2') format('woff2');
  font-weight:400 700;
  font-style:normal;
  font-stretch:75% 100%;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  src:url('/assets/fonts/ibm-plex-mono-normal-400.woff2') format('woff2');
  font-weight:400;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  src:url('/assets/fonts/ibm-plex-mono-normal-500.woff2') format('woff2');
  font-weight:500;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  src:url('/assets/fonts/ibm-plex-mono-normal-600.woff2') format('woff2');
  font-weight:600;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   V2B Agency — Vision to Business
   Palette   ink / bone / card + one accent: ultramarine
   Type      Archivo (display, italic accent word) · Instrument Sans (body)
             · IBM Plex Mono (labels and data)
   Signature one italic accent word per headline
   ========================================================================== */

:root{
  --ink:        #0B0D12;
  --graphite:   #14181F;
  --ink-line:   rgba(242,239,233,.13);
  --bone:       #F2EFE9;
  --card:       #E7E1D6;
  --paper:      #FBFAF7;
  --klein:      #2B4BFF;
  --klein-deep: #1D34C4;
  --klein-lt:   #6E9CFF;   /* accent on dark surfaces */
  --klein-pale: #D2DBFF;   /* accent on the ultramarine band */
  --ash:        #575D67;
  --ash-d:      #949AA6;
  --line:       rgba(11,13,18,.13);
  --line-soft:  rgba(11,13,18,.07);   /* the whisper rule — between work rows */
  --surface:    var(--bone);   /* the surface the current band paints */

  --f-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --f-body:    'Instrument Sans', 'Segoe UI', system-ui, -apple-system, sans-serif;
  --f-mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --wrap: 1200px;
  --gut:  clamp(20px, 4vw, 40px);
  --nav-h: 68px;

  /* radius scale — small relative to the element, never a pill. Four steps plus
     a thumbnail size, ported from the original site: boxy, not sharp. */
  --r-xs: 4px;    /* badges, chips, nested plates */
  --r-sm: 6px;    /* buttons, inputs, small controls, icon plates */
  --r-md: 8px;    /* thumbnails and image frames */
  --r:    12px;   /* cards, media panels */
  --r-lg: 16px;   /* the big coloured surfaces */
  --ease: cubic-bezier(.2,.7,.2,1);
}

/* ------------------------------- reset ---------------------------------- */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body{
  margin:0; background:var(--bone); color:var(--ink);
  font-family:var(--f-body); font-size:17px; line-height:1.62;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
img,svg{ max-width:100%; }
img{ display:block; height:auto; }
h1,h2,h3,h4,p,ul,ol,dl,dd,figure,fieldset,legend,blockquote{ margin:0; padding:0; }
ul,ol{ list-style:none; }
a{ color:inherit; text-decoration:none; }
button,input,select,textarea{ font:inherit; color:inherit; }
:focus-visible{ outline:2px solid var(--klein); outline-offset:3px; border-radius:2px; }
.band--ink :focus-visible,.band--klein :focus-visible,.hero :focus-visible,
.phead :focus-visible,.nav :focus-visible,.drawer :focus-visible,
.foot :focus-visible,.panel :focus-visible,.sticky-cta :focus-visible{ outline-color:var(--bone); }

.skip{
  position:absolute; left:16px; top:-70px; z-index:200;
  background:var(--klein); color:#fff; padding:0 18px; border-radius:var(--r-sm);
  font:500 14px/1 var(--f-mono); transition:top .2s;
  min-height:44px; display:inline-flex; align-items:center;
}
.skip:focus{ top:16px; }

/* ------------------------------ primitives ------------------------------- */
.wrap{ width:100%; max-width:var(--wrap); margin-inline:auto; padding-inline:var(--gut); }

/* Every band publishes the surface it paints, so anything that has to blend
   into its own background (the process dot's halo) follows the band instead of
   assuming bone. */
.band{ padding-block:clamp(68px, 8.5vw, 132px); scroll-margin-top:calc(var(--nav-h) + 14px); }
.band--bone { background:var(--bone);  --surface:var(--bone); }
.band--card { background:var(--card);  --surface:var(--card); }
.band--ink  { background:var(--ink);   color:var(--bone); --surface:var(--ink); }
.band--klein{ background:var(--klein); color:#fff;        --surface:var(--klein); }

/* the signature: one italic accent word per headline */
.it{ font-style:italic; color:var(--klein); }
.hero .it,.phead .it,.band--ink .it,.foot .it{ color:var(--klein-lt); }
.band--klein .it,.panel .it{ color:var(--klein-pale); }

/* The one thing that moves on purpose. The headline claims the design moves;
   Archivo is already loaded with its full width axis, so the accent word
   arrives a little condensed and opens to its set width as line 2 lands.
   .lift:nth-child(2) starts at .15s and runs .95s, so the line arrives at
   1.10s — starting at .7s makes this one continuous motion, not two events.
   96 -> 114, not lower: animating width reflows the line, and a bigger step
   shoves ", not" sideways far enough to read as a glitch. Hero only. */
@keyframes settle{
  from{ font-variation-settings:'wdth' 96; }
  to  { font-variation-settings:'wdth' 114; }
}
.hero .it{ animation:settle 1s var(--ease) .7s backwards; }

.eyebrow{
  font:500 12px/1 var(--f-mono);
  letter-spacing:.17em; text-transform:uppercase; color:var(--klein);
  display:flex; align-items:center; gap:10px;
}
/* The house marker: a 6px diamond, the same glyph the ticker sets between
   disciplines. It replaces a 22px rule — the line read as a generic editorial
   kicker, the diamond is ours.

   **Section eyebrows only.** .case__kick used to carry it too, and in the work
   band that put two identical klein-mono-uppercase-with-a-diamond lines within
   one screen of each other — the section's own eyebrow, then the first case's.
   Two markers of the same rank reading as a list of one thing. The kick is a
   plain label now; the diamond marks the section, and nothing inside it.

   It catches the light. Not a pulse — a pulse is a heartbeat and the eye locks
   onto it — but a long hold with one brief lift, the way a facet catches a
   room as you move past it. Nine seconds, and it is bright for well under one
   of them; opacity only, between .55 and 1, so nothing moves or resizes and
   the line of text never shifts. Every marker on the page runs the same clock,
   which reads as one deliberate effect rather than several independent ones. */
.eyebrow::before{
  content:''; width:6px; height:6px; flex:none;
  background:currentColor; transform:rotate(45deg);
  animation:facet 9s ease-in-out infinite;
}
@keyframes facet{
  0%, 78%, 100%{ opacity:.55; }
  86%          { opacity:1;   }
}
.eyebrow--on-ink{ color:var(--klein-lt); }
.eyebrow--on-klein{ color:rgba(255,255,255,.88); }

.h2{
  font-family:var(--f-display); font-weight:800; font-stretch:112%;
  font-size:clamp(2.05rem, 4.5vw, 3.4rem);
  line-height:.98; letter-spacing:-.03em; margin-top:22px;
}
.h3{
  font-family:var(--f-display); font-weight:700; font-stretch:106%;
  font-size:clamp(1.2rem, 2vw, 1.5rem); line-height:1.1; letter-spacing:-.026em;
}
.copy{ margin-top:14px; color:var(--ash); max-width:56ch; }
.band--ink .copy,.band--klein .copy{ color:var(--ash-d); }


/* Line breaking. Two different problems, two different keywords.

   Headings get `balance`: a display face at 2rem+ on a 335px phone fits three
   or four words a line, so the browser's greedy fill regularly strands the
   last word alone — "Four stages. No / mystery in / between." Balance evens
   the lines instead, and the ragged edge stops looking like a mistake.

   Body copy gets `pretty`, not `balance` — balance is for a few lines, and on
   a six-line FAQ answer it would rework the whole paragraph. `pretty` touches
   only the end, which is where the widow is.

   The hero is exempt. Its lockup is set by hand and the wrap points are the
   design; letting the browser rebalance them would undo that. */
:where(h2,h3,h4,.h2,.h3){ text-wrap:balance; }
:where(p,li,dd,dt,figcaption){ text-wrap:pretty; }
.hero :is(h1,h2,h3,p,li,dd){ text-wrap:wrap; }

/* The hard breaks in the section headings are a wide-screen lockup — they set
   a deliberate two-line shape. Below 640 the heading already wraps on its own
   and the forced break only adds a line, so it comes out. */
/* `display:none`, not a zero-height break: the element has to leave the flow
   entirely so the words either side can share a line. That means the markup
   carries a real space before each one — without it "Three things," and "done"
   would butt together the moment the break disappears. The space collapses at
   the line end wherever the break is showing, so it costs nothing there. */
.br-wide{ display:none; }

.head{ max-width:760px; margin-bottom:clamp(40px,5.5vw,72px); }
.head__lead{ margin-top:24px; color:var(--ash); font-size:clamp(1rem,1.35vw,1.14rem); max-width:56ch; }
.head__lead--on-ink{ color:var(--ash-d); }
.head--split{ max-width:none; display:grid; gap:clamp(22px,3.5vw,60px); grid-template-columns:minmax(0,1fr); }
.head__lead--r{ margin-top:0; }

/* buttons */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  min-height:52px; padding:0 20px; border:1px solid transparent; border-radius:var(--r-sm);
  font-family:var(--f-body); font-weight:600; font-size:16px; letter-spacing:-.005em;
  cursor:pointer; text-align:center;
  transition:transform .2s var(--ease), background-color .2s, color .2s, border-color .2s;
}
.btn--sm{ min-height:44px; padding:0 19px; font-size:14.5px; }
.btn--full{ width:100%; }
.btn--accent{ background:var(--klein); color:#fff; }
.btn--accent:hover{ background:var(--klein-deep); transform:translateY(-2px); }
.btn--ghost{ border-color:rgba(242,239,233,.3); color:var(--bone); }
.btn--ghost:hover{ border-color:var(--bone); background:rgba(242,239,233,.08); transform:translateY(-2px); }
.btn--ink{ background:var(--ink); color:var(--bone); }
.btn--ink:hover{ background:#1d242f; transform:translateY(-2px); }
.btn--line{ border-color:rgba(11,13,18,.24); color:var(--ink); }
.btn--line:hover{ border-color:var(--ink); background:rgba(11,13,18,.05); transform:translateY(-2px); }
.btn__a{ display:grid; place-content:center; width:13px; height:13px; flex:none; }
.btn__a svg{ width:10px; height:10px; transition:transform .25s var(--ease); }
.btn:hover .btn__a svg{ transform:translate(2px,-2px); }
.btn-row{ display:flex; flex-wrap:wrap; gap:12px; }
.btn-row--mt{ margin-top:clamp(34px,4vw,54px); }
/* The closing row of a band, ruled off. Without the hairline the two buttons
   sit alone in the void the last case leaves — its info column ends on the
   right, the buttons start on the left, and the eye crosses empty space on the
   diagonal to find them. The rule is the same weight as .case + .case, so the
   row reads as the section's last item rather than as something adrift. */
.btn-row--rule{
  margin-top:clamp(24px,2.8vw,36px);
  padding-top:clamp(22px,2.6vw,32px);
  border-top:1px solid var(--line-soft);
}

.tlink{
  position:relative;
  display:inline-flex; align-items:center; gap:9px; margin-top:22px; min-height:44px;
  font-weight:600; font-size:15px; color:var(--klein);
}
/* the whole case row is a link — hovering anywhere in it should say so */
.tlink::after{
  content:''; position:absolute; left:0; right:0; bottom:12px; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left;
  transition:transform .32s var(--ease);
}
.tlink:hover::after,.case:hover .tlink::after{ transform:scaleX(1); }
.tlink svg{ width:11px; height:11px; transition:transform .25s var(--ease); }
.tlink:hover svg,.case:hover .tlink svg{ transform:translate(2px,-2px); }

/* reveals */
.fade-up{ opacity:0; transform:translateY(16px); animation:rise .8s var(--ease) forwards; }
.fade-up--2{ animation-delay:.30s; }
.fade-up--3{ animation-delay:.44s; }
@keyframes rise{ to{ opacity:1; transform:none; } }

/* Three reveals, not one. A paragraph, a card and a photograph are different
   kinds of thing and should not arrive the same way — one motion applied to
   everything is what makes a page read as generated. main.js assigns the
   variant; .io alone never appears. */
.io{ opacity:0; }
.io.is-in{ opacity:1; }

/* text — rises a little, quickly. It is there to be read. */
.io--text{ transform:translateY(14px); transition:opacity .6s var(--ease), transform .6s var(--ease); }

/* cards — no travel. They scale up into the grid as a set. */
.io--card{ transform:scale(.97); transition:opacity .5s var(--ease), transform .5s var(--ease); }

.io--text.is-in,.io--card.is-in{ transform:none; }

/* media — the container only fades; the picture inside settles. Photographs
   have mass, so this one is slow. No fill-mode: the image has to land back on
   its base transform or the 1.02x hover below has nothing to transition from. */
.io--media{ transition:opacity .9s var(--ease); }
.io--media.is-in .case__media img,
.io--media.is-in > img,
.io--media.is-in .figframe img{ animation:mediaSettle 1.1s var(--ease); }
@keyframes mediaSettle{ from{ transform:scale(1.05); } }

/* ================================= nav =================================== */
.nav{
  position:fixed; inset:0 0 auto 0; z-index:90;
  background:rgba(11,13,18,.82);
  border-bottom:1px solid rgba(242,239,233,.08);
  transition:background-color .3s;
}
.nav.is-stuck{ background:rgba(11,13,18,.9); }
.nav__in{
  width:100%; max-width:var(--wrap); margin-inline:auto; padding-inline:var(--gut);
  min-height:var(--nav-h); display:flex; align-items:center; gap:28px;
}
.nav__logo{ flex:none; display:flex; align-items:center; min-height:44px; }
.nav__logo img{ width:104px; height:auto; }
.nav__links{ display:none; margin-left:auto; gap:30px; }
.nav__links a{
  position:relative; color:var(--bone); font-size:15px; font-weight:500; opacity:.82;
  padding-block:11px; transition:opacity .2s;
}
.nav__links a::after{
  content:''; position:absolute; left:0; bottom:4px; height:1.5px; width:0;
  background:var(--klein-lt); transition:width .28s var(--ease);
}
.nav__links a:hover{ opacity:1; }
.nav__links a:hover::after,.nav__links a[aria-current="page"]::after{ width:100%; }
.nav__links a[aria-current="page"]{ opacity:1; }
.nav__end{ margin-left:auto; display:flex; align-items:center; gap:14px; }
.nav__cta{ display:none; }

.burger{
  width:44px; height:44px; display:grid; place-content:center; gap:6px;
  background:none; border:1px solid rgba(242,239,233,.24); border-radius:var(--r-sm); cursor:pointer;
}
.burger span{ display:block; width:17px; height:1.6px; background:var(--bone); transition:transform .28s var(--ease); }
.burger[aria-expanded="true"] span:first-child{ transform:translateY(3.8px) rotate(45deg); }
.burger[aria-expanded="true"] span:last-child{ transform:translateY(-3.8px) rotate(-45deg); }

.drawer{
  position:fixed; inset:var(--nav-h) 0 0 0; z-index:89;
  background:var(--ink); padding:34px var(--gut) 40px;
  display:flex; flex-direction:column; gap:24px;
  animation:drawerIn .3s var(--ease);
}
.drawer[hidden]{ display:none; }
@keyframes drawerIn{ from{ opacity:0; transform:translateY(-10px); } }
.drawer__nav{ display:flex; flex-direction:column; }
.drawer__nav a{
  display:flex; align-items:baseline; gap:16px;
  font-family:var(--f-display); font-weight:700; font-stretch:105%;
  font-size:clamp(1.65rem,6.6vw,2.2rem); letter-spacing:-.024em; color:var(--bone);
  padding-block:15px; border-bottom:1px solid var(--ink-line);
}
.drawer__cta{ margin-top:auto; }
.drawer__note{ font:400 13px/1 var(--f-mono); color:var(--ash-d); text-align:center; }

/* ================================= hero ================================== */
.hero{
  background:var(--ink); color:var(--bone);
  padding-top:calc(var(--nav-h) + clamp(40px,5.5vw,64px));
  padding-bottom:clamp(44px,5.5vw,68px);
  position:relative; isolation:isolate;
}
/* The ground: V1's pixel field, ported. Four passes got here — a drifting dot
   screen, two counter-rotating outlines, an ultramarine bloom — and the brief
   that settled it is solid blocks of colour, no gradients. So the band is not
   painted at all now; main.js draws it, one klein cell at a time on a 23px
   module, alpha quantised to eighths so a cell is lit or it is not.

   Everything about the composition lives in main.js. All this owes it is a
   clipped box to sit in and a canvas that fills it. */
.ground{
  position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
}
.ground canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }

.hero__in{
  width:100%; max-width:var(--wrap); margin-inline:auto; padding-inline:var(--gut);
  position:relative; z-index:1;
}

.h-hero{
  margin-top:20px;
  font-family:var(--f-display); font-weight:800; font-stretch:114%;
  /* the lines are hand-broken, so the scale is set by the longest one
     ("Design that moves") fitting the column at every width down to 320px */
  font-size:clamp(1.72rem, 8.4vw, 5.2rem);
  line-height:.94; letter-spacing:-.038em;
}
.h-hero--page{ font-size:clamp(1.72rem, 8.4vw, 5.6rem); }
.lift{ display:block; overflow:hidden; padding-bottom:.1em; margin-bottom:-.1em; }
.lift > span{ display:block; transform:translateY(108%); animation:liftUp .95s var(--ease) forwards; }
.lift:nth-child(1) > span{ animation-delay:.05s; }
.lift:nth-child(2) > span{ animation-delay:.15s; }
.lift:nth-child(3) > span{ animation-delay:.25s; }
@keyframes liftUp{ to{ transform:none; } }

.hero__base{ display:grid; gap:clamp(32px,4vw,54px); margin-top:clamp(32px,4vw,48px); }
.hero__lede{ color:var(--ash-d); font-size:clamp(1.02rem,1.4vw,1.16rem); max-width:42ch; }
.hero__foot .btn-row{ margin-top:28px; }

/* featured card, driven by the work index */
.fcard{
  background:var(--graphite); border:1px solid var(--ink-line);
  border-radius:var(--r); overflow:hidden;
  box-shadow:0 30px 60px -28px rgba(0,0,0,.8);
}
.fcard__txt{
  padding:16px 18px 14px;
  font-family:var(--f-display); font-weight:700; font-stretch:104%;
  font-size:clamp(.95rem,1.25vw,1.08rem); letter-spacing:-.018em; line-height:1.25;
}
.fcard__media{ position:relative; aspect-ratio:16/10; background:var(--ink); }
.fcard__fig{ position:absolute; inset:0; opacity:0; transition:opacity .5s var(--ease); }
.fcard__fig.is-active{ opacity:1; }
.fcard__fig img{ width:100%; height:100%; object-fit:cover; }
.fcard__foot{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding:13px 14px 13px 18px; border-top:1px solid var(--ink-line);
}
.fcard__meta{ font:400 11.5px/1.4 var(--f-mono); color:var(--ash-d); letter-spacing:.03em; }
.fcard__go{
  flex:none; width:44px; height:44px; border-radius:var(--r-sm);
  display:grid; place-content:center; color:var(--bone);
  border:1px solid var(--ink-line); transition:background-color .25s, border-color .25s;
}
.fcard__go svg{ width:11px; height:11px; transition:transform .25s var(--ease); }
.fcard__go:hover{ background:var(--klein); border-color:var(--klein); }
.fcard__go:hover svg{ transform:translate(2px,-2px); }

/* work index — fills the column beside the card on desktop */
.index{ margin-top:clamp(30px,3.4vw,40px); border-top:1px solid var(--ink-line); }
.index__btn{
  display:grid; grid-template-columns:1fr auto; align-items:center; gap:4px 20px;
  padding:16px 0; border-bottom:1px solid var(--ink-line);
  transition:padding-inline .35s var(--ease), background-color .35s;
}
.index__n{
  grid-column:1; font:500 11px/1.4 var(--f-mono);
  letter-spacing:.14em; text-transform:uppercase; color:var(--ash-d);
  transition:color .3s;
}
.index__t{
  grid-column:1;
  font-family:var(--f-display); font-weight:800; font-stretch:110%;
  font-size:clamp(1.4rem,3vw,1.8rem); line-height:1; letter-spacing:-.032em;
}
.index__k{
  grid-column:2; grid-row:1 / span 2; width:14px; height:14px;
  opacity:0; transform:translateX(-8px);
  transition:opacity .3s, transform .3s var(--ease); color:var(--klein-lt);
}
.index__btn:hover,.index__btn.is-active{ padding-inline:14px; background:rgba(242,239,233,.045); }
.index__btn:hover .index__n,.index__btn.is-active .index__n{ color:var(--klein-lt); }
.index__btn:hover .index__k,.index__btn.is-active .index__k{ opacity:1; transform:none; }

/* ================================ ticker ================================= */
.ticker{ background:var(--klein); color:#fff; overflow:hidden; padding-block:16px; }
.ticker__run{
  display:flex; width:max-content; animation:run 42s linear infinite;
  /* Its own layer. Without it the wrap repaints a 5,000px run of display type
     on the main thread, and the lap restart is where you see the cost. */
  will-change:transform;
}
.ticker__run span{
  font-family:var(--f-display); font-weight:700; font-stretch:108%;
  font-size:clamp(.92rem,1.8vw,1.25rem); letter-spacing:-.012em; white-space:nowrap;
}
.ticker__run i{ font-style:normal; color:rgba(255,255,255,.45); padding-inline:22px; font-size:.7em; vertical-align:middle; }
/* translate3d, not translateX — same motion, but it keeps the run on the
   compositor. The 50% fallback is the no-JS path: two segments in the markup,
   so half the run is exactly one of them. */
@keyframes run{ to{ transform:translate3d(calc(-1 * var(--seg, 50%)),0,0); } }

/* =============================== work cases ============================== */
/* A hairline above every row, the first one included. The gap alone left the
   three cases reading as one run of images; at .07 the rule separates them
   without becoming a table. The first rule does a second job — it closes the
   section header off from the case that follows it, which is what stopped the
   eyebrow and the first case's kick reading as one stack of labels.

   Uniform padding-block is what makes the rhythm work: every rule has the same
   44px under it and the same 44px over the next one. The first case briefly
   carried a smaller top padding to buy that separation by air instead; the
   rule does it better, so the override is gone. */
.case{
  display:grid; gap:clamp(26px,3.4vw,52px);
  padding-block:clamp(24px,3vw,44px);
  border-top:1px solid var(--line-soft);
}

/* no marker: the diamond belongs to section eyebrows only — see the
   design-system block for why this one lost it */
.case__kick{
  display:block;
  font:500 11.5px/1.5 var(--f-mono); letter-spacing:.11em; text-transform:uppercase; color:var(--klein);
}
.case__name{
  font-family:var(--f-display); font-weight:800; font-stretch:112%;
  font-size:clamp(1.9rem,4.2vw,3rem); line-height:.94; letter-spacing:-.032em;
}
.case__body{ color:var(--ash); max-width:46ch; }

/* the text column — one uniform gap, centred against the image.
   The `> *` reset kills the margin-top on .case__meta and .tlink, which are
   shared with work.html and keep their margins there. */
.case__info{ display:grid; align-content:center; gap:clamp(14px,1.5vw,19px); }
.case__info > *{ margin-top:0; }
.case__info .tlink{ justify-self:start; }

.case__meta{ margin-top:26px; border-top:1px solid var(--line); }
.case__meta > div{
  display:flex; gap:14px; align-items:baseline;
  padding-block:11px; border-bottom:1px solid var(--line);
}
.case__meta dt{
  flex:none; width:76px; font:500 10.5px/1.6 var(--f-mono);
  letter-spacing:.13em; text-transform:uppercase; color:var(--ash);
}
.case__meta dd{ font-size:14.5px; }
.case__meta--wide{ max-width:44rem; margin-top:clamp(28px,3.5vw,44px); }

/* homepage only — value hard right, one rule above the block, no row rules.
   work.html keeps the ruled rows: its two --wide tables run to 44rem and a
   right-aligned value would strand itself half a page from its label. */
.case__info .case__meta{ padding-top:14px; display:grid; gap:8px; }
.case__info .case__meta > div{ padding-block:0; border-bottom:0; justify-content:space-between; }
.case__info .case__meta dd{ text-align:right; font-weight:600; }

/* the one big image — bare in its column. No frame, no caption, no mat.
   It is also a link: the row already draws the "See the project" underline on
   hover, so the picture the pointer is actually over has to go there too. */
.case__media a{ display:block; }
.case__media{
  border-radius:var(--r); overflow:hidden;
  background:var(--card); position:relative;
}
.case__media img{
  width:100%; height:auto; aspect-ratio:16/11;
  object-fit:cover; object-position:top center;
  transition:transform .9s var(--ease);
}
.case__media--portrait img{ aspect-ratio:4/5; object-position:center; }
.case:hover .case__media img{ transform:scale(1.02); }

/* the supporting images, inside the text column */
.case__pair{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
.case__pair--one{ grid-template-columns:minmax(0,1fr); }
.case__pair img{
  width:100%; height:auto; aspect-ratio:16/11;
  object-fit:cover; object-position:top center;
  border-radius:var(--r-md);
}
.case__pair--one img{ aspect-ratio:16/9; object-position:center; }

/* image frame + caption bar — work.html only since the cases went bare */
.figframe{ border-radius:var(--r-md); overflow:hidden; background:var(--paper); }
.figframe img{ width:100%; }
.figcap{
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  padding:9px 13px; border-top:1px solid var(--line);
  font:400 11px/1.5 var(--f-mono); letter-spacing:.02em; color:var(--ink);
}
/* Two tiers, carried by colour rather than by fading one of them out: the
   label is ink, the detail is ash. The .72 opacity this replaces measured
   3.37:1 on --paper, under AA at 11px. */
.figcap span:last-child{ text-align:right; color:var(--ash); }
.figframe--lead{ margin-top:clamp(4px,1vw,10px); }

/* ============================== what we do =============================== */
.svc{ display:grid; gap:clamp(16px,1.8vw,24px); }
/* Flat graphite, and nothing on it. Four different decorations have been tried
   in this corner — a dot screen, a sliding top rule, an outlined plate number,
   an ultramarine wash — and each one lost the same argument with the copy. The
   card's job is to be read; the diamond plate is the only mark it needs, and
   the wash went with the no-gradients rule anyway. */
.svc__card{
  position:relative; background:var(--graphite);
  border:1px solid var(--ink-line); border-radius:var(--r);
  padding:clamp(22px,2.2vw,28px);
  display:flex; flex-direction:column; gap:15px;
  transition:border-color .3s, transform .3s var(--ease), background-color .3s;
}
.svc__card:hover{
  border-color:rgba(110,156,255,.45); transform:translateY(-4px);
  background:#171C24;
}
/* Straddles the top edge on the trailing side, half in and half out.
   **Currently unused.** Its one consumer was the "Client favorite" badge on the
   first service card, removed in the copy pass as the site's only unsourced
   claim. The rule is kept as the affordance — a badge here needs a fact behind
   it, and there will be one. Same treatment as --surface. */
.svc__tag{
  position:absolute; top:0; right:clamp(22px,2.2vw,28px); left:auto;
  transform:translateY(-50%);
  background:var(--klein); color:#fff;
  font:600 10px/1 var(--f-mono); letter-spacing:.13em; text-transform:uppercase;
  padding:7px 10px; border-radius:var(--r-xs);
}
/* The plate stands on its point — the same diamond as the eyebrow marker, at
   the one size where it can hold an icon. Rotation does not reflow, so the
   flex item still measures 40px while the figure reads about 57px corner to
   corner; the 15px column gap absorbs the overhang onto the heading below.
   The glyph is turned back so it sits upright inside the diamond. */
.ic-plate{
  width:40px; height:40px; border-radius:var(--r-xs); flex:none;
  background:rgba(110,156,255,.12); border:1px solid rgba(110,156,255,.22);
  display:grid; place-content:center; color:var(--klein-lt);
  rotate:45deg; margin-left:8px;
  transition:background-color .35s, border-color .35s, color .35s;
}
.ic-plate svg{ width:20px; height:20px; rotate:-45deg; }
/* the plate is the one thing that fully inverts on hover — the card goes from
   holding an icon to being signed by it */
.svc__card:hover .ic-plate{ background:var(--klein); border-color:var(--klein); color:#fff; }
.svc__h{
  font-family:var(--f-display); font-weight:700; font-stretch:108%;
  font-size:clamp(1.25rem,1.9vw,1.5rem); line-height:1.08; letter-spacing:-.028em;
}
.svc__b{ color:var(--ash-d); font-size:15.5px; }
/* auto pushes the spec list to the card floor so all three align.
   These are five short sentences, not table rows — spacing does the
   separating, not a rule per line. Ruling every row made the card twice
   as tall as it needed to be. */
.svc__list{
  margin-top:auto; padding-top:15px;
  display:grid; gap:7px;
  position:relative;
}
.svc__list::before{ content:''; position:absolute; top:0; left:0; right:0; height:1px; background:var(--ink-line); }
.svc__list li{
  display:flex; align-items:flex-start; gap:10px;
  font:400 13px/1.5 var(--f-mono); letter-spacing:-.01em; color:var(--bone);
}
.svc__list li::before{
  content:''; flex:none;
  width:9px; height:2px; margin-top:.62em; background:var(--klein-lt);
}
/* The hub page turns each card heading into a link to that service page. It is
   still the heading — the link inherits it and only picks up a rule on hover. */
.svc__h a{ color:inherit; text-decoration:none; }
.svc__h a:hover{ text-decoration:underline; text-underline-offset:3px; }
/* .tlink is ultramarine, which is correct on bone and card and too dark on the
   graphite service card — the light tint is the on-dark pair. */
.svc__card .tlink{ color:var(--klein-lt); margin-top:15px; }

/* ============================== studio note ============================== */
.panel{
  position:relative; overflow:hidden;
  background:var(--klein); color:#fff;
  border-radius:var(--r-lg); padding:clamp(30px,3.8vw,58px);
  display:grid; gap:clamp(20px,3vw,44px);
}
.note__q{
  font-family:var(--f-display); font-weight:800; font-stretch:110%;
  font-size:clamp(1.5rem,3vw,2.3rem); line-height:1.04; letter-spacing:-.032em;
}
.note__b{ position:relative; z-index:1; }
.note__b p{ color:rgba(255,255,255,.88); }
.note__b .btn-row{ margin-top:26px; }
.note__by{
  margin-top:18px; font:400 11.5px/1 var(--f-mono);
  letter-spacing:.15em; text-transform:uppercase; color:rgba(255,255,255,.85);
}
/* The note used to own a band of its own. Inside process it needs the air the
   band padding used to give it — it is the principle the four stages express,
   so it reads as the close of the argument, then the CTA. */
.panel--inset{ margin-top:clamp(38px,4.6vw,68px); }
.note__knot{
  position:absolute; right:-56px; bottom:-72px; width:270px; height:auto;
  opacity:.1; pointer-events:none; z-index:0;
}

/* =============================== process ================================= */
.steps{ display:grid; gap:clamp(26px,3vw,30px); }
/* The stage number is the marker now. It used to be a klein dot on the rule
   with "STAGE 01" set in mono underneath — two objects saying one thing, and
   the second of them spending a whole line on the word "stage". A solid klein
   block carrying the numeral does both jobs at once, and it straddles the rule
   the same way .svc__tag straddles the top of a service card, so the two dark
   and light bands mark their items the same way.

   The bodies were cut to one sentence each in the same pass — four stages of
   three-sentence prose was a wall of text where a timeline was wanted. */
.step{ position:relative; padding-top:38px; border-top:1px solid var(--line); }
.step__n{
  position:absolute; top:0; left:0; transform:translateY(-50%);
  background:var(--klein); color:#fff;
  font:600 11px/1 var(--f-mono); letter-spacing:.1em;
  padding:7px 9px; border-radius:var(--r-xs);
}
.step__t{
  font-family:var(--f-display); font-weight:700; font-stretch:108%;
  font-size:clamp(1.3rem,2vw,1.6rem); letter-spacing:-.026em; line-height:1.1;
}
.step__b{ margin-top:12px; color:var(--ash); font-size:15.5px; }
/* No opacity here: --ash at .7 composites to 2.86:1 on --card, under AA. The
   mono face, the size and the tracking already separate it from .step__b. */
.step__w{ display:inline-block; margin-top:16px; font:400 11.5px/1 var(--f-mono); color:var(--ash); letter-spacing:.06em; }

/* ================================= faq ===================================
   Tightened: the six rows were carrying 22px of padding each and a 1.22rem
   question, which put the last answer a long way below the fold. Row padding,
   question size, the glyph and the answer block all came in a notch. */
.faqsplit{ display:grid; gap:clamp(34px,4.5vw,68px); }
.faqsplit__l .btn-row{ margin-top:28px; }
.acc{ border-top:1px solid var(--ink-line); }
.acc__item{ border-bottom:1px solid var(--ink-line); }
.acc__h{ margin:0; }
.acc__q{
  width:100%; background:none; border:0; cursor:pointer; text-align:left;
  display:flex; align-items:flex-start; justify-content:space-between; gap:20px;
  padding-block:15px;
  font-family:var(--f-display); font-weight:600; font-stretch:104%;
  font-size:clamp(.98rem,1.5vw,1.12rem); letter-spacing:-.022em; line-height:1.28;
  color:var(--bone); transition:color .2s;
}
.acc__q:hover{ color:var(--klein-lt); }
.acc__q i{ position:relative; flex:none; width:14px; height:14px; margin-top:4px; }
.acc__q i::before,.acc__q i::after{
  content:''; position:absolute; left:0; top:6.5px; width:14px; height:1.5px;
  background:currentColor; transition:transform .3s var(--ease);
}
.acc__q i::after{ transform:rotate(90deg); }
.acc__q[aria-expanded="true"] i::after{ transform:rotate(0deg); }
/* Scoped to .js on purpose: the collapse is something JavaScript operates, so
   without it the six answers are simply open rather than shut and unopenable.
   The +/- glyph goes with it — no point advertising a control that isn't there. */
.js .acc__a{ height:0; overflow:hidden; transition:height .34s var(--ease); }
html:not(.js) .acc__q{ cursor:default; }
html:not(.js) .acc__q i{ display:none; }
.acc__a > div{ padding-bottom:18px; }
.acc__a p{ color:var(--ash-d); max-width:62ch; font-size:14.8px; line-height:1.55; }
.acc__a p + p{ margin-top:11px; }

/* =============================== contact ================================= */
.contact{ display:grid; gap:clamp(34px,4.2vw,60px); align-items:start; }
.talk{
  margin-top:20px; display:flex; flex-wrap:wrap; gap:0 .26em;
  font-family:var(--f-display); font-weight:800; font-stretch:112%;
  font-size:clamp(2.3rem,5.6vw,4.2rem); line-height:.96; letter-spacing:-.036em;
}
/* words start hidden only when JS is there to reveal them */
.talk__w{ display:inline-block; }
.js .talk__w{ opacity:0; transform:translateY(26px); }
.talk.is-in .talk__w{ animation:talkIn .75s var(--ease) forwards; }
.talk.is-in .talk__w:nth-child(1){ animation-delay:0ms; }
.talk.is-in .talk__w:nth-child(2){ animation-delay:70ms; }
.talk.is-in .talk__w:nth-child(3){ animation-delay:140ms; }
.talk.is-in .talk__w:nth-child(4){ animation-delay:210ms; }
@keyframes talkIn{ to{ opacity:1; transform:none; } }

.contact__lead{ margin-top:24px; color:rgba(255,255,255,.85); font-size:clamp(1rem,1.4vw,1.1rem); max-width:42ch; }
.next{ margin-top:34px; padding-top:8px; border-top:1px solid rgba(255,255,255,.28); max-width:34ch; }
.next li{
  display:flex; gap:14px; align-items:baseline;
  padding-block:13px; border-bottom:1px solid rgba(255,255,255,.16);
  font-size:15.5px; color:rgba(255,255,255,.92);
}
.next li:last-child{ border-bottom:0; }
.next span{
  flex:none; width:22px; height:22px; border-radius:50%; background:rgba(255,255,255,.1);
  display:grid; place-content:center; font:500 11px/1 var(--f-mono); color:#fff;
  transform:translateY(3px);
}
/* the line is set as a 44px row so the address inside it is a real tap
   target — it was 18px, the smallest thing on the page you were asked to hit */
.contact__alt{
  margin-top:18px; display:flex; flex-wrap:wrap; align-items:center; gap:0 6px;
  font:400 13.5px/1 var(--f-mono); color:rgba(255,255,255,.88);
}
.contact__alt a{
  color:#fff; text-decoration:underline; text-underline-offset:4px; text-decoration-thickness:1px;
  display:inline-flex; align-items:center; min-height:44px;
}

.formcard{ background:var(--paper); color:var(--ink); border-radius:var(--r-lg); padding:clamp(24px,2.8vw,38px); }
.form{ display:grid; gap:19px; }
.form__row{ display:grid; gap:19px; }
.field{ display:grid; gap:8px; border:0; }
.field > label,.field > legend{
  font:500 12px/1 var(--f-mono); letter-spacing:.09em; text-transform:uppercase;
  color:var(--ash); padding:0;
}
/* lowercase and untracked is the whole difference — .65 opacity took it to
   2.92:1 on --paper, and the word is a real instruction, not a whisper */
.field__opt{ text-transform:none; letter-spacing:.02em; }
.field input,.field select,.field textarea{
  width:100%; background:#fff; border:1px solid var(--line); border-radius:var(--r-sm);
  padding:13px 14px; font-size:16px; font-family:var(--f-body); color:var(--ink);
  transition:border-color .2s, box-shadow .2s;
}
.field textarea{ resize:vertical; min-height:104px; }
.field select{
  appearance:none; padding-right:40px;
  background-image:linear-gradient(45deg,transparent 50%,var(--ash) 50%),linear-gradient(135deg,var(--ash) 50%,transparent 50%);
  background-position:calc(100% - 19px) 22px,calc(100% - 14px) 22px;
  background-size:5px 5px,5px 5px; background-repeat:no-repeat;
}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:0; border-color:var(--klein); box-shadow:0 0 0 3px rgba(43,75,255,.16);
}
.field input::placeholder{ color:#A9AEB6; }
.field__err{ font:400 12.5px/1.4 var(--f-mono); color:#C2261B; display:none; }
.field.is-bad input,.field.is-bad textarea{ border-color:#C2261B; }
.field.is-bad .field__err{ display:block; }
.field--set{ margin:0; }
.checks{ display:grid; grid-template-columns:minmax(0,1fr); gap:8px; margin-top:2px; }
.check{
  display:flex; align-items:center; gap:10px; cursor:pointer; font-size:15px;
  padding:11px 13px; border:1px solid var(--line); border-radius:var(--r-sm); background:#fff;
  transition:border-color .2s, background-color .2s;
}
.check input{ width:17px; height:17px; accent-color:var(--klein); flex:none; margin:0; }
.check:hover{ border-color:rgba(11,13,18,.3); }
.check:has(input:checked){ border-color:var(--klein); background:rgba(43,75,255,.05); }
/* Shown only when a submit actually failed, or when no endpoint is configured.
   Empty by default and collapsed with :empty, so it costs nothing until the JS
   puts a sentence in it. Carries role="alert" so it gets spoken when it appears. */
.form__fail{ font:400 12.5px/1.5 var(--f-mono); color:#C2261B; text-align:center; margin-bottom:12px; }
.form__fail:empty{ display:none; }
.form__fail a{ color:inherit; text-decoration:underline; text-underline-offset:2px; }
.form__fine{ font:400 12.5px/1.55 var(--f-mono); color:var(--ash); text-align:center; }

.sent{ text-align:center; padding:clamp(20px,4vw,44px) 0; }
.sent__tick{ width:44px; height:44px; margin:0 auto 20px; fill:none; stroke:var(--klein); stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
.sent h3{ font-family:var(--f-display); font-weight:800; font-stretch:110%; font-size:1.55rem; letter-spacing:-.03em; }
.sent p{ margin-top:12px; color:var(--ash); max-width:38ch; margin-inline:auto; }

/* ========================= work page: head & splits ====================== */
.phead{
  background:var(--ink); color:var(--bone);
  padding-top:calc(var(--nav-h) + clamp(48px,6.5vw,86px));
  padding-bottom:clamp(48px,6.5vw,86px);
  position:relative; isolation:isolate;
}
.phead > .wrap{ position:relative; z-index:1; }
.phead__lede{ margin-top:28px; color:var(--ash-d); font-size:clamp(1.02rem,1.4vw,1.16rem); max-width:54ch; }

.split{ display:grid; gap:clamp(22px,3vw,46px); margin-top:clamp(26px,3.6vw,50px); }
.stack{ display:grid; gap:clamp(20px,2.6vw,30px); align-content:start; }
.tiles{ display:grid; gap:clamp(12px,1.6vw,20px); margin-top:clamp(26px,3.6vw,50px); }

/* ================================ footer ================================= */
.foot{ background:var(--ink); color:var(--bone); padding-top:clamp(52px,5.5vw,80px); padding-bottom:28px; }
.foot__in{ display:grid; gap:34px; padding-bottom:42px; border-bottom:1px solid var(--ink-line); }
.foot__brand img{ width:118px; }
.foot__brand p{ margin-top:18px; color:var(--ash-d); font-size:15px; max-width:34ch; }
/* No street address and no phone — the studio has neither to publish. This line
   is the whole local claim, and the Organization JSON-LD says the same thing in
   areaServed. See docs/SCOPE.md, Deliberately absent. */
.foot__area{ margin-top:14px; font:400 12.5px/1.5 var(--f-mono); color:var(--klein-lt); }
.foot__h{ font:500 11px/1 var(--f-mono); letter-spacing:.15em; text-transform:uppercase; color:var(--klein-lt); margin-bottom:14px; }
.foot__col{ display:flex; flex-direction:column; }
.foot__col a{ color:var(--ash-d); font-size:15px; width:fit-content; min-height:44px; display:flex; align-items:center; transition:color .2s; }
.foot__col a:hover{ color:var(--bone); }
.foot__base{ padding-top:22px; display:flex; flex-wrap:wrap; gap:10px 24px; justify-content:space-between; }
.foot__base p{ font:400 12.5px/1.5 var(--f-mono); color:var(--ash-d); }

/* =========================== mobile sticky bar =========================== */
.sticky-cta{
  position:fixed; left:0; right:0; bottom:0; z-index:85; display:none; gap:10px;
  padding:11px var(--gut) calc(11px + env(safe-area-inset-bottom, 0px));
  background:rgba(11,13,18,.95); border-top:1px solid var(--ink-line);
  transform:translateY(115%); transition:transform .35s var(--ease);
}
.sticky-cta.is-up{ transform:none; }
/* must follow .is-up — equal specificity, source order decides */
.drawer-open .sticky-cta{ transform:translateY(115%); }
.sticky-cta .btn{ flex:1 1 0; min-width:0; }
.sticky-cta .btn--line{ border-color:rgba(242,239,233,.3); color:var(--bone); }
.sticky-cta .btn--line:hover{ border-color:var(--bone); background:rgba(242,239,233,.08); }

/* =============================== responsive ============================== */
@media (min-width:480px){
  .checks{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
/* A pair of buttons stays a pair. At 360 — a Pixel, most Samsungs — the two
   default-width buttons come to 324px against 320px of room, so the row wraps
   and the second button drops to a line of its own with a phone's width of
   empty space beside it. Four pixels shouldn't cost a whole line.

   Below 370 the pair goes nowrap and gets a compact set of metrics: 12px side
   padding, an 8px icon gap, a 15px label. That brings the pair to ~278px, so
   it clears 320 with room and the buttons keep their natural widths — no
   stretching to halves, which would read as a different component. 370 and up
   is untouched, because there the row already fits as designed.

   `:has(.btn + .btn)` keeps this off single-button rows; those size to their
   label at every width, which is the whole point of them. */
@media (max-width:369px){
  .btn-row:has(.btn + .btn){ flex-wrap:nowrap; gap:10px; }
  .btn-row:has(.btn + .btn) > .btn{
    flex:0 1 auto; min-width:0; white-space:nowrap;
    padding:0 12px; gap:8px; font-size:15px;
  }
}
@media (max-width:899px){
  .sticky-cta{ display:flex; }

  /* The sticky bar carries Get a quote and Email us at this width, pinned a
     thumb away from the bottom of the screen. Every in-content copy of the same
     ask is therefore a duplicate of a button that is already on screen, so they
     come off. Hidden, not deleted: above 899px the sticky bar does not exist and
     these are the only ask on the page. The nav CTA is already gone by here.
     Marked with .btn--quote in the HTML because CSS cannot select on label. */
  .btn--quote{ display:none; }

  /* Some rows hold nothing but the quote CTA — the per-case CTA, and the closing
     panel on /work and /services. Hiding the button alone would leave the rows
     margin, and btn-row--rule its hairline, floating with nothing under them. */
  .btn-row:not(:has(.btn:not(.btn--quote))){ display:none; }
}
@media (min-width:640px){
  .br-wide{ display:inline; }
  .form__row{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .foot__in{ grid-template-columns:1.6fr 1fr 1fr 1fr; gap:40px; }
  .tiles{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (min-width:820px){
  .split{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .split--wide-left{ grid-template-columns:minmax(0,1fr) minmax(0,1.12fr); }
  .split--top{ align-items:start; }
}
@media (min-width:900px){
  .nav__links{ display:flex; }
  .nav__cta{ display:inline-flex; }
  .burger{ display:none; }
  .head--split{ grid-template-columns:minmax(0,1.3fr) minmax(0,.7fr); align-items:end; }
  .head__lead--r{ padding-bottom:6px; }
  /* stays full width until 1080 — see the .svc block */
  .steps{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .case{ grid-template-columns:minmax(0,1.35fr) minmax(0,1fr); align-items:center;
         column-gap:clamp(28px,4vw,56px); }
  .case--flip{ grid-template-columns:minmax(0,1fr) minmax(0,1.35fr); }
  .case--flip .case__media{ grid-column:2; grid-row:1; }
  .case--flip .case__info { grid-column:1; grid-row:1; }
  .contact{ grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr); }
  .contact__pitch{ position:sticky; top:calc(var(--nav-h) + 42px); }
  .faqsplit{ grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr); align-items:start; }
  .faqsplit__l{ position:sticky; top:calc(var(--nav-h) + 42px); }
  .panel{ grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr); align-items:start; }
  /* the poster rides along with the reading column instead of stranding it */
  .split--wide-left > .figframe{ position:sticky; top:calc(var(--nav-h) + 42px); }
  .hero__base{
    grid-template-columns:minmax(0,1fr) minmax(0,.92fr);
    grid-template-areas:"foot card" "index card";
    grid-template-rows:auto 1fr;
    column-gap:clamp(32px,4vw,56px); row-gap:0;
    align-items:start;
  }
  .hero__foot{ grid-area:foot; }
  .fcard      { grid-area:card; align-self:start; }
  .index      { grid-area:index; }
}
@media (min-width:1040px){
  .steps{ grid-template-columns:repeat(4,minmax(0,1fr)); }
}
/* 1080, not 900. At 900 a three-up card leaves ~22 characters a line, which
   reads as a stack of fragments rather than a paragraph — and makes the card
   a tall narrow rectangle. Full width below this. */
@media (min-width:1080px){
  .svc{ grid-template-columns:repeat(3,minmax(0,1fr)); }
}

/* ============================ reduced motion ============================= */
@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .fade-up{ opacity:1; transform:none; }
  .lift > span{ transform:none; }
  /* .js prefix required — .js .talk__w outranks a bare .talk__w and the words
     would sit invisible until the observer fired */
  .js .talk__w{ opacity:1; transform:none; }
  .io,.io--text,.io--card,.io--media{ opacity:1; transform:none; }
  /* the global rule crushes duration but NOT delay, and this one fills
     backwards — without an explicit reset the word sits condensed for 700ms */
  .hero .it{ animation:none; }
  .io--media.is-in .case__media img,
  .io--media.is-in > img,
  .io--media.is-in .figframe img{ animation:none; }
  .ticker__run{ animation:none; will-change:auto; }
  /* main.js draws one frozen frame instead of looping; markers stop catching */
  .eyebrow::before{ animation:none; opacity:1; }
}
