/* ============================================================
   form●works — Portfolio
   Edition 02 · Editorial Quietude
   Shared stylesheet. Bound to DESIGN.md tokens.

   Webfont is loaded by a <link> in each page head rather than from inside this
   file, so it resolves in parallel with the stylesheet instead of behind it.
   ============================================================ */

:root{
  /* surfaces + ink */
  --fw-cream:  #F5EFE6;
  --fw-ink:    #1A1410;
  --fw-forest: #3D4A3E;
  --fw-turm:   #D4913A;

  /* derived text */
  --ink-mute:   #5A5450;
  --ink-strong: #3A332E;
  --cream-mute: #B6B7AD;              /* 4.61:1 on forest, 8.99:1 on ink */

  --hairline-light: rgba(61,74,62,0.20);
  --hairline-dark:  rgba(245,239,230,0.22);
  --rule-strong:      rgba(61,74,62,0.62);      /* 3.01:1 on cream */
  --rule-strong-dark: rgba(245,239,230,0.55);

  /* type */
  --sans: "ABC Diatype", "Hanken Grotesk", system-ui, sans-serif;
  --logo: "Kilimanjaro Sans", "Hanken Grotesk", system-ui, sans-serif;

  /* spatial system: base 8 */
  --nav-h: 64px;
  --pad: clamp(20px, 5.5vw, 112px);
  --gut: clamp(20px, 3.4vw, 72px);
  --maxw: 1440px;
  --maxw-read: 1180px;

  --accent-text: var(--fw-forest);
  --accent-display: var(--fw-forest);
  --err: #8C3A1E;
}

/* ------------------------------------------------------------
   THE WORDMARK FACE
   Subset to the seven letters the lockup uses, which is the whole of
   its job on this site. 1.2 KB. Self-hosted, because relying on the
   font being installed locally meant the logo rendered correctly for
   the studio and in the body typeface for every actual visitor.
   ------------------------------------------------------------ */
@font-face{
  font-family:"Kilimanjaro Sans";
  src:url("fonts/kilimanjaro-wordmark.woff2") format("woff2");
  font-weight:400 700;      /* one cut, declared across the range so nothing is faux-bolded */
  font-style:normal;
  font-display:swap;
  unicode-range:U+0066,U+006B,U+006D,U+006F,U+0072,U+0073,U+0077;
}

/* ------------------------------------------------------------
   ACCENT STANDING RULE
   Turmeric #D4913A is 2.32:1 on cream, 3.52:1 on forest, 6.86:1 on ink.
   On cream: turmeric may be a background fill, the ● glyph, or a rule.
             NEVER body text, and never the sole indicator of state.
   On forest: turmeric text only at >= 24px (large-text threshold).
   On ink:   turmeric text at any size.
   --accent-text  is the always-legal role for small text.
   --accent-display is the large-display role.
   ------------------------------------------------------------ */

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
html,body{overflow-x:clip}   /* clip NOT hidden — hidden kills position:sticky */
body{
  font-family:var(--sans);
  background:var(--fw-cream);
  color:var(--fw-ink);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  font-feature-settings:"kern" 1;
}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}

/* ---------- type scale ---------- */
.t-mega   { font:600 clamp(52px,11.5vw,168px)/0.86 var(--sans); letter-spacing:-0.045em; text-wrap:balance; overflow-wrap:break-word; }
.t-display{ font:600 clamp(46px,7.6vw,116px)/0.90  var(--sans); letter-spacing:-0.045em; text-wrap:balance; overflow-wrap:break-word; }
.t-hero   { font:500 clamp(30px,4.4vw,62px)/1.02   var(--sans); letter-spacing:-0.032em; text-wrap:balance; }
.t-title  { font:500 clamp(23px,2.6vw,34px)/1.10   var(--sans); letter-spacing:-0.025em; }
.t-italic { font:italic 400 clamp(22px,3.2vw,36px)/1.18 var(--sans); letter-spacing:-0.02em; }
.t-lede   { font:400 clamp(19px,1.9vw,26px)/1.42   var(--sans); letter-spacing:-0.018em; max-width:46ch; color:var(--ink-strong); }
.t-body-lg{ font:400 clamp(17px,1.35vw,20px)/1.55  var(--sans); letter-spacing:-0.012em; max-width:58ch; }
.t-body   { font:400 17px/1.62 var(--sans); max-width:62ch; color:var(--ink-strong); }
.t-eyebrow{ font:500 11px/1.5 var(--sans); letter-spacing:0.22em; text-transform:uppercase; color:var(--accent-text); }
.t-num    { font:600 clamp(56px,8.5vw,132px)/0.9 var(--sans); letter-spacing:-0.04em; font-variant-numeric:tabular-nums; }
.t-eyebrow.mute{ color:var(--ink-mute); }

.mute{color:var(--ink-mute)}
.accent{color:var(--accent-display)}
/* The standing rule, enforced in code: turmeric on forest is 3.52:1, legal for
   large text only, so accent type on a forest surface never falls below 24px.
   Same clamp shape as the base tier, minimum lifted to the legal floor. */
.forest .t-italic.accent{font-size:clamp(24px,3.2vw,36px)}
.forest .t-title.accent {font-size:clamp(24px,2.6vw,34px)}
em,i{font-style:italic}

/* turmeric dot */
.dot{color:var(--fw-turm)}
.dot-sep{color:var(--fw-turm);font-size:0.5em;vertical-align:0.16em;padding:0 .55em}

/* ---------- wordmark ---------- */
.wordmark{font-family:var(--logo);font-weight:500;letter-spacing:-0.03em;line-height:1;display:inline-block}
/* Kilimanjaro Sans contains no U+25CF, so a typed dot falls through to
   whatever font the browser reaches next: Times on Windows, something else
   elsewhere, at a different size and height each time. The brand's signature
   is too important to leave to that, so it is drawn. Sized and positioned to
   match what the fallback produced, so nothing shifts. */
.wordmark .dot{
  display:inline-block;font-size:inherit;
  width:0.17em;height:0.17em;border-radius:50%;
  background:var(--fw-turm);
  color:transparent;overflow:hidden;
  vertical-align:-0.02em;margin-inline:0.055em}

/* ---------- layout primitives ---------- */
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad)}
.wrap.read{max-width:var(--maxw-read)}
/* Full bleed by negative inline margin, NOT by margin-left:50% plus a
   translate. The old pair had two faults: `.fig{margin:0}` appears later in
   this file at equal specificity and silently zeroed the margin-left, leaving
   the translate to drag every figure half a viewport to the left; and holding
   position in `transform` collides with anything else that wants to transform
   the same element. margin-inline has neither problem. */
.bleed{width:100vw;max-width:100vw;margin-inline:calc(50% - 50vw)}
.section       {padding-block:clamp(72px,8vw,132px)}
.section.tight {padding-block:clamp(44px,4.5vw,72px)}
.section.loose {padding-block:clamp(104px,13vw,220px)}
.section.flush {padding-block:0}
.hair{border:0;border-top:0.5px solid var(--hairline-light)}

