/* ═══════════════════════════════════════════════════════════════════════════
   THOMAS LEVENE — risograph separation
   Plates: Fluorescent Pink · Federal Blue · Yellow · Key
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── Inks ───────────────────────────────────────────────────────────── */
  --stock:        #DEDDD6;
  --stock-hi:     #EFEEE8;
  --ink-pink:     #FF48B0;
  --ink-blue:     #2A2DC4;
  --ink-yellow:   #FFE800;
  --ink-key:      #16161A;

  /* Multiply results, documented — never author these as new colours */
  --overprint-a:  #2A0D87;   /* pink × blue  */
  --overprint-b:  #FF4100;   /* pink × yellow */

  /* ── Secondary text ─────────────────────────────────────────────────
     Tinted from each ground, never gray-by-opacity. Measured ratios:  */
  --on-stock:      #16161A;  /* 14.8:1 on stock */
  --on-stock-soft: #4B4B54;  /*  6.1:1 on stock */
  --on-key:        #DEDDD6;  /* 14.8:1 on key   */
  --on-key-soft:   #A9A8A2;  /*  7.6:1 on key   */
  --on-blue:       #EFEEE8;  /*  8.1:1 on blue  */
  --on-blue-soft:  #C9CAF0;  /*  6.0:1 on blue  */
  --on-pink:       #16161A;  /*  6.4:1 on pink  */
  --on-pink-soft:  #3D0E28;  /*  5.2:1 on pink  */

  /* ── Type ───────────────────────────────────────────────────────────── */
  --display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif;
  --text:    "Archivo", ui-sans-serif, system-ui, sans-serif;
  --mono:    "Martian Mono", ui-monospace, "SFMono-Regular", monospace;

  /* ── Registration ───────────────────────────────────────────────────── */
  --reg-x: 0px;          /* live misregistration, driven by pointer   */
  --reg-y: 0px;

  /* Resting plate offset on set type.
     A real press misregisters by a physical distance, so the offset used to
     be a flat 3–4px at every size. That is faithful and it is also why small
     type died: at 90px a 3px fringe is 3% of the cap height and reads as
     ink, at 21px it is 14% and reads as a second, wrong word. These clamps
     make the offset proportional with a floor and a ceiling, so display type
     keeps exactly the fringe it has now and small type gets a fringe scaled
     to it. Set in em: on a registered <length> property em resolves against
     the element's own font-size, which is precisely the quantity at issue. */
  --plate-o:  clamp(1.2px, 0.045em, 3px);   /* [data-plate] headings     */
  --plate-oj: clamp(1.6px, 0.075em, 4px);   /* the work index, set large */
  --plate-y:  0.55;                          /* vertical share of offset  */

  --gut: clamp(20px, 5vw, 72px);
  --ease: cubic-bezier(.16, 1, .3, 1);
}

/* ═══════════════════════════ RESET ═══════════════════════════ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* full-viewport fixed layers (.sheet, .grain) span the scrollbar gutter and
     would otherwise leave a few px of horizontal scroll. `clip`, not
     `hidden`: no scroll container is created. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce){ html { scroll-behavior: auto; } }

body{
  background: var(--stock);
  color: var(--ink-key);
  font-family: var(--text);
  font-size: clamp(16px, 0.5vw + 14px, 18px);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, canvas, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
h1, h2, h3 { font-weight: 700; line-height: 0.9; letter-spacing: -0.03em; }
ul, ol { list-style: none; }

::selection { background: var(--ink-pink); color: var(--ink-key); }

:focus-visible{
  outline: 2px solid var(--ink-pink);
  outline-offset: 4px;
}

.wrap{
  max-width: 1560px;
  margin-inline: auto;
  padding-inline: var(--gut);
}

/* Mono is for measurement — plate names, densities, angles, time.
   Sized to be read, not to be decorative. */
.mono{
  font-family: var(--mono);
  font-size: clamp(11px, 0.2vw + 10px, 12.5px);
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.6;
}

/* ═══════════════════════════ SHEET FURNITURE ═══════════════════════════ */
.sheet{ position: fixed; inset: 0; pointer-events: none; z-index: 90; }

.trim{
  position: absolute;
  width: 26px; height: 26px;
  opacity: .55;
}
.trim::before, .trim::after{
  content: ""; position: absolute; background: var(--ink-key);
}
.trim::before { width: 100%; height: 1px; top: 50%; }
.trim::after  { height: 100%; width: 1px; left: 50%; }

.trim--tl { top: 16px;    left: 16px; }
.trim--tr { top: 16px;    right: 16px; }
.trim--bl { bottom: 16px; left: 16px; }
.trim--br { bottom: 16px; right: 16px; }

/* Paper tooth */
.grain{
  position: fixed; inset: -50%;
  z-index: 95;
  pointer-events: none;
  opacity: .34;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ═══════════════════════════ NAV ═══════════════════════════ */
.nav{
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 80;
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px;
  padding: 22px var(--gut);
  transition: transform .5s var(--ease), color .4s var(--ease),
              background-color .4s var(--ease), border-color .4s var(--ease);
  border-bottom: 1px solid transparent;
}
.nav[data-hidden="true"]{ transform: translateY(-110%); }

/* Past the hero the nav lands on its own paper strip */
.nav[data-stuck="true"]{
  background: var(--stock);
  border-bottom-color: rgba(22, 22, 26, .18);
}

/* Over the key flood the nav reverses out */
.nav[data-invert="true"]{ color: var(--stock); }
.nav[data-invert="true"][data-stuck="true"]{
  background: var(--ink-key);
  border-bottom-color: rgba(222, 221, 214, .22);
}
/* On the key flood the solid chip flips, or it would be key on key */
.nav[data-invert="true"] .nav__cta{
  background: var(--stock);
  border-color: var(--stock);
  color: var(--ink-key);
}
.nav[data-invert="true"] .nav__cta:hover{
  background: var(--ink-yellow); border-color: var(--ink-yellow); color: var(--ink-key);
}
.nav[data-invert="true"] .nav__cta-dot{ background: var(--ink-pink); }
/* multiply over the key flood turns both dots black — they need normal blend */
.nav[data-invert="true"] .nav__dots i,
.nav[data-invert="true"] .nav__toggle-dots i{ mix-blend-mode: normal; }

.nav__mark{ display: flex; align-items: center; gap: 10px; }

.nav__dots{ position: relative; width: 26px; height: 14px; flex: none; }
.nav__dots i{
  position: absolute; top: 0; width: 14px; height: 14px; border-radius: 50%;
  mix-blend-mode: multiply;
  transition: transform .55s var(--ease);
}
.nav__dots i:first-child { left: 0;  background: var(--ink-pink); }
.nav__dots i:last-child  { left: 12px; background: var(--ink-blue); }
.nav__mark:hover .nav__dots i:first-child { transform: translateX(-4px); }
.nav__mark:hover .nav__dots i:last-child  { transform: translateX(4px); }

.nav__name{ font-weight: 600; font-size: 14px; letter-spacing: -0.01em; }

.nav__links{ display: flex; gap: 28px; font-size: 14px; }
.nav__links a{ position: relative; padding-block: 2px; }
.nav__links a::after{
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 2px; background: var(--ink-pink);
  transform: scaleX(0); transform-origin: left;
  transition: transform .4s var(--ease);
}
.nav__links a:hover::after{ transform: scaleX(1); }

/* Solid, not outlined: the primary action sits over a live dot field and an
   outline alone disappears into it. */
.nav__cta{
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600;
  padding: 10px 18px;
  background: var(--ink-key);
  color: var(--stock);
  border: 1.5px solid var(--ink-key);
  border-radius: 999px;
  transition: background .35s var(--ease), color .35s var(--ease),
              transform .35s var(--ease);
}
.nav__cta:hover{ background: var(--ink-pink); border-color: var(--ink-pink); color: var(--ink-key); transform: translateY(-1px); }
.nav__cta-dot{
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ink-pink);
  animation: throb 2.4s var(--ease) infinite;
}
.nav__cta:hover{ background: var(--ink-key); color: var(--stock); }
.nav__cta:hover .nav__cta-dot{ background: var(--ink-yellow); }

@keyframes throb{
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(.55); opacity: .5; }
}

/* ═══════════════════════════ 01 · HERO ═══════════════════════════ */
.hero{
  position: relative;
  min-height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 104px var(--gut) 34px;
  overflow: hidden;
  isolation: auto;
}

.hero__field{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 1;
  mix-blend-mode: multiply;
}

.hero__inner{
  position: relative;          /* no z-index: the canvas must sit between
                                  the ink field and the type */
  grid-row: 1 / -1;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 24px;
}

/* The blue is a real box in the layout, so the reversed wordmark can never
   land on bare paper — no JS, no percentages, nothing to fall out of sync. */
.hero__ink{
  position: relative;
  align-self: end;
  display: flex;
  flex-direction: column;
  gap: clamp(26px, 4vw, 52px);
  margin-inline: calc(var(--gut) * -1);
  margin-bottom: -34px;
  padding: clamp(20px, 2.4vw, 34px) var(--gut) 34px;
}
.hero__ink::before{
  content: "";
  position: absolute; inset: 0;
  background: var(--ink-blue);
  z-index: 0;
}

/* Type paints above the halftone canvas and stays crisp */
.plates, .wordmark, .hero__foot{ position: relative; z-index: 2; }

/* Plate manifest */
.plates{
  display: flex; flex-wrap: wrap; gap: 8px 26px;
  font-family: var(--mono);
  font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-stock);
}
.plates span{ display: inline-flex; align-items: center; gap: 7px; }
.plates b{
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--patch);
  mix-blend-mode: multiply;
}

