/* ==========================================================================
   LAYOUT CREATIVE — bylayoutcreative.com
   Visual direction: Poster Logic → "The Press Sheet"
   Hard cuts, a visible grid, type that sets itself. No fade-everything-in.
   ========================================================================== */

/* --------------------------------------------------------------- 01 FONTS */
@font-face{
  font-family:'Anton';
  src:url('../fonts/anton-400.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Instrument Sans';
  src:url('../fonts/instrument-var.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Plex Mono';
  src:url('../fonts/plexmono-400.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Plex Mono';
  src:url('../fonts/plexmono-500.woff2') format('woff2');
  font-weight:500; font-style:normal; font-display:swap;
}

/* -------------------------------------------------------------- 02 TOKENS */
:root{
  /* Approved palette — do not add colours to this list */
  --orange:#F87F0E;   /* matched to the logo (was #F2681B) */
  --ink:#1A1410;
  --butter:#F6DD93;
  --ember:#C4471A;
  --paper:#FBFAF7;
  --newsprint:#F2EEE5;

  /* Derived, for rules and secondary text only */
  --ink-70:rgba(26,20,16,.70);
  --ink-45:rgba(26,20,16,.45);
  --ink-14:rgba(26,20,16,.14);
  --paper-70:rgba(251,250,247,.70);
  --paper-40:rgba(251,250,247,.40);
  --paper-16:rgba(251,250,247,.16);

  --f-display:'Anton','Arial Narrow',Impact,sans-serif;
  --f-sans:'Instrument Sans',-apple-system,BlinkMacSystemFont,'Helvetica Neue',Arial,sans-serif;
  --f-mono:'Plex Mono',ui-monospace,'SF Mono',Menlo,monospace;

  /* Fluid type scale */
  --t-hero:clamp(3.6rem,12.5vw,12rem);
  --t-d1:clamp(2.6rem,7.4vw,6.4rem);
  --t-d2:clamp(2rem,4.4vw,3.6rem);
  --t-d3:clamp(1.5rem,2.8vw,2.2rem);
  --t-lede:clamp(1.1rem,1.7vw,1.45rem);
  --t-body:clamp(1rem,1.05vw,1.1rem);
  --t-sm:.9rem;
  --t-label:.7rem;

  --gut:clamp(20px,4.5vw,72px);        /* page margin */
  --rail:clamp(46px,5.2vw,74px);       /* bottom metadata rail height */
  --sec-y:clamp(72px,10vw,168px);      /* section vertical rhythm */
  --maxw:1560px;

  --ease:cubic-bezier(.2,.7,.2,1);
  --ease-cut:cubic-bezier(.75,0,.15,1);
}

/* ---------------------------------------------------------------- 03 BASE */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--f-sans);
  font-size:var(--t-body);
  /* Instrument Sans at 400 sets a little light against the grain and the ink
     grounds; 450 holds its colour without becoming a medium, which would start
     competing with Anton rather than sitting under it. */
  font-weight:450;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  padding-bottom:var(--rail);          /* clear the fixed rail */
}
img,video,svg{display:block;max-width:100%;height:auto}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
h1,h2,h3,h4,p,figure,blockquote{margin:0}
ul,ol{margin:0;padding:0;list-style:none}

:focus-visible{outline:3px solid var(--orange);outline-offset:3px}
.skip{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--ink);color:var(--paper);padding:14px 20px;
  font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.18em;text-transform:uppercase;
}
.skip:focus{left:0}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ------------------------------------------------------------ 04 GRAIN/TEX */
/* Only ever on large colour fields. Never behind body copy. */
.grain{position:relative;isolation:isolate}
/* Relief on the orange fields.

   .grain only ever sits on the orange sections, and those are mid-tone, which
   is the one place `overlay` works properly: it darkens below mid-grey and
   lightens above, so the bright fibres in the logo survive as well as the dark
   ones. That two-way relief is what makes the hero read like the logo rather
   than like orange with dirt on it — the page-wide multiply layer alone can
   only darken, and the hero looked flat without this.

   This did once make the orange muddy, but only because the page-wide layer
   was a heavy grey multiply at the time. That layer now costs ~8% brightness,
   so the two sit together without the orange going brown. */
.grain::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background-image:url('../img/brand/astroturf-relief.png');
  background-size:250px 250px;
  mix-blend-mode:overlay;opacity:.85;
}
.grain>*{position:relative;z-index:2}

/* --------------------------------------------------------------- 05 SHELL */
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gut)}

/* The underlying grid, made visible. Structure, not decoration. */
.gridlines{position:fixed;inset:0;z-index:1;pointer-events:none;
  max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gut)}
.gridlines i{
  position:absolute;top:0;bottom:0;width:1px;background:var(--ink-14);
}
.gridlines i:nth-child(1){left:var(--gut)}
.gridlines i:nth-child(2){left:25%}
.gridlines i:nth-child(3){left:50%}
.gridlines i:nth-child(4){left:75%}
.gridlines i:nth-child(5){right:var(--gut);left:auto}
/* gridlines are fixed, so they still key off the JS ground attribute */
[data-ground="ink"] .gridlines i{background:var(--paper-16)}
@media (max-width:820px){.gridlines i:nth-child(2),.gridlines i:nth-child(4){display:none}}

/* ----------------------------------------------------------- 06 TYPOGRAPHY */
.disp{font-family:var(--f-display);text-transform:uppercase;
  line-height:.92;letter-spacing:-.022em;font-weight:400}
/* inline on desktop — they only stack on a phone */
.hero-type .w{display:inline}
.hero-type{font-size:var(--t-hero);line-height:.84;letter-spacing:-.03em}
.d1{font-size:var(--t-d1)}
.d2{font-size:var(--t-d2)}
.d3{font-size:var(--t-d3);line-height:1}

/* Dimmed-word emphasis: lets a headline carry two weights of meaning */
.disp .quiet{color:var(--ink-45)}
.ground-orange .disp .quiet{color:rgba(26,20,16,.54)}
.ground-orange .mut{color:rgba(26,20,16,.78)}
.ground-ink .disp .quiet,
.ground-ink .disp .quiet *{color:var(--paper-40)}
.disp .hot{color:var(--orange)}

.label{font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.22em;text-transform:uppercase}
.label-b::before{content:"[ "}
.label-b::after{content:" ]"}