/* surface modifiers */
.forest{background:var(--fw-forest);color:var(--fw-cream);
  --accent-text:var(--fw-cream);
  --accent-display:var(--fw-turm);
  --ink-mute:var(--cream-mute);--ink-strong:var(--fw-cream);
  --hairline-light:var(--hairline-dark);--rule-strong:var(--rule-strong-dark);}
.ink{background:var(--fw-ink);color:var(--fw-cream);
  --accent-text:var(--fw-turm);   /* 6.86:1 — legal at any size */
  --accent-display:var(--fw-turm);
  --ink-mute:var(--cream-mute);--ink-strong:var(--fw-cream);
  --hairline-light:var(--hairline-dark);--rule-strong:var(--rule-strong-dark);}

/* ---------- top nav ---------- */
.nav{position:sticky;top:0;z-index:50;background:color-mix(in srgb,var(--fw-cream) 92%,transparent);
  backdrop-filter:saturate(140%) blur(12px);border-bottom:0.5px solid var(--hairline-light)}
@supports not (backdrop-filter:blur(1px)){.nav{background:var(--fw-cream)}}
.nav .wrap{display:flex;align-items:center;gap:16px;justify-content:space-between;min-height:var(--nav-h)}
.nav .wordmark{font-size:clamp(21px,2vw,27px)}
.nav-links{display:flex;align-items:center;gap:clamp(16px,2.4vw,38px)}
.nav-links a{font-size:14px;letter-spacing:0.02em;color:var(--ink-strong);position:relative;display:inline-flex;align-items:center;min-height:44px}
.nav-links a::after{content:"";position:absolute;left:0;right:100%;bottom:12px;height:1px;background:var(--fw-turm);transition:right .3s ease}
.nav-links a:hover::after,.nav-links a[aria-current="page"]::after{right:0}
.nav-actions{display:flex;align-items:center;gap:10px}
.nav-cta{font-size:13px;letter-spacing:0.02em;border:1px solid var(--rule-strong);padding:11px 18px;min-height:44px;border-radius:999px;display:inline-flex;align-items:center;transition:translate .35s cubic-bezier(.22,.61,.36,1),background-color .25s,color .25s,border-color .25s}
.nav-wa{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;min-width:44px;padding:0 14px;border-radius:999px;background:var(--fw-forest);color:var(--fw-cream);font:500 13px/1 var(--sans);transition:translate .35s cubic-bezier(.22,.61,.36,1),background-color .25s}
.nav-wa svg{width:16px;height:16px;flex:none;fill:currentColor}
.nav-toggle{display:none;background:none;border:0;cursor:pointer;min-height:44px;min-width:44px;padding:0 10px;font:500 12px/1 var(--sans);letter-spacing:0.16em;color:var(--fw-ink)}
.nav-scrim{position:fixed;inset:0;background:var(--fw-ink);opacity:0;pointer-events:none;z-index:39;transition:opacity .3s ease}
.nav-scrim.on{opacity:.4;pointer-events:auto}
/* Specificity matters here: .nav-links a is (0,1,1) and a media query adds no
   specificity of its own, so these show/hide rules must match it or lose. */
.nav-links a.nav-drawer-cta{display:none}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font:500 15px/1 var(--sans);letter-spacing:0.01em;padding:16px 28px;min-height:48px;
  border-radius:999px;border:1px solid transparent;cursor:pointer;
  transition:background .25s,color .25s,border-color .25s,translate .2s}
.btn-fill{background:var(--fw-turm);color:var(--fw-ink)}
.btn-fill:hover{translate:0 -2px;background:var(--fw-ink);color:var(--fw-cream)}
.btn-line{border-color:var(--rule-strong);color:inherit}
.btn-line:hover{border-color:currentColor;background:rgba(61,74,62,0.05)}
.btn .arr{transition:translate .25s}
.btn:hover .arr{translate:4px 0}
.btn-wa{background:var(--fw-forest);color:var(--fw-cream);border-color:var(--fw-forest)}
.btn-wa:hover{background:var(--fw-ink);border-color:var(--fw-ink)}
.forest .btn-wa,.ink .btn-wa{background:var(--fw-cream);color:var(--fw-ink);border-color:var(--fw-cream)}
.btn-wa svg{width:18px;height:18px;flex:none;fill:currentColor}
.btn-row{display:flex;flex-wrap:wrap;gap:14px}

/* ---------- focus ---------- */
:where(a,button,input,textarea,select,summary,[tabindex]):focus-visible{
  outline:2px solid var(--fw-ink);outline-offset:3px;border-radius:2px}
.forest :focus-visible,.ink :focus-visible,.foot :focus-visible{outline-color:var(--fw-cream)}
.field input:focus-visible,.field textarea:focus-visible{outline-offset:2px}

/* ---------- figures ---------- */
.fig{display:block}   /* margin already zeroed by the universal reset; re-stating it here overrode .bleed */
.fig .fr{display:block;overflow:hidden;border:0.5px solid var(--hairline-light);background:rgba(61,74,62,0.04)}
.fig img{display:block;width:100%;height:100%;object-fit:cover}
.fig .cap{font:500 11px/1.5 var(--sans);letter-spacing:0.14em;text-transform:uppercase;color:var(--ink-mute);padding-top:12px}
.fig[data-r="16x9"] img{aspect-ratio:16/9}
.fig[data-r="5x4"]  img{aspect-ratio:5/4}
.fig[data-r="4x3"]  img{aspect-ratio:4/3}
.fig[data-r="4x5"]  img{aspect-ratio:4/5}
.fig[data-r="9x16"] img{aspect-ratio:9/16}
.fig[data-r="3x1"]  img{aspect-ratio:3/1}
.fig.stick{position:sticky;top:calc(var(--nav-h) + 28px)}
/* drawings shown whole, never cropped */
.fig.drawing .fr{background:var(--fw-cream);border-color:var(--rule-strong);padding:clamp(16px,3vw,40px)}
.fig.drawing img{object-fit:contain;aspect-ratio:9/16;max-height:min(84svh,900px);width:auto;margin-inline:auto}
/* full-bleed band: ratio from viewport, not from the file */
.fig.band .fr{border:0}
.fig.band img{aspect-ratio:auto;height:var(--band-h,60svh);object-position:50% 55%}
.fig.band .cap{padding-inline:var(--pad)}
/* A bleed figure that holds its native ratio inside a height budget. Capping
   the HEIGHT of a 100vw figure would force object-fit to crop (a 5:4 file at
   1440px wide wants 1152px of height, far past any sane cap), so the WIDTH is
   capped by the height allowance instead. Below the cap it is still full bleed. */