/* ── The wordmark: three plates, out of register ──────────────────────── */
.wordmark{
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 96, "opsz" 96;
  text-transform: none;
  line-height: 0.82;
  letter-spacing: -0.035em;
  user-select: none;
}

.wordmark__row{
  position: relative;
  display: block;
  font-size: clamp(3.2rem, 12.5vw, 10rem);
}
.wordmark__row--lg{
  font-size: clamp(4.6rem, 23vw, 19.5rem);
  letter-spacing: -0.045em;
  margin-top: -0.04em;
}

.plate{ display: block; white-space: nowrap; }

.plate--pink, .plate--blue{
  position: absolute; inset: 0;
  mix-blend-mode: multiply;
  user-select: none;
  color: transparent;
  -webkit-background-clip: text;
  background-clip: text;
  will-change: transform;
}

/* Halftone screens — different pitch per plate reads as a different angle */
.plate--pink{
  background-image:
    radial-gradient(var(--ink-pink) 41%, transparent 43%),
    radial-gradient(var(--ink-pink) 41%, transparent 43%);
  background-size: 7px 7px;
  background-position: 0 0, 3.5px 3.5px;
  transform: translate3d(calc(var(--reg-x) * -1), calc(var(--reg-y) * -1), 0);
}
.plate--blue{
  background-image:
    radial-gradient(var(--ink-blue) 44%, transparent 46%),
    radial-gradient(var(--ink-blue) 44%, transparent 46%);
  background-size: 9px 9px;
  background-position: 0 0, 4.5px 4.5px;
  transform: translate3d(var(--reg-x), var(--reg-y), 0);
}

/* Key plate carries the readable text: solid, opaque, never blended.
   The whole wordmark sits on the flood, so it reverses out of the ink. */
.plate--key{
  position: relative;
  color: var(--stock);
}

/* Vertical density bar */
.hero__bar{
  position: absolute; left: 0; top: 30%;
  z-index: 2;
  display: flex; flex-direction: column;
}
.hero__bar i{
  width: clamp(10px, 1.4vw, 18px);
  height: clamp(22px, 3.4vw, 44px);
  background: var(--patch);
  mix-blend-mode: multiply;
}

/* Registration targets */
.reg{
  position: absolute; z-index: 2;
  width: 34px; height: 34px;
  fill: none; stroke: var(--ink-key); stroke-width: 1.25;
  opacity: .5;
  animation: spin 26s linear infinite;
}
.reg--a { top: 92px; right: var(--gut); }
/* Sits in the gap between the role copy and the trim block */
.reg--b { bottom: 44px; right: 46%; stroke: var(--stock); opacity: .6; }

@keyframes spin{ to { transform: rotate(360deg); } }

.hero__foot{
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 40px; flex-wrap: wrap;
}

.hero__role{
  max-width: 36ch;
  font-size: clamp(16px, 1vw + 11px, 21px);
  line-height: 1.4;
  color: var(--on-blue);
  text-wrap: balance;
}

.hero__cue{
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--mono);
  font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-blue);
  padding: 10px 16px 10px 12px;
  border: 1px solid rgba(222, 221, 214, .5);
  border-radius: 999px;
}
.hero__cue-icon{
  position: relative; width: 15px; height: 15px; flex: none;
}
.hero__cue-icon::before, .hero__cue-icon::after{
  content: ""; position: absolute; top: 2px;
  width: 11px; height: 11px; border-radius: 50%;
}
.hero__cue-icon::before{ left: 0; background: var(--ink-pink); }
.hero__cue-icon::after { left: 4px; background: var(--ink-yellow); mix-blend-mode: multiply; }

.hero__foot-right{
  display: flex; flex-direction: column; align-items: flex-end; gap: 14px;
}

.hero__meta{
  display: flex; gap: 18px;
  font-family: var(--mono);
  font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-blue-soft);
}

/* ═══════════════════════════ COLOUR BAR ═══════════════════════════ */
.colourbar{
  background: var(--ink-key);
  color: var(--stock);
  padding: 15px 0;
  overflow: hidden;
}
.colourbar__track{ display: flex; width: max-content; }
.colourbar__group{
  display: flex; align-items: center; gap: 20px;
  padding-right: 20px;
  font-family: var(--mono);
  font-size: clamp(11px, 0.2vw + 10px, 12.5px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--on-key);
}
.colourbar__group em{
  width: 30px; height: 13px; flex: none;
  background: var(--patch);
  font-style: normal;
}

/* ═══════════════════════════ STATEMENT ═══════════════════════════ */
.statement{ padding: clamp(90px, 13vw, 190px) 0 clamp(60px, 8vw, 110px); }

.statement__copy{
  font-family: var(--display);
  font-weight: 600;
  font-variation-settings: "wdth" 92, "opsz" 40;
  font-size: clamp(1.6rem, 4.4vw, 4rem);
  line-height: 1.06;
  letter-spacing: -0.03em;
  max-width: 19ch;
  text-wrap: pretty;
}
.statement__copy mark{
  background: var(--ink-pink);
  color: var(--ink-key);
  mix-blend-mode: multiply;
  padding: 0 .06em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* word-by-word ink-down */
[data-reveal] .wd{
  display: inline-block;
  opacity: .16;
  transition: opacity .5s var(--ease);
}
[data-reveal] .wd.on{ opacity: 1; }

/* ═══════════════════════════ SECTION HEADS ═══════════════════════════ */
.sec-head{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
  padding-bottom: 26px;
  border-bottom: 1px solid rgba(22, 22, 26, .22);
  margin-bottom: 8px;
}
.sec-head h2{
  font-family: var(--display);
  font-variation-settings: "wdth" 94, "opsz" 48;
  font-size: clamp(1.5rem, 2.6vw, 2.6rem);
}
.sec-head .mono{ color: var(--on-stock-soft); }

/* ═══════════════════════════ WORK ═══════════════════════════ */
.work{ padding-bottom: clamp(80px, 11vw, 150px); }

.index__row{ border-bottom: 1px solid rgba(22, 22, 26, .22); }

.index__row a{
  --o: 0px;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) auto;
  align-items: center;
  gap: 20px;
  padding: clamp(20px, 2.6vw, 38px) 0;
}

.index__name{
  font-family: var(--display);
  font-weight: 700;
  font-variation-settings: "wdth" 96, "opsz" 72;
  font-size: clamp(2rem, 5.6vw, 5rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  /* Misregistration on hover — pink and blue fringe either side of key */
  text-shadow:
    calc(var(--o) * -1) 0 0 var(--ink-pink),
    var(--o) 0 0 var(--ink-blue);
  transition: text-shadow .55s var(--ease), transform .55s var(--ease);
}
.index__row a:hover .index__name,
.index__row a:focus-visible .index__name{ --o: 5px; }

.index__cat{
  font-size: clamp(14px, 0.7vw + 10px, 17px);
  color: var(--on-stock-soft);
  transition: transform .55s var(--ease);
}

/* The row leans in — transform, so nothing reflows */
.index__row a:hover .index__name,
.index__row a:focus-visible .index__name,
.index__row a:hover .index__cat,
.index__row a:focus-visible .index__cat{
  transform: translateX(clamp(10px, 1.6vw, 30px));
}
.index__year{
  font-family: var(--mono); font-size: 12px;
  letter-spacing: .14em; color: var(--on-stock-soft);
}

/* Floating separation proof */
.proof{
  position: fixed; top: 0; left: 0;
  z-index: 85;
  width: clamp(220px, 21vw, 320px);
  aspect-ratio: 520 / 660;
  pointer-events: none;
  opacity: 0;
  /* A proof is ink on paper, not ink in mid-air */
  background: var(--stock-hi);
  box-shadow: 10px 14px 34px rgba(22, 22, 26, .22);
  transition: opacity .4s var(--ease);
  will-change: transform;
}
.proof[data-on="true"]{ opacity: 1; }
.proof canvas{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  mix-blend-mode: multiply;
  will-change: transform;
}

/* ═══════════════════════════ CAPABILITIES ═══════════════════════════ */
.caps{ padding-bottom: clamp(90px, 12vw, 170px); }

.spec li{
  display: grid;
  grid-template-columns: 26px minmax(0, 20ch) minmax(0, 1fr);
  gap: 20px clamp(20px, 3vw, 56px);
  align-items: start;
  padding: clamp(22px, 2.4vw, 34px) 0;
  border-bottom: 1px solid rgba(22, 22, 26, .16);
}
.spec b{
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--patch);
  mix-blend-mode: multiply;
  margin-top: .35em;
}
.spec h3{
  font-family: var(--display);
  font-variation-settings: "wdth" 94, "opsz" 32;
  font-size: clamp(1.15rem, 1.9vw, 1.75rem);
  line-height: 1;
}
.spec p{ max-width: 62ch; color: var(--on-stock-soft); }

/* ═══════════════════════════ APPROACH · PINK FLOOD ═══════════════════════════ */
.approach{
  background: var(--ink-pink);
  color: var(--ink-key);
  padding: clamp(80px, 11vw, 150px) 0 clamp(80px, 11vw, 150px);
}

.plate-label{
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono);
  font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: clamp(40px, 6vw, 80px);
}
.plate-label b{
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--patch);
  border: 1px solid rgba(22,22,26,.35);
}

.approach__lead{
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 90, "opsz" 72;
  font-size: clamp(2rem, 6.2vw, 5.6rem);
  line-height: 0.94;
  letter-spacing: -0.04em;
  max-width: 16ch;
  text-wrap: balance;
  margin-bottom: clamp(56px, 8vw, 110px);
}