.lede{font-size:var(--t-lede);line-height:1.45;max-width:34ch}
.body-w{max-width:62ch}
.sm{font-size:var(--t-sm)}
.mut{color:var(--ink-70)}
.ground-ink .mut{color:var(--paper-70)}

/* ------------------------------------------------------------- 07 GROUNDS */
section{position:relative}
.ground-paper{background:var(--paper);color:var(--ink)}
.ground-news{background:var(--newsprint);color:var(--ink)}
.ground-ink{background:var(--ink);color:var(--paper)}
.ground-orange{background:var(--orange);color:var(--ink)}
.ground-butter{background:var(--butter);color:var(--ink)}
.pad-y{padding-block:var(--sec-y)}

/* Hard cut between grounds — the press-sheet edge. No gradients. */
.cut{border-top:2px solid var(--ink)}
.ground-ink+.ground-paper,.ground-paper+.ground-ink{border-top:0}

/* --------------------------------------------------------------- 08 HEADER */
.hdr{
  position:fixed;top:0;left:0;right:0;z-index:90;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:14px var(--gut);
  background:transparent;
  transition:background .35s var(--ease),border-color .35s var(--ease);
  border-bottom:1px solid transparent;
}
.hdr{color:var(--ink)}
body.top-ink .hdr{color:var(--paper)}
/* once it gains a solid background the header is always on paper */
.hdr.is-stuck,body.top-ink .hdr.is-stuck{
  background:var(--paper);color:var(--ink);border-bottom-color:var(--ink-14)}
.hdr-mark{display:block;line-height:1}
.hdr-mark b{font-family:var(--f-display);text-transform:uppercase;font-weight:400;
  font-size:clamp(1.5rem,2.1vw,1.9rem);letter-spacing:-.032em;line-height:.84;display:block}
.hdr-mark span{font-family:var(--f-display);text-transform:uppercase;font-weight:400;
  font-size:clamp(.4rem,.56vw,.5rem);letter-spacing:.62em;line-height:1;display:block;
  margin-top:3px;text-indent:.62em}
.nav{display:flex;align-items:center;gap:clamp(16px,2.4vw,34px)}
.nav a{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  text-transform:uppercase;font-weight:500;position:relative;padding-block:4px}
.nav a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:2px;
  background:var(--orange);transition:right .3s var(--ease)}
.nav a:hover::after,.nav a[aria-current="page"]::after{right:0}
.cta-btn{
  display:inline-block;background:var(--orange);color:var(--ink);
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;
  padding:13px 20px;border:2px solid var(--orange);
  transition:background .25s var(--ease),color .25s var(--ease);
}
.cta-btn:hover{background:transparent;color:var(--orange)}
[data-ground="ink"] .cta-btn:hover{color:var(--orange)}
.burger{display:none;width:44px;height:44px;position:relative}
.burger i{position:absolute;left:11px;right:11px;height:2px;background:currentColor;
  transition:transform .3s var(--ease),opacity .2s}
.burger i:nth-child(1){top:16px}
.burger i:nth-child(2){bottom:16px}
.burger[aria-expanded="true"] i:nth-child(1){transform:translateY(5px) rotate(45deg)}
.burger[aria-expanded="true"] i:nth-child(2){transform:translateY(-5px) rotate(-45deg)}

@media (max-width:900px){
  .nav-desk{display:none}
  .burger{display:block}
}
.menu{
  position:fixed;inset:0;z-index:80;background:var(--ink);color:var(--paper);
  display:flex;flex-direction:column;justify-content:center;gap:6px;
  padding:0 var(--gut);
  clip-path:inset(0 0 100% 0);transition:clip-path .5s var(--ease-cut);
}
.menu.is-open{clip-path:inset(0 0 0 0)}
.menu a{font-family:var(--f-display);text-transform:uppercase;font-size:clamp(2.4rem,11vw,4.5rem);
  line-height:1;letter-spacing:-.025em}
.menu a:hover{color:var(--orange)}
.menu .label{margin-bottom:18px;color:var(--orange)}

/* ---------------------------------------------------------------- 09 RAIL */
/* Persistent metadata strip — location, status, socials. */
.rail{
  position:fixed;left:0;right:0;bottom:0;z-index:85;height:var(--rail);
  display:flex;align-items:center;justify-content:space-between;gap:18px;
  padding-inline:var(--gut);
  background:var(--ink);color:var(--paper);
  border-top:2px solid var(--orange);
  font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.18em;text-transform:uppercase;
}
.rail a:hover{color:var(--orange)}
.rail-mid{display:flex;align-items:center;gap:10px;color:var(--paper-70)}
.rail-dot{width:7px;height:7px;background:var(--orange);border-radius:50%;
  animation:pulse 2.6s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.28}}
.rail-right{display:flex;gap:clamp(12px,2vw,26px)}
@media (max-width:760px){
  .rail-mid{display:none}
  .rail{font-size:.62rem;letter-spacing:.14em}
}

/* --------------------------------------------------------------- 10 SPINE */
/* Rotated section label — the spine of a press sheet. */
.spine{
  position:absolute;left:calc(var(--gut) * -1 + 6px);top:0;
  writing-mode:vertical-rl;transform:rotate(180deg);
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.32em;text-transform:uppercase;color:var(--ink-45);
  white-space:nowrap;
}
.ground-ink .spine{color:var(--paper-40)}
@media (max-width:1180px){.spine{display:none}}

/* ---------------------------------------------------------- 11 SECTION HEAD */
.sec-head{display:flex;flex-direction:column;gap:18px;margin-bottom:clamp(30px,4.4vw,60px)}
.sec-num{display:flex;align-items:baseline;justify-content:space-between;gap:20px;
  padding-bottom:12px;border-bottom:2px solid currentColor}
.sec-num .n{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.22em;
  font-weight:500;color:var(--orange)}
.sec-num .k{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.22em;
  font-weight:500;opacity:.55}

/* Case-study step numbers, set as display type.
   Scoped to .split > .sec-num on purpose: that combination occurs only in the
   numbered rows of a case study, where the number sits alone in a wide column
   with nothing to hold it. Everywhere else .sec-num is a full-width section
   header bar and the number must stay small, so this must not reach it.
   The mono tracking is dropped — .22em is right for a small label and far too
   loose on a large numeral. */