/* Narrower than the viewport by design, so it has to re-centre itself:
   the negative inline margins .bleed hands it would otherwise pin it to
   the left edge. Over-constrained at full width, auto resolves to zero
   on the left and it bleeds as before. */
.fig.fit-5x4{width:min(100vw,calc(82svh * 5 / 4));margin-inline:auto}
.fig.fit-5x4 .cap{padding-inline:0}

/* ---------- hero ----------
   Two bands: type above, full-bleed photograph below, the pair sized to the
   first viewport. Rows are 1fr/auto so the copy centres in whatever is left
   and the band always sits on the bottom edge.
   NOTE: this stays a two-band stack at every width. The side-by-side variant
   (a 44ch copy column at >=1024) cannot hold .t-mega: 44ch resolves to ~394px
   at the root size, while a 168px headline needs ~1000px of measure. */
.hero{display:grid;grid-template-rows:1fr auto;
  min-height:calc(100vh - var(--nav-h));
  min-height:calc(100svh - var(--nav-h))}
.hero-copy{align-self:center;padding-block:clamp(36px,6vw,72px) clamp(28px,4vw,56px)}

/* ---------- index/list rows ---------- */
.idx{border-top:0.5px solid var(--hairline-light)}
.idx-row{display:grid;grid-template-columns:72px minmax(0,1fr) minmax(0,26ch);
  gap:var(--gut);align-items:baseline;padding-block:clamp(26px,3.2vw,44px);
  border-bottom:0.5px solid var(--hairline-light);
  transition:padding-left .35s cubic-bezier(.22,.61,.36,1),background-color .35s}
.idx-row:hover,.idx-row:focus-visible{padding-left:16px;background:rgba(61,74,62,0.035)}
.idx-num{font:500 13px/1 var(--sans);color:var(--ink-mute);letter-spacing:0.14em}
.idx-num::after{content:"●";color:var(--fw-turm);font-size:0.8em;padding-left:.5em;vertical-align:0.08em}
.idx-name{font:500 clamp(24px,3.6vw,52px)/1.06 var(--sans);letter-spacing:-0.03em}
.idx-name .loc{color:var(--ink-mute);font-weight:400}
.idx-meta{font:400 13px/1.5 var(--sans);color:var(--ink-mute)}

/* ---------- grids ---------- */
.cols-2{display:grid;grid-template-columns:1fr 1fr;gap:var(--gut)}
.cols-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gut)}
.split{display:grid;grid-template-columns:0.9fr 1.1fr;gap:clamp(32px,6vw,96px);align-items:start}
.split.text-left{grid-template-columns:1.1fr 0.9fr}
.split.even{grid-template-columns:1fr 1fr}

/* ---------- meta strip ---------- */
.meta-strip{display:flex;flex-wrap:wrap;align-items:center;gap:6px 0;font:400 14px/1.6 var(--sans);color:var(--ink-mute)}
.meta-strip span + span::before{content:"●";color:var(--fw-turm);font-size:0.55em;vertical-align:0.18em;padding:0 .7em}

/* ---------- measure band ---------- */
.s-measure{--band-cols:4}
.s-measure .grid{display:grid;grid-template-columns:repeat(var(--band-cols),minmax(0,1fr));gap:clamp(28px,4vw,72px);align-items:end}
.s-measure .m-val{font:600 clamp(52px,7.2vw,124px)/0.88 var(--sans);letter-spacing:-0.045em;color:var(--fw-turm);font-variant-numeric:tabular-nums}
.s-measure .m-lab{font:500 11px/1.5 var(--sans);letter-spacing:0.22em;text-transform:uppercase;color:var(--fw-cream);margin-top:14px;padding-top:14px;border-top:0.5px solid var(--rule-strong-dark)}

/* ---------- before / after ---------- */
.s-ba{display:grid;grid-template-columns:1fr 1fr;gap:2px;background:var(--rule-strong)}
.s-ba > figure{margin:0;background:var(--fw-cream);position:relative}
/* height:auto is load-bearing: the width/height attributes on every <img> are
   presentational hints that make the box definite, and a definite box makes the
   browser ignore aspect-ratio entirely. Without this the before/after panes
   render at their raw pixel heights (50% and 68% crop measured). */
.s-ba img{aspect-ratio:5/4;object-fit:cover;width:100%;height:auto}
.s-ba .tag{position:absolute;top:0;left:0;font:500 11px/1 var(--sans);letter-spacing:0.22em;text-transform:uppercase;background:var(--fw-ink);color:var(--fw-cream);padding:10px 14px}

/* ---------- ledger ---------- */
.ledger{border-top:0.5px solid var(--hairline-light)}
.ledger-row{display:grid;grid-template-columns:clamp(56px,6vw,96px) minmax(0,clamp(180px,22vw,320px)) minmax(0,1fr);
  gap:clamp(16px,3vw,56px);align-items:baseline;padding-block:clamp(24px,2.8vw,40px);
  border-bottom:0.5px solid var(--hairline-light)}
.ledger-num{font:500 clamp(15px,1.4vw,20px)/1 var(--sans);color:var(--ink-mute);letter-spacing:0.06em;font-variant-numeric:tabular-nums}
.ledger-name{font:500 clamp(20px,2.2vw,30px)/1.12 var(--sans);letter-spacing:-0.024em}
.ledger-break{padding-block:clamp(22px,2.6vw,36px);border-bottom:0.5px solid var(--hairline-light)}

/* ---------- pull quote ---------- */
.pull{font:400 clamp(26px,4.2vw,58px)/1.16 var(--sans);letter-spacing:-0.032em;max-width:20ch;
  margin-block:clamp(48px,7vw,96px);padding-left:clamp(18px,2.2vw,32px);
  border-left:3px solid var(--fw-turm);text-wrap:balance}

/* ---------- faq ---------- */
.faq details{border-bottom:0.5px solid var(--hairline-light)}
.faq summary{list-style:none;cursor:pointer;padding-block:22px;min-height:44px;
  font:500 clamp(17px,1.7vw,21px)/1.4 var(--sans);letter-spacing:-0.015em;
  display:flex;justify-content:space-between;gap:20px;align-items:center}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";color:var(--fw-turm);font-size:1.4em;line-height:0;font-weight:400}
.faq details[open] summary::after{content:"−"}
.faq .ans{padding-bottom:24px;max-width:62ch;color:var(--ink-strong)}

/* ---------- footer ---------- */
.foot{background:var(--fw-forest);color:var(--fw-cream);
  --accent-text:var(--fw-cream);--accent-display:var(--fw-turm);
  --ink-mute:var(--cream-mute);--hairline-light:var(--hairline-dark);--rule-strong:var(--rule-strong-dark)}