.principles{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(32px, 4vw, 64px);
  border-top: 1.5px solid var(--ink-key);
  padding-top: 32px;
}
.principles li > span{
  display: block;
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--on-pink-soft);
  margin-bottom: 18px;
}
.principles p{ color: var(--on-pink-soft); }
.principles h3{
  font-family: var(--display);
  font-variation-settings: "wdth" 94, "opsz" 32;
  font-size: clamp(1.2rem, 2vw, 1.7rem);
  margin-bottom: 12px;
}
.principles p{ max-width: 42ch; }

/* ═══════════════════════════ CONTACT · KEY FLOOD ═══════════════════════════ */
.contact{
  background: var(--ink-key);
  color: var(--stock);
  padding-top: clamp(80px, 11vw, 150px);
}

.cta{
  display: block;
  position: relative;
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 92, "opsz" 96;
  font-size: clamp(2.6rem, 11vw, 10rem);
  line-height: 0.86;
  letter-spacing: -0.045em;
  padding-bottom: clamp(50px, 7vw, 90px);
}
.cta__row{ display: block; }

.cta__row .ch{
  display: inline-block;
  transition: transform .5s var(--ease), color .5s var(--ease);
}
.cta:hover .ch{ color: var(--ink-yellow); }
.cta .ch[data-sp]{ width: .28em; }

.cta__arrow{
  display: inline-block;
  font-family: var(--text);
  font-size: clamp(1.4rem, 3vw, 2.6rem);
  vertical-align: super;
  transition: transform .5s var(--ease);
}
.cta:hover .cta__arrow{ transform: translateX(.35em); }

.contact__grid{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: clamp(26px, 3vw, 48px);
  border-top: 1px solid rgba(222, 221, 214, .28);
  padding-top: 30px;
  padding-bottom: clamp(60px, 8vw, 110px);
}
.contact__grid dt{
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--on-key-soft);
  margin-bottom: 10px;
}
.contact__grid dd{ font-size: clamp(15px, 1vw + 11px, 18px); }
.contact__grid a{ border-bottom: 1px solid rgba(222,221,214,.35); }
.contact__grid a:hover{ color: var(--ink-pink); border-color: var(--ink-pink); }

/* ── The close ────────────────────────────────────────────────────────────
   The last thing on every page. A headline and an arrow was not an ask — it
   needs a real action, a second way through, and the two facts that decide
   whether someone writes: whether there's room, and how fast a reply comes.
   ───────────────────────────────────────────────────────────────────────── */
.close__act{
  display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(12px, 1.6vw, 20px);
  margin-top: clamp(30px, 4vw, 52px);
}
.btn{
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--text); font-weight: 600;
  font-size: clamp(15px, 0.6vw + 12px, 18px);
  padding: clamp(14px, 1.5vw, 19px) clamp(22px, 2.4vw, 32px);
  border-radius: 999px;
  border: 1.5px solid transparent;
  transition: background .35s var(--ease), color .35s var(--ease),
              border-color .35s var(--ease), transform .35s var(--ease);
}
.btn span{ transition: transform .4s var(--ease); }
.btn:hover span{ transform: translateX(5px); }
.btn:hover{ transform: translateY(-2px); }

.btn--primary{ background: var(--ink-yellow); color: var(--ink-key); border-color: var(--ink-yellow); }
.btn--primary:hover{ background: var(--ink-pink); border-color: var(--ink-pink); }

.btn--ghost{ color: var(--on-key); border-color: rgba(222, 221, 214, .45); }
.btn--ghost:hover{ border-color: var(--on-key); background: rgba(222, 221, 214, .07); }

.close__act .btn--ghost{
  background: var(--stock);
  border-color: var(--stock);
  color: var(--ink-key);
}
.close__act .btn--ghost:hover{
  background: var(--stock-hi);
  border-color: var(--stock-hi);
  color: var(--ink-key);
}

.close__status{
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  margin-top: clamp(24px, 3vw, 36px);
  font-family: var(--mono);
  font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--on-key-soft);
}
.close__status i{
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--ink-yellow); flex: none;
  animation: throb 2.4s var(--ease) infinite;
}

/* the plate number, overprinted like every other panel */
.contact .svc__num{ color: var(--ink-pink); mix-blend-mode: normal; }

/* ── The close, as the hero's negative ────────────────────────────────────
   The hero is ink printed on paper. This is the same press run inverted:
   paper-bright dots laid onto solid key, drifting, blooming under the
   cursor. Same machinery, opposite polarity — which is what makes it the
   second real moment on the site rather than a footer with big type.
   ───────────────────────────────────────────────────────────────────────── */
.contact{
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.contact > .wrap,
.contact > .foot{ position: relative; z-index: 2; }

/* normal blend, not multiply: multiply over near-black erases everything.
   Typed as canvas.* deliberately — the base .field rule is declared far
   later in this file, so a bare class would lose on source order. */
canvas.field--close{
  mix-blend-mode: normal;
  z-index: 0;
}

/* the headline carries the live registration, like every other plated head */
.contact .cta{
  --pl-a: var(--ink-pink);
  --pl-b: var(--ink-blue);
}

@media (prefers-reduced-motion: reduce){
  .close__status i{ animation: none; }
}

/* ── Footer ───────────────────────────────────────────────────────────────
   A proper close: sitemap, real detail, and a density bar. The CTA above it
   does the asking; this does the answering.
   ───────────────────────────────────────────────────────────────────────── */
.foot{
  border-top: 1px solid rgba(222, 221, 214, .28);
  margin-top: clamp(50px, 7vw, 90px);
  padding-top: clamp(34px, 4.5vw, 56px);
  color: var(--on-key-soft);
  /* solid ground: the closing field is densest at the foot of the panel and
     would otherwise run straight through the sitemap */
  background: var(--ink-key);
}
.foot__grid{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 175px), 1fr));
  gap: clamp(28px, 3.5vw, 54px);
  padding-bottom: clamp(34px, 4.5vw, 56px);
}
.foot__col{ display: grid; gap: 9px; align-content: start; }
.foot__h{
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-yellow);
  margin-bottom: 5px;
}
.foot__col a,
.foot__col span{ font-size: 15px; color: var(--on-key-soft); }
.foot__col a{ width: fit-content; border-bottom: 1px solid transparent; transition: color .3s var(--ease), border-color .3s var(--ease); }
.foot__col a:hover{ color: var(--on-key); border-bottom-color: var(--ink-pink); }
.foot__col strong{ color: var(--on-key); font-weight: 600; }

/* density bar, same furniture as the hero */
.foot__bar{
  display: flex; height: 12px;
  margin-bottom: clamp(18px, 2vw, 26px);
}
.foot__bar i{ flex: 1; background: var(--patch); }

.foot__legal{
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding-bottom: 24px;
  color: var(--on-key-soft);
}
.foot__legal a:hover{ color: var(--ink-yellow); }

/* ═══════════════════════════ RESPONSIVE ═══════════════════════════ */

/* Lateral registration on small screens.
   A press misregisters in two named ways: laterally, across the web, and
   circumferentially, around the cylinder. The site normally shows both at
   once — a diagonal offset. On a phone the vertical half is the half that
   destroys the type: it drops the ghost into the x-height band, filling the
   counters and welding each line to the one under it. So below 900px the
   plates go lateral only. It is not a watered-down version of the effect,
   it is the other real one, and it leaves the horizontal strokes clean.
   The live --reg shear is untouched: pulling the registration still moves
   the plates in both axes, because that is the visitor's own hand. */
@media (max-width: 900px){
  :root{ --plate-y: 0; }
}

@media (max-width: 900px){
  .nav__links{ display: none; }
  .hero__bar{ display: none; }
  .reg--a{ top: 78px; }
  .reg--b{ display: none; }
  .index__row a{ grid-template-columns: 1fr auto; row-gap: 6px; }
  .index__cat{ grid-column: 1; }
  .index__year{ grid-column: 2; grid-row: 1; }
  .spec li{ grid-template-columns: 18px 1fr; }
  .spec p{ grid-column: 2; }
  .proof{ display: none; }
}

@media (max-width: 560px){
  .hero{ padding-top: 96px; }
  .hero__foot{ flex-direction: column; align-items: flex-start; gap: 22px; }
  .hero__meta{ display: none; }
  .plates span:nth-child(3){ display: none; }
  .trim{ width: 18px; height: 18px; }
}

/* ═══════════════════════════ REDUCED MOTION ═══════════════════════════ */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  :root{ --reg-x: 2px; --reg-y: 1px; }   /* plates rest slightly off, as they do */
  [data-reveal] .wd{ opacity: 1; }
  .proof{ display: none; }
}

/* No-JS: everything is already visible; reveal spans simply never appear */


/* ═══════════════════════════════════════════════════════════════════════════
   NAV TOGGLE + FULL-SHEET MENU
   ═══════════════════════════════════════════════════════════════════════════ */
.nav__toggle{
  display: none;
  align-items: center; gap: 10px;
  font-family: var(--mono);
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase;
  background: none; border: 1.5px solid currentColor; color: inherit;
  border-radius: 999px; padding: 9px 15px;
  cursor: pointer;
}
.nav__toggle-dots{ position: relative; width: 20px; height: 10px; flex: none; }
.nav__toggle-dots i{
  position: absolute; top: 0; width: 10px; height: 10px; border-radius: 50%;
  mix-blend-mode: multiply;
  transition: transform .45s var(--ease);
}
.nav__toggle-dots i:first-child{ left: 0;  background: var(--ink-pink); }
.nav__toggle-dots i:last-child { left: 8px; background: var(--ink-blue); }
.nav__toggle[aria-expanded="true"] .nav__toggle-dots i:first-child{ transform: translateX(-3px); }
.nav__toggle[aria-expanded="true"] .nav__toggle-dots i:last-child { transform: translateX(3px); }