.split > .sec-num .n{
  font-family:var(--f-display);font-weight:400;
  font-size:var(--t-d2);line-height:.8;letter-spacing:-.015em;
  /* Anton sits high in its box; nudge it so the numeral's cap aligns with the
     first line of the copy in the next column rather than floating above it. */
  position:relative;top:-.06em}

/* --------------------------------------------------------------- 12 MOTION */
/* Type sets itself: a wipe from the baseline, not a fade. */
.set{overflow:hidden;display:block}
.set>span{display:block;transform:translateY(102%);
  transition:transform .85s var(--ease-cut)}
.is-set .set>span{transform:translateY(0)}
.set:nth-child(2)>span{transition-delay:.07s}
.set:nth-child(3)>span{transition-delay:.14s}
.set:nth-child(4)>span{transition-delay:.21s}

/* Blocks arrive by clip, so the edge stays hard */
.reveal{clip-path:inset(0 0 100% 0);transition:clip-path .8s var(--ease-cut)}
.is-in .reveal,.reveal.is-in{clip-path:inset(0 0 0 0)}

@media (prefers-reduced-motion:reduce){
  .set>span{transform:none!important;transition:none!important}
  .reveal{clip-path:none!important;transition:none!important}
  .rail-dot{animation:none}
}

/* ------------------------------------------------------------- 12b HERO */
/* These lived as inline styles on the section, which meant no media query
   could reach them — inline always wins. On a phone the combination of a
   full-screen min-height and bottom-aligned content left roughly 270px of
   empty orange above the headline, because the type is small enough there to
   leave slack that gets pushed to the top. Moved here so the phone can simply
   opt out of both. */
.hero-sec{min-height:100svh;display:flex;flex-direction:column;
  justify-content:flex-end;
  padding-block:clamp(88px,8.5vw,110px) clamp(36px,5vw,64px)}
@media (max-width:820px){
  /* On a phone the hero should still fill the screen — a two-thirds orange
     block reads as a mistake. The earlier fix here dropped it to content
     height, which killed the 270px gap above the headline but cost the
     full-bleed.

     Both are possible because the gap was never really about height: the
     section has a single child, so justify-content could only pool ALL the
     slack in one place — at the top, above the type. Letting the wrap itself
     distribute means the label sits under the header, the headline holds the
     middle, and the copy and button sit at the foot. The screen fills and the
     space is shared out instead of collecting anywhere. */
  /* svh reserves room for the address bar, so it left a sliver of the next
     section showing under the hero. dvh matches whatever is actually visible,
     which is what "the whole first screen is orange" needs. svh stays first as
     the fallback for anything that does not know dvh — it errs on the side of
     never clipping the content. */
  .hero-sec{min-height:100svh;min-height:100dvh;justify-content:stretch;
    padding-block:clamp(84px,21vw,112px) clamp(34px,7vw,52px)}
  .hero-sec > .wrap{flex:1;display:flex;flex-direction:column;
    justify-content:space-between;gap:clamp(20px,5vw,38px)}
  /* the h1 carries its own bottom margin inline; the gap handles it here */
  .hero-sec h1{margin-bottom:0!important}

  /* "AS GOOD AS IT IS." measured 364px against 335px of usable width — it was
     overflowing and being silently clipped by the body's overflow-x:hidden.
     Breaking it after "as good" makes "BUSINESS LOOK" the longest line, which
     is short enough to let the type get BIGGER rather than smaller: four lines
     at a larger size fill the screen far better than three clipped ones.
     The size is tied to the viewport so it still fits on a 320px phone. */
  /* One word per line. The line length was the thing capping the size: with
     "BUSINESS LOOK" on one line the type could never exceed 60px, because that
     line measured 323px against 335px of usable width. Stacked, the longest
     line becomes "BUSINESS" at 202px, which lets the type run to ~96px — the
     headline goes from a quarter of the screen to well over half, which is
     what a poster headline on a phone should be doing.
     22vw rather than the 24vw the width alone would allow: the label, the
     paragraph and the button still have to fit under it inside one screen. */
  .hero-sec h1 .w,
  .hero-sec h1 .quiet{display:block}
  /* 22vw fitted the WIDTH but not the height — the hero grew past the screen
     and pushed the button below the fold. Six lines cost ~5px of height per 1px
     of type, so this is sized to leave the label, paragraph and button inside
     one screen rather than to the width alone. */
  .hero-sec h1{font-size:clamp(3.1rem,19vw,5.2rem);line-height:.84}
}

/* --------------------------------------------------------------- 14 BUTTON */
.btn{
  display:inline-flex;align-items:center;gap:14px;
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;
  padding:18px 26px;border:2px solid currentColor;
  transition:background .28s var(--ease),color .28s var(--ease),gap .28s var(--ease);
}
.btn:hover{gap:22px;background:var(--orange);border-color:var(--orange);color:var(--ink)}
.btn-fill{background:var(--orange);border-color:var(--orange);color:var(--ink)}
.btn-fill:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.btn .arw{transition:transform .28s var(--ease)}
.btn:hover .arw{transform:translateX(3px)}

/* ---------------------------------------------------------------- 15 GRIDS */
.g2{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(20px,3vw,52px)}
.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(18px,2.4vw,38px)}
.split{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(28px,5vw,90px);align-items:start}
@media (max-width:900px){.g2,.g3,.split{grid-template-columns:1fr}}

/* ------------------------------------------------------------ 16 ACCORDION */
/* Services. A visitor opens only what's relevant to them. */
.acc{border-top:2px solid currentColor}
.acc-item{border-bottom:2px solid currentColor}
.acc-btn{width:100%;display:grid;grid-template-columns:auto 1fr auto;gap:clamp(16px,3vw,44px);
  align-items:center;text-align:left;padding-block:clamp(20px,2.6vw,34px)}
.acc-btn .idx{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  font-weight:500;color:var(--orange)}
.acc-btn .ttl{font-family:var(--f-display);text-transform:uppercase;
  font-size:clamp(2rem,5.6vw,4.4rem);line-height:.9;letter-spacing:-.025em;
  transition:color .25s var(--ease),transform .4s var(--ease)}