.foot .wrap{padding-block:clamp(56px,7vw,96px)}
.foot-top{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:var(--gut);padding-bottom:48px;border-bottom:0.5px solid var(--hairline-dark)}
.foot .wordmark{font-size:34px;color:var(--fw-cream)}
.foot a:hover{color:var(--fw-turm)}
.foot-col{display:flex;flex-direction:column;align-items:flex-start}
.foot-col h4{font:500 11px/1.5 var(--sans);letter-spacing:0.22em;text-transform:uppercase;color:var(--cream-mute);margin-bottom:12px}
.foot-col a{display:inline-flex;align-items:center;min-height:44px;margin-bottom:2px;font-size:15px;color:var(--fw-cream)}
.foot-col p{display:block;font-size:15px;color:var(--fw-cream);margin-bottom:9px}
.foot-bottom{display:flex;justify-content:space-between;flex-wrap:wrap;gap:16px;padding-top:28px;font-size:12px;color:var(--cream-mute);letter-spacing:0.03em}

/* ---------- form ---------- */
.field{margin-bottom:28px}
.field label{display:block;font:500 11px/1.5 var(--sans);letter-spacing:0.18em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:10px}
.field input,.field textarea{width:100%;font:400 17px/1.5 var(--sans);color:var(--fw-ink);
  background:transparent;border:0;border-bottom:1px solid var(--rule-strong);
  padding:12px 0;min-height:48px;outline:none;transition:border-color .25s}
.field input:focus,.field textarea:focus{border-bottom-color:var(--fw-ink);border-bottom-width:2px;padding-bottom:11px}
.field textarea{resize:vertical;min-height:132px}
.field.err input,.field.err textarea{border-bottom-color:var(--err);border-bottom-width:2px}
.field .msg{font:400 13px/1.4 var(--sans);color:var(--err);margin-top:8px;display:none}
.field.err .msg{display:block}
.field.err label::after{content:" ●";color:var(--err)}  /* error not signalled by colour alone */
.form-note{display:none;gap:14px;align-items:flex-start;padding:22px 24px;border:1px solid var(--rule-strong);margin-top:24px}
.form-note.show{display:flex}
.form-note.ok{background:var(--fw-ink);color:var(--fw-cream);border-color:var(--fw-ink)}
.form-note.ok .dot{color:var(--fw-turm)}
.form-note.bad{border-color:var(--err)}
.form-note.ok .btn-wa{background:var(--fw-cream);color:var(--fw-ink);border-color:var(--fw-cream)}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* ---------- mobile action bar ---------- */
.mbar{display:none}

/* ---------- dateline ---------- */
.dateline{display:flex;flex-wrap:wrap;align-items:center;gap:0 14px;font:500 11px/1.6 var(--sans);letter-spacing:0.2em;text-transform:uppercase;color:var(--ink-mute)}
.dateline .dot{color:var(--fw-turm)}

/* ================= kinetic layer =================
   Motion is opt-in per element and every rule below is disabled wholesale by
   the prefers-reduced-motion block at the foot of this file. Entry uses `scale`,
   scroll-linked parallax uses `translate` — separate properties, so they never
   fight over a single `transform`. */

.progress{position:fixed;top:0;left:0;right:0;height:2px;background:var(--fw-turm);transform:scaleX(0);transform-origin:0 50%;z-index:60;will-change:transform;pointer-events:none}
.js .rv{opacity:0;translate:0 28px;transition:opacity .8s cubic-bezier(.22,.61,.36,1),translate .8s cubic-bezier(.22,.61,.36,1)}
.js .rv.in{opacity:1;translate:none}
.js .stagger > *{opacity:0;translate:0 32px;transition:opacity .85s cubic-bezier(.22,.61,.36,1),translate .85s cubic-bezier(.22,.61,.36,1);transition-delay:calc(var(--i,0) * 110ms)}
.js .stagger.in > *{opacity:1;translate:none}
.js .fig img{opacity:0;scale:1.12;transition:scale 1.25s cubic-bezier(.22,.61,.36,1),opacity .9s ease}
.js .fig.in img{opacity:1;scale:1}    /* settles to 1.0, NOT 1.03 */

/* PARALLAX ENVELOPE IS ARITHMETIC, NOT TASTE.
   Constraint: drift <= (scale - 1) / (2 * scale).
   At scale 1.09 the ceiling is 4.13%; we use 3.6%. Lower the scale or raise the
   drift and cream gaps appear at the image edges during scroll. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .fig.band img,.fig[data-r="16x9"] img,.fig[data-r="3x1"] img{
      scale:1.09;animation:drift linear both;animation-timeline:view();animation-range:entry 0% exit 100%}
    @keyframes drift{from{translate:0 -3.6%}to{translate:0 3.6%}}
  }
}

/* word-level mask reveal — headings rise from a clipped baseline.
   Clip box is extended so descenders are not cut; layout is pulled back
   by the matching negative margin. */
.mask-w{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:0.16em;margin-bottom:-0.16em}
.mask-w > span{display:inline-block;translate:0 115%;transition:translate 1s cubic-bezier(.22,.61,.36,1);transition-delay:calc(var(--i,0) * 60ms)}
.mask.in .mask-w > span{translate:none}

/* hover peek — a frame that trails the cursor over the work index */
.peek{position:fixed;top:0;left:0;width:min(26vw,340px);aspect-ratio:4/3;overflow:hidden;border:0.5px solid var(--hairline-light);opacity:0;scale:.92;translate:-50% -50%;transition:opacity .32s ease,scale .32s cubic-bezier(.22,.61,.36,1);pointer-events:none;z-index:50}
.peek img{display:block;width:100%;height:100%;object-fit:cover}
.peek.on{opacity:1;scale:1}

/* ================= responsive ladder =================
   1280 · 1024 · 900 · 768 · 560 · 380, plus (hover:none) and reduced motion. */

@media (min-width:1024px){
  .hero-copy{padding-block:clamp(48px,6vw,96px)}
}

@media (max-width:1024px){
  .cols-3{grid-template-columns:repeat(2,1fr)}
  .split,.split.text-left,.split.even{grid-template-columns:1fr;gap:clamp(28px,7vw,64px)}
  .fig.stick{position:static;top:auto}
  .foot-top{grid-template-columns:1fr 1fr}
  .form-wrap{max-width:620px;margin-inline:auto}   /* only once the contact split has stacked */
}