/* The sheet is the only navigation a phone gets, so it is built as a page of
   the site rather than a list on a black rectangle: a plate label at the
   head, the five pages numbered and specced like the ink chart, a two-ink
   screen ramping up out of the bottom edge, and the CTA that .nav__cta has
   to give up at this width. */
.menu{
  position: fixed; inset: 0; z-index: 100;
  background: var(--ink-key);
  color: var(--on-key);
  display: flex; flex-direction: column;
  gap: clamp(18px, 4vh, 34px);
  padding: 96px var(--gut) max(28px, env(safe-area-inset-bottom));
  clip-path: inset(0 0 100% 0);
  transition: clip-path .78s var(--ease);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.menu[data-open="true"]{ clip-path: inset(0 0 0 0); }
.menu[aria-hidden="true"]{ pointer-events: none; }
.menu[data-open="true"]{ pointer-events: auto; }
/* tabindex="-1" so it can be focused on open; it is not tab-reachable, and a
   ring around a full-screen sheet tells nobody anything. */
.menu:focus{ outline: none; }

/* ── Two-ink screen, on the real angles, ramping out of the bottom ──────
   Same halftone language as the dissolves, but built in CSS: it costs no
   canvas and no frame budget, which matters on the one surface that is
   always a phone. It shears with the registration spring like everything
   else, so dragging the sheet pulls the screen apart too. */
.menu__screen{
  position: absolute; inset: 0;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity .9s var(--ease) .18s;
  /* A band, not a wash. Ramped in behind the lower half of the list and out
     again before the foot, so the screen reads as ink laid across the sheet
     and the CTA and contact line still sit on clean key. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 26%, #000 60%, #000 72%, transparent 93%);
          mask-image: linear-gradient(to bottom, transparent 26%, #000 60%, #000 72%, transparent 93%);
}
/* Kept low enough that the 7.6:1 mono over it still clears AA */
.menu[data-open="true"] .menu__screen{ opacity: .28; }
.menu__screen::before,
.menu__screen::after{
  content: ""; position: absolute; inset: -50%;
  background-image: radial-gradient(circle, currentColor 32%, transparent 33%);
}
/* Different pitch on each ink as well as different angle: two identical
   grids 60° apart beat against each other into visible blotches, which is
   real moiré but reads as noise rather than as a screen. */
.menu__screen::before{
  color: var(--ink-pink);
  background-size: 15px 15px;
  transform: rotate(15deg) translate3d(var(--reg-x), var(--reg-y), 0);
}
.menu__screen::after{
  color: var(--ink-blue);
  background-size: 19px 19px;
  transform: rotate(75deg) translate3d(calc(var(--reg-x) * -1), calc(var(--reg-y) * -1), 0);
}

/* ── Plate label ─────────────────────────────────────────────────────── */
.menu__head{
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  border-bottom: 1px solid rgba(222, 221, 214, .26);
  padding-bottom: 12px;
}
.menu__plate{
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--on-key-soft);
}
.menu__inks{ display: flex; gap: 5px; flex: none; }
.menu__inks i{ width: 9px; height: 9px; border-radius: 50%; background: var(--patch); }

/* ── The five pages ──────────────────────────────────────────────────── */
.menu__list{
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  margin-block: auto;              /* centred in the sheet, foot stays pinned */
}
.menu__list li{
  overflow: hidden;                /* the words rise out from behind the rule */
  border-bottom: 1px solid rgba(222, 221, 214, .16);
}
.menu__list li:last-child{ border-bottom: 0; }

.menu__list a{
  --o: 0px;
  display: grid;
  grid-template-columns: 2.4em 1fr auto;
  align-items: baseline;
  gap: 0 .5em;
  padding-block: clamp(9px, 1.6vh, 16px);
  color: var(--on-key);
  transform: translateY(105%);
  transition: transform .7s var(--ease);
}
.menu[data-open="true"] .menu__list a{ transform: translateY(0); }

.menu__n{
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .1em;
  color: var(--n-ink, var(--on-key-soft));
  transform: translateY(-.15em);   /* mono baseline sits low against display */
}
/* Pink and yellow only. Federal Blue on a Key flood measures 2:1 — it is a
   real ink on this sheet but it cannot carry text at 11px, so it stays in
   the screen behind and out of the numbering. */
.menu__list li:nth-child(odd)  a{ --n-ink: var(--ink-pink); }    /* 5.9:1  */
.menu__list li:nth-child(even) a{ --n-ink: var(--ink-yellow); }  /* 14.7:1 */

.menu__w{
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 94, "opsz" 72;
  /* min() against svh so the five words never push the CTA off a short
     phone — the sheet scrolls if it has to, but it should not have to */
  font-size: clamp(1.9rem, min(12vw, 6.4svh), 5rem);
  line-height: 1.0;
  letter-spacing: -0.04em;
  transition: text-shadow .45s var(--ease), color .45s var(--ease);
  text-shadow:
    calc(var(--o) * -1) 0 0 var(--ink-pink),
    var(--o) 0 0 var(--ink-blue);
}
.menu__m{
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--on-key-soft);
  transform: translateY(-.2em);
}

@media (hover: hover){
  .menu__list a:hover .menu__w{ --o: 6px; }
  .menu__list a:hover .menu__m{ color: var(--on-key); }
}
.menu__list a:focus-visible .menu__w{ --o: 6px; }

/* The page you are on is already printed: yellow, its number filled, and the
   two other inks pulled out either side of it. Three plates on one word is
   the loudest thing the sheet can say, which is the point — it is the only
   item in the list you cannot navigate to. */
.menu__list a[aria-current="page"] .menu__w{
  --o: 0.13em;
  color: var(--ink-yellow);
}
.menu__list a[aria-current="page"] .menu__n{
  color: var(--ink-key);
  background: var(--ink-yellow);
  padding: .25em .45em;
  margin-left: -.45em;
}

/* ── Foot: the CTA the top bar gives up at this width, then the details ── */
.menu__foot{
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 18px;
}
.menu__cta{
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--ink-yellow);
  color: var(--ink-key);
  font-weight: 600;
  font-size: clamp(15px, 4.2vw, 18px);
  border-radius: 999px;
  padding: 15px 22px;
  transition: transform .45s var(--ease), background .3s var(--ease);
}
.menu__cta span{ transition: transform .45s var(--ease); }
@media (hover: hover){
  .menu__cta:hover{ background: var(--ink-pink); }
  .menu__cta:hover span{ transform: translateX(5px); }
}
.menu__cta:active{ transform: scale(.985); }

.menu__meta{
  display: flex; flex-wrap: wrap; gap: 10px 28px;
  justify-content: space-between; align-items: flex-end;
  border-top: 1px solid rgba(222, 221, 214, .26);
  padding-top: 16px;
  font-size: 13px;
  color: var(--on-key-soft);
}
.menu__meta a{ color: var(--on-key); border-bottom: 1px solid rgba(222,221,214,.4); }
.menu__meta a:hover{ color: var(--ink-yellow); border-color: var(--ink-yellow); }

/* An SE-sized screen fits all five words, the CTA and the contact line with
   about two pixels to spare — which is to say it doesn't. Buy the room back
   from the margins rather than the type. */
@media (max-height: 720px){
  .menu{ padding-top: 82px; gap: 14px; }
  .menu__list a{ padding-block: 8px; }
}

@media (prefers-reduced-motion: reduce){
  .menu__list a{ transform: none; }
  .menu__screen{ opacity: .28; transition: none; }
}

body[data-menu="open"]{ overflow: hidden; }

/* The sheet covers the nav, so lift the toggle above it — otherwise there is
   no visible way back out. Reversed to read on the key flood. */
body[data-menu="open"] .nav{
  z-index: 110;
  color: var(--on-key);
  transform: none;
}
body[data-menu="open"] .nav[data-stuck="true"]{
  background: transparent;
  border-bottom-color: transparent;
}
body[data-menu="open"] .nav__dots i,
body[data-menu="open"] .nav__toggle-dots i{ mix-blend-mode: normal; }