.acc-item:hover .ttl,.acc-item.is-open .ttl{color:var(--orange)}
.acc-btn .sign{position:relative;width:26px;height:26px;flex:none}
.acc-btn .sign::before,.acc-btn .sign::after{content:"";position:absolute;background:currentColor;
  transition:transform .35s var(--ease),opacity .25s}
.acc-btn .sign::before{left:0;right:0;top:12px;height:2px}
.acc-btn .sign::after{top:0;bottom:0;left:12px;width:2px}
.acc-item.is-open .sign::after{transform:scaleY(0)}
.acc-panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .5s var(--ease-cut)}
.acc-item.is-open .acc-panel{grid-template-rows:1fr}
.acc-panel>div{overflow:hidden}
.acc-inner{display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(20px,4vw,64px);
  padding-bottom:clamp(26px,3.4vw,44px)}
.acc-inner ul{display:flex;flex-wrap:wrap;gap:8px}
.acc-inner li{font-family:var(--f-mono);font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;
  border:1px solid currentColor;padding:8px 12px;opacity:.85}
@media (max-width:820px){
  .acc-inner{grid-template-columns:1fr}
  .acc-btn{grid-template-columns:auto 1fr auto;gap:14px}
}

/* -------------------------------------------------------------- 17 PROCESS */
.proc{display:grid;grid-template-columns:repeat(6,1fr);gap:2px;background:currentColor}
.proc-step{background:var(--paper);padding:clamp(18px,1.8vw,26px);
  display:flex;flex-direction:column;gap:10px;min-height:clamp(200px,20vw,290px);
  transition:background .3s var(--ease),color .3s var(--ease)}
.ground-ink .proc-step{background:var(--ink)}
.proc-step .n{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  font-weight:500;opacity:.5}
.proc-step .t{font-family:var(--f-display);text-transform:uppercase;
  font-size:clamp(1.05rem,1.7vw,1.5rem);line-height:.95;letter-spacing:-.015em}
.proc-step .d{font-size:.95rem;line-height:1.5;opacity:.72;margin-top:auto}
.proc-step.gate{background:var(--orange);color:var(--ink)}
.proc-step.gate .n,.proc-step.gate .d{opacity:.82}
.proc-step:hover{background:var(--orange);color:var(--ink)}
.proc-step:hover .n,.proc-step:hover .d{opacity:.82}
@media (max-width:1100px){.proc{grid-template-columns:repeat(3,1fr)}}
@media (max-width:620px){.proc{grid-template-columns:repeat(2,1fr)}}

/* ----------------------------------------------------------------- 18 WORK */
.work-row{display:block;border-bottom:2px solid currentColor;padding-block:clamp(22px,3vw,40px);
  position:relative;overflow:hidden}
.work-row:first-child{border-top:2px solid currentColor}
.work-head{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(16px,3vw,40px);align-items:center}
.work-row .idx{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  font-weight:500;color:var(--orange)}
.work-row .name{font-family:var(--f-display);text-transform:uppercase;
  font-size:clamp(1.8rem,5vw,4rem);line-height:.9;letter-spacing:-.025em;
  transition:transform .45s var(--ease),color .3s var(--ease)}
.work-row:hover .name{color:var(--orange);transform:translateX(10px)}
.work-row .meta{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.18em;
  text-transform:uppercase;opacity:.6;text-align:right}
@media (max-width:760px){
  .work-head{grid-template-columns:auto 1fr}
  .work-row .meta{display:none}
}

/* --------------------------------------------------------------- 19 FIGURE */
.fig{position:relative;overflow:hidden;background:var(--newsprint)}
/* Screenshots of client sites sit ABOVE the page texture. The grain is Layout's
   press sheet, not theirs — letting it wash over a screenshot makes it look as
   though the client's own site is grainy, which misrepresents the work. The
   texture stopping at the frame is also the more honest print metaphor: ink
   grain belongs to the paper, not to a photograph mounted on it.
   Header is 90 and the bottom rail 85, so 4 clears the cursor field (3) only. */
.fig-shot{z-index:4}

/* --- PRESS PROOF -------------------------------------------------------
   A client site presented as it would come off a press: printed to the sheet,
   crop marks at the trim, and the job spec set in mono up the left edge.

   A laptop mockup was built first and rejected. Two reasons it lost. A machine
   dates — the drawn laptop would look wrong in three years the same way the
   beige CRT looks wrong now — and more to the point, this site's whole argument
   is that an out-of-date website costs its owner the job. Framing a client's
   brand-new site inside a picture of a computer works against that; framing it
   as something freshly printed does not. Crop marks and a spec label are also
   the language the rest of the brand already speaks.

   PINNING. The sheet is nearly as tall as the viewport, so there was almost no
   scroll distance while it was on screen — the site inside had scrolled past
   its own homepage before the sheet was fully visible. It now sticks inside a
   taller stage and the site travels through it while it is held still, the same
   technique the six-stage process section uses. */
.proof-stage{position:relative}
.proof-pin{display:flex;flex-direction:column;justify-content:center}
@media (min-width:900px){
  .proof-stage{height:250vh}
  .proof-pin{position:sticky;top:0;height:100vh;perspective:1900px;
    perspective-origin:52% 40%}
}
.proof{margin:0;width:100%}
@media (min-width:900px){
  /* tilted, because straight-on reads as a screenshot rather than an object */
  .proof{transform:rotateX(9.5deg) rotateY(-16deg) rotateZ(-1.2deg);
    transform-style:preserve-3d}
}

.proof-sheet{
  position:relative;
  background:var(--paper);
  padding:clamp(26px,3vw,48px);
  box-shadow:0 44px 90px -34px rgba(26,20,16,.5),
             0 2px 0 0 rgba(26,20,16,.10);
}
.proof-window{
  position:relative;overflow:hidden;
  aspect-ratio:16/10;
  background:var(--newsprint);
}
.proof-shot{
  position:absolute;top:0;left:0;
  width:100%;height:auto;display:block;
  will-change:transform;
}
/* crop marks at the trim — the same language the contact-sheet plates use */
.proof-reg{
  position:absolute;inset:clamp(11px,1.3vw,20px);pointer-events:none;
  background:
    linear-gradient(var(--ink),var(--ink)) left top /1px 15px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left top /15px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top /1px 15px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top /15px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left bottom /1px 15px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left bottom /15px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom /1px 15px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom /15px 1px no-repeat;
  opacity:.5;
}
/* the job line, up the left edge like a spine */
.proof-spec{
  position:absolute;left:clamp(0px,.3vw,6px);top:50%;
  transform:translateY(-50%) rotate(180deg);writing-mode:vertical-rl;
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.22em;text-transform:uppercase;opacity:.45;
  white-space:nowrap;pointer-events:none;
}
.proof .fig-cap{margin-top:clamp(14px,1.6vw,22px)}