@media (max-width:900px){
  .idx-row{grid-template-columns:1fr;gap:8px}
  .idx-num{order:-1}
  .idx-meta{display:block;font-size:12px;color:var(--ink-mute)}
  .s-measure .grid{grid-template-columns:1fr 1fr}
  .nav-links{position:fixed;inset:var(--nav-h) 0 auto 0;flex-direction:column;align-items:flex-start;gap:0;
    background:var(--fw-cream);border-bottom:0.5px solid var(--hairline-light);
    padding:8px var(--pad) 28px;translate:0 -120%;transition:translate .32s cubic-bezier(.22,.61,.36,1);z-index:40;
    max-height:calc(100dvh - var(--nav-h));overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
  .nav-links.open{translate:none}
  .nav-links a{width:100%;padding-block:16px;border-bottom:0.5px solid var(--hairline-light);font-size:17px}
  .nav-links a::after{display:none}
  .nav-links a.nav-cta{display:none}
  .nav-links a.nav-drawer-cta{display:flex;width:100%;margin-top:22px;border:0}
  .nav-toggle{display:block}

  .mbar{display:grid;grid-template-columns:1fr 1fr;position:fixed;left:0;right:0;bottom:0;z-index:55;
    background:var(--fw-ink);border-top:0.5px solid var(--rule-strong-dark);
    padding-bottom:env(safe-area-inset-bottom,0px);
    translate:0 100%;transition:translate .3s cubic-bezier(.22,.61,.36,1)}
  .mbar.on{translate:0 0}
  .mbar a{display:flex;align-items:center;justify-content:center;gap:8px;min-height:56px;
    font:500 14px/1 var(--sans);letter-spacing:0.02em;color:var(--fw-cream)}
  .mbar a + a{border-left:0.5px solid var(--rule-strong-dark)}
  .mbar a.wa{background:var(--fw-forest)}
  .mbar svg{width:17px;height:17px;flex:none;fill:currentColor}
  body.has-mbar{padding-bottom:calc(56px + env(safe-area-inset-bottom,0px))}
}

@media (max-width:768px){
  .cols-3,.cols-2{grid-template-columns:1fr;gap:clamp(24px,5vw,40px)}
  .ledger-row{grid-template-columns:auto 1fr;gap:6px 18px}
  .ledger-row > p{grid-column:2}
  .s-ba{grid-template-columns:1fr}
  .strip{display:grid;grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:78%;gap:12px;overflow-x:auto;
    scroll-snap-type:x mandatory;padding-inline:var(--pad);margin-inline:calc(var(--pad) * -1);scrollbar-width:none}
  .strip::-webkit-scrollbar{display:none}
  .strip > *{scroll-snap-align:start}
}

@media (max-width:560px){
  .foot-top{grid-template-columns:1fr;gap:36px}
  .foot-col + .foot-col{padding-top:28px;border-top:0.5px solid var(--rule-strong-dark)}
}

@media (max-width:480px){
  .s-measure .grid{grid-template-columns:1fr;gap:24px}
  .s-measure .m-val{font-size:56px}
}

@media (max-width:560px){
  .nav-wa .lbl{display:none}   /* pill collapses to the 44x44 glyph, label stays in aria-label */
}

@media (max-width:380px){
  :root{--pad:20px}
  .t-mega{font-size:46px}
  .t-display{font-size:40px}
}

@media (max-height:520px){
  .fig.drawing img{max-height:70svh}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  .js .rv,.js .stagger > *{opacity:1!important;translate:none!important;transform:none!important}
  .js .fig img{opacity:1!important;scale:1!important;translate:none!important}
  .js .mask-w > span{translate:none!important;transform:none!important}
  .mbar{translate:none!important}
  .progress,.peek{display:none!important}
  html{scroll-behavior:auto}
}

@media (hover:none){
  .peek{display:none!important}
  .idx-row:hover{padding-left:0;background:none}
  .nav-links a::after{right:0;opacity:.4}
}

/* ============================================================
   KINETIC LAYER · Edition 02.1
   Every rule here is opt-in per element, and every one of them is
   switched off wholesale by the reduced-motion block at the foot of
   this file. Nothing below carries content: if the script never runs,
   or motion is refused, the page still reads in full.

   Bound to the same four tokens. No gradients, no shadows, no third
   typeface, no cool grey. Hairlines stay 0.5px.
   ============================================================ */

/* ---------- cross-document transitions ----------
   Progressive enhancement: browsers without it simply navigate. The nav
   and the footer are named so they hold still while the page between
   them cross-fades, which is what makes five documents feel like one
   continuous surface. */
@media (prefers-reduced-motion:no-preference){
  @view-transition{navigation:auto}
  .nav{view-transition-name:fw-nav}
  .foot{view-transition-name:fw-foot}
  ::view-transition-old(root){animation-duration:.28s}
  ::view-transition-new(root){animation-duration:.34s}
}

/* ---------- entry curtain ----------
   Shown once per session, not once per page: a studio site people click
   through should not re-introduce itself at every navigation. */
.curtain{display:none}
/* The head script decides, before first paint, whether this session has
   already been greeted. Gating on the class means neither state flashes:
   no curtain on a return page, no page behind the curtain on a first one. */
html.curtained .curtain{position:fixed;inset:0;z-index:200;background:var(--fw-forest);
  display:grid;place-items:center;pointer-events:none;
  clip-path:inset(0 0 0 0);
  transition:clip-path 1s cubic-bezier(.76,0,.24,1)}
.curtain .wordmark{font-size:clamp(38px,7vw,88px);color:var(--fw-cream);
  opacity:0;translate:0 14px;
  transition:opacity .7s ease .12s,translate .9s cubic-bezier(.22,.61,.36,1) .12s}
.curtain .rule{position:absolute;left:var(--pad);right:var(--pad);bottom:clamp(36px,6vw,72px);
  height:1px;background:var(--rule-strong-dark);transform:scaleX(0);transform-origin:0 50%;
  transition:transform 1.05s cubic-bezier(.22,.61,.36,1) .1s}
.curtain.lit .wordmark{opacity:1;translate:none}
.curtain.lit .rule{transform:scaleX(1)}
.curtain.lit .dot{animation:pulseDot 1.1s ease-in-out .35s 1}
.curtain.up{clip-path:inset(0 0 100% 0)}
@keyframes pulseDot{0%,100%{scale:1;opacity:1}50%{scale:1.18;opacity:.55}}
html.curtained{overflow:hidden}

/* ---------- pointer ----------
   The brand signature doing the work an arrow usually does: a turmeric
   dot at the point, a ring trailing behind it. Fine pointers only, and
   the native cursor is never taken away from anything you type into. */
/* Position is written into the `translate` property by the engine, centring
   included, and NOT into `transform`. The order the two compose in is fixed
   (translate, then rotate, then scale, then transform), so a position held in
   `transform` would be applied inside `scale` and multiplied by it: the dot
   would fly to 45% of the pointer's coordinates on hover, and collapse onto
   0,0 whenever scale hit zero. Kept here, `scale` shrinks the dot in place. */
.cur,.cur-ring{position:fixed;top:0;left:0;z-index:150;pointer-events:none;
  will-change:translate}
.cur{width:7px;height:7px;border-radius:50%;background:var(--fw-turm);
  transition:scale .28s cubic-bezier(.22,.61,.36,1),opacity .25s}
.cur-ring{width:34px;height:34px;border-radius:50%;border:1px solid var(--rule-strong);
  display:grid;place-items:center;
  transition:width .32s cubic-bezier(.22,.61,.36,1),height .32s cubic-bezier(.22,.61,.36,1),
             border-color .3s,background-color .3s,opacity .25s}
.cur-ring .lbl{font:500 10px/1 var(--sans);letter-spacing:.18em;text-transform:uppercase;
  color:var(--fw-cream);opacity:0;transition:opacity .2s;white-space:nowrap}
body.cur-on{cursor:none}
body.cur-on a,body.cur-on button,body.cur-on summary,body.cur-on [data-cur]{cursor:none}
body.cur-on input,body.cur-on textarea,body.cur-on select{cursor:auto}
.cur-idle .cur,.cur-idle .cur-ring{opacity:0}
/* over anything clickable, the ring opens and the dot shrinks into it */
.cur-hot .cur{scale:.45}
.cur-hot .cur-ring{width:52px;height:52px;border-color:var(--fw-turm)}
/* over a figure or a gallery, the ring fills and names the gesture */
.cur-label .cur{scale:0}
.cur-label .cur-ring{width:78px;height:78px;background:var(--fw-ink);border-color:var(--fw-ink)}
.cur-label .cur-ring .lbl{opacity:1}
@media (hover:none),(pointer:coarse){.cur,.cur-ring{display:none!important}body.cur-on{cursor:auto}}

/* ---------- nav: tucks away going down, returns coming up ---------- */
.nav{transition:translate .42s cubic-bezier(.22,.61,.36,1),border-color .3s}
.nav.tuck{translate:0 -101%}
/* ---------- section rail ----------
   A running index of the page down the right edge. Labels on hover, real
   anchors underneath, so it is navigation and not decoration. */
.rail{position:fixed;right:clamp(14px,1.6vw,26px);top:50%;translate:0 -50%;z-index:45;
  display:flex;flex-direction:column;gap:14px;align-items:flex-end;
  opacity:0;transition:opacity .4s ease;pointer-events:none}
.rail.on{opacity:1;pointer-events:auto}
.rail a{display:flex;align-items:center;gap:10px;justify-content:flex-end;height:14px}
.rail .tick{width:18px;height:1px;background:var(--fw-ink);opacity:.32;flex:none;
  transition:width .34s cubic-bezier(.22,.61,.36,1),opacity .3s,background-color .3s}
.rail .name{font:500 10px/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-mute);opacity:0;translate:6px 0;white-space:nowrap;
  transition:opacity .3s,translate .3s cubic-bezier(.22,.61,.36,1)}