@media (max-width: 900px){
  .nav__toggle{ display: inline-flex; }
  .nav__cta{ display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   RISO IMAGE — any photograph, separated into spot-ink plates
   ═══════════════════════════════════════════════════════════════════════════ */
.riso{
  position: relative;
  display: block;
  aspect-ratio: var(--ratio, 4 / 5);   /* holds layout whether or not the
                                          image file exists yet */
  background: var(--stock-hi);
  overflow: hidden;
  isolation: isolate;
}
.riso img{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* The image element stays in the DOM so its alt text stays in the a11y tree */
.riso[data-ready="true"] img{ opacity: 0; }

.riso__plate{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  mix-blend-mode: multiply;
  pointer-events: none;
}
/* --sep is scroll-driven: the plates drift apart as the image travels the
   viewport, so a still photograph is never quite still. */
.riso__plate--pink{ transform: translate3d(calc(var(--reg-x) * -0.45 - var(--sep, 0px)), calc(var(--reg-y) * -0.45), 0); }
.riso__plate--blue{ transform: translate3d(calc(var(--reg-x) *  0.45 + var(--sep, 0px)), calc(var(--reg-y) *  0.45), 0); }

/* ═══════════════════════════════════════════════════════════════════════════
   SCREEN CAPTURE — mounted, not separated
   A website screenshot put through the halftone becomes unreadable, and the
   whole point of the image is to show the work. So the capture stays clean
   and the world is carried by the mount: paper margin, key rule, and the two
   ink plates sitting behind it, which pull out into view on the drag.
   ═══════════════════════════════════════════════════════════════════════════ */
.shot{
  --pad: clamp(9px, 1.1vw, 16px);
  position: relative;
  display: block;
  aspect-ratio: var(--ratio, 4 / 5);
  background: var(--stock-hi);
  padding: var(--pad);
  border: 1px solid rgba(22, 22, 26, .22);
  isolation: isolate;
}
.shot::before, .shot::after{
  content: "";
  position: absolute; inset: var(--pad);
  z-index: 0;
  mix-blend-mode: multiply;
  will-change: transform;
}
.shot::before{
  background: var(--ink-pink);
  transform: translate3d(calc(var(--reg-x) * -1 - var(--sep, 0px)), calc(var(--reg-y) * -1), 0);
}
.shot::after{
  background: var(--ink-blue);
  transform: translate3d(calc(var(--reg-x) + var(--sep, 0px)), var(--reg-y), 0);
}
.shot img{
  position: relative; z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}
.workcard .shot{ transition: transform .6s var(--ease); }
.workcard a:hover .shot, .workcard a:focus-visible .shot{ transform: translateY(-6px); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE CLIENT SHEET — work index
   No cards, no frames. Every mark is burned to a stencil and pulled in two
   spot inks straight onto the paper, deliberately out of register. Colour is
   assigned per panel, the way a screenprinter picks ink, not taken from the
   client's brand guidelines.
   ═══════════════════════════════════════════════════════════════════════════ */
.wall{
  padding-bottom: clamp(70px, 9vw, 130px);
  /* Un-printed plates rest 34px off-register, which would otherwise widen the
     document. `clip` rather than `hidden`: no scroll container is created. */
  overflow-x: clip;
}

.sheet-head__run{
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 11.5px;
  letter-spacing: .14em; text-transform: uppercase;
  background: none; color: inherit; cursor: pointer;
  border: 1.5px solid var(--ink-key);
  border-radius: 999px; padding: 11px 19px;
  transition: background .35s var(--ease), color .35s var(--ease);
}
.sheet-head__run:hover{ background: var(--ink-key); color: var(--stock); }
.sheet-head__run i{
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--ink-pink);
}
.sheet-head__run:hover i{ background: var(--ink-yellow); }

.wall__band{
  position: relative;
  padding-block: clamp(96px, 11vw, 160px) clamp(56px, 8vw, 120px);
}
/* the marks must sit above the band's plate number and dissolve */
.wall__band > .wrap{ position: relative; z-index: 2; }
.wall__band--flood{
  background: var(--ink-blue);
  margin-block: clamp(40px, 6vw, 90px);
}

.jobs{
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(56px, 8vw, 130px) clamp(34px, 5vw, 80px);
  align-items: end;
}
.job{ grid-column: span var(--span, 6); }
.job--drop{ margin-top: clamp(34px, 7vw, 110px); }

/* ── The impression ───────────────────────────────────────────────────── */
.mark{
  position: relative;
  display: block;
  line-height: 0;
}
/* The stencil stays in the DOM for its alt text, but never prints itself */
.mark > img{
  width: 100%; height: auto;
  visibility: hidden;
}
/* size and offset are set inline — the canvas is grown past the mark so
   edge dots are never sliced */
.mark__plate{
  position: absolute;
  mix-blend-mode: multiply;
  opacity: 0;
  will-change: transform;
  transition: transform 1.05s cubic-bezier(.14, .9, .2, 1), opacity .45s linear;
}
.mark__plate--norm{ mix-blend-mode: normal; }

/* Off the press: the two screens arrive from opposite sides and converge.
   Fixed px, not %, or a wide wordmark throws its plates into the next column. */
.mark__plate--0{ transform: translate3d(-34px, -16px, 0) rotate(-2.4deg) scale(1.03); }
.mark__plate--1{ transform: translate3d( 34px,  16px, 0) rotate( 2.4deg) scale(1.03); }

.job.is-printed .mark__plate{ opacity: 1; }
.job.is-printed .mark__plate--0{
  transform: translate3d(calc(var(--reg-x) * -1 - 2px), calc(var(--reg-y) * -1 - 1px), 0);
}
.job.is-printed .mark__plate--1{
  transform: translate3d(calc(var(--reg-x) + 2px), calc(var(--reg-y) + 1px), 0);
}
.job.is-printed .mark__plate--1{ transition-delay: .09s; }

/* Hover pulls the registration apart — the misprint, on purpose */
.job a:hover .mark__plate--0,
.job a:focus-visible .mark__plate--0{
  transform: translate3d(-26px, -12px, 0) rotate(-1.1deg);
  transition-duration: .5s;
}
.job a:hover .mark__plate--1,
.job a:focus-visible .mark__plate--1{
  transform: translate3d(26px, 12px, 0) rotate(1.1deg);
  transition-duration: .5s; transition-delay: 0s;
}

/* ── Caption ──────────────────────────────────────────────────────────── */
.job__line{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px 20px; flex-wrap: wrap;
  margin-top: clamp(20px, 2.4vw, 34px);
  padding-top: 14px;
  border-top: 1.5px solid currentColor;
}
.job__name{
  font-family: var(--display); font-weight: 700;
  font-variation-settings: "wdth" 96, "opsz" 32;
  font-size: clamp(1.15rem, 1.7vw, 1.7rem);
  line-height: 1; letter-spacing: -0.03em;
}
.job__cat{ display: block; font-size: 14.5px; margin-top: 7px; opacity: .78; }
.job__ink{
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .14em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 7px;
  opacity: .78;
}
.job__ink i{ width: 9px; height: 9px; border-radius: 50%; background: var(--sw); }

.wall__band--flood .job{ color: var(--on-blue); }
.wall__band--flood .job__cat,
.wall__band--flood .job__ink{ opacity: .92; }

/* ── A job, set in type ───────────────────────────────────────────────────
   The whole index is typographic. Client marks were tried and pulled: seven
   logos in seven visual languages read as seven brands, not one sheet, and
   the stencils looked stuck-on. The name set large in two inks is the mark.
   ───────────────────────────────────────────────────────────────────────── */
@property --to {
  syntax: "<length>";
  inherits: false;
  initial-value: 4px;
}

.job__type{
  --to: var(--plate-oj);
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 90, "opsz" 96;
  font-size: var(--tsize, clamp(2.1rem, 5.5vw, 4.6rem));
  line-height: 0.84;
  letter-spacing: -0.05em;
  text-wrap: balance;
  color: var(--type-fg, var(--ink-key));
  text-shadow:
    calc((var(--reg-x) * -1) - var(--to)) calc((var(--reg-y) * -1) - var(--to) * var(--plate-y)) 0 var(--pl-a, var(--ink-pink)),
    calc(var(--reg-x) + var(--to))        calc(var(--reg-y) + var(--to) * var(--plate-y))        0 var(--pl-b, var(--ink-blue));
  transition: --to .55s var(--ease);
}
/* Hover only where hovering exists. On a touch screen :hover latches on tap,
   so this used to fire the 17px pull apart as the visitor left for the case
   study — the last thing they saw of the index was it falling out of joint. */
@media (hover: hover){
  .job a:hover .job__type{ --to: 17px; }
}
.job a:focus-visible .job__type{ --to: 17px; }

.wall__band--flood .job__type{
  --type-fg: var(--on-blue);
  --pl-a: var(--ink-yellow);
  --pl-b: var(--ink-pink);
}

@media (prefers-reduced-motion: reduce){
  .job__type{ transition: none; }
  .job a:hover .job__type{ --to: var(--plate-oj); }
}

/* ── Set in type, where there is no mark yet ──────────────────────────── */
.job__set{
  display: block;
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 90, "opsz" 96;
  font-size: clamp(2.6rem, 7vw, 5.4rem);
  line-height: 0.88; letter-spacing: -0.045em;
  color: var(--ink-key);
  --so: var(--plate-oj);
  text-shadow:
    calc(var(--reg-x) * -1 - var(--so)) calc(var(--reg-y) * -1 - var(--so) * var(--plate-y)) 0 var(--ink-pink),
    calc(var(--reg-x) + var(--so))      calc(var(--reg-y) + var(--so) * var(--plate-y))      0 var(--ink-blue);
}

@media (max-width: 900px){
  .job{ grid-column: span 12; }
  .job--drop{ margin-top: 0; }
  .jobs{ gap: clamp(48px, 12vw, 80px) 0; }
  /* 0.84 is a display leading. At phone sizes the names wrap to two lines and
     the ghost of one line landed inside the next; open it up enough to part
     them without losing the stacked, set-solid look. */
  .job__type{ line-height: 0.92; }

  /* One ink behind the name instead of two.
     Which pair a job pulls is authored per entry, and of the five inks only
     Fluorescent Pink stays legible as a fringe at phone sizes: Federal Blue
     is dark enough on stock to read as a blurred second word, and Key behind
     Key type is not an impression at all, just a thickening. So on a phone
     every job pulls pink alone, against the key.
     !important is load-bearing here — the pair is set in a style attribute
     on each entry, and nothing but !important outranks inline. */
  .job__type{
    --pl-a: var(--ink-pink) !important;
    --pl-b: transparent !important;
  }
}

@media (prefers-reduced-motion: reduce){
  .mark__plate{ opacity: 1; transform: none !important; }
  .job.is-printed .mark__plate--0{ transform: translate3d(-2px, -1px, 0) !important; }
  .job.is-printed .mark__plate--1{ transform: translate3d( 2px,  1px, 0) !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE INK CHART — services
   One service per spot ink, each owning a whole flooded panel. Between panels
   the ink doesn't stop at a line: it breaks into a halftone gradient and
   dissolves into the next colour, the way a riso duct runs dry.
   Namespace is its own (svc-) — this page shares nothing but the shell.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Chart head ───────────────────────────────────────────────────────── */
.svc-head{ padding: clamp(128px, 16vw, 200px) 0 clamp(40px, 5vw, 66px); }
.svc-head h1{
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 90, "opsz" 96;
  font-size: clamp(2.9rem, 11.5vw, 8.5rem);
  line-height: 0.86; letter-spacing: -0.045em;
  margin-bottom: clamp(22px, 3vw, 38px);
}
.svc-head p{
  max-width: 46ch;
  font-size: clamp(17px, 0.8vw + 13px, 21px);
  line-height: 1.45; color: var(--on-stock-soft);
}

/* The chart itself: five ducts, and the page's contents list */
.chart{
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 2px;
  margin-top: clamp(40px, 6vw, 76px);
  border: 1.5px solid var(--ink-key);
  background: var(--ink-key);
}
.chart a{
  display: block;
  background: var(--stock);
  padding: 0;
}
.chart__patch{
  display: block; height: clamp(64px, 11vw, 128px);
  background: var(--duct);
  /* scaleY, not height: animating height reflows the whole chart every frame */
  transform-origin: top;
  transition: transform .5s var(--ease);
}
.chart a:hover .chart__patch,
.chart a:focus-visible .chart__patch{ transform: scaleY(1.07); }
.chart__cap{
  display: block; padding: 15px clamp(8px, 1vw, 14px) 13px;
  font-family: var(--mono); font-size: clamp(8.5px, 0.55vw, 11px);
  letter-spacing: .1em; text-transform: uppercase;
  line-height: 1.45; color: var(--on-stock);
}
.chart__cap b{ display: block; font-weight: 500; }
.chart__cap span{ display: block; color: var(--on-stock-soft); }

@media (max-width: 700px){
  .chart{ grid-template-columns: repeat(2, 1fr); }
  .chart a:last-child{ grid-column: span 2; }
}

/* ── A flooded service panel ──────────────────────────────────────────── */
.svc{
  position: relative;
  background: var(--ink);
  color: var(--fg);
  padding: clamp(96px, 13vw, 190px) 0 clamp(70px, 10vw, 140px);
  overflow: hidden;
}
.svc__grid{
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(30px, 5vw, 90px);
  align-items: start;
}
@media (max-width: 880px){ .svc__grid{ grid-template-columns: 1fr; } }

/* The plate number, printed as a second ink over the flood */
/* Fully inside the panel — the panel clips for the dissolve, so a numeral
   hanging off the top edge gets sliced and breaks the illusion. */
.svc__num{
  position: absolute;
  top: clamp(16px, 2.4vw, 38px);
  right: var(--gut);
  z-index: 1;
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 100, "opsz" 96;
  font-size: clamp(5rem, 16vw, 14rem);
  line-height: 0.74; letter-spacing: -0.06em;
  color: var(--num);
  mix-blend-mode: multiply;
  pointer-events: none; user-select: none;
  /* Scroll drift only. It was wired to the registration drag once and pulled
     back out: a numeral this size sliding under the cursor reads as a bug,
     not as ink. Free-standing marks stay put; only plates misregister. */
  transform: translate3d(0, var(--drift, 0px), 0);
}
.svc__num--over{ mix-blend-mode: normal; }

.svc__label{
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: clamp(10.5px, 0.2vw + 9.5px, 12px);
  letter-spacing: .14em; text-transform: uppercase;
  margin-bottom: clamp(18px, 2.4vw, 30px);
}
.svc__label i{
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--fg);
}
.svc__title{
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 88, "opsz" 96;
  font-size: clamp(2.4rem, 6.4vw, 5.6rem);
  line-height: 0.88; letter-spacing: -0.045em;
  margin-bottom: clamp(20px, 2.6vw, 34px);
}
.svc__lead{
  max-width: 34ch;
  font-size: clamp(17px, 0.9vw + 13px, 22px);
  line-height: 1.38;
}

/* Spec list — hairline rules in the panel's own foreground */
.svc__list{ border-top: 1.5px solid currentColor; }
.svc__list li{
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 16px;
  align-items: baseline;
  padding: clamp(13px, 1.5vw, 20px) 0;
  border-bottom: 1px solid currentColor;
  font-size: clamp(15px, 0.5vw + 13px, 18px);
  line-height: 1.4;
}
.svc__list li > b{
  font-family: var(--mono); font-weight: 400;
  font-size: 11px; letter-spacing: .1em;
  opacity: .75;
}

/* Coverage note, set against the right edge */
/* writing-mode, not rotate(): rotating a long nowrap line pivots it about its
   own centre, which drags it hundreds of pixels into the content column. */
.svc__edge{
  position: absolute; z-index: 2;
  /* left gutter — the duct rail owns the right edge */
  left: clamp(4px, 0.8vw, 14px); top: 50%;
  transform: translateY(-50%) rotate(180deg);
  writing-mode: vertical-rl;
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .22em; text-transform: uppercase;
  white-space: nowrap;
  opacity: .7;
}
@media (max-width: 1100px){ .svc__edge{ display: none; } }

/* ── The dissolve between two inks ────────────────────────────────────── */
.dissolve{
  position: absolute; top: 0; left: 0; right: 0;
  height: clamp(90px, 11vw, 170px);
  z-index: 1;
  pointer-events: none;
}
.dissolve canvas{ width: 100%; height: 100%; display: block; }

/* ── Sticky ink duct rail ─────────────────────────────────────────────── */
.rail{
  position: fixed; z-index: 70;
  right: clamp(14px, 2vw, 30px); top: 50%;
  transform: translateY(-50%);
  display: grid; gap: 9px;
}
/* Fixed box, scaled contents — no width/height animation */
.rail a{
  display: grid; place-items: center;
  width: 22px; height: 46px;
}
.rail a::before{
  content: ""; display: block;
  width: 22px; height: 46px;
  background: var(--duct);
  border: 1px solid rgba(22, 22, 26, .35);
  transform: scale(.58);
  transition: transform .45s var(--ease);
}
.rail a[data-on="true"]::before{ transform: scale(1); }
@media (max-width: 1100px){ .rail{ display: none; } }

/* ── About: portrait + story ──────────────────────────────────────────── */
.about-intro{
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
}
@media (max-width: 880px){ .about-intro{ grid-template-columns: 1fr; } }

.about-lede{
  font-family: var(--display);
  font-weight: 600;
  font-variation-settings: "wdth" 92, "opsz" 40;
  font-size: clamp(1.35rem, 2.6vw, 2.1rem);
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--on-stock);
  margin-bottom: 6px;
}

/* ── What's on the desk ───────────────────────────────────────────────── */
.kit{ border-top: 1.5px solid var(--ink-key); }
.kit li{
  display: grid;
  grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
  gap: 14px clamp(20px, 3vw, 44px);
  align-items: baseline;
  padding: clamp(15px, 1.8vw, 24px) 0;
  border-bottom: 1px solid rgba(22, 22, 26, .2);
}
.kit b{ color: var(--on-stock-soft); }
.kit span{ color: var(--on-stock); max-width: 62ch; }
@media (max-width: 620px){
  .kit li{ grid-template-columns: 1fr; gap: 5px; }
}

/* ── Dockets ──────────────────────────────────────────────────────────── */
.dockets{
  background: var(--ink-key); color: var(--on-key);
  padding: clamp(72px, 10vw, 130px) 0;
}
.dockets__head{
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 90, "opsz" 72;
  font-size: clamp(1.9rem, 5vw, 4rem);
  line-height: 0.92; letter-spacing: -0.04em;
  max-width: 16ch;
  margin-bottom: clamp(40px, 6vw, 76px);
}
.docket-grid{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: 1px; background: rgba(222, 221, 214, .3);
  border: 1px solid rgba(222, 221, 214, .3);
}
.docket{
  background: var(--ink-key);
  padding: clamp(26px, 3vw, 40px);
  display: grid; gap: 14px; align-content: start;
}
.docket__no{
  font-family: var(--mono); font-size: 11px;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-yellow);
}
.docket h3{
  font-family: var(--display);
  font-variation-settings: "wdth" 94, "opsz" 32;
  font-size: clamp(1.2rem, 1.9vw, 1.6rem); line-height: 1;
}
.docket p{ color: var(--on-key-soft); font-size: 15.5px; }

@media (prefers-reduced-motion: reduce){
  .chart a:hover .chart__patch,
  .chart a:focus-visible .chart__patch{ height: clamp(64px, 11vw, 128px); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ARRIVALS
   Two motions, not one applied everywhere. Rows print (a wipe across, the way
   a line lands). Blocks register (they arrive out of alignment and settle).
   Everything is visible by default and only hidden once JS confirms it can
   bring it back.
   ═══════════════════════════════════════════════════════════════════════════ */
[data-ink] > *{ will-change: auto; }

/* Rows — the wipe, plus the rule drawing ahead of the text */
html.ink [data-ink="rows"] > *{
  clip-path: inset(0 100% 0 0);
  transition: clip-path .8s var(--ease);
}
html.ink [data-ink="rows"] > *.is-in{ clip-path: inset(0 0 0 0); }

/* Blocks — arrive off-register and settle */
html.ink [data-ink="blocks"] > *{
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition: opacity .55s linear, transform .95s var(--ease);
}
html.ink [data-ink="blocks"] > *.is-in{ opacity: 1; transform: none; }

/* ── Plated headings ──────────────────────────────────────────────────────
   The offset pink/blue impression, live. It arrives far out of register and
   pulls in, but never all the way to zero: it settles onto the registration
   spring, so it keeps drifting with the pointer and shearing with the scroll
   for as long as the page is open.
   @property is what makes this possible — transitioning the *variable* lets
   the shadow recompute from live --reg every frame, instead of a text-shadow
   transition fighting the per-frame updates.
   ───────────────────────────────────────────────────────────────────────── */
@property --po {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

[data-plate]{
  --po: var(--plate-o);
  text-shadow:
    calc((var(--reg-x) * -1) - var(--po))      calc((var(--reg-y) * -1) - var(--po) * var(--plate-y)) 0 var(--pl-a, var(--ink-pink)),
    calc(var(--reg-x) + var(--po))             calc(var(--reg-y) + var(--po) * var(--plate-y))        0 var(--pl-b, var(--ink-blue));
}
html.ink [data-plate]{
  --po: 20px;
  transition: --po 1.15s var(--ease);
}
html.ink [data-plate].is-in{ --po: var(--plate-o); }

@media (prefers-reduced-motion: reduce){
  html.ink [data-ink="rows"] > *,
  html.ink [data-ink="blocks"] > *{
    clip-path: none; opacity: 1; transform: none;
  }
  html.ink [data-plate]{ --po: var(--plate-o); transition: none; }
}

/* ── Ink field ────────────────────────────────────────────────────────────
   The homepage hero's live halftone, generalised. Any section can carry one.
   ───────────────────────────────────────────────────────────────────────── */
.field{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  mix-blend-mode: multiply;
  pointer-events: none;
}
.page-head, .svc-head, .cs-head, .wall__band, .statement, .dockets{ position: relative; }

/* Copy always sits above the decorative canvases.
   A positioned canvas — even at z-index 0 — paints above static text in the
   same stacking context, so every content wrapper has to claim a layer.
   This is the fix for text being lost behind the dots. */
.page-head  > .wrap,
.svc-head   > .wrap,
.cs-head    > .wrap,
.dockets    > .wrap,
.statement  > .wrap,
.section    > .wrap,
.wall__band > .wrap,
.svc        > .wrap{
  position: relative;
  z-index: 2;
}

/* A dissolve is up to 170px tall and hangs from the section's top edge, so
   any section carrying one needs enough head-room to clear its own copy. */
section:has(> .dissolve){ padding-top: clamp(130px, 15vw, 230px); }

/* Flooded panels centre their content in the space left below the dissolve.
   Same specificity as the rule above and declared after it, so it wins.
   Without this the copy sits jammed under the dissolve with a large dead
   area beneath it. */
section.svc{
  display: grid;
  align-content: center;
  min-height: min(88svh, 880px);
  /* Top padding is derived, not guessed: it is the bottom padding PLUS the
     dissolve's own height, so the copy ends up optically centred in the space
     the visitor actually sees rather than centred behind the dot band. Both
     halves use the same clamps as .dissolve, so they track together. */
  padding-top: calc(clamp(90px, 10vw, 150px) + clamp(90px, 11vw, 170px));
  padding-bottom: clamp(90px, 10vw, 150px);
}

/* ── Paper halo ───────────────────────────────────────────────────────────
   Small copy sitting over a live dot field needs the ink masked off behind
   it. A blurred patch of the stock colour reads as the sheet being kept
   clear, rather than as a box drawn around the text.
   ───────────────────────────────────────────────────────────────────────── */
.page-head .page-lead,
.page-head .plate-label,
.page-head .page-head__act,
.cs-head   .page-lead,
.cs-head   .plate-label,
.svc-head  p,
.svc-head  .plate-label,
.dockets   .dockets__head{
  position: relative;
  z-index: 1;
}
.page-head .page-lead::before,
.page-head .plate-label::before,
.page-head .page-head__act::before,
.cs-head   .page-lead::before,
.cs-head   .plate-label::before,
.svc-head  p::before,
.svc-head  .plate-label::before{
  content: "";
  position: absolute;
  inset: -0.75em -1.4em;
  background: var(--stock);
  filter: blur(19px);
  opacity: .93;
  z-index: -1;
  pointer-events: none;
}
/* the label is a short pill, so it wants a tighter clearing */
.page-head .plate-label::before,
.cs-head   .plate-label::before,
.svc-head  .plate-label::before{ inset: -0.6em -1em; }

/* A whole column of body copy over a field gets one clearing, not a halo per
   paragraph — per-paragraph blurs stack into a muddy patchwork. */
.about-intro .prose,
.about-intro > div:first-child{
  position: relative;
  z-index: 1;
}
.about-intro .prose::before,
.about-intro > div:first-child::before{
  content: "";
  position: absolute;
  inset: -1.4em -1.8em;
  background: var(--stock);
  filter: blur(26px);
  opacity: .94;
  z-index: -1;
  pointer-events: none;
}

/* ── Page transitions ─────────────────────────────────────────────────────
   The page itself is screened. A halftone mask is laid over every top-level
   layer and its dot radius animated: at 9.5px on a 13px grid the dots overlap
   into a solid sheet and the page is simply itself; shrink the radius and the
   page breaks into a dot screen and disperses. Reverse it and the next page
   re-prints from dots.

   Nothing overlays the page — this IS the page, screened. Which is why it had
   to be a mask rather than a curtain.

   The mask only exists while a transition runs (class-gated), so there is no
   compositing cost at rest.
   ───────────────────────────────────────────────────────────────────────── */
html.dots-in{ --dotr: 0px; }

html.dots    :is(body > main, body > header, body > .menu, body > .rail, body > .sheet, body > .grain),
html.dots-in :is(body > main, body > header, body > .menu, body > .rail, body > .sheet, body > .grain){
  -webkit-mask-image: radial-gradient(circle, #000 var(--dotr, 9.5px), rgba(0,0,0,0) calc(var(--dotr, 9.5px) + 0.7px));
          mask-image: radial-gradient(circle, #000 var(--dotr, 9.5px), rgba(0,0,0,0) calc(var(--dotr, 9.5px) + 0.7px));
  -webkit-mask-size: var(--dotp, 13px) var(--dotp, 13px);
          mask-size: var(--dotp, 13px) var(--dotp, 13px);
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
  -webkit-mask-position: center;
          mask-position: center;
}

@media (prefers-reduced-motion: reduce){
  html.dots, html.dots-in{ --dotr: 9.5px; }
  html.dots    :is(body > main, body > header, body > .menu, body > .rail, body > .sheet, body > .grain),
  html.dots-in :is(body > main, body > header, body > .menu, body > .rail, body > .sheet, body > .grain){
    -webkit-mask-image: none; mask-image: none;
  }
}



/* ═══════════════════════════════════════════════════════════════════════════
   INNER PAGE HEAD
   ═══════════════════════════════════════════════════════════════════════════ */
.page-head{ padding: clamp(130px, 17vw, 210px) 0 clamp(44px, 6vw, 80px); }
.page-head h1{
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 92, "opsz" 96;
  font-size: clamp(2.8rem, 11vw, 8rem);
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin-bottom: clamp(22px, 3vw, 40px);
}
/* Deliberately class-scoped: a bare `.page-head p` outranks .plate-label
   and silently resizes every label in the header. */
.page-head .page-lead{
  max-width: 54ch;
  font-size: clamp(17px, 0.8vw + 13px, 21px);
  line-height: 1.45;
  color: var(--on-stock-soft);
}
.page-head .plate-label{ margin-bottom: clamp(26px, 4vw, 48px); }
.page-head__act{ margin-top: clamp(24px, 3vw, 38px); }

/* ═══════════════════════════════════════════════════════════════════════════
   404 — THE SPOILED SHEET
   Every run begins by throwing sheets away: they come off during make-ready
   with the plates not yet locked on, and the pressman pulls them until the
   colour registers. Those are spoils. This page is one, so it is the only
   sheet on the site allowed to be badly out of register.
   ═══════════════════════════════════════════════════════════════════════════ */
.nf__h{ display: block; margin-bottom: 0; }

.nf__num{
  /* Far further out than anything else here — set through --plate-o, which
     [data-plate] already reads, so the reveal and the live registration
     shear come along for free. */
  --plate-o: clamp(6px, 1.7vw, 16px);
  --plate-y: 0.42;                    /* holds its diagonal even on a phone,
                                         where 30vw of numeral can afford it */
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 86, "opsz" 96;
  font-size: clamp(6rem, 29vw, 19rem);
  line-height: 0.76;
  letter-spacing: -0.055em;
  margin-left: -0.05em;               /* optical: 4 carries a wide left side */
  color: var(--ink-key);
}

.nf__title{
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 92, "opsz" 96;
  font-size: clamp(1.9rem, 6.2vw, 4.2rem);
  line-height: 0.94;
  letter-spacing: -0.04em;
  margin-top: clamp(12px, 1.8vw, 24px);
}

.nf__act{
  display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(12px, 1.6vw, 20px);
}
/* .btn--ghost is authored for the dark closing panel; on stock it needs the
   opposite pair or it prints stock-on-stock and disappears. */
.nf__act .btn--ghost{
  color: var(--ink-key);
  border-color: rgba(22, 22, 26, .4);
  background: transparent;
}
.nf__act .btn--ghost:hover{
  background: var(--ink-key);
  border-color: var(--ink-key);
  color: var(--stock);
}

/* ═══════════════════════════════════════════════════════════════════════════
   WORK GRID
   ═══════════════════════════════════════════════════════════════════════════ */
.workgrid{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
  gap: clamp(30px, 4vw, 60px);
  padding-bottom: clamp(80px, 11vw, 150px);
}
.workcard a{ display: block; }
.workcard .riso{ transition: transform .6s var(--ease); }
.workcard a:hover .riso, .workcard a:focus-visible .riso{ transform: translateY(-6px); }
.workcard__meta{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding-top: 16px;
  border-top: 1px solid rgba(22, 22, 26, .22);
  margin-top: 16px;
}
.workcard__name{
  font-family: var(--display);
  font-weight: 700;
  font-variation-settings: "wdth" 96, "opsz" 32;
  font-size: clamp(1.3rem, 2.2vw, 2rem);
  line-height: 1;
  letter-spacing: -0.03em;
}
.workcard__cat{ display: block; color: var(--on-stock-soft); font-size: 15px; margin-top: 6px; }

/* ═══════════════════════════════════════════════════════════════════════════
   SERVICE ROWS
   ═══════════════════════════════════════════════════════════════════════════ */
.service{
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
  padding: clamp(44px, 6vw, 86px) 0;
  border-top: 1px solid rgba(22, 22, 26, .22);
}
.service__label{
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 11.5px;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-stock-soft);
  margin-bottom: 18px;
}
.service__label b{ width: 11px; height: 11px; border-radius: 50%; background: var(--patch); mix-blend-mode: multiply; }
.service h2{
  font-family: var(--display);
  font-variation-settings: "wdth" 92, "opsz" 64;
  font-size: clamp(1.9rem, 4.4vw, 3.6rem);
  line-height: 0.94;
  margin-bottom: 20px;
}
.service__lead{ max-width: 46ch; font-size: clamp(16px, 0.6vw + 13px, 19px); }
.service__list{ display: grid; gap: 12px; }
.service__list li{
  display: grid; grid-template-columns: 13px 1fr; gap: 14px;
  align-items: baseline;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(22, 22, 26, .14);
  color: var(--on-stock-soft);
  max-width: 62ch;
}
.service__list li::before{
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--patch, var(--ink-key)); mix-blend-mode: multiply;
  transform: translateY(1px);
}

@media (max-width: 820px){
  .service{ grid-template-columns: 1fr; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CONTACT FORM (mailto — there is no backend)
   ═══════════════════════════════════════════════════════════════════════════ */
.form{ display: grid; gap: 22px; max-width: 640px; }
.form label{
  display: block;
  font-family: var(--mono); font-size: 11.5px;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-key-soft);
  margin-bottom: 9px;
}
.form input, .form textarea, .form select{
  width: 100%;
  font-family: var(--text); font-size: 17px;
  color: var(--on-key);
  background: transparent;
  border: 0; border-bottom: 1.5px solid rgba(222, 221, 214, .4);
  padding: 10px 0;
  border-radius: 0;
}
.form select option{ color: var(--ink-key); }
.form textarea{ resize: vertical; min-height: 110px; }
.form input:focus, .form textarea:focus, .form select:focus{
  outline: none; border-bottom-color: var(--ink-pink);
}
.form button{
  justify-self: start;
  font-family: var(--mono); font-size: 12px;
  letter-spacing: .14em; text-transform: uppercase;
  background: var(--ink-yellow); color: var(--ink-key);
  border: 0; border-radius: 999px;
  padding: 15px 26px;
  cursor: pointer;
  transition: transform .4s var(--ease);
}
.form button:hover{ transform: translateY(-2px); }

/* ═══════════════════════════════════════════════════════════════════════════
   SHARED SECTION UTILITIES
   ═══════════════════════════════════════════════════════════════════════════ */
.section{ padding-bottom: clamp(80px, 11vw, 150px); }
.section--pad{ padding-top: clamp(70px, 9vw, 130px); }
.lede{
  font-family: var(--display);
  font-weight: 600;
  font-variation-settings: "wdth" 92, "opsz" 40;
  font-size: clamp(1.5rem, 3.6vw, 3rem);
  line-height: 1.08;
  letter-spacing: -0.03em;
  max-width: 22ch;
  text-wrap: pretty;
}
.prose{ max-width: 68ch; display: grid; gap: 18px; color: var(--on-stock-soft); }
.prose strong{ color: var(--on-stock); font-weight: 600; }

/* ═══════════════════════════════════════════════════════════════════════════
   CASE STUDY
   ═══════════════════════════════════════════════════════════════════════════ */
.cs-head{ padding: clamp(120px, 15vw, 190px) 0 clamp(30px, 4vw, 52px); }
.cs-head h1{
  font-family: var(--display);
  font-weight: 800;
  font-variation-settings: "wdth" 92, "opsz" 96;
  font-size: clamp(2.6rem, 10vw, 7.5rem);
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin-bottom: clamp(18px, 2.5vw, 30px);
}
.cs-head .page-lead{
  max-width: 52ch;
  font-size: clamp(17px, 0.8vw + 13px, 21px);
  line-height: 1.45;
  color: var(--on-stock-soft);
}

/* Full-bleed plate */
.cs-bleed{
  margin-inline: calc(var(--gut) * -1);
  margin-block: clamp(40px, 6vw, 90px);
}
.cs-bleed .riso{ aspect-ratio: var(--ratio, 16 / 10); }

/* Fact table — client, sector, scope, live link */
.cs-facts{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 175px), 1fr));
  gap: clamp(22px, 3vw, 42px);
  border-top: 1.5px solid var(--ink-key);
  border-bottom: 1px solid rgba(22, 22, 26, .22);
  padding: 28px 0;
}
.cs-facts dt{
  font-family: var(--mono); font-size: 11.5px;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-stock-soft);
  margin-bottom: 9px;
}
.cs-facts dd{ font-size: 16px; line-height: 1.45; }
.cs-facts dd a{ border-bottom: 1.5px solid var(--ink-pink); }
.cs-facts dd a:hover{ color: var(--ink-pink); }

/* Two-up and three-up image rows */
.cs-pair{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: clamp(18px, 2.5vw, 34px);
  margin-block: clamp(36px, 5vw, 70px);
}
.cs-cap{
  margin-top: 12px;
  font-family: var(--mono); font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--on-stock-soft);
}

/* Body blocks */
.cs-block{
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(24px, 4vw, 70px);
  padding: clamp(34px, 4.5vw, 60px) 0;
  border-top: 1px solid rgba(22, 22, 26, .22);
}
.cs-block h2{
  font-family: var(--display);
  font-variation-settings: "wdth" 94, "opsz" 40;
  font-size: clamp(1.4rem, 2.6vw, 2.2rem);
  line-height: 1;
}
@media (max-width: 820px){ .cs-block{ grid-template-columns: 1fr; } }

/* Next project */
.cs-next{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  padding: clamp(34px, 5vw, 64px) 0;
  border-top: 1.5px solid var(--ink-key);
}
.cs-next__name{
  --o: 0px;
  font-family: var(--display); font-weight: 800;
  font-variation-settings: "wdth" 94, "opsz" 72;
  font-size: clamp(2rem, 6vw, 4.6rem);
  line-height: 0.94; letter-spacing: -0.035em;
  text-shadow:
    calc(var(--o) * -1) 0 0 var(--ink-pink),
    var(--o) 0 0 var(--ink-blue);
  transition: text-shadow .5s var(--ease);
}
.cs-next a:hover .cs-next__name, .cs-next a:focus-visible .cs-next__name{ --o: 5px; }

/* ═══════════════════════════════════════════════════════════════════════════
   CLIENT STRIP
   ═══════════════════════════════════════════════════════════════════════════ */
.clients{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 1px;
  background: rgba(22, 22, 26, .22);
  border: 1px solid rgba(22, 22, 26, .22);
}
.clients li{
  background: var(--stock);
  padding: clamp(18px, 2.2vw, 28px);
  display: flex; flex-direction: column; gap: 5px;
}
.clients b{
  font-family: var(--display);
  font-weight: 700;
  font-variation-settings: "wdth" 96, "opsz" 24;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.clients span{
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--on-stock-soft);
  line-height: 1.5;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CONTACT PAGE
   ═══════════════════════════════════════════════════════════════════════════ */
.contact--page{
  min-height: 100svh;
  display: flex; flex-direction: column;
  padding-top: clamp(130px, 16vw, 200px);
}
.contact--page > .wrap{ flex: 1; }

/* It's a heading here, not a link — so it wears the hover state permanently */
.cta--static{ cursor: default; }
.cta--static .ch{ color: var(--ink-yellow); }

.contact__split{
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.85fr);
  gap: clamp(38px, 5vw, 84px);
  padding-bottom: clamp(60px, 8vw, 110px);
}
.contact__grid--stack{
  grid-template-columns: 1fr;
  border-top: 0;
  padding-top: 0;
  padding-bottom: 0;
  gap: 22px;
}
.contact__aside{ border-left: 1px solid rgba(222, 221, 214, .22); padding-left: clamp(20px, 3vw, 44px); }

/* ── "See all work →" ─────────────────────────────────────────────────── */
.section-more{ padding-top: clamp(28px, 3.5vw, 46px); }
.link-arrow{
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--mono);
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  padding-bottom: 5px;
  border-bottom: 1.5px solid var(--ink-key);
}
.link-arrow span{ transition: transform .45s var(--ease); }
.link-arrow:hover{ color: var(--ink-pink); border-color: var(--ink-pink); }
.link-arrow:hover span{ transform: translateX(5px); }

@media (max-width: 820px){
  .contact__split{ grid-template-columns: 1fr; }
  .contact__aside{ border-left: 0; padding-left: 0; border-top: 1px solid rgba(222,221,214,.22); padding-top: 30px; }
}