@media (prefers-reduced-motion:reduce){
  .proof{transform:none!important}
  .proof-pin{perspective:none}
  .proof-shot{transform:none!important}
}

/* ---------------------------------------------------------------- 20 FORM */
.field{display:flex;flex-direction:column;gap:9px;margin-bottom:22px}
.field label{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  text-transform:uppercase;font-weight:500;opacity:.75}
.field input,.field textarea,.field select{
  font-family:var(--f-sans);font-size:1.02rem;color:inherit;
  background:transparent;border:0;border-bottom:2px solid currentColor;
  padding:12px 2px;border-radius:0;
  transition:border-color .25s var(--ease)
}
.field textarea{resize:vertical;min-height:130px}
.field input:focus,.field textarea:focus,.field select:focus{
  outline:none;border-bottom-color:var(--orange)}
.field input::placeholder,.field textarea::placeholder{color:currentColor;opacity:.35}
.chips{display:flex;flex-wrap:wrap;gap:9px}

/* --- direct contact, set as display type --------------------------------
   These two links are the only things on the page a visitor can act on
   without filling anything in, so they carry the weight of a headline
   rather than sitting in the paragraph stack looking like body copy.
   The previous 2px orange rule under each one read as a warning stripe;
   a hairline in ink that warms to orange on hover keeps them obviously
   clickable without shouting. */
.contact-direct{display:flex;flex-direction:column;
  gap:clamp(10px,1.2vw,15px);margin-top:clamp(4px,.6vw,8px)}
.contact-direct a{
  font-family:var(--f-display);text-transform:uppercase;font-weight:400;
  font-size:clamp(1.15rem,2vw,1.7rem);line-height:1.05;letter-spacing:.005em;
  align-self:flex-start;max-width:100%;
  overflow-wrap:anywhere;                 /* the email is long in a narrow column */
  border-bottom:2px solid var(--ink-14);padding-bottom:6px;
  transition:color .25s var(--ease),border-color .25s var(--ease)}
.contact-direct a:hover,
.contact-direct a:focus-visible{color:var(--orange);border-bottom-color:var(--orange)}

/* --- enquiry form: confirmation and failure, both shown in place ---------
   The panel and the error line live INSIDE the form on purpose: the form's
   parent is a two-column grid, and a third child would drop onto a new row.
   Swapping the form's own contents keeps the column exactly where it was.
   Both are hidden until layout.js flips a class, so with JavaScript off the
   form posts the ordinary way and neither is ever seen. */
.form-done{display:none}
form.is-sent > *{display:none}
form.is-sent > .form-done{display:block}
.form-done-h{font-family:var(--f-display);text-transform:uppercase;
  font-size:var(--t-d2);line-height:.9;letter-spacing:-.02em;
  margin:clamp(10px,1.2vw,16px) 0 clamp(10px,1.4vw,18px)}
.form-error{display:none;margin:14px 0 0;color:var(--ember);font-size:var(--t-body)}
.form-error a{text-decoration:underline;text-underline-offset:3px}
form.has-error > .form-error{display:block}
/* the button reads as busy while the request is in flight */
form.is-sending button[type="submit"]{opacity:.55;pointer-events:none}
.chip{position:relative}
.chip input{position:absolute;opacity:0;width:0;height:0}
.chip span{display:block;font-family:var(--f-mono);font-size:.66rem;letter-spacing:.14em;
  text-transform:uppercase;border:2px solid currentColor;padding:11px 15px;cursor:pointer;
  transition:background .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease)}
.chip input:checked+span{background:var(--orange);border-color:var(--orange);color:var(--ink)}
.chip input:focus-visible+span{outline:3px solid var(--orange);outline-offset:3px}

/* -------------------------------------------------------------- 21 FOOTER */
.foot{background:var(--ink);color:var(--paper);padding-block:clamp(56px,7vw,110px)}
.foot-word{font-family:var(--f-display);text-transform:uppercase;color:var(--orange);
  font-size:clamp(3.4rem,17vw,15rem);line-height:.8;letter-spacing:-.032em;margin-bottom:8px}
.foot-sub{font-family:var(--f-display);text-transform:uppercase;color:var(--orange);
  opacity:.55;font-size:clamp(.6rem,2.3vw,1.55rem);letter-spacing:.55em;margin-bottom:40px}
.foot-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3vw,44px);
  border-top:2px solid var(--paper-16);padding-top:32px}
.foot-grid h4{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;
  text-transform:uppercase;color:var(--orange);margin-bottom:14px;font-weight:500}
.foot-grid li{margin-bottom:8px;opacity:.8}
.foot-grid a:hover{color:var(--orange);opacity:1}
@media (max-width:760px){.foot-grid{grid-template-columns:1fr}}

/* --------------------------------------------------------------- 22 UTILS */
.stack-sm{display:flex;flex-direction:column;gap:14px}
.stack-md{display:flex;flex-direction:column;gap:26px}
.row-gap{display:flex;flex-wrap:wrap;gap:14px;align-items:center}
.hr{height:2px;background:currentColor;opacity:.16;border:0;margin-block:clamp(28px,3.4vw,52px)}
.orange{color:var(--orange)}
.tar{text-align:right}
.mt-a{margin-top:auto}


/* ==========================================================================
   23 · MOTION — Poster Logic, interactive
   Transform-only. All of it disabled under prefers-reduced-motion.
   ========================================================================== */

/* Ink layer drifts under the type, driven by pointer + scroll (motion.js) */
.grain::after{
  background-position:var(--gx,0) var(--gy,0);
  transition:none;
}

/* Headlines and spines drift at their own rate */
[data-drift]{will-change:transform}
.spine{will-change:transform}