.rail a:hover .name,.rail a:focus-visible .name{opacity:1;translate:none}
.rail a:hover .tick{width:30px;opacity:.7}
.rail a[aria-current="true"] .tick{width:34px;opacity:1;background:var(--fw-turm)}
.rail a[aria-current="true"] .name{opacity:1;translate:none;color:var(--ink-strong)}
/* the rail reads whichever surface happens to be behind it */
.rail.over-dark .tick{background:var(--fw-cream)}
.rail.over-dark a[aria-current="true"] .tick{background:var(--fw-turm)}
.rail.over-dark .name{color:var(--cream-mute)}
.rail.over-dark a[aria-current="true"] .name{color:var(--fw-cream)}
@media (max-width:1180px){.rail{display:none}}

/* ---------- character reveal ----------
   Words stay whole words: no whitespace is injected inside one, so the
   heading is still a single string to a screen reader. Only paint splits. */
/* These have to out-specify `.mask-w > span` above, which is the word-level
   rule they are refining: a bare `.ch` would lose to it and every character
   in a word would move as one block. */
.mask-w > .ch{display:inline-block;translate:0 110%;
  transition:translate .9s cubic-bezier(.22,.61,.36,1);
  transition-delay:calc(var(--c,0) * 26ms + var(--w,0) * 34ms)}
.mask.in .mask-w > .ch{translate:none}

/* ---------- curtain wipe on figures ----------
   The photograph is uncovered rather than faded in: a clip edge travels
   up the frame while the image settles out of its overscale. */
.js .fig[data-wipe] .fr{clip-path:inset(0 0 100% 0);
  transition:clip-path 1.15s cubic-bezier(.76,0,.24,1)}
.js .fig[data-wipe].in .fr{clip-path:inset(0 0 0 0)}

/* ---------- scroll cue ---------- */
.cue{display:flex;align-items:center;gap:14px;margin-top:clamp(24px,3vw,40px)}
.cue .line{width:1px;height:clamp(34px,5vw,64px);background:var(--rule-strong);
  position:relative;overflow:hidden;flex:none}
.cue .line::after{content:"";position:absolute;inset:0;background:var(--fw-turm);
  animation:cueRun 2.4s cubic-bezier(.65,0,.35,1) infinite}
@keyframes cueRun{0%{translate:0 -100%}55%,100%{translate:0 100%}}
.cue .t-eyebrow{color:var(--ink-mute)}

/* ---------- counters ---------- */
.m-val[data-count]{font-variant-numeric:tabular-nums}

/* ---------- ledger: hover draws a rule under the row ---------- */
.ledger-row{position:relative;transition:padding-left .4s cubic-bezier(.22,.61,.36,1)}
.ledger-row::before{content:"";position:absolute;left:0;bottom:-0.5px;height:1px;width:100%;
  background:var(--fw-turm);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .55s cubic-bezier(.22,.61,.36,1)}
.ledger-row:hover::before,.ledger-row:focus-within::before{transform:scaleX(1)}
.ledger-row:hover{padding-left:clamp(8px,1vw,18px)}
.ledger-row .ledger-num{transition:color .35s}
.ledger-row:hover .ledger-num{color:var(--accent-display)}
@media (hover:none){.ledger-row:hover{padding-left:0}.ledger-row::before{display:none}}

/* ---------- inline link: the underline wipes through ----------
   The rule is a solid 1px bar, not a gradient trick: on hover the origin
   moves to the right edge and it retracts that way, and on release the
   origin returns to the left so it grows back in the direction you read. */
.ilink{position:relative;display:inline-block;color:var(--accent-text);padding-bottom:2px}
.ilink::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:currentColor;transform:scaleX(1);transform-origin:left center;
  transition:transform .38s cubic-bezier(.76,0,.24,1)}
.ilink:hover::after,.ilink:focus-visible::after{transform:scaleX(0);transform-origin:right center}

/* ---------- materials: a selector, not a list ----------
   Each swatch carries a real line about where that material goes, so the
   interaction delivers content rather than movement. */
.mats{margin-top:clamp(28px,4vw,52px)}
.mats-row{display:flex;flex-wrap:wrap;gap:10px}
.mats-row button{font:500 clamp(15px,1.5vw,19px)/1 var(--sans);letter-spacing:-.01em;
  color:inherit;background:none;cursor:pointer;
  border:1px solid var(--rule-strong);border-radius:999px;padding:13px 20px;min-height:46px;
  transition:background-color .3s,color .3s,border-color .3s,translate .3s cubic-bezier(.22,.61,.36,1)}
.mats-row button:hover{translate:0 -2px;border-color:currentColor}
.mats-row button[aria-selected="true"]{background:var(--fw-turm);color:var(--fw-ink);
  border-color:var(--fw-turm)}
.mats-note{margin-top:clamp(24px,3vw,40px);min-height:5.2em;position:relative}
.mats-note p{opacity:0;translate:0 10px;transition:opacity .45s ease,translate .45s cubic-bezier(.22,.61,.36,1)}
.mats-note p.on{opacity:1;translate:none}

/* ---------- marquee, speed carried by the scroll ---------- */
.marq{overflow:hidden;border-block:0.5px solid var(--hairline-light);
  padding-block:clamp(18px,2.2vw,30px);-webkit-user-select:none;user-select:none}
.marq-track{display:flex;width:max-content;will-change:transform}
.marq-track span{font:500 clamp(22px,3.4vw,46px)/1 var(--sans);letter-spacing:-.03em;
  padding-inline:.42em;display:inline-flex;align-items:center;gap:.42em;color:var(--ink-strong)}
.marq-track span::after{content:"●";color:var(--fw-turm);font-size:.3em;vertical-align:middle}

/* ---------- hero filmstrip ----------
   A drifting row of work rather than one still photograph. Heights are
   fixed and widths fall out of each image's own proportion, so portrait
   and landscape frames set their own rhythm along the row.

   Without the script this is a plain horizontally scrollable strip; the
   script takes the scrolling over and sets `drifting`. */
.filmstrip{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  -webkit-user-select:none;user-select:none;
  border-block:0.5px solid var(--hairline-light);
  --fh:min(46svh,32vw,440px)}
.filmstrip::-webkit-scrollbar{display:none}
.filmstrip.drifting{overflow:hidden}
.filmstrip .track{display:flex;gap:2px;width:max-content;will-change:transform}
.filmstrip a{display:block;flex:none;position:relative;height:var(--fh);
  overflow:hidden;background:rgba(61,74,62,0.05)}
.filmstrip img{display:block;height:100%;width:auto;max-width:none;
  transition:scale .9s cubic-bezier(.22,.61,.36,1)}
.filmstrip a:hover img{scale:1.04}
.filmstrip .cap{position:absolute;left:0;right:0;bottom:0;
  background:var(--fw-ink);color:var(--fw-cream);
  font:500 10px/1.3 var(--sans);letter-spacing:.16em;text-transform:uppercase;
  padding:11px 14px;translate:0 101%;
  transition:translate .38s cubic-bezier(.22,.61,.36,1)}
.filmstrip a:hover .cap,.filmstrip a:focus-visible .cap{translate:0 0}
@media (max-width:640px){.filmstrip{--fh:min(40svh,62vw,320px)}}

/* ---------- horizontal gallery ---------- */
.gal-wrap{container-type:inline-size}
.gal-head{display:flex;justify-content:space-between;align-items:baseline;gap:20px;
  flex-wrap:wrap;margin-bottom:clamp(20px,2.6vw,36px)}
.gal{display:grid;grid-auto-flow:column;grid-auto-columns:clamp(240px,34vw,520px);
  gap:clamp(12px,1.6vw,24px);overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:none;
  padding-inline:var(--pad);margin-inline:calc(var(--pad) * -1);
  cursor:grab}
.gal::-webkit-scrollbar{display:none}
.gal.drag{cursor:grabbing;scroll-snap-type:none;scroll-behavior:auto}
.gal.drag *{pointer-events:none}
.gal > figure{scroll-snap-align:center;margin:0}
.gal .fr{display:block;overflow:hidden;border:0.5px solid var(--hairline-light)}
.gal img{width:100%;height:clamp(240px,38vw,560px);object-fit:cover;
  transition:scale .8s cubic-bezier(.22,.61,.36,1)}
.gal > figure:hover img{scale:1.03}
.gal .cap{font:500 11px/1.5 var(--sans);letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-mute);padding-top:12px}
.gal-bar{height:1px;background:var(--hairline-light);margin-top:clamp(20px,2.4vw,32px);
  position:relative;overflow:hidden}
.gal-bar i{position:absolute;inset:0 auto 0 0;background:var(--fw-turm);
  width:var(--w,24%);translate:var(--x,0px) 0}

/* ---------- before / after comparison ----------
   Pointer drag, keyboard arrows, and a real range input underneath for
   assistive tech. The split itself is a 1px turmeric rule, nothing more. */
.cmp{position:relative;overflow:hidden;border:0.5px solid var(--hairline-light);
  touch-action:pan-y;--p:50%;--ar:5/4;background:var(--fw-cream)}
/* The frame takes its ratio from the pair it holds. A portrait pair forced
   into a landscape box loses exactly the part of the room the comparison is
   about, so --ar is set per instance rather than fixed here. */
.cmp .base{display:block;width:100%;height:auto;aspect-ratio:var(--ar);object-fit:cover}
.cmp .over{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--p)) 0 0)}
.cmp .over img{width:100%;height:100%;object-fit:cover;display:block}
.cmp .tag{position:absolute;top:0;font:500 11px/1 var(--sans);letter-spacing:.22em;
  text-transform:uppercase;background:var(--fw-ink);color:var(--fw-cream);padding:10px 14px;z-index:2}
.cmp .tag.l{left:0}
.cmp .tag.r{right:0}
.cmp .bar{position:absolute;top:0;bottom:0;left:var(--p);width:1px;background:var(--fw-turm);
  translate:-50% 0;pointer-events:none;z-index:3}
.cmp .knob{position:absolute;top:50%;left:var(--p);translate:-50% -50%;z-index:4;
  width:56px;height:56px;border-radius:50%;background:var(--fw-turm);border:0;cursor:ew-resize;
  display:grid;place-items:center;color:var(--fw-ink);
  font:500 14px/1 var(--sans);letter-spacing:.1em;
  transition:scale .28s cubic-bezier(.22,.61,.36,1)}
.cmp .knob:hover{scale:1.08}
.cmp .knob:active{cursor:grabbing}
.cmp .sr{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* ---------- lightbox ---------- */
.lb{position:fixed;inset:0;z-index:180;background:var(--fw-ink);
  display:grid;grid-template-rows:auto minmax(0,1fr) auto;
  opacity:0;visibility:hidden;transition:opacity .38s ease,visibility .38s}
.lb.on{opacity:1;visibility:visible}
.lb-head,.lb-foot{display:flex;justify-content:space-between;align-items:center;gap:20px;
  padding:clamp(14px,1.8vw,26px) var(--pad);color:var(--cream-mute);
  font:500 11px/1.5 var(--sans);letter-spacing:.2em;text-transform:uppercase}
.lb-head .wordmark{font-size:22px;color:var(--fw-cream);letter-spacing:-.03em;text-transform:none}
.lb-close{background:none;border:0;color:var(--fw-cream);cursor:pointer;min-height:44px;
  padding:0 6px;font:500 11px/1 var(--sans);letter-spacing:.2em;text-transform:uppercase}
.lb-stage{display:grid;place-items:center;padding-inline:var(--pad);min-height:0}
.lb-stage img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;
  opacity:0;scale:.98;transition:opacity .4s ease,scale .5s cubic-bezier(.22,.61,.36,1)}