/* Work rows: a colour field wipes in from the side the cursor entered */
.work-row{--wipe-x:0%;isolation:isolate}
.work-row::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:var(--ink);
  transform:scaleX(0);transform-origin:var(--wipe-x) 50%;
  transition:transform .55s cubic-bezier(.75,0,.15,1);
}
.work-row.is-hot::before{transform:scaleX(1)}
.work-row.is-hot .name{color:var(--orange)}
.work-row.is-hot .idx,.work-row.is-hot .meta{color:var(--paper)}
.work-row.is-hot .mut{color:var(--paper-70)}
.work-row .work-head,.work-row p{transition:padding-inline .55s cubic-bezier(.75,0,.15,1)}
.work-row.is-hot .work-head,.work-row.is-hot p{padding-inline:clamp(14px,1.6vw,26px)}

/* Section edges overlap slightly so colour fields ride over one another */
.overlap{margin-top:calc(var(--sec-y) * -0.14);position:relative;z-index:2}

@media (prefers-reduced-motion:reduce){
  .work-row::before{display:none}
  .work-row.is-hot .work-head,.work-row.is-hot p{padding-inline:0}
  .overlap{margin-top:0}
}


/* ==========================================================================
   24 · STACKING SERVICES
   Each area is a full panel with its detail already visible. As you scroll the
   next panel rides over it, leaving only its title bar showing. No clicking.
   ========================================================================== */
.stack{--peek:76px;--anchor:76px;position:relative}
.stack-card{
  position:sticky;
  top:calc(var(--anchor) + var(--i) * var(--peek));
  min-height:62vh;
  display:flex;flex-direction:column;
  border-top:2px solid var(--ink);
  padding:clamp(20px,2.6vw,34px) 0 clamp(28px,4vw,56px);
}
.stack-card.s-paper{background:var(--paper);color:var(--ink)}
.stack-card.s-news {background:var(--newsprint);color:var(--ink)}
.stack-card.s-ink  {background:var(--ink);color:var(--paper);border-top-color:var(--paper)}
.stack-card.s-orange{background:var(--orange);color:var(--ink);border-top-color:var(--ink)}
.stack-card.s-butter{background:var(--butter);color:var(--ink)}

.stack-bar{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(14px,2.6vw,38px);
  align-items:center;padding-bottom:clamp(14px,1.8vw,24px)}
.stack-bar .idx{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.2em;font-weight:500}
.stack-card.s-paper .stack-bar .idx,.stack-card.s-news .stack-bar .idx,
.stack-card.s-ink .stack-bar .idx{color:var(--orange)}
.stack-bar .ttl{font-family:var(--f-display);text-transform:uppercase;
  font-size:clamp(2.2rem,6.4vw,5.2rem);line-height:.88;letter-spacing:-.028em}
.stack-bar .tag{font-family:var(--f-mono);font-size:var(--t-label);letter-spacing:.18em;
  text-transform:uppercase;opacity:.6;text-align:right}

.stack-body{display:grid;grid-template-columns:1fr 1.1fr;gap:clamp(20px,4vw,64px);
  padding-top:clamp(14px,2vw,26px);border-top:1px solid currentColor}