.lb-stage img.ready{opacity:1;scale:1}
.lb-nav{display:flex;gap:10px}
.lb-nav button{background:none;border:1px solid var(--rule-strong-dark);border-radius:999px;
  color:var(--fw-cream);cursor:pointer;min-width:46px;min-height:46px;font-size:15px;
  transition:background-color .28s,border-color .28s,color .28s}
.lb-nav button:hover{background:var(--fw-cream);color:var(--fw-ink);border-color:var(--fw-cream)}
.lb-cap{color:var(--fw-cream);letter-spacing:.14em}
@media (max-width:560px){.lb-head .wordmark{font-size:18px}.lb-foot{flex-wrap:wrap;gap:12px}}

/* ---------- process stepper ---------- */
.steps{position:relative;border-top:0.5px solid var(--hairline-light)}
.steps::before{content:"";position:absolute;left:clamp(26px,3vw,46px);top:0;bottom:0;width:1px;
  background:var(--hairline-light)}
.steps .spine{position:absolute;left:clamp(26px,3vw,46px);top:0;width:1px;background:var(--fw-turm);
  height:var(--h,0%);transition:height .18s linear}
.step{position:relative;border-bottom:0.5px solid var(--hairline-light)}
.step-btn{display:grid;grid-template-columns:clamp(56px,6vw,96px) minmax(0,1fr) auto;
  gap:clamp(14px,2.4vw,40px);align-items:center;width:100%;text-align:left;
  background:none;border:0;cursor:pointer;color:inherit;
  padding-block:clamp(20px,2.4vw,34px);font-family:var(--sans)}
.step-n{font:500 clamp(14px,1.3vw,18px)/1 var(--sans);letter-spacing:.06em;color:var(--ink-mute);
  font-variant-numeric:tabular-nums;position:relative;z-index:1;
  background:var(--fw-cream);padding-block:6px;transition:color .3s}
.step-t{font:500 clamp(19px,2.1vw,29px)/1.12 var(--sans);letter-spacing:-.024em}
.step-i{width:22px;height:22px;position:relative;flex:none;justify-self:end}
.step-i::before,.step-i::after{content:"";position:absolute;inset:50% 0 auto 0;height:1px;
  background:var(--accent-display);transition:rotate .42s cubic-bezier(.22,.61,.36,1),opacity .3s}
.step-i::after{rotate:90deg}
.step[data-open="true"] .step-i::after{rotate:0deg;opacity:0}
.step[data-open="true"] .step-n{color:var(--accent-display)}
.step-body{overflow:hidden;height:0;transition:height .5s cubic-bezier(.22,.61,.36,1)}
.step-body .inner{padding:0 0 clamp(24px,3vw,40px) clamp(70px,8.4vw,136px);max-width:64ch}
/* Each step title is a real heading wrapping a real button, so the page
   still has an outline when the steps are closed. The wrapper contributes
   nothing of its own. */
.step h3{font:inherit;margin:0}
.step-break{padding:clamp(22px,2.6vw,36px) 0 clamp(22px,2.6vw,36px) clamp(70px,8.4vw,136px);
  border-bottom:0.5px solid var(--hairline-light);position:relative;background:var(--fw-cream)}
@media (max-width:640px){
  .steps::before,.steps .spine{left:0}
  .step-btn{grid-template-columns:auto minmax(0,1fr) auto;gap:14px}
  .step-body .inner,.step-break{padding-left:0}
}

/* ---------- figures become openable ---------- */
.fig[data-lb] .fr{cursor:zoom-in}

/* ---------- peek gets a caption and a tilt ---------- */
.peek{transition:opacity .32s ease,scale .32s cubic-bezier(.22,.61,.36,1),
  rotate .55s cubic-bezier(.22,.61,.36,1)}
.peek .peek-cap{position:absolute;left:0;right:0;bottom:0;background:var(--fw-ink);
  color:var(--fw-cream);font:500 10px/1 var(--sans);letter-spacing:.18em;
  text-transform:uppercase;padding:9px 12px}

/* ---------- back to top ---------- */
.totop{position:fixed;right:clamp(16px,2vw,32px);bottom:clamp(16px,2vw,32px);z-index:46;
  width:52px;height:52px;border-radius:50%;border:1px solid var(--rule-strong);
  background:var(--fw-cream);color:var(--fw-ink);cursor:pointer;
  display:grid;place-items:center;font-size:15px;
  opacity:0;translate:0 12px;pointer-events:none;
  transition:opacity .35s ease,translate .35s cubic-bezier(.22,.61,.36,1),
             background-color .28s,color .28s}
.totop.on{opacity:1;translate:none;pointer-events:auto}
.totop:hover{background:var(--fw-ink);color:var(--fw-cream);border-color:var(--fw-ink)}
@media (max-width:900px){.totop{display:none}}

/* ---------- form, alive ---------- */
.field{position:relative}
.field .bar{position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--fw-ink);
  transform:scaleX(0);transform-origin:0 50%;pointer-events:none;
  transition:transform .45s cubic-bezier(.22,.61,.36,1)}
.field.focus .bar{transform:scaleX(1)}
.field.err .bar{background:var(--err);transform:scaleX(1)}
.field label{transition:color .3s}
.field.focus label{color:var(--ink-strong)}
/* a filled, valid field earns a turmeric ● beside its label */
.field.good label::after{content:" ●";color:var(--fw-turm)}
.field.err.good label::after{content:" ●";color:var(--err)}
.charcount{font:400 12px/1.4 var(--sans);color:var(--ink-mute);margin-top:8px;
  opacity:0;transition:opacity .3s}
.field.focus .charcount,.field.good .charcount{opacity:1}

/* ---------- reduced motion, again, for everything above ---------- */
@media (prefers-reduced-motion:reduce){
  .curtain{display:none!important}
  html.curtained{overflow:auto}
  .cur,.cur-ring,.rail,.totop{display:none!important}
  body.cur-on{cursor:auto}
  .js .mask-w > .ch{translate:none!important}
  .js .fig[data-wipe] .fr{clip-path:none!important}
  .cue .line::after{animation:none!important;translate:0 0!important}
  .marq-track,.filmstrip .track{transform:none!important}
  .filmstrip{overflow-x:auto}
  .filmstrip.drifting{overflow-x:auto}
  .gal{scroll-behavior:auto}
  .step-body{transition:none!important}
  .nav.tuck{translate:none!important}
}