.stack-card.s-ink .mut{color:var(--paper-70)}
.stack-card.s-orange .mut{color:rgba(26,20,16,.80)}
.stack-card.s-butter .mut{color:rgba(26,20,16,.78)}
.stack-card.s-ink .lede{color:var(--paper)}
.stack-body ul{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.stack-body li{font-family:var(--f-mono);font-size:.66rem;letter-spacing:.14em;
  text-transform:uppercase;border:1px solid currentColor;padding:8px 12px;opacity:.85}
@media (max-width:900px){
  .stack{--peek:60px}
  .stack-card{min-height:auto}
  .stack-bar{grid-template-columns:auto 1fr}
  .stack-bar .tag{display:none}
  .stack-body{grid-template-columns:1fr;gap:16px}
}

/* ==========================================================================
   25 · CURSOR FIELD
   A single fixed grain layer over the whole page that drifts with the pointer.
   One quiet effect running the full length of the site, rather than per-section.
   ========================================================================== */
.cursorfield{
  position:fixed;inset:-12%;z-index:3;pointer-events:none;
  /* The texture lifted out of Logo.png — the curly fibre the wordmark sits on,
     so the site and the logo are the same surface.

     It carries its own alpha rather than being a grey tile: transparent across
     most of the tile, with ink only in the fibre lines. That matters because a
     flat grey multiply darkens EVERYTHING it covers — at readable strength it
     took #F87F0E down to #C6650B and the whole page went muddy brown. This
     version leaves 91.7% of the ground's own colour intact while still moving
     the fibre lines by ~64 levels, which is what makes it visible on the pale
     sections as well as the orange.
     To soften it, drop this opacity; the tile itself needs no changing. */
  background-image:url('../img/brand/astroturf.png');
  background-size:250px 250px;
  mix-blend-mode:multiply;opacity:1;
  transform:translate3d(var(--cfx,0),var(--cfy,0),0);
  will-change:transform;
}
/* The plain CSS grain layer stays ON for touch. It used to be hidden here on
   the reasoning that a "cursor field" is pointless without a cursor — but the
   pointer only ever drove its drift, not its existence, and switching it off
   left phones with no page-wide texture at all while every desktop visitor
   got one. Without a fine pointer motion.js never sets --cfx/--cfy, so it
   simply sits still, which is exactly what is wanted. Only the WebGL layer,
   which exists solely to react to a pointer, is hidden below. */

/* The GPU version of the same layer. Identical texture, size and blend, but
   the speckles within reach of the pointer are displaced one by one instead
   of the sheet sliding as a whole (assets/js/field.js).
   Hidden until field.js has the texture loaded and sets .gl-field on <html>,
   so the CSS layer above covers the gap and there is never a blank frame. */
.cursorfield-gl{
  position:fixed;inset:0;width:100%;height:100%;z-index:3;pointer-events:none;
  mix-blend-mode:multiply;opacity:.16;
  display:none;
}
.gl-field .cursorfield-gl{display:block}
.gl-field .cursorfield{display:none}
@media (hover:none){.cursorfield-gl{display:none!important}}

@media (prefers-reduced-motion:reduce){
  .cursorfield,.cursorfield-gl{display:none!important}
  .stack-card{position:static;min-height:auto}
}

/* Case studies need an obvious way back that isn't at the foot of the page */
.back-link{display:inline-flex;align-items:center;gap:9px;
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;
  border-bottom:2px solid currentColor;padding-bottom:5px;
  margin-bottom:clamp(22px,3vw,36px);opacity:.75;
  transition:opacity .25s var(--ease),color .25s var(--ease)}
.back-link:hover{opacity:1;color:var(--orange)}


/* ==========================================================================
   26 · HORIZONTAL PROCESS STAGE  (restored)
   Previously the pinned area needed 655px of height; on a 13in laptop viewport
   that clipped the step descriptions. The heading is smaller inside the stage
   and the cards are shorter, so everything fits on a short screen.
   ========================================================================== */
[data-hstage]{position:relative}
@media (min-width:1100px){
  /* Stage height sets how much scroll the sideways sweep is spread over.
     It is tuned against the card width: wider cards mean more travel, so the
     stage grows with them to keep the sweep moving at about the same rate.
     330px cards over 230vh gave ~0.58px sideways per px of scroll; 400px
     cards over 290vh gives ~0.64. Change one, re-check the other. */
  [data-hstage]{height:290vh}
  [data-hstage] .hpin{position:sticky;top:0;height:100vh;
    display:flex;flex-direction:column;justify-content:center;overflow:hidden}
  /* compact the heading so the cards always clear a short viewport */
  [data-hstage] .sec-head{margin-bottom:clamp(14px,1.6vw,22px)}
  [data-hstage] .disp.d1{font-size:clamp(2rem,3.6vw,3.2rem)}
  [data-hstage] .sec-head .mut{font-size:.92rem}
  [data-hscroll]{overflow:hidden}
  [data-hscroll]>.proc{
    grid-template-columns:repeat(6,400px);
    width:max-content;will-change:transform;padding-inline:var(--gut);
  }
  /* Height is viewport-relative and capped, so a tall card can never outgrow
     the pinned 100vh and get clipped — that was the original bug here. */
  [data-hstage] .proc-step{min-height:min(46vh,420px);height:auto;
    padding:clamp(24px,2.4vw,36px);gap:14px}
  [data-hstage] .proc-step .t{font-size:clamp(1.4rem,2.1vw,2rem)}
  [data-hstage] .proc-step .d{margin-top:auto;font-size:1.06rem;line-height:1.52}
}
@media (max-width:1099px){
  [data-hstage]{height:auto}
  [data-hstage] .hpin{position:static;height:auto}

  /* --- the six stages on a phone ---------------------------------------
     The desktop version pins the section and sweeps the cards sideways as
     you scroll. Porting that to a phone means taking the scroll away from
     the reader, which fights iOS momentum and re-breaks every time the
     address bar resizes the viewport.

     The native equivalent of the same idea is to let them drive it: the six
     stages become a row you swipe, snapping card to card. Same sideways
     movement through the process, no scroll-jacking, and the cards rise in
     sequence as the section arrives so it is not dead on the page. */
  [data-hscroll]{overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    /* break the gutter so the row runs to the screen edge and reads as
       continuing past it, rather than stopping inside the column */
    margin-inline:calc(var(--gut) * -1);
    padding-inline:var(--gut);scroll-padding-inline:var(--gut)}
  [data-hscroll]::-webkit-scrollbar{display:none}
  [data-hscroll]>.proc{grid-template-columns:repeat(6,clamp(215px,70vw,310px));
    width:max-content}
  [data-hstage] .proc-step{scroll-snap-align:center;
    min-height:clamp(215px,56vw,300px);
    opacity:0;transform:translateY(15px);
    transition:opacity .5s var(--ease),transform .5s var(--ease-cut)}
  [data-hstage].is-in .proc-step{opacity:1;transform:none}
  [data-hstage] .proc-step:nth-child(1){transition-delay:0ms}
  [data-hstage] .proc-step:nth-child(2){transition-delay:70ms}
  [data-hstage] .proc-step:nth-child(3){transition-delay:140ms}
  [data-hstage] .proc-step:nth-child(4){transition-delay:210ms}
  [data-hstage] .proc-step:nth-child(5){transition-delay:280ms}
  [data-hstage] .proc-step:nth-child(6){transition-delay:350ms}
}

/* ==========================================================================
   28 · CONTACT SHEET  (about — "the rest of it")
   Base state is a plain scroll-snapping strip, so with no JavaScript — or
   with reduced-motion asked for — it still works and reads properly.
   cover.js adds .is-3d and the same markup becomes a cylinder of plates.
   ========================================================================== */
.sheet{position:relative;user-select:none}
.sheet-kicker{margin:0 0 clamp(10px,1.2vw,16px)}
.sheet-stage{display:flex;gap:12px;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;padding-bottom:10px;margin:0}
.plate{position:relative;margin:0;background:var(--newsprint);
  flex:0 0 clamp(150px,44%,215px);scroll-snap-align:center}
.plate img{display:block;width:100%;aspect-ratio:3/4;object-fit:cover;
  -webkit-user-drag:none;user-select:none}

/* --- turned into a cylinder by cover.js -------------------------------- */
.sheet-frame{position:relative}
.sheet.is-3d .sheet-frame{perspective:820px;overflow:hidden;cursor:grab;
  height:clamp(330px,35vw,450px);touch-action:pan-y;
  width:calc(100% + var(--gut));margin-right:calc(var(--gut) * -1);
  /* The cylinder has to be clipped or the plates would spill into the copy
     beside it, but a bare overflow:hidden guillotines them — on the left it
     reads as a hard rectangle cut straight through the photographs. The mask
     lets them dissolve instead, so the stack looks like it carries on turning
     past the edge rather than stopping at one.
     The right edge bleeds a gutter past the column and lands on the viewport
     edge at narrow widths, but on a wide screen it becomes visible too, so it
     gets the same treatment. */
  -webkit-mask-image:linear-gradient(to right,
     transparent 0, #000 15%, #000 92%, transparent 100%);
          mask-image:linear-gradient(to right,
     transparent 0, #000 15%, #000 92%, transparent 100%)}
.sheet.is-3d.is-grab .sheet-frame{cursor:grabbing}
.sheet.is-3d .sheet-stage{display:block;overflow:visible;height:100%;
  padding:0;scroll-snap-type:none}
.sheet.is-3d .plate{position:absolute;inset:0;margin:auto;flex:none;
  width:clamp(172px,18.5vw,250px);aspect-ratio:3/4;height:auto;
  backface-visibility:hidden;will-change:transform,opacity}
.sheet.is-3d .plate img{height:100%;aspect-ratio:auto}

/* Crop marks sit on the plate that is facing front — print-shop language
   rather than a highlight ring, and it doubles as the "this one" signal. */
.sheet.is-3d .plate::after{
  content:"";position:absolute;inset:-9px;pointer-events:none;
  opacity:0;transition:opacity .45s var(--ease);
  background:
    linear-gradient(var(--ink),var(--ink)) left  top    /1px 13px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left  top    /13px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top    /1px 13px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right top    /13px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left  bottom /1px 13px no-repeat,
    linear-gradient(var(--ink),var(--ink)) left  bottom /13px 1px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom /1px 13px no-repeat,
    linear-gradient(var(--ink),var(--ink)) right bottom /13px 1px no-repeat;
}
.sheet.is-3d .plate.is-live::after{opacity:1}

/* --- the rail under it: counter, caption, position ---------------------- */
.sheet-bar{display:flex;align-items:center;gap:clamp(10px,1.4vw,18px);
  margin-top:clamp(14px,1.6vw,22px);padding-top:12px;
  border-top:2px solid var(--ink);
  font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.2em;text-transform:uppercase}
.sheet-n b{font-weight:500}
.sheet-n i{font-style:normal;opacity:.35;padding:0 .4em}
.sheet-lab{flex:1;min-width:0;text-align:right;opacity:.5;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sheet-cap{margin:10px 0 0;font-family:var(--f-sans);font-size:var(--t-body);
  line-height:1.3;letter-spacing:-.01em;text-transform:none}
.sheet-rule{flex:0 0 clamp(48px,6vw,88px);height:2px;
  background:var(--ink-14);overflow:hidden}
.sheet-rule i{display:block;height:100%;background:var(--orange);
  transform:scaleX(.111);transform-origin:left;
  transition:transform .55s var(--ease-cut)}

/* A single quiet nudge that it moves, gone the moment you touch it */
.sheet-hint{position:absolute;right:0;top:0;margin:0;z-index:300;
  font-family:var(--f-mono);font-size:.6rem;letter-spacing:.24em;
  text-transform:uppercase;pointer-events:none;opacity:.45;
  transition:opacity .4s var(--ease)}
.sheet:not(.is-3d) .sheet-hint,
.sheet.is-touched .sheet-hint{opacity:0}

@media (max-width:900px){
  .sheet.is-3d .sheet-frame{height:clamp(330px,72vw,450px)}
  .sheet.is-3d .plate{width:clamp(180px,43vw,250px)}
}

/* ==========================================================================
   27 · HERO MISREGISTRATION
   Two plates behind the headline, offset horizontally only. Ghosts overlay the
   headline box exactly rather than stretching to it, which is what made the
   first attempt look misaligned.

   NAMING: the plates are .plate-a / .plate-b, NOT .g1 / .g2. They used to be
   g1/g2, which collided with the .g2 two-column grid utility in section 13 —
   the second plate silently became a grid and reflowed its three lines into
   grid cells, throwing it out of alignment. Keep plate class names namespaced.
   display:block below is belt-and-braces against the same class of mistake.
   ========================================================================== */
.misreg{position:relative;display:block}
.misreg .ghost{
  position:absolute;top:0;left:0;width:100%;
  display:block;
  pointer-events:none;user-select:none;margin:0;
  mix-blend-mode:multiply;will-change:transform;
}
.misreg .ghost.plate-a{color:var(--ember);
  transform:translate3d(calc(var(--rx,0) * -1px),0,0)}
.misreg .ghost.plate-b{color:var(--butter);
  transform:translate3d(calc(var(--rx,0) * 1px),0,0)}
.misreg > h1{position:relative;z-index:2;margin:0}

@media (prefers-reduced-motion:reduce){
  .misreg .ghost{display:none}
  [data-hstage]{height:auto!important}
  [data-hstage] .hpin{position:static!important;height:auto!important}
  /* No sideways sweep, so the six stages stack into a readable grid instead.
     Six across would leave columns far too narrow for the enlarged cards. */
  [data-hscroll]>.proc{width:auto!important;transform:none!important;
    grid-template-columns:repeat(3,1fr)!important}
  [data-hstage] .proc-step{min-height:clamp(200px,20vw,290px)!important;
    padding:clamp(18px,1.8vw,26px)!important}
  [data-hstage] .proc-step .t{font-size:clamp(1.05rem,1.7vw,1.5rem)!important}
  /* the phone carousel's entrance must not leave cards stuck invisible */
  [data-hstage] .proc-step{opacity:1!important;transform:none!important;
    transition:none!important}
  [data-hscroll]{overflow-x:visible!important;scroll-snap-type:none!important;
    margin-inline:0!important;padding-inline:0!important}
}


/* ==========================================================================
   28 · BIO — the About block, treated as an editorial spread
   ========================================================================== */
.bio{display:grid;grid-template-columns:.82fr 1.18fr;gap:clamp(26px,5vw,86px);align-items:start}
.bio-fig{position:relative;margin:0}
.bio-fig img{width:100%;aspect-ratio:4/5;object-fit:cover;display:block}
.bio-fig figcaption{display:flex;flex-direction:column;gap:3px;
  padding-top:12px;border-top:2px solid var(--ink);margin-top:12px}
.bio-text{display:flex;flex-direction:column;gap:clamp(16px,2vw,26px)}

.facts{display:grid;gap:0;margin:6px 0 0;border-top:2px solid var(--ink)}
.facts > div{display:grid;grid-template-columns:170px 1fr;gap:clamp(12px,2vw,28px);
  padding:14px 0;border-bottom:1px solid var(--ink-14)}
.facts dt{font-family:var(--f-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:.2em;text-transform:uppercase;color:var(--orange)}
.facts dd{margin:0;font-size:.96rem;line-height:1.5;color:var(--ink-70)}
@media (max-width:900px){
  .bio{grid-template-columns:1fr}
  .facts > div{grid-template-columns:1fr;gap:4px;padding:12px 0}
}
