/* ============================================================
   MNRL — Namma light world (ref: studionamma.com)
   Warm paper ground, dark-mode toggle, heavy centered display,
   large photo blocks, giant stretched wordmark, endless loop.
   Interface monochrome; client work supplies the color.
   ============================================================ */
:root {
  --paper: #e6e5e2; --ink: #0b0b0a; --quiet: #6d6c67; --rule: rgba(11,11,10,0.15); --print:#fbfbf9;
}
body.nm { background: var(--paper); color: var(--ink);
  transition: background .6s var(--ease), color .6s var(--ease); }
body.nm.dark { --paper:#0c0c0b; --ink:#eeece6; --quiet:#8f8e88; --rule:rgba(238,236,230,0.18); --print:#161513; }
body.nm ::selection { background: var(--ink); color: var(--paper); }
body.nm .col-swatch { transition: none; }

.nm-display { font-family: var(--font-display); font-weight: 900; text-transform: uppercase; letter-spacing: -0.035em; line-height: 0.84; }
.nm-mono { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; }

/* ---- Preloader (thumbnail cascade) ---- */
.nm-pre { position: fixed; inset: 0; z-index: 200; background: var(--paper); display:grid; place-content:center;
  transition: opacity .8s var(--ease), visibility .8s; }
.nm-pre.done { opacity: 0; visibility: hidden; }
.nm-pre .stack { position: relative; width: min(46vw, 360px); aspect-ratio: 4/3; }
.nm-pre .stack .c { position:absolute; inset:0; overflow:hidden; box-shadow: 0 30px 60px -30px rgba(0,0,0,.28); background:var(--paper); color:var(--ink); border:1px solid var(--rule);
  opacity:0; transform: translateY(40px) rotate(0deg) scale(.9); }
.nm-pre .stack .c img { width:100%; height:100%; object-fit: contain; padding: 6%; }
/* same reason: the loader runs on a dark ground in dark mode */
body.dark .nm-pre .stack .c img { filter: invert(1); }
.nm-pre.run .c { animation: nm-deal .55s var(--ease) forwards; }
.nm-pre.run .c:nth-child(1){ animation-delay:.15s; --r:-7deg; }
.nm-pre.run .c:nth-child(2){ animation-delay:.35s; --r:5deg; }
.nm-pre.run .c:nth-child(3){ animation-delay:.55s; --r:-3deg; }
.nm-pre.run .c:nth-child(4){ animation-delay:.75s; --r:8deg; }
.nm-pre.run .c:nth-child(5){ animation-delay:.95s; --r:0deg; }
@keyframes nm-deal { to { opacity:1; transform: translateY(0) rotate(var(--r)) scale(1); } }
.nm-pre .count { position:absolute; bottom: 26px; right: var(--gutter); font-family:var(--font-impact);
  font-size: clamp(40px,7vw,90px); line-height:1; }
.nm-pre .lab { position:absolute; bottom: 30px; left: var(--gutter); font-family:var(--font-mono); font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:var(--quiet); }

/* ---- Nav ---- */
.nm-nav { position: fixed; inset: 0 0 auto 0; z-index: 60; display:grid; grid-template-columns: 1fr auto 1fr;
  align-items:center; padding: 20px var(--gutter); mix-blend-mode: difference; color:#fff;
  transition: transform .5s var(--ease); }
.nm-nav.nav--hidden { transform: translateY(-120%); }
.nm-nav a, .nm-nav button, .nm-nav .c { font-family: var(--font-mono); font-size: 11px; letter-spacing:0.14em; text-transform:uppercase; color:#fff; }
.nm-nav .l { display:flex; gap:18px; align-items:center; }
.nm-nav .mark { font-family: var(--font-display); font-weight: 800; letter-spacing: 0.4em; text-indent:0.4em; font-size: 14px; }
.nm-nav .c { text-align:center; }
.nm-nav .r { display:flex; gap:18px; align-items:center; justify-content:flex-end; }
.nm-toggle { display:inline-flex; gap:8px; align-items:center; }
.nm-toggle .dot { width:8px; height:8px; border-radius:50%; border:1px solid currentColor; }
body.nm.dark .nm-toggle .dot { background: currentColor; }
.nm-cta { border-bottom:1px solid #fff; padding-bottom:2px; }
/* v4 · six persistent destinations wrap instead of dropping out on small screens */
@media (max-width: 1000px) {
  .nm-nav { grid-template-columns: 1fr auto; row-gap: 8px; padding-block: 12px; }
  .nm-nav .c { grid-column: 2; text-align: right; }
  .nm-nav .r { grid-column: 1 / -1; flex-wrap: wrap; gap: 8px 14px; justify-content: flex-start; }
  .nm-nav a, .nm-nav button, .nm-nav .c { font-size: 10px; letter-spacing: 0.1em; }
}

/* ---- Hero ---- */
.nm-hero { min-height: 100svh; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding: 120px var(--gutter) 60px; position:relative; }
.nm-hero h1 { font-size: clamp(48px,12.6vw,224px); }
.nm-hero h1 .row { display:block; overflow:hidden; }
.nm-hero h1 .row span { display:inline-block; }
.js body.nm .nm-hero h1 .row > span { transform: translateY(110%); transition: transform 1s var(--ease); }
.js body.nm .nm-hero.in h1 .row > span { transform:none; }
/* Subhead sentence — the one place on the page that speaks in plain prose. */
.nm-hero .lede { margin-top: clamp(22px,2.4vw,34px); max-width: 54ch; text-wrap: balance;
  font-size: clamp(15px,1.55vw,20px); line-height:1.45; color: var(--quiet); }
.nm-hero .tag { margin-top: clamp(18px,1.8vw,26px); font-family: var(--font-mono); font-size: clamp(11px,1.2vw,13px); letter-spacing:0.24em; text-transform:uppercase; color:var(--quiet); }
.nm-corners { position:absolute; bottom:22px; left:0; right:0; display:flex; justify-content:space-between; padding:0 var(--gutter);
  font-family:var(--font-mono); font-size:11px; letter-spacing:0.14em; text-transform:uppercase; color:var(--quiet); }

/* ---- Manifesto ---- */
.nm-mani { min-height: 76svh; display:grid; place-content:center; text-align:center; padding: 60px var(--gutter); }
.nm-mani .line { font-family: var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(30px,8vw,120px); line-height:0.9; letter-spacing:-0.03em; transition: opacity .5s var(--ease), transform .5s var(--ease); }

/* ---- Section label ---- */
.nm-sec { padding: clamp(50px,9vh,120px) 0; }
.nm-lab { display:flex; justify-content:space-between; align-items:baseline; padding: 20px var(--gutter) 0; border-top:1px solid var(--rule);
  font-family:var(--font-mono); font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:var(--quiet); }

/* ---- Selected work: large alternating project blocks ---- */
.nm-projects { display:grid; gap: clamp(40px,7vw,120px); margin-top: clamp(40px,6vw,80px); }
.nm-proj { display:grid; grid-template-columns: repeat(12,1fr); column-gap: clamp(14px,1.6vw,28px); align-items:center; padding-inline: var(--gutter); }
.nm-proj .media { position:relative; overflow:hidden; }
.nm-proj .media img, .nm-proj .media video { width:100%; height:100%; object-fit:cover; display:block; transition: transform 1.2s var(--ease); }
.nm-proj:hover .media img, .nm-proj:hover .media video { transform: scale(1.03); }
.nm-proj .info { display:flex; flex-direction:column; gap: 14px; }
.nm-proj .num { font-family:var(--font-mono); font-size:11px; letter-spacing:0.16em; text-transform:uppercase; color:var(--quiet); }
.nm-proj .name { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(32px,4vw,64px); line-height:0.9; letter-spacing:-0.03em; overflow-wrap:break-word; }
.nm-proj .desc { color:var(--quiet); font-size: clamp(15px,1.4vw,18px); max-width: 40ch; }
.nm-proj .meta { font-family:var(--font-mono); font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--quiet); }
.nm-proj .go { font-family:var(--font-mono); font-size:12px; letter-spacing:0.1em; text-transform:uppercase; border-bottom:1px solid var(--ink); padding-bottom:3px; align-self:flex-start; }
/* layout variants */
.nm-proj--l .media { grid-column: 1 / 8; aspect-ratio: 4/3; }
.nm-proj--l .info { grid-column: 9 / 13; }
.nm-proj--r .media { grid-column: 6 / 13; order:2; aspect-ratio: 4/3; }
.nm-proj--r .info { grid-column: 1 / 5; order:1; }
.nm-proj .sub-media { grid-column: 1/13; display:grid; grid-template-columns: repeat(12,1fr); gap: clamp(12px,1.6vw,24px); margin-top: clamp(16px,2vw,28px); }
.nm-proj .sub-media > * { overflow:hidden; }
@media (max-width: 860px){
  .nm-proj--l .media, .nm-proj--l .info, .nm-proj--r .media, .nm-proj--r .info { grid-column: 1/13; }
  .nm-proj--r .media { order:1; } .nm-proj--r .info { order:2; }
  .nm-proj .info { margin-top: 20px; }
}

/* ---- Playground horizontal strip ---- */
.nm-play { overflow:hidden; padding: 10px 0; }
.nm-play .track { display:flex; gap: clamp(12px,1.4vw,20px); width:max-content; animation: nm-marq 40s linear infinite; }
.nm-play:hover .track { animation-play-state: paused; }
.nm-play .cell { flex:0 0 auto; width: clamp(180px,20vw,280px); aspect-ratio: 4/3; overflow:hidden; }
.nm-play .cell img, .nm-play .cell video { width:100%; height:100%; object-fit:cover; }
@keyframes nm-marq { to { transform: translateX(-50%); } }

/* ---- Services ---- */
.nm-serv { padding-inline: var(--gutter); max-width: 1280px; margin: 0 auto; }
.nm-serv h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(30px,5vw,84px); letter-spacing:-0.03em; margin-bottom: 30px; }
.nm-srow { display:grid; grid-template-columns: 60px 1fr 1fr; gap:20px; align-items:center; border-top:1px solid var(--rule); padding: clamp(16px,2.4vw,28px) 0; }
.nm-srow:last-child { border-bottom:1px solid var(--rule); }
.nm-srow .n { font-family:var(--font-mono); font-size:11px; color:var(--quiet); }
.nm-srow .a { font-family:var(--font-display); font-weight:800; text-transform:uppercase; font-size: clamp(18px,2.6vw,34px); letter-spacing:-0.01em; }
.nm-srow .b { font-family:var(--font-mono); font-size:12px; letter-spacing:0.06em; text-transform:uppercase; color:var(--quiet); }
@media (max-width:640px){ .nm-srow { grid-template-columns:30px 1fr; } .nm-srow .b { grid-column:2; } }

/* ---- CTA ---- */
.nm-cta-sec { min-height: 80svh; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding: 80px var(--gutter); }
.nm-cta-sec h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(40px,10vw,180px); line-height:0.84; letter-spacing:-0.04em; }
.nm-cta-sec p { margin-top: 22px; color:var(--quiet); font-size: clamp(15px,1.6vw,19px); max-width: 40ch; }
.nm-say { margin-top: 38px; display:inline-flex; gap:12px; align-items:center; font-family:var(--font-mono); font-size:13px; letter-spacing:0.16em; text-transform:uppercase; border:1px solid var(--ink); border-radius:100px; padding:16px 34px; transition: background .2s var(--ease), color .2s var(--ease); }
body.nm .nm-say:hover { background:var(--ink); color:var(--paper); }

/* ---- Footer + giant stretched wordmark ---- */
.nm-foot { padding: clamp(50px,8vh,90px) var(--gutter) 0; border-top:1px solid var(--rule); }
.nm-foot .cols { display:grid; grid-template-columns: 1fr 1fr; gap: 30px; font-family:var(--font-mono); font-size:12px; letter-spacing:0.06em; text-transform:uppercase; }
.nm-foot .nav a { display:block; padding: 3px 0; color:var(--quiet); transition: color .2s var(--ease); }
.nm-foot .nav a:hover { color:var(--ink); }
.nm-foot .say { color:var(--quiet); line-height:1.7; }
.nm-foot .say a { color:var(--ink); }
.nm-foot .say .big { display:block; color:var(--ink); font-size: 14px; margin-top: 10px; }
.nm-wordmark { width:100%; display:block; margin-top: clamp(30px,5vw,60px); }
.nm-wordmark svg { width:100%; height:auto; display:block; }
.nm-wordmark text { fill: var(--ink); font-family: var(--font-impact); }
.nm-foot .base { display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px; padding: 18px 0 30px;
  font-family:var(--font-mono); font-size:11px; letter-spacing:0.12em; text-transform:uppercase; color:var(--quiet); }
@media (max-width:640px){ .nm-foot .cols { grid-template-columns:1fr; } }

/* ---- Case study (Namma) ---- */
.cs2-hero { min-height: 96svh; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding: 130px var(--gutter) 60px; }
.cs2-hero .cat { font-family:var(--font-mono); font-size: 12px; letter-spacing:0.18em; text-transform:uppercase; color:var(--quiet); margin-bottom: 26px; }
.cs2-hero h1 { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(54px,15vw,240px); line-height:0.82; letter-spacing:-0.04em; }
.cs2-hero .lead { margin-top: 28px; max-width: 40ch; font-size: clamp(17px,2vw,24px); }
.cs2-lead-media { margin: clamp(30px,5vw,70px) 0; }
.cs2-full { width:100%; }
.cs2-full img, .cs2-full video { width:100%; display:block; object-fit:cover; }
.cs2-body { max-width: 1280px; margin: 0 auto; padding: clamp(50px,9vh,120px) var(--gutter); }
.cs2-chapter { display:grid; grid-template-columns: repeat(12,1fr); column-gap: clamp(14px,1.6vw,28px); row-gap: 24px; align-items:start; }
.cs2-chapter .k { grid-column: 1 / 4; font-family:var(--font-mono); font-size:11px; letter-spacing:0.16em; text-transform:uppercase; color:var(--quiet); position:sticky; top: 90px; }
.cs2-chapter .t { grid-column: 4 / 13; font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(28px,5vw,72px); line-height:0.9; letter-spacing:-0.03em; }
.cs2-chapter .p { grid-column: 4 / 11; color: var(--ink); font-size: clamp(16px,1.5vw,19px); line-height:1.6; }
.cs2-chapter .p.q { color: var(--quiet); }
.cs2-chapter .p + .p { margin-top: 18px; }
@media (max-width:820px){ .cs2-chapter .k,.cs2-chapter .t,.cs2-chapter .p { grid-column:1/13; } .cs2-chapter .k { position:static; } }
.cs2-grid { display:grid; gap: clamp(12px,1.6vw,24px); padding: 0 var(--gutter); max-width: 1600px; margin: clamp(30px,5vw,60px) auto; }
.cs2-grid--2 { grid-template-columns: 1fr 1fr; }
.cs2-grid--3 { grid-template-columns: repeat(3,1fr); }
.cs2-grid > * { overflow:hidden; }
.cs2-grid img, .cs2-grid video { width:100%; height:100%; object-fit:cover; display:block; }
@media (max-width:720px){ .cs2-grid--2,.cs2-grid--3 { grid-template-columns:1fr; } }
.cs2-quote { text-align:center; padding: clamp(60px,11vh,150px) var(--gutter); }
.cs2-quote blockquote { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(28px,6vw,92px); line-height:0.9; letter-spacing:-0.03em; max-width: 18ch; margin: 0 auto; }
.cs2-quote cite { display:block; margin-top: 22px; font-family:var(--font-mono); font-style:normal; font-size:11px; letter-spacing:0.14em; text-transform:uppercase; color:var(--quiet); }
.cs2-next { border-top:1px solid var(--rule); padding: clamp(40px,7vh,90px) var(--gutter); text-align:center; }
.cs2-next .k { font-family:var(--font-mono); font-size:11px; letter-spacing:0.16em; text-transform:uppercase; color:var(--quiet); }
.cs2-next .t { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size: clamp(40px,10vw,160px); line-height:0.86; letter-spacing:-0.04em; transition: letter-spacing .4s var(--ease); display:inline-block; margin-top: 14px; }
.cs2-next a:hover .t { letter-spacing:0; }

/* ============================================================
   V2 — premium interaction layer
   ============================================================ */

/* ---- Interactive headings (liquid hover + hold-to-magnify) ---- */
.h-int { cursor: default; }
.h-int .wd { display: inline-block; white-space: nowrap; }
.h-int .ltr { display: inline-block; will-change: transform;
  transition: transform .5s var(--ease); transform-origin: center 80%; }
.h-int:active { cursor: grabbing; }
@media (hover:hover) and (pointer:fine){ .h-int { cursor: grab; } }

/* Hero letters animate up on reveal — softened, staggered, cinematic */
.nm-hero h1 { letter-spacing: calc(-0.035em + var(--wide,0em)); }
/* Signature: the last word elongates vertically on scroll, then releases.
   It sits INSIDE the row's span, not as the span itself, so the entrance
   translateY (scoped to `.row > span`) and this scaleY never fight over the
   same transform property. Two motions, two elements, one composed result. */
.nm-hero .stretch-word { display:inline-block; transform: scaleY(var(--rstretch,1));
  transform-origin: center top; will-change: transform; }
.nm-hero .lede, .nm-hero .tag, .nm-hero .nm-corners { opacity: var(--hfade,1); }
.js body.nm .nm-hero h1 .row > span { transform: translateY(112%); transition: transform 1.35s var(--ease); }
.js body.nm .nm-hero.in h1 .row > span { transform: none; }
.js body.nm .nm-hero.in h1 .row:nth-child(1) > span { transition-delay:.05s; }
.js body.nm .nm-hero.in h1 .row:nth-child(2) > span { transition-delay:.16s; }
.js body.nm .nm-hero.in h1 .row:nth-child(3) > span { transition-delay:.27s; }
.nm-hero .lede, .nm-hero .tag { transition: opacity 1s var(--ease) .5s, transform 1s var(--ease) .5s; }
.js body.nm .nm-hero .lede { opacity:0; transform: translateY(14px); }
.js body.nm .nm-hero.in .lede { opacity: var(--hfade,1); transform:none; }
.js body.nm .nm-hero .tag { opacity:0; transform: translateY(14px); transition-delay:.62s; }
.js body.nm .nm-hero.in .tag { opacity: var(--hfade,1); transform:none; }

/* ---- Cinematic depth wrapper ---- */
.nm-cine { transform: translate3d(0,var(--cy,0),0) scale(var(--cs,1));
  opacity: var(--co,1); will-change: transform, opacity; }

/* ---- Selected work: single premium media (galleries removed) ---- */
.nm-proj .media { aspect-ratio: 16/9; }
.nm-proj--l .media { grid-column: 1 / 8; }
.nm-proj--r .media { grid-column: 6 / 13; }
.nm-proj .media::after { content:""; position:absolute; inset:0; box-shadow: inset 0 0 0 1px var(--rule); pointer-events:none; }
.nm-media-tag { position:absolute; left:14px; bottom:12px; z-index:2; font-family:var(--font-mono);
  font-size:10px; letter-spacing:0.14em; text-transform:uppercase; color:#fff; mix-blend-mode:difference; }

/* ---- Where are you right now? ---- */
.nm-paths { padding: clamp(50px,8vh,110px) 0; }
.nm-paths .head { padding-inline: var(--gutter); }
.nm-paths h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(34px,7vw,110px); line-height:0.86; letter-spacing:-0.035em; }
.nm-paths .sub { margin-top:18px; color:var(--quiet); font-family:var(--font-mono); font-size:12px; letter-spacing:0.06em; text-transform:uppercase; }
.nm-path { display:grid; grid-template-columns: 64px 1fr auto; gap: clamp(16px,3vw,48px); align-items:baseline;
  padding: clamp(22px,3vw,40px) var(--gutter); border-top:1px solid var(--rule); position:relative; overflow:hidden;
  transition: color .3s var(--ease); }
.nm-paths .nm-path:last-child { border-bottom:1px solid var(--rule); }
.nm-path .n { font-family:var(--font-mono); font-size:12px; color:var(--quiet); }
.nm-path .t { font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size: clamp(22px,3.4vw,46px); letter-spacing:-0.02em; }
.nm-path .d { grid-column:2; max-width:52ch; margin-top:12px; color:var(--quiet); font-size: clamp(14px,1.3vw,17px); }
.nm-path .go { align-self:center; font-family:var(--font-mono); font-size:11px; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--quiet); transition: transform .35s var(--ease), color .3s var(--ease); }
.nm-path::before { content:""; position:absolute; left:0; top:0; bottom:0; width:100%; background:var(--ink);
  transform: scaleX(0); transform-origin:left; transition: transform .5s var(--ease); z-index:-1; }
body.nm .nm-path:hover { color:var(--paper); }
body.nm .nm-path:hover::before { transform: scaleX(1); }
body.nm .nm-path:hover .n, body.nm .nm-path:hover .d, body.nm .nm-path:hover .go { color:var(--paper); }
body.nm .nm-path:hover .go { transform: translateX(8px); }
@media (max-width:640px){ .nm-path { grid-template-columns: 1fr; } .nm-path .go { display:none; } .nm-path .d { grid-column:1; } }

/* ---- No buzzwords grid (restored strong block layout) ---- */
.nm-buzz { padding: clamp(50px,9vh,120px) 0; }
.nm-buzz .head { padding-inline:var(--gutter); max-width:1280px; margin:0 auto; }
.nm-buzz h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(28px,4.6vw,68px); line-height:0.9; letter-spacing:-0.03em; max-width:16ch; }
.nm-buzz-grid { max-width:1280px; margin: clamp(34px,5vw,60px) auto 0; padding-inline:var(--gutter);
  display:grid; grid-template-columns: repeat(3,1fr); }
.nm-buzz-grid > div { padding: clamp(24px,2.6vw,40px) clamp(20px,2vw,34px) clamp(30px,4vw,56px) 0;
  border-top:1px solid var(--rule); }
.nm-buzz-grid h3 { font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size: clamp(17px,1.8vw,24px); letter-spacing:-0.01em; margin-bottom:12px; }
.nm-buzz-grid p { color:var(--quiet); font-size: clamp(14px,1.3vw,16px); line-height:1.5; }
@media (max-width:860px){ .nm-buzz-grid { grid-template-columns:1fr 1fr; } }
@media (max-width:560px){ .nm-buzz-grid { grid-template-columns:1fr; } }

/* ---- How we work (storytelling + premium visual) ---- */
.nm-how { padding: clamp(50px,9vh,120px) 0; }
.nm-how .inner { max-width:1600px; margin:0 auto; padding-inline:var(--gutter);
  display:grid; grid-template-columns: 1.05fr 1fr; gap: clamp(30px,5vw,80px); align-items:center; }
.nm-how .story h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(30px,4.4vw,64px); line-height:0.9; letter-spacing:-0.03em; }
.nm-how .steps { margin-top: clamp(24px,3vw,44px); }
.nm-how-item { display:grid; grid-template-columns: 40px 1fr; gap:18px; padding: clamp(16px,2vw,26px) 0; border-top:1px solid var(--rule); }
.nm-how-item .num { font-family:var(--font-mono); font-size:12px; color:var(--quiet); padding-top:4px; }
.nm-how-item h3 { font-family:var(--font-display); font-weight:800; text-transform:uppercase; font-size:clamp(16px,1.7vw,22px); margin-bottom:8px; }
.nm-how-item p { color:var(--quiet); font-size:clamp(14px,1.3vw,16px); line-height:1.5; }
.nm-how .pov { margin-top: clamp(30px,4vw,50px); font-family:var(--font-display); font-weight:900;
  text-transform:uppercase; font-size: clamp(22px,3vw,42px); line-height:0.94; letter-spacing:-0.03em; }
.nm-how .pov b { color: var(--ink); }
.nm-how .visual { position:relative; aspect-ratio: 3/4; overflow:hidden; }
.nm-how .visual video, .nm-how .visual img { width:100%; height:100%; object-fit:cover; }
.nm-how .visual::after { content:""; position:absolute; inset:0; box-shadow: inset 0 0 0 1px var(--rule); }
@media (max-width:900px){ .nm-how .inner { grid-template-columns:1fr; } .nm-how .visual { aspect-ratio:16/10; } }

/* ---- Services: Motion as its own category ---- */
.nm-srow .b .tags { display:flex; flex-wrap:wrap; gap:6px 14px; }
.nm-motion { margin-top: clamp(30px,4vw,54px); padding-top: clamp(24px,3vw,40px); border-top:1px solid var(--rule); }
.nm-motion .cap { display:flex; justify-content:space-between; align-items:baseline; margin-bottom:22px;
  font-family:var(--font-mono); font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:var(--quiet); }
.nm-motion h3 { font-family:var(--font-display); font-weight:900; text-transform:uppercase; font-size:clamp(24px,3.6vw,52px); letter-spacing:-0.03em; margin-bottom:22px; }
.nm-motion-grid { display:grid; grid-template-columns: repeat(4,1fr); gap:1px; background:var(--rule); border:1px solid var(--rule); }
.nm-motion-grid span { background:var(--paper); padding: clamp(16px,1.8vw,24px) clamp(14px,1.4vw,20px);
  font-family:var(--font-mono); font-size:12px; letter-spacing:0.04em; text-transform:uppercase; color:var(--ink);
  transition: background .25s var(--ease), color .25s var(--ease); }
body.nm .nm-motion-grid span:hover { background:var(--ink); color:var(--paper); }
@media (max-width:820px){ .nm-motion-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:440px){ .nm-motion-grid { grid-template-columns:1fr; } }

/* ---- Type breathes ~7% taller (V3) ---- */
.nm-hero h1 { line-height: 0.9; }
.nm-mani .line { line-height: 0.96; }
.nm-proj .name { line-height: 0.96; }
.nm-cta-sec h2 { line-height: 0.9; }
.nm-paths h2, .nm-buzz h2, .nm-how .story h2 { line-height: 0.94; }
.cs2-hero h1 { line-height: 0.88; }
.cs2-quote blockquote { line-height: 0.96; }

/* ---- Interactive-heading lens (crisp base, distorted circle) ---- */
.h-int-lens { color: inherit; transform-origin: 50% 50%;
  transition: transform .45s var(--ease); }
.h-int-lens .ltr { transform: none !important; }

/* ---- Hero: breathing / float ----
   Applied only after the reveal lands (JS adds .floated), because the rows
   clip their letters during the entrance. Each line drifts on its own timing
   so the block never moves as one rigid slab. */
.nm-hero.floated h1 .row { overflow: visible; animation: nm-breathe 7s var(--ease-inout) infinite; }
.nm-hero.floated h1 .row:nth-child(1) { animation-duration: 7.4s; animation-delay: -0.2s; }
.nm-hero.floated h1 .row:nth-child(2) { animation-duration: 8.6s; animation-delay: -2.1s; }
.nm-hero.floated h1 .row:nth-child(3) { animation-duration: 6.8s; animation-delay: -4.3s; }
@keyframes nm-breathe {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
@media (prefers-reduced-motion: reduce){
  .nm-hero.floated h1 .row { animation: none; }
  .h-int-lens { transition: none; }
}

/* ---- Custom cursor ---- */
.nm-has-cursor, .nm-has-cursor a, .nm-has-cursor button,
.nm-has-cursor .nm-path, .nm-has-cursor .card { cursor: none; }
.nm-cursor, .nm-cursor-ring { position: fixed; top:0; left:0; z-index: 9999; pointer-events:none;
  border-radius:50%; mix-blend-mode: difference; }
.nm-cursor { width:7px; height:7px; margin:-3.5px 0 0 -3.5px; background:#fff; }
.nm-cursor-ring { width:34px; height:34px; margin:-17px 0 0 -17px; border:1px solid #fff;
  transition: width .28s var(--ease), height .28s var(--ease), margin .28s var(--ease), opacity .28s; }
.nm-cursor-ring.hot { width:54px; height:54px; margin:-27px 0 0 -27px; }
.nm-cursor-ring.down { width:22px; height:22px; margin:-11px 0 0 -11px; }

/* ---- Manifesto network backdrop ---- */
.nm-mani { position: relative; }
#nm-net { position:absolute; inset:0; z-index:0; pointer-events:none; }
#nm-net canvas { width:100%; height:100%; display:block; }
.nm-mani .line { position: relative; z-index:1; }

/* ---- Selected Work: horizontal scroll-lock (right → left) ---- */
/* Default (no-JS / reduced-motion / mobile) = clean vertical stack. */
.nm-hscroll { position: relative; }
.nm-hscroll-track { display: flex; flex-direction: column; gap: clamp(40px,7vw,110px); }
.hpanel { display: grid; grid-template-columns: 1.15fr 0.85fr; align-items: center;
  gap: clamp(24px,4vw,72px); padding: 0 var(--gutter); }
.hpanel .pmedia-wrap { position: relative; aspect-ratio: 16/9; overflow: hidden; }
.hpanel .pmedia { position: absolute; inset: -6% ; width: 112%; height: 112%; }
.hpanel .pmedia video, .hpanel .pmedia img { width: 100%; height: 100%; object-fit: cover; display:block; }
.hpanel .pmedia-wrap::after { content:""; position:absolute; inset:0; box-shadow: inset 0 0 0 1px var(--rule); pointer-events:none; }
.hpanel .info { display: flex; flex-direction: column; gap: 16px; }
.hpanel .num { font-family:var(--font-mono); font-size:12px; letter-spacing:0.16em; text-transform:uppercase; color:var(--quiet); }
.hpanel .name { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(38px,4.6vw,84px); line-height:0.9; letter-spacing:-0.035em; }
.hpanel .desc { color:var(--quiet); font-size: clamp(15px,1.4vw,19px); max-width: 42ch; }
.hpanel .meta { font-family:var(--font-mono); font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--quiet); }
.hpanel .go { font-family:var(--font-mono); font-size:12px; letter-spacing:0.1em; text-transform:uppercase;
  border-bottom:1px solid var(--ink); padding-bottom:3px; align-self:flex-start; }
.nm-hscroll-progress { display:none; }

/* Locked mode (added by JS on capable desktop) */
.nm-hscroll.is-hlocked .nm-hscroll-sticky { position: sticky; top: 0; height: 100svh; overflow: hidden; display:flex; align-items:center; }
.nm-hscroll.is-hlocked .nm-hscroll-track { flex-direction: row; gap: 0; height: 100%; will-change: transform; }
.nm-hscroll.is-hlocked .hpanel { flex: 0 0 100vw; height: 100%; align-content:center; }
.nm-hscroll.is-hlocked .pmedia { transition: none; }
.nm-hscroll.is-hlocked .nm-hscroll-progress { display:block; position:absolute; left:var(--gutter); right:var(--gutter); bottom:34px; height:1px; background:var(--rule); z-index:3; }
.nm-hscroll.is-hlocked .nm-hscroll-progress span { display:block; height:100%; background:var(--ink); transform-origin:left; transform:scaleX(0); }
.nm-hscroll.is-hlocked .hscroll-cue { position:absolute; right:var(--gutter); bottom:44px; z-index:3;
  font-family:var(--font-mono); font-size:11px; letter-spacing:0.16em; text-transform:uppercase; color:var(--quiet); }
@media (max-width:860px){ .hpanel { grid-template-columns:1fr; } .hpanel .info { margin-top:18px; } }

/* ---- Where are you: three large vertical cards ---- */
.nm-paths-grid { display:grid; grid-template-columns: repeat(3,1fr); gap: clamp(12px,1.4vw,20px);
  padding: clamp(30px,4vw,56px) var(--gutter) 0; }
.nm-vcard { position:relative; display:flex; flex-direction:column; justify-content:space-between;
  min-height: clamp(360px,52vh,560px); padding: clamp(24px,2.4vw,40px); overflow:hidden;
  border:1px solid var(--rule); background: var(--print);
  transition: transform .5s var(--ease), background .4s var(--ease), color .4s var(--ease); }
.nm-vcard .idx { font-family:var(--font-mono); font-size:12px; letter-spacing:0.14em; text-transform:uppercase; color:var(--quiet); }
.nm-vcard .t { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(26px,2.5vw,44px); line-height:0.94; letter-spacing:-0.03em; margin-top:auto; }
.nm-vcard .d { color:var(--quiet); font-size: clamp(14px,1.2vw,16px); margin-top:16px; max-width:34ch; }
.nm-vcard .go { margin-top:24px; font-family:var(--font-mono); font-size:11px; letter-spacing:0.14em;
  text-transform:uppercase; display:inline-flex; gap:10px; align-items:center; transition: gap .3s var(--ease); }
.nm-vcard::before { content:""; position:absolute; inset:0; background:var(--ink); transform: translateY(100%);
  transition: transform .55s var(--ease); z-index:-1; }
body.nm .nm-vcard:hover { color:var(--paper); transform: translateY(-6px); }
body.nm .nm-vcard:hover::before { transform: translateY(0); }
body.nm .nm-vcard:hover .idx, body.nm .nm-vcard:hover .d { color: rgba(255,255,255,.7); }
body.nm .nm-vcard:hover .go { gap:18px; }
@media (max-width:820px){ .nm-paths-grid { grid-template-columns:1fr; } .nm-vcard { min-height: 260px; } }

/* ---- Services: one interactive category at a time ---- */
.nm-serv-wrap { max-width:1280px; margin:0 auto; padding-inline:var(--gutter);
  display:grid; grid-template-columns: minmax(240px,0.9fr) 1.1fr; gap: clamp(24px,4vw,72px); align-items:start; }
.nm-serv-cats { display:flex; flex-direction:column; }
.nm-serv-cat { text-align:left; border-top:1px solid var(--rule); padding: clamp(14px,1.8vw,24px) 0;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase; letter-spacing:-0.02em;
  font-size: clamp(22px,2.8vw,40px); color:var(--quiet); display:flex; justify-content:space-between; align-items:center;
  transition: color .3s var(--ease), padding-left .35s var(--ease); }
.nm-serv-cats .nm-serv-cat:last-child { border-bottom:1px solid var(--rule); }
.nm-serv-cat::after { content:"→"; font-family:var(--font-mono); font-size:16px; opacity:0; transform:translateX(-8px); transition:.3s var(--ease); }
body.nm .nm-serv-cat:hover { color:var(--ink); }
.nm-serv-cat.on { color:var(--ink); padding-left:12px; }
.nm-serv-cat.on::after { opacity:1; transform:none; }
.nm-serv-panel { position:sticky; top:110px; min-height: 260px; }
.nm-serv-panel .cap { font-family:var(--font-mono); font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:var(--quiet); margin-bottom:24px; }
.nm-serv-panel ul { display:flex; flex-direction:column; }
.nm-serv-panel li { font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size: clamp(20px,2vw,30px); letter-spacing:-0.02em; padding: clamp(10px,1.2vw,16px) 0; border-bottom:1px solid var(--rule);
  opacity:0; transform:translateY(14px); }
.nm-serv-panel.in li { animation: nm-srise .5s var(--ease) forwards; animation-delay: calc(var(--i) * 70ms); }
@keyframes nm-srise { to { opacity:1; transform:none; } }
@media (max-width:760px){ .nm-serv-wrap { grid-template-columns:1fr; } .nm-serv-panel { position:static; } }

/* ---- Blending in is expensive (own statement section) ---- */
.nm-blend { min-height: 78svh; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; padding: clamp(60px,10vh,140px) var(--gutter); }
.nm-blend h2 { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size: clamp(40px,9vw,150px); line-height:0.86; letter-spacing:-0.04em; }
.nm-blend p { margin-top:26px; max-width: 46ch; color:var(--quiet); font-size: clamp(16px,1.6vw,20px); }

/* ---- Footer wordmark: shared elastic vertical stretch (loops) ---- */
.nm-wordmark svg { transform-origin: center top; animation: nm-wordstretch 6.5s var(--ease-inout) infinite; }
@keyframes nm-wordstretch {
  0%, 62% { transform: scaleY(1); }
  80% { transform: scaleY(1.5); }
  88% { transform: scaleY(1.62); }
  100% { transform: scaleY(1); }
}

@media (prefers-reduced-motion: reduce){
  .nm-cine { transform:none; opacity:1; filter:none; }
  .nm-hero .stretch-word { transform:none; }
  .nm-wordmark svg { animation:none; }
  .h-int .ltr { transform:none !important; }
}

/* ============================================================
   v4 · Creative Approval Gate empty state
   Where approved media does not exist yet, the slot says so out loud
   rather than borrowing a stand-in. Monochrome, legible, non-decorative.
   ============================================================ */
.pmedia-wrap--gate { display:block; }
.nm-gate { position:absolute; inset:0; display:flex; flex-direction:column; justify-content:center;
  gap:10px; padding:32px 32px 60px; background:
    repeating-linear-gradient(135deg, rgba(255,255,255,.045) 0 2px, transparent 2px 9px),
    var(--paper-2, #131313);
  border:1px solid rgba(255,255,255,.16); color:#fff; }
body.nm:not(.dark) .nm-gate { background:
    repeating-linear-gradient(135deg, rgba(0,0,0,.05) 0 2px, transparent 2px 9px), #f2f2f2;
  border-color: rgba(0,0,0,.16); color:#111; }
.nm-gate-k { font-family: var(--font-mono); font-size:10px; letter-spacing:.16em; text-transform:uppercase; opacity:.62; }
.nm-gate-t { font-family: var(--font-display); font-weight:900; text-transform:uppercase; letter-spacing:-.03em; font-size: clamp(26px,2.8vw,46px); line-height:1; }
.nm-gate-d { font-size:12px; opacity:.62; max-width:34ch; }

/* v4 · footer density — same information, less empty vertical space */
.nm-foot { padding-block: 64px 28px; }
.nm-foot .cols { gap: 28px; }
.nm-foot .say { font-size: 15px; line-height: 1.42; }

/* ============================================================
   v4 · DENSITY PASS
   One reversible block. The minimums are untouched — small screens were
   never the problem. What shrinks is the desktop ceiling, where sections
   had grown far enough apart that scrolling passed through dead space
   instead of revealing something. Hierarchy comes from the ratios between
   these values, so they move together rather than individually.
   ============================================================ */
.nm-hero { min-height: 94svh; padding: 96px var(--gutter) 48px; }
.nm-hero .tag { margin-top: 20px; }
.nm-mani { min-height: 66svh; padding: 44px var(--gutter); }
.nm-sec { padding: clamp(44px,6.5vh,84px) 0; }
.nm-paths { padding: clamp(44px,6vh,78px) 0; }
.nm-buzz { padding: clamp(44px,6.5vh,84px) 0; }
.nm-how { padding: clamp(44px,6.5vh,84px) 0; }
.nm-blend { min-height: 68svh; padding: clamp(48px,7vh,96px) var(--gutter); }
.nm-cta-sec { min-height: 68svh; padding: 60px var(--gutter); }
.nm-hscroll-track { gap: clamp(32px,5vw,76px); }
.nm-projects { gap: clamp(32px,5vw,84px); margin-top: clamp(32px,4vw,56px); }
.nm-how .pov { margin-top: clamp(24px,3vw,38px); }
.nm-motion { margin-top: clamp(24px,3vw,40px); padding-top: clamp(20px,2.4vw,30px); }

/* case studies — same treatment, and the quote no longer sits in a void */
.cs2-hero { min-height: 88svh; padding: 104px var(--gutter) 48px; }
.cs2-body { padding: clamp(44px,6.5vh,84px) var(--gutter); }
.cs2-grid { margin: clamp(24px,3.6vw,44px) auto; }
.cs2-quote { padding: clamp(48px,8vh,104px) var(--gutter); }
.cs2-next { padding: clamp(34px,5vh,64px) var(--gutter); }

/* v4 density · in the pinned lane the panel media kept a fixed 16:9 and left
   the tall half of the viewport empty. Letting it grow to the available height
   fills the frame on tall screens without cropping differently on short ones. */
.nm-hscroll.is-hlocked .hpanel .pmedia-wrap { height: 72svh; aspect-ratio: auto; }
@media (max-aspect-ratio: 1/1) {
  .nm-hscroll.is-hlocked .hpanel .pmedia-wrap { height: auto; aspect-ratio: 16/9; }
}

/* ============================================================
   v4 · HOW WE WORK — four steps, forward motion
   The list is now an <ol>, so it needs its own reset. The rail down the
   left is the "implies forward progress" part: each step draws its own
   segment as it reveals, so the line builds downward with the reading.
   ============================================================ */
.nm-how .steps { list-style: none; padding: 0; position: relative; }
.nm-how-item { position: relative; }
.nm-how-item::before {                       /* the segment this step draws */
  content: ""; position: absolute; left: 5px; top: 0; bottom: 0; width: 1px;
  background: var(--ink); transform: scaleY(0); transform-origin: top;
  transition: transform .7s var(--ease); }
.js .nm-how-item.is-in::before { transform: scaleY(1); }
.nm-how-item::after {                        /* the marker at the step head */
  content: ""; position: absolute; left: 1px; top: clamp(20px,2.2vw,30px);
  width: 9px; height: 9px; border-radius: 50%; border: 1px solid var(--ink);
  background: var(--paper); transform: scale(0);
  transition: transform .5s var(--ease) .12s, background .5s var(--ease); }
.js .nm-how-item.is-in::after { transform: scale(1); }
.js .nm-how-item.is-in:last-child::after { background: var(--ink); }
.nm-how-item .num { padding-left: 22px; }
@media (prefers-reduced-motion: reduce) {
  .nm-how-item::before { transform: scaleY(1); }
  .nm-how-item::after { transform: scale(1); }
}

/* the gate sits in the figure the Ron loop used to fill */
.nm-how .visual .nm-gate { position: absolute; inset: 0; }

/* ============================================================
   V4-REF-09 · "It's never just a logo" — fractal/orb slot
   A quiet circular field behind the statement that grows as the pointer
   approaches. --near (0..1) is driven by JS from pointer distance; every
   visual property reads from it, so the whole response is one number and
   the resting state is genuinely at rest. Deliberately not sci-fi: one
   ring, one soft field, no particles.
   ============================================================ */
.nm-orb {
  --near: 0;
  position: absolute; left: 50%; top: 50%; z-index: 0;
  width: clamp(240px, 34vw, 460px); aspect-ratio: 1;
  display: grid; place-items: center; border-radius: 50%;
  transform: translate(-50%, -50%) scale(calc(1 + var(--near) * 0.14));
  transition: transform .9s var(--ease-out, ease-out), opacity .6s var(--ease);
  background: radial-gradient(circle at 50% 50%,
    rgba(11,11,10, calc(0.05 + var(--near) * 0.05)) 0%,
    rgba(11,11,10, calc(0.02 + var(--near) * 0.03)) 46%,
    transparent 72%);
  outline: none;
}
body.nm.dark .nm-orb { background: radial-gradient(circle at 50% 50%,
    rgba(238,236,230, calc(0.06 + var(--near) * 0.06)) 0%,
    rgba(238,236,230, calc(0.02 + var(--near) * 0.035)) 46%,
    transparent 72%); }
.nm-orb-ring {
  position: absolute; inset: 12%; border-radius: 50%;
  border: 1px solid var(--rule);
  opacity: calc(0.5 + var(--near) * 0.5);
  transform: scale(calc(1 + var(--near) * 0.06)) rotate(calc(var(--near) * 12deg));
  transition: opacity .7s var(--ease), transform 1.1s var(--ease);
}
/* the gate text stays out of the way until the slot is actually approached */
.nm-gate--orb { position: absolute; inset: auto auto -14% 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 4px; flex-direction: column;
  text-align: center; padding: 0 18px; width: max-content; max-width: 92vw;
  background: none; border: 0; color: var(--quiet);
  opacity: calc(var(--near) * 0.9); transition: opacity .5s var(--ease); }
body.nm:not(.dark) .nm-gate--orb { background: none; border: 0; color: var(--quiet); }
.nm-gate--orb .nm-gate-d { font-size: 11px; max-width: 26ch; }
/* keyboard reaches the same state, and says so visibly */
.nm-orb:focus-visible { --near: 1; }
.nm-orb:focus-visible .nm-orb-ring { border-color: var(--ink); border-width: 2px; }
.nm-mani .line { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .nm-orb, .nm-orb-ring, .nm-gate--orb { transition: none; }
  .nm-orb { transform: translate(-50%, -50%); }
}
@media (max-width: 600px) { .nm-orb { width: clamp(200px, 62vw, 300px); } }

/* ============================================================
   V4-REF-08 · hero cut-up portrait layer
   Seven vertical slices behind the headline. Each carries its own depth
   (--d) and lag, so cursor movement shears them past one another and the
   image reassembles as you cross it — the "cut-up" read from the reference.

   The headline sits above on its own stacking context and never dims: the
   layer is capped low enough that legibility cannot depend on where the
   cursor happens to be. Empty until an approved portrait lands; the slices
   currently show a neutral tone so the motion can be judged without
   standing in for the image.
   ============================================================ */
.nm-hero { position: relative; }
/* the headline is static by default, so a positioned z-index:0 layer would
   paint over it — the type is lifted explicitly instead */
.nm-hero h1, .nm-hero .lede, .nm-hero .tag { position: relative; z-index: 2; }
/* corners are already absolutely positioned — they need the z-index only.
   Setting position:relative here dropped them back into flow, on top of
   the tagline. */
.nm-hero .nm-corners { z-index: 2; }
.nm-cut {
  --mx: 0; --my: 0;                      /* cursor, -1..1, set by interactions.js */
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  display: flex; gap: 2px; justify-content: center; align-items: stretch;
  padding: 12svh 6vw; overflow: hidden;
  opacity: 0; transition: opacity 1.4s var(--ease) .4s;
}
.nm-hero.in .nm-cut { opacity: 1; }
.nm-cut-slice {
  flex: 1 1 0; max-width: 190px; position: relative; overflow: hidden;
  border-radius: 2px;
  background:
    linear-gradient(180deg, rgba(127,127,127,.16), rgba(127,127,127,.04) 62%, rgba(127,127,127,.10));
  transform: translate3d(calc(var(--mx) * var(--d) * 26px),
                         calc(var(--my) * var(--d) * 18px), 0)
             scaleY(calc(1 + var(--my) * var(--d) * 0.03));
  transition: transform .9s var(--ease);
}
/* alternating depth, so the slices shear against each other rather than
   sliding as one plane */
.nm-cut-slice:nth-child(1) { --d: 1.00; }
.nm-cut-slice:nth-child(2) { --d: -0.55; }
.nm-cut-slice:nth-child(3) { --d: 0.72; }
.nm-cut-slice:nth-child(4) { --d: -0.30; }
.nm-cut-slice:nth-child(5) { --d: 0.88; }
.nm-cut-slice:nth-child(6) { --d: -0.66; }
.nm-cut-slice:nth-child(7) { --d: 0.44; }
/* The slices fade out toward the centre so the headline always wins.
   A two-stop radial ramp bands badly on a near-black ground — it draws
   visible concentric rings. Hand-eased stops plus a fine dither texture
   break the steps up. */
.nm-cut::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 64% 48% at 50% 50%,
    var(--paper) 0%,
    color-mix(in srgb, var(--paper) 96%, transparent) 24%,
    color-mix(in srgb, var(--paper) 82%, transparent) 42%,
    color-mix(in srgb, var(--paper) 56%, transparent) 58%,
    color-mix(in srgb, var(--paper) 26%, transparent) 72%,
    color-mix(in srgb, var(--paper) 8%, transparent) 86%,
    rgba(0,0,0,0) 100%);
}
/* dither: sub-pixel noise at very low alpha, enough to scatter the steps
   the eye reads as rings without being visible as texture itself */
.nm-cut::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  opacity: .5; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
  background-size: 120px 120px;
}
/* the label belongs to the layer, not to the message — it sits top-left,
   clear of the headline and the tagline rather than stacking a second line
   of mono copy underneath them */
.nm-cut-gate { position: absolute; left: var(--gutter); top: 13svh;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; color: var(--quiet); opacity: .5; max-width: 26ch; }
.nm-cut-gate .nm-gate-d { font-size: 10px; }

/* keyboard and touch get the layer at rest rather than a dead flat plane */
@media (hover: none), (pointer: coarse) {
  .nm-cut-slice { transition: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nm-cut-slice { transform: none; transition: none; }
}
@media (max-width: 700px) {
  .nm-cut { padding: 18svh 4vw; gap: 1px; }
  .nm-cut-slice:nth-child(n+6) { display: none; }
}

/* ============================================================
   V4-REF-APEX · framed product preview
   A loop presented as a working product rather than a screen recording.
   The chrome is deliberately generic — dots, a URL, a badge — because it
   frames a real site the visitor can open, and inventing browser branding
   would be dishonest about what they are looking at.

   Reusable: any [data-frame] figure gets the behaviour.
   ============================================================ */
/* Width is capped to the source, not to the layout. The Apex capture is
   640px wide; stretched to full bleed it was upscaled 2.1x and went soft.
   Held near native it reads sharp, and a browser-framed panel at honest
   scale is a truer presentation of a website than a full-bleed blur.
   --frame-max lets a higher-resolution capture reclaim the width later
   without touching this rule. */
.nm-frame { margin: clamp(28px,4vw,56px) auto;
  max-width: min(var(--frame-max, 1500px), 100%);
  padding-inline: var(--gutter); position: relative; }
.nm-frame-chrome { display: flex; align-items: center; gap: 14px;
  padding: 10px 14px; border: 1px solid var(--rule); border-bottom: 0;
  border-radius: 8px 8px 0 0; background: var(--print); }
.nm-frame-chrome .dots { display: flex; gap: 6px; }
.nm-frame-chrome .dots i { width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid var(--rule); }
.nm-frame-chrome .url { flex: 1; font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .08em; color: var(--quiet); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nm-frame-chrome .badge { font-family: var(--font-mono); font-size: 9px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--quiet);
  border: 1px solid var(--rule); border-radius: 3px; padding: 3px 7px; }
.nm-frame-view { position: relative; aspect-ratio: 16/9; overflow: hidden;
  border: 1px solid var(--rule); border-radius: 0 0 8px 8px; background: var(--print); }
.nm-frame-media { width: 100%; height: 100%; object-fit: cover; display: block; }

/* loading state — a hairline that runs until the media can actually play */
.nm-frame-load { position: absolute; left: 0; right: 0; top: 0; height: 2px;
  overflow: hidden; }
.nm-frame-load i { display: block; height: 100%; width: 34%;
  background: var(--ink); animation: nm-frame-load 1.15s var(--ease) infinite; }
@keyframes nm-frame-load { 0% { transform: translateX(-100%); } 100% { transform: translateX(392%); } }
.nm-frame.is-ready .nm-frame-load { display: none; }

/* reduced motion / no autoplay: an explicit control instead of movement */
.nm-frame-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  display: inline-flex; align-items: center; gap: 9px; padding: 12px 20px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--paper); background: var(--ink);
  border: 0; border-radius: 4px; cursor: pointer; }
.nm-frame-play[hidden] { display: none; }
.nm-frame-play:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.nm-frame-cap { display: flex; flex-wrap: wrap; gap: 6px 20px;
  justify-content: space-between; align-items: baseline; margin-top: 12px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .06em;
  color: var(--quiet); }
.nm-frame-cap a { border-bottom: 1px solid currentColor; padding-bottom: 2px; color: inherit; }
.nm-frame-gate { display: block; margin-top: 6px; font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--quiet); opacity: .55; }
@media (max-width: 640px) { .nm-frame-chrome .badge { display: none; } }

/* ============================================================
   SplitStep · interactive brand system (branding-v1)
   A rail of boards on the left, one large stage on the right. Boards are
   dense 6000x3375 compositions — shown one at a time at real size they can
   be read; stacked five-high at thumbnail scale they are wallpaper.
   Clicking the stage opens the 2400px derivative in a native <dialog>.

   MNRL's interface stays monochrome. The green belongs to SplitStep and
   appears only inside the client's own swatches, board art, and the CTA
   rule — the rule working as intended, not an exception to it.
   ============================================================ */
.ss-brand { --ss-green: #7CFF00; padding-bottom: clamp(34px,5vh,64px); }

.ss-sys { display: grid; grid-template-columns: minmax(240px, 300px) 1fr;
  gap: clamp(18px,2.4vw,36px); align-items: start;
  padding: clamp(26px,3.4vw,48px) var(--gutter) 0; max-width: 1600px; margin: 0 auto; }

/* --- rail --- */
.ss-rail { display: flex; flex-direction: column; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); border-radius: 5px; overflow: hidden; }
.ss-tab { display: grid; grid-template-columns: 26px 1fr; gap: 12px; text-align: left;
  padding: 15px 16px; background: var(--print); border: 0; cursor: pointer;
  color: var(--ink); position: relative; transition: background .25s var(--ease); }
.ss-tab .n { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em;
  color: var(--quiet); padding-top: 3px; }
.ss-tab .l { display: flex; flex-direction: column; gap: 3px; }
.ss-tab b { font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; font-weight: 500; }
.ss-tab i { font-style: normal; font-size: 12px; line-height: 1.35; color: var(--quiet); }
.ss-tab::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--ss-green); transform: scaleY(0); transform-origin: top;
  transition: transform .3s var(--ease); }
.ss-tab:hover { background: color-mix(in srgb, var(--ink) 6%, var(--print)); }
.ss-tab[aria-selected="true"] { background: color-mix(in srgb, var(--ink) 9%, var(--print)); }
.ss-tab[aria-selected="true"]::before { transform: scaleY(1); }
.ss-tab[aria-selected="true"] b { color: var(--ink); }
.ss-tab:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* --- stage --- */
.ss-stage { position: relative; }
.ss-panel[hidden] { display: none; }
.ss-plate { display: block; width: 100%; padding: 0; border: 1px solid var(--rule);
  border-radius: 5px; overflow: hidden; background: var(--print); cursor: zoom-in;
  position: relative; }
.ss-plate picture { display: block; }
.ss-plate img { width: 100%; height: auto; aspect-ratio: 16/9; object-fit: cover;
  display: block; transition: transform .8s var(--ease); }
.ss-plate:hover img { transform: scale(1.012); }
.ss-plate:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.ss-zoomhint { position: absolute; right: 12px; bottom: 12px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; padding: 6px 10px; border-radius: 3px;
  background: rgba(0,0,0,.55); color: #fff; backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .3s var(--ease); }
.ss-plate:hover .ss-zoomhint, .ss-plate:focus-visible .ss-zoomhint { opacity: 1; }
@media (hover: none) { .ss-zoomhint { opacity: 1; } }

/* --- tokens --- */
.ss-tokens { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px,3vw,48px); padding: clamp(30px,3.6vw,52px) var(--gutter) 0;
  max-width: 1600px; margin: 0 auto; }
.ss-lab { font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--quiet); padding-bottom: 10px;
  border-bottom: 1px solid var(--rule); margin-bottom: 14px; }
.ss-swatches, .ss-list { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 9px; font-size: 13px; line-height: 1.45; }
.ss-swatches li { display: grid; grid-template-columns: 16px 1fr auto; gap: 10px; align-items: center; }
.ss-swatches .sw { width: 16px; height: 16px; border-radius: 3px; background: var(--c);
  box-shadow: inset 0 0 0 1px var(--rule); }
.ss-swatches code { font-family: var(--font-mono); font-size: 11px; color: var(--quiet); }
.ss-list li { color: var(--quiet); }
.ss-list b, .ss-swatches b { color: var(--ink); font-weight: 600; }

/* --- CTA to the live product --- */
.ss-cta { display: grid; gap: 8px; max-width: 1600px;
  margin: clamp(30px,4vw,58px) auto 0; padding: clamp(26px,3vw,44px) var(--gutter);
  border-top: 1px solid var(--rule); position: relative; }
.ss-cta::after { content: ""; position: absolute; left: var(--gutter); top: -1px;
  width: 0; height: 2px; background: var(--ss-green); transition: width .6s var(--ease); }
.ss-cta:hover::after, .ss-cta:focus-visible::after { width: 220px; }
.ss-cta .k { font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--quiet); }
.ss-cta .t { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(26px,3.6vw,58px); line-height: .94; letter-spacing: -.03em; }
.ss-cta .d { color: var(--quiet); max-width: 56ch; font-size: clamp(14px,1.2vw,16px); }
.ss-cta .go { font-family: var(--font-mono); font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; margin-top: 6px; }

/* --- lightbox --- */
.ss-lightbox { border: 0; padding: 0; background: transparent; max-width: 96vw; max-height: 94vh; }
.ss-lightbox::backdrop { background: rgba(0,0,0,.86); backdrop-filter: blur(6px); }
.ss-lightbox img { display: block; max-width: 96vw; max-height: 88vh;
  width: auto; height: auto; border-radius: 4px; }
.ss-lb-close { position: sticky; top: 0; margin-left: auto; display: block;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: #fff; background: rgba(0,0,0,.6);
  border: 1px solid rgba(255,255,255,.28); border-radius: 4px;
  padding: 9px 14px; cursor: pointer; margin-bottom: 10px; }
.ss-lb-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (max-width: 900px) {
  .ss-sys { grid-template-columns: 1fr; }
  .ss-rail { flex-direction: row; overflow-x: auto; }
  .ss-tab { min-width: 210px; }
  .ss-tokens { grid-template-columns: 1fr; gap: 26px; }
}

/* ============================================================
   V4-REF-02 · SplitStep movement visualization slot
   Copy on the left, the reserved surface on the right. The slot is held at
   doubles-court proportions so the approved animation arrives without
   moving anything around it, and the four states exist as an ordered list
   in HTML — the meaning survives with the animation switched off, on a
   screen reader, or before the media has loaded.
   ============================================================ */
.ss-viz { display: grid; grid-template-columns: minmax(280px, 0.85fr) 1.15fr;
  gap: clamp(24px,3.4vw,64px); align-items: center;
  max-width: 1500px; margin: clamp(20px,3vw,44px) auto clamp(24px,3.4vw,52px);
  padding-inline: var(--gutter); }
.ss-viz h3 { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(24px,2.8vw,46px); line-height: .96; letter-spacing: -.03em;
  margin: 0 0 14px; }
.ss-viz p { color: var(--quiet); font-size: clamp(14px,1.2vw,17px); line-height: 1.5;
  max-width: 46ch; margin: 0 0 16px; }
.ss-viz-steps { list-style: none; counter-reset: v; padding: 0; margin: 0 0 16px;
  display: flex; flex-direction: column; }
.ss-viz-steps li { counter-increment: v; position: relative; padding: 11px 0 11px 34px;
  border-top: 1px solid var(--rule); font-size: 14px; color: var(--quiet); line-height: 1.45; }
.ss-viz-steps li:last-child { border-bottom: 1px solid var(--rule); }
.ss-viz-steps li::before { content: counter(v,decimal-leading-zero);
  position: absolute; left: 0; top: 12px; font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .1em; color: var(--quiet); opacity: .7; }
.ss-viz-steps b { color: var(--ink); font-weight: 600; }
.ss-viz-note { font-size: 12px !important; opacity: .72; font-style: italic; }

/* 44x20ft doubles court — the ratio the approved animation will arrive at */
.ss-viz-court { position: relative; aspect-ratio: 44 / 20; overflow: hidden;
  border: 1px solid var(--rule); border-radius: 4px; background: var(--print); }
.ss-viz-load { position: absolute; left: 0; right: 0; top: 0; height: 2px; overflow: hidden; z-index: 2; }
.ss-viz-load i { display: block; height: 100%; width: 34%; background: var(--ink);
  animation: nm-frame-load 1.15s var(--ease) infinite; }
.ss-viz[data-loaded] .ss-viz-load { display: none; }
.ss-viz-cap { margin-top: 10px !important; font-family: var(--font-mono);
  font-size: 10px !important; letter-spacing: .1em; text-transform: uppercase; opacity: .6; }

@media (prefers-reduced-motion: reduce) { .ss-viz-load i { animation: none; width: 100%; opacity: .3; } }
@media (max-width: 900px) { .ss-viz { grid-template-columns: 1fr; } }

/* ============================================================
   A2 · Manifesto — right-to-left chasing statement
   The section is tall and its inner block sticks, so scroll progress drives
   the sequence directly. Each line rides in from the right, holds legible at
   rest, and slides off left as the next arrives — they overlap slightly so
   the statement chases rather than snapping between slides.

   The glass is a sheen layer per line, not a distortion of the text: the
   words never deform, which is the whole point of the restraint here.
   ============================================================ */
/* place-content:center survives from the old centred manifesto, and modern
   Chrome applies block-level alignment — which pushed the sticky child to
   the middle of the tall section and stopped it ever sticking. Reset it. */
/* R2.1 · a short transition band, not a chapter. 340svh → 115svh, and the
   visible band is held skinny so there is no blank headroom or foot room. */
/* The stage starts underneath the pinned band rather than after it, so the
   first BSP approach is already on screen as the statement finishes — the
   manifesto dissolves into it instead of leaving a screen of empty ground.
   The pull was -58svh, which overlapped so far that the statement painted on
   top of the first project for most of its run. Measured across -58/-40/-28/
   -18/-10/0: -10svh is the point where visible text over the stage hits 0px
   while the two still meet (-63px at the end), so the hand-off keeps its
   tightness and nothing reads over the work. */
.nm-mani { margin-bottom: -10svh; z-index: 96;
  position: relative; height: 115svh; padding: 0; min-height: 0;
  display: block; place-content: normal; align-content: normal; }
/* The pin used to be a full 100svh around a 38svh band, so ~31svh sat empty
   above AND below the statement. The pin is now the band: it sticks in the
   upper-middle of the viewport and the incoming Selected Work stage fills
   the space underneath instead of blank ground. */
.nm-mani-sticky { position: sticky; top: 12svh; height: 44svh;
  display: grid; place-items: center; padding: 0 var(--gutter);
  /* clipped again: the chase relies on lines entering and leaving out of
     frame, and `visible` let a mid-transition line spill off-canvas */
  overflow: hidden; }
/* The band is skinny and vertically centred. `inset:0` plus an explicit
   height over-constrains and pins it to the top with dead space below, so
   it is anchored at 50% and pulled back by half its own height instead. */
.nm-mani-sticky { --glitch: 0; }
.nm-mani #nm-net { position: absolute; inset: 0; z-index: 0; }
/* The block fills the sticky viewport and every line is absolutely centred
   inside it. Sizing the block from the first line made the longer lines
   overflow downward — they were centring inside a one-line-tall box. */
.mlines { position: absolute; left: 0; right: 0; top: 50%;
  height: 100%; transform: translateY(-50%); z-index: 1;
  display: grid; place-items: center; padding: 0 var(--gutter); }
.mline { --p: 0;                      /* -1 incoming · 0 at rest · 1 outgoing */
  font-size: clamp(24px, 4.6vw, 74px);
  position: absolute; inset: 0; margin: 0; width: 100%; max-width: min(1400px, 100%);
  left: 50%; transform-origin: center;
  display: grid; place-items: center; text-align: center;
  font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  /* 6.6vw put the longest statement past the gutter, so it clipped on both
     edges ("VE BUILD… TALK ABOU"). Sized to fit the measure instead, with
     optical tracking tightened as the type gets larger. */
  font-size: clamp(28px, 5.2vw, 82px); line-height: .94; letter-spacing: -.035em;
  text-wrap: balance;
  /* A parallax pass, not a slide: the line grows continuously as it travels
     toward and through the viewer — 0.82 far, 1.00 at rest, 1.18 leaving —
     while opacity rises from nothing and falls back to nothing. The steeper
     falloff (x1.9) means each line is only briefly at full strength instead
     of sitting legible across most of its window. The lateral drift is now
     a minor accent (-16%) so depth is the dominant movement. */
  /* The chase used to slide each line ±16% of its own width through a clipped
     box, so a headline you were still reading hung past the frame edge with
     its first or last letters sliced off. The travel is now small enough to
     stay inside the measure; depth carries the transition instead — scale and
     a faster opacity roll-off, so lines hand over without ever cutting a word. */
  transform: translate3d(calc(-50% + var(--p) * -3.5%), 0, 0)
             scale(calc(0.90 + (clamp(-1, var(--p), 1) + 1) * 0.10));
  opacity: calc(1 - min(1, abs(var(--p)) * 1.9));
  transition: none; will-change: transform, opacity; }
.mline .tx { position: relative; z-index: 1; display: block; }
/* the caret only shows while a line is still typing itself on */
.mline .tx::after { content: "_"; opacity: 0; margin-left: .06em;
  transition: opacity .18s linear; }
.mline[style*="--p:-0"] .tx::after,
.mline[style*="--p:-1"] .tx::after { opacity: .55; }
/* R2.1 glitch: a 1–3px slice/channel split as the band hands off. It is
   driven by --glitch (0..1) from scroll, so it happens once on the way out
   rather than flickering — and the text stays readable throughout. */
.mline .tx { text-shadow:
    calc(var(--glitch, 0) * 2px) 0 0 rgba(255,0,80, calc(var(--glitch,0) * .5)),
    calc(var(--glitch, 0) * -2px) 0 0 rgba(0,180,255, calc(var(--glitch,0) * .5)); }
.mlines { transform: translateY(calc(-50% + var(--par, 0px)))
    scale(calc(1 + var(--glitch, 0) * 0.06));
  clip-path: inset(calc(var(--glitch,0) * 3px) 0 calc(var(--glitch,0) * 3px) 0); }
/* the glass: a soft moving sheen that brightens as the line settles and
   follows the pointer; it sits behind the words and never distorts them */
.mline .glass { position: absolute; inset: -8% -4%; z-index: 0; pointer-events: none;
  border-radius: 999px;
  background: radial-gradient(60% 120% at calc(50% + var(--gx,0px)) calc(50% + var(--gy,0px)),
    rgba(127,127,127,.20) 0%, rgba(127,127,127,.07) 42%, transparent 72%);
  opacity: calc(1 - min(1, abs(var(--p)) * 1.6));
  filter: blur(6px); transition: opacity .4s var(--ease); }
@supports (backdrop-filter: blur(6px)) {
  .mline .glass { backdrop-filter: blur(2.5px) saturate(1.15); }
}

@media (prefers-reduced-motion: reduce) {
  /* the complete statement, at rest, no lateral chasing, no glitch */
  .nm-mani { height: auto; }
  .mlines { height: auto; transform: none; clip-path: none; }
  .mline .tx { text-shadow: none; }
  .nm-mani-sticky { position: static; height: auto; padding-block: clamp(48px,9vh,110px); }
  .mlines { display: flex; flex-direction: column; gap: clamp(18px,3vh,40px); }
  .mlines { position: static; inset: auto; padding: 0; height: auto;
    transform: none; }
  .mline .tx::after { display: none; }
  .mline { position: relative !important; inset: auto; left: auto; width: auto;
    max-width: none; transform: none !important;
    opacity: 1 !important; font-size: clamp(24px,4.4vw,58px); }
  .mline .glass { display: none; }
}

/* ============================================================
   A7 · Blending In — compact, with a bubble/liquid-glass response
   ============================================================ */
.nm-blend { position: relative; overflow: hidden;
  min-height: 0; padding: clamp(52px,8vh,110px) var(--gutter);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; }
.nm-blend h2 { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(30px,6.4vw,104px); line-height: .9; letter-spacing: -.035em; position: relative; z-index: 1; }
.nm-blend p { margin-top: 16px; color: var(--quiet); max-width: 48ch;
  font-size: clamp(14px,1.3vw,18px); position: relative; z-index: 1; }
.blend-bubbles { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.blend-bubbles span { position: absolute; border-radius: 50%;
  background: radial-gradient(circle at 34% 30%,
    rgba(127,127,127,.22), rgba(127,127,127,.07) 46%, transparent 70%);
  animation: blend-rise var(--dur,17s) linear infinite;
  animation-delay: var(--dl,0s);
  transform: translate3d(var(--bx,0px), 0, 0); }
@supports (backdrop-filter: blur(4px)) { .blend-bubbles span { backdrop-filter: blur(2px); } }
.blend-bubbles span:nth-child(1){ left:8%;  width:120px; height:120px; --dur:19s; --dl:-2s; }
.blend-bubbles span:nth-child(2){ left:26%; width:70px;  height:70px;  --dur:15s; --dl:-7s; }
.blend-bubbles span:nth-child(3){ left:47%; width:160px; height:160px; --dur:23s; --dl:-11s; }
.blend-bubbles span:nth-child(4){ left:66%; width:90px;  height:90px;  --dur:17s; --dl:-4s; }
.blend-bubbles span:nth-child(5){ left:82%; width:130px; height:130px; --dur:21s; --dl:-14s; }
.blend-bubbles span:nth-child(6){ left:92%; width:60px;  height:60px;  --dur:13s; --dl:-9s; }
@keyframes blend-rise {
  0%   { top: 105%; opacity: 0; transform: translate3d(var(--bx,0px),0,0) scale(.8); }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { top: -30%; opacity: 0; transform: translate3d(var(--bx,0px),0,0) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) { .blend-bubbles { display: none; } }

/* ============================================================
   A8 · No Buzzwords — eight square glass cards, two rows of four
   Squares by construction (aspect-ratio:1), fluid width, so the grid is
   responsive without ever being pinned to a fixed canvas.
   ============================================================ */
.sqgrid { list-style: none; margin: clamp(26px,3.4vw,52px) 0 0; padding: 0 var(--gutter);
  display: grid; grid-template-columns: repeat(4, 1fr);      /* 2 rows x 4 at desktop */
  gap: clamp(10px,1.2vw,18px); }
.sq { position: relative; }
.sq-btn { position: relative; width: 100%; aspect-ratio: 1; display: flex;
  flex-direction: column; align-items: flex-start; justify-content: flex-end;
  gap: 7px; text-align: left; padding: clamp(14px,1.5vw,22px);
  border: 1px solid var(--rule); border-radius: 4px; cursor: pointer; color: inherit;
  background: color-mix(in srgb, var(--print) 70%, transparent);
  transition: border-color .35s var(--ease), background .35s var(--ease),
              transform .5s var(--ease); }
@supports (backdrop-filter: blur(8px)) {
  .sq-btn { backdrop-filter: blur(9px) saturate(1.12); }
}
.sq-n { position: absolute; top: clamp(12px,1.4vw,18px); left: clamp(14px,1.5vw,22px);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .16em; color: var(--quiet); }
.sq-t { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(15px,1.5vw,24px); line-height: .95; letter-spacing: -.02em; }
/* the explanation spells out on hover/focus — the width animates, so the
   letters are revealed rather than faded in as a block */
.sq-l { font-size: clamp(12px,.92vw,14px); color: var(--quiet); line-height: 1.35;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height .45s var(--ease), opacity .35s var(--ease); }
.sq-d { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--quiet); line-height: 1.5;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height .5s var(--ease) .06s, opacity .4s var(--ease) .06s; }
.sq-btn:hover, .sq-btn:focus, .sq-btn:focus-visible, .sq-btn[aria-expanded="true"] {
  border-color: var(--ink);
  background: color-mix(in srgb, var(--print) 92%, transparent);
  transform: translateY(-3px); }
.sq-btn:hover .sq-l, .sq-btn:focus .sq-l, .sq-btn:focus-visible .sq-l,
.sq-btn[aria-expanded="true"] .sq-l {
  max-height: 6em; opacity: 1; }
.sq-btn:hover .sq-d, .sq-btn:focus .sq-d, .sq-btn:focus-visible .sq-d,
.sq-btn[aria-expanded="true"] .sq-d {
  max-height: 10em; opacity: .85; }
.sq-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

@media (max-width: 1000px) { .sqgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .sqgrid { grid-template-columns: 1fr; }
  .sq-btn { aspect-ratio: auto; min-height: 132px; } }
@media (prefers-reduced-motion: reduce) {
  .sq-btn, .sq-l, .sq-d { transition: none; }
  /* essential information stays available without motion */
  .sq-l, .sq-d { max-height: none; opacity: 1; }
  .sq-btn { justify-content: flex-start; padding-top: 42px; }
}

/* ============================================================
   v4.2 R2 · SELECTED WORK — one centered full-frame stage
   The two-column media-left/info-right composition is superseded. One large
   centered 16:9 surface fills the stage; the approved copy sits on it in a
   glass panel in the lower-left safe area.

   Sizing is viewport-aware, never hardcoded: the frame is the smaller of
   92vw and what 16:9 allows in the available height, so it stays inside the
   viewport at any aspect and zoom keeps working. Only the MEDIA is 16:9 —
   no page section or text block carries a ratio.
   ============================================================ */
.wstage { position: relative; height: 560svh; }
.wstage-pin { position: sticky; top: 0; height: 100svh;
  display: grid; place-items: center; overflow: hidden; }

.wslide { grid-area: 1 / 1;
  /* 92vw wide, but never taller than the stage allows */
  width: min(92vw, calc((100svh - 9rem) * 16 / 9));
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .5s var(--ease), visibility 0s linear .5s; }
.wslide.is-active { opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity .5s var(--ease), visibility 0s; }

/* the camera: --z is the lifecycle scale (0.76 → 1 → ~1.15), set by JS */
.wframe { position: relative; aspect-ratio: 16/9; overflow: hidden;
  border-radius: 4px; background: var(--print);
  box-shadow: 0 60px 120px -70px rgba(0,0,0,.8);
  transform: scale(var(--z, 1));
  transform-origin: center 50%;
  filter: blur(calc(var(--zblur, 0) * 1px));
  will-change: transform; }
.wframe::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px var(--rule); border-radius: inherit; z-index: 6; }

/* layer 1 media · layer 2 monochrome — colour is revealed, not repainted */
.wframe video, .wframe img { position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: grayscale(1) contrast(1.05);
  /* a slow fade into colour, not a switch */
  transition: filter .9s var(--ease);
  transform: scale(var(--mz, 1));              /* restrained internal depth */
  transform-origin: center; }
.wframe:hover video, .wframe:hover img,
.wslide:focus-within .wframe video, .wslide:focus-within .wframe img,
.wframe.is-colour video, .wframe.is-colour img { filter: grayscale(0) contrast(1); }
.wframe .nm-gate { z-index: 2; }

/* layer 3 · ~3% grain — stays over the colour */
.wgrain { position: absolute; inset: 0; z-index: 3; pointer-events: none; opacity: .03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 140px 140px; mix-blend-mode: overlay; }

/* BSP six-photo crossfade — hero is the permanent base beneath */
.wmedia-bsp { position: absolute; inset: 0; }
.wmedia-bsp picture { position: absolute; inset: 0; opacity: 0;
  transition: opacity .42s var(--ease); }
.wmedia-bsp picture.is-base, .wmedia-bsp picture.is-on { opacity: 1; }

.wdots { position: absolute; left: 50%; transform: translateX(-50%); bottom: 22px;
  display: flex; gap: 7px; list-style: none; margin: 0; padding: 0; z-index: 7; }
.wdots li { width: 22px; height: 2px; background: var(--rule); transition: background .3s var(--ease); }
.wdots li.on { background: var(--ink); }

@media (max-width: 900px) {
  .wcopy { width: min(46ch, 86%); }
  .wcopy .wtitle { font-size: clamp(20px, 4.6vw, 32px); }
}
/* narrow / coarse / reduced motion: plain stack, copy under its media so
   neither the image nor the text is squeezed */
@media (max-width: 640px), (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .wstage { height: auto; }
  .wstage-pin { position: static; height: auto; display: block; overflow: visible;
    padding-block: clamp(18px, 4vh, 40px); }
  .wslide { grid-area: auto; width: auto; opacity: 1 !important; visibility: visible !important;
    pointer-events: auto !important; margin-bottom: clamp(44px, 8vh, 84px);
    padding-inline: var(--gutter); }
  .wframe { transform: none !important; filter: none !important; }
  .wframe video, .wframe img { transform: none !important; }
  .wcopy { position: static; width: auto; margin-top: 14px; backdrop-filter: none;
    background: none; border: 0; box-shadow: none; padding: 0; }
  .wdots { display: none; }
}

/* ---- R2 · side H1 title, shown as each project approaches ----
   The project title appears beside the stage while the frame is still
   travelling toward the viewer, then hands over to the in-frame glass panel
   as it locks. It carries .h-int, so it gets exactly the same liquid lens,
   hold-to-magnify and sound-board behaviour as every other H1 on the site.
   aria-hidden: the same title is announced from the in-frame panel, so this
   is a visual restatement, not a second heading in the accessibility tree. */
/* The title floats above the project media for the whole lifecycle — it sits
   over the frame (z 8) but under the case-study window (z 9), so the window
   always wins where the two overlap. It is never display:none or
   visibility:hidden, which is what keeps the liquid lens, hold-to-magnify and
   sound board alive on it throughout. */
.wside { position: absolute; left: clamp(16px, 4vw, 84px); top: 50%;
  z-index: 8; pointer-events: auto;
  display: flex; flex-direction: column; gap: 10px; max-width: min(38vw, 520px);
  opacity: var(--sv, 1);
  transform: translateY(calc(-50% + var(--float, 0px)));
  animation: wside-float 9s var(--ease-inout, ease-in-out) infinite;
  transition: opacity .35s var(--ease); }
@keyframes wside-float {
  0%, 100% { --float: 0px; }
  50%      { --float: -9px; }
}
@property --float { syntax: "<length>"; inherits: false; initial-value: 0px; }
@media (prefers-reduced-motion: reduce) { .wside { animation: none; } }
.wside .k { font-family: var(--font-mono); font-size: clamp(9px,.72vw,11px);
  letter-spacing: .18em; text-transform: uppercase; color: var(--quiet); }
.wside-t { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(30px, 5.4vw, 96px); line-height: .86; letter-spacing: -.04em;
  margin: 0; position: relative; }
@media (max-width: 900px) { .wside { display: none; } }
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .wside { display: none; }
}

/* ============================================================
   R3.1 · theme-aware separation for the side H1
   The title's colour stays --ink; the shadow is separation only, never a
   substitute for contrast. Light mode gets a restrained dark edge so the
   letters hold over warm gray and over busy approaching media; dark mode
   keeps its existing look with only a whisper of pale edge.
   The lens clone gets the SAME values as the base, so the treatment never
   doubles or brightens as the ripple passes over the letters.
   ============================================================ */
/* R4 · The floating case-study titles are ALWAYS white on a black edge, in
   both themes. They sit over client photography whose brightness we do not
   control, so deriving their colour from the theme meant light mode put dark
   ink over mid-tone media and lost the letters. White with a black shadow is
   the one combination that holds over any frame, and it stays inside the
   monochrome rule. The lens clone inherits identically so the ripple never
   doubles the edge. */
body.nm .wside-t,
body.nm .wside-t .h-int-lens,
body.nm.dark .wside-t,
body.nm.dark .wside-t .h-int-lens {
  color: #fff;
  text-shadow:
    0 0 1px rgba(0,0,0,.85),                 /* tight edge — keeps counters open */
    0 2px 6px rgba(0,0,0,.72),
    0 8px 30px rgba(0,0,0,.55);
}
/* the kicker rides the same treatment so the pair reads as one unit */
body.nm .wside .k, body.nm.dark .wside .k {
  color: rgba(255,255,255,.82);
  text-shadow: 0 1px 5px rgba(0,0,0,.75);
}
/* the clone must not read as a second, softer title behind the first */
.wside-t .h-int-lens { text-shadow: inherit; }

/* ============================================================
   R3.2 · one small persistent case-study window
   Sibling to .wframe: the media scales, blurs and passes behind it while
   this stays fixed in place and stays clickable through the whole cycle.
   ============================================================ */
/* The stage sits above the decorative liquid pane (z 90) so the case-study
   window is never covered by it; the persistent nav is lifted above both, so
   the order stays nav > window > pane > media. */
.wstage { z-index: 95; }
.nm-nav { z-index: 100; }
.wcase-wrap { position: absolute; inset: 0; z-index: 9;
  pointer-events: none;                     /* wrapper never eats input */
  display: flex; align-items: flex-end; justify-content: flex-end;
  padding: clamp(18px, 3.2vw, 64px); }
.wcase { pointer-events: auto;              /* the window itself does */
  width: clamp(270px, 28vw, 420px);
  display: flex; flex-direction: column; gap: 10px;
  padding: clamp(18px, 1.5vw, 24px);
  border-radius: 5px;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  background: color-mix(in srgb, var(--print) 66%, transparent);
  box-shadow: 0 26px 60px -34px rgba(0,0,0,.55),
              inset 0 1px 0 color-mix(in srgb, var(--paper) 60%, transparent);
  transition: opacity .32s var(--ease); }
@supports (backdrop-filter: blur(14px)) {
  .wcase { background: color-mix(in srgb, var(--print) 40%, transparent);
    backdrop-filter: blur(18px) saturate(1.18); }
}
.wcase.is-swapping { opacity: 0; }           /* gentle fade on project change */
.wcase-t { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(17px, 1.5vw, 26px); line-height: .96; letter-spacing: -.025em; margin: 0; }
.wcase-p { margin: 0; color: var(--ink); opacity: .82;
  font-size: clamp(12px, .88vw, 14px); line-height: 1.5;
  max-width: 46ch; }                         /* ~35–48 characters per line */
.wcase-a, .wcase-soon { font-family: var(--font-mono);
  font-size: clamp(10px, .8vw, 12px); letter-spacing: .1em; text-transform: uppercase;
  align-self: flex-start; }
/* 44x44 hit target even though the visible underline is smaller */
.wcase-a { position: relative; color: inherit; padding: 4px 0;
  border-bottom: 1px solid var(--ink); }
.wcase-a::after { content: ""; position: absolute; left: -8px; right: -8px;
  top: 50%; height: 44px; transform: translateY(-50%); }
.wcase-a:focus-visible { outline: 2px solid var(--ink); outline-offset: 5px; }
.wcase-soon { color: var(--quiet); border-bottom: 1px dashed var(--rule); padding-bottom: 3px; }
.wcase-soon[hidden], .wcase-a[hidden] { display: none; }

@media (max-width: 900px) { .wcase { width: min(420px, 72vw); } }
/* narrow / coarse / reduced motion: the same content in normal flow under
   the media, with the real link still visible and touchable */
@media (max-width: 640px), (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .wcase-wrap { position: static; padding: 0; display: block; pointer-events: auto; }
  .wcase { width: auto; margin: 14px var(--gutter) 0; backdrop-filter: none;
    background: none; border: 0; box-shadow: none; padding: 0; }
}

/* ---- close the dead space between the manifesto and Selected Work ----
   The manifesto is 115svh over a 100svh pin, so the last 15svh is the pin
   releasing — the band has already travelled up and left an empty screen
   behind it. Selected Work is pulled up by exactly that tail so the release
   happens underneath it, and the label row loses its oversized padding. */
.nm-worklab { margin-top: 0; padding-top: 0;
  padding-bottom: clamp(8px, 1.2vh, 18px); position: relative; z-index: 96; }
.nm-worklab .nm-lab { border-top: 0; padding-top: 0; }
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  /* stacked mode has no pin, so nothing needs to be pulled together */
  .nm-mani { margin-bottom: 0; }
  .nm-worklab { margin-top: 0; padding-top: clamp(20px, 3vh, 40px); }
}

/* ============================================================
   Selected Work stage · layer order, top to bottom
     z 9  .wcase-wrap   case-study glass box + link out
     z 8  .wside        H1 liquid-glass ripple title
     z 5  #nm-glass     liquid-glass pane — ripple, caustic, typed readout
     z 3  .wgrain       monochrome grain
     z 0  media         parallax project media, scroll-driven
   No colour sheet sits on the artwork: the photographs stay black and white
   and fade to their original colour on hover. Colour belongs to the client
   work, and it arrives by revealing the image rather than tinting it.
   ============================================================ */

/* R2.3 · touch equivalent for the colour reveal — only rendered on the
   stacked/coarse path, where hover does not exist. */
.wcolour { display: none; }
@media (max-width: 640px), (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .wcolour { display: inline-flex; align-items: center; margin: 12px var(--gutter) 0;
    min-height: 44px; padding: 10px 0; background: none; border: 0; cursor: pointer;
    font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em;
    text-transform: uppercase; color: var(--quiet);
    border-bottom: 1px dashed var(--rule); }
  .wcolour[aria-pressed="true"] { color: var(--ink); border-bottom-style: solid; }
  .wcolour:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }
}

/* ============================================================
   v4 §3 · standardized case-study spine
   Hero → Overview → Challenge → Strategy → Interactive Experience →
   Process → Deliverables → Results → Behind the Scenes → CTA.
   The editorial headline stays the visible voice of each chapter; the
   canonical stage name rides above it so all three case studies read as
   one system and can be navigated the same way.
   ============================================================ */
.cs2-chapter .k .stage { display:block; margin-top:4px; color:var(--ink); opacity:.55; }

/* §5 Interactive Experience — a working interface, not an image gallery. */
.cs2-ix { max-width:1280px; margin:0 auto; padding:0 var(--gutter) clamp(50px,9vh,120px); }
.cs2-ix-frame { border:1px solid var(--rule); border-radius:5px; overflow:hidden;
  background:color-mix(in srgb, var(--print) 55%, transparent); }
.cs2-ix-steps { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap;
  border-bottom:1px solid var(--rule); }
.cs2-ix-steps li { flex:1 1 auto; }
.cs2-ix-steps button { width:100%; min-height:52px; padding:12px 14px; text-align:left;
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase;
  color:var(--quiet); border-right:1px solid var(--rule);
  transition:color .3s var(--ease), background .3s var(--ease); }
.cs2-ix-steps li:last-child button { border-right:0; }
.cs2-ix-steps button[aria-selected="true"] { color:var(--ink); background:color-mix(in srgb, var(--ink) 6%, transparent); }
#nm-loop .cs2-ix-steps button:hover { color:var(--ink); }
.cs2-ix-steps button:focus-visible { outline:2px solid var(--ink); outline-offset:-3px; }
.cs2-ix-panel { padding:clamp(20px,3vw,40px); display:grid; gap:14px;
  grid-template-columns:minmax(0,1fr); min-height:190px; }
/* `display:grid` from a class outranks the UA's [hidden]{display:none}, so the
   inactive panels stayed on screen and the tablist looked like a long list. */
.cs2-ix-panel[hidden] { display:none; }
.cs2-ix-panel .n { font-family:var(--font-impact); font-size:clamp(30px,5vw,64px); line-height:.8;
  color:var(--quiet); }
.cs2-ix-panel .h { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size:clamp(19px,2.2vw,30px); line-height:.98; letter-spacing:-.02em; }
.cs2-ix-panel .d { color:var(--ink); font-size:clamp(15px,1.35vw,17px); line-height:1.6; max-width:62ch; }
.cs2-ix-cap { margin-top:12px; font-family:var(--font-mono); font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--quiet); }
@media (prefers-reduced-motion: reduce) { .cs2-ix-steps button { transition:none; } }

/* §7 Deliverables — modules, not an undifferentiated list. */
.cs2-deliver { max-width:1280px; margin:0 auto; padding:0 var(--gutter) clamp(50px,9vh,120px); }
.cs2-deliver ul { list-style:none; margin:clamp(18px,2.4vw,32px) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:1px;
  background:var(--rule); border:1px solid var(--rule); }
.cs2-deliver li { background:var(--paper); padding:clamp(15px,1.6vw,22px);
  min-height:96px; display:flex; align-items:flex-end;
  font-family:var(--font-mono); font-size:11.5px; letter-spacing:.05em; text-transform:uppercase;
  color:var(--ink); transition:background .3s var(--ease); }
body.nm.dark .cs2-deliver li { background:var(--print); }
#nm-loop .cs2-deliver li:hover { background:color-mix(in srgb, var(--ink) 5%, transparent); }

/* §10 CTA */
.cs2-cta { max-width:1280px; margin:0 auto; padding:0 var(--gutter) clamp(40px,7vh,90px); }
.cs2-cta-in { border-top:1px solid var(--rule); padding-top:clamp(22px,3vw,40px);
  display:flex; flex-wrap:wrap; align-items:baseline; gap:14px 26px; }
.cs2-cta .b { font-family:var(--font-display); font-weight:900; text-transform:uppercase;
  font-size:clamp(24px,3.4vw,52px); letter-spacing:-.03em; line-height:1; }
.cs2-cta .go { font-family:var(--font-mono); font-size:12px; letter-spacing:.16em;
  text-transform:uppercase; border-bottom:1px solid var(--ink); padding-bottom:3px; }

/* §4 · BSP approved non-destructive jacket crop (MNRL_BSP_Crop_Reference.md).
   CSS only — the source JPG/WebP are untouched and no overlay is baked in. */
.bsp-jacket-crop { position:relative; width:100%; aspect-ratio:16/9; overflow:hidden; }
.bsp-jacket-crop img { width:100%; height:100%; object-fit:cover; object-position:50% 50%; display:block; }
@media (max-width:640px){ .bsp-jacket-crop { aspect-ratio:4/3; } }

/* ---- Formula FunCast captures inside the Apex case study ----
   The sources are 960x476 — that is the ceiling, the supplied GIFs are the
   same size. They were rendering across the full column, i.e. upscaled ~1.35x,
   which is what made them look soft. Cap at native width and centre; the pair
   below sits under it at half scale, so neither is ever enlarged. */
.fc-lead { max-width: 960px; margin: clamp(20px,3vw,40px) auto 0; padding-inline: var(--gutter); }
.fc-lead video, .fc-lead img { width: 100%; height: auto; display: block; border-radius: 4px; }
.fc-pair { max-width: 960px; margin: clamp(14px,2vw,26px) auto 0; padding-inline: var(--gutter);
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(10px,1.4vw,20px); }
.fc-pair figure { margin: 0; }
.fc-pair video, .fc-pair img { width: 100%; height: auto; display: block; border-radius: 4px; }
@media (max-width: 640px) { .fc-pair { grid-template-columns: 1fr; } }

/* one media element per section — the phone stands alone now that the paired
   site-ui shot is gone, so it centres instead of sitting in a dead column. */
.ss2-phone--solo { margin-inline: auto; }

/* ============================================================
   SIGNAL — the live-conditions Easter egg (assets/js/signal.js)
   Injected on demand from the hero's location corner. Every mark
   inside is stroke-only in currentColor, so the panel inverts with
   the dark toggle from `color` alone — no second palette exists.
   ============================================================ */
.nm-loc-hot { cursor: pointer; position: relative; }
.nm-loc-hot::after { content:""; position:absolute; left:0; right:0; bottom:-3px; height:1px;
  background: currentColor; opacity:.28; transform: scaleX(0); transform-origin:left;
  transition: transform .4s var(--ease); }
.nm-loc-hot:hover::after, .nm-loc-hot:focus-visible::after { transform: scaleX(1); }

.nm-signal { position: fixed; right: clamp(12px,2vw,28px); bottom: clamp(12px,2vw,28px);
  z-index: 999; width: min(324px, calc(100vw - 24px));
  padding: 18px 18px 14px; border: 1px solid currentColor;
  background: var(--bg); color: var(--fg);
  opacity: 0; transform: translateY(14px) scale(.985);
  transition: opacity .42s var(--ease), transform .42s var(--ease); }
.nm-signal.on { opacity: 1; transform: none; }

.sig-head { display:grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items:baseline;
  padding-bottom: 12px; border-bottom: 1px solid currentColor; }
.sig-t { font-family: var(--font-mono); font-size: 12px; letter-spacing:.24em; text-transform:uppercase; }
.sig-s { grid-column:1; font-family: var(--font-mono); font-size: 10px; letter-spacing:.1em;
  opacity:.55; }
.sig-x { grid-column:2; grid-row:1 / span 2; align-self:center; background:none; border:none;
  padding:4px 2px; color:inherit; cursor:pointer;
  font-family: var(--font-mono); font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  opacity:.6; transition: opacity .25s var(--ease); }
.sig-x:hover, .sig-x:focus-visible { opacity:1; }

.sig-grid { display:grid; gap: 16px; padding: 16px 0 4px; }
.sig-cell { display:grid; grid-template-columns: auto 1fr; gap: 2px 10px; }
.sig-k { font-family: var(--font-mono); font-size:10px; letter-spacing:.18em;
  text-transform:uppercase; opacity:.55; }
.sig-v { font-family: var(--font-mono); font-size:11px; letter-spacing:.06em; text-align:right;
  font-variant-numeric: tabular-nums; }
.sig-g { grid-column: 1 / -1; margin-top: 4px; }
.sig-g svg { display:block; width:100%; height:auto; }
.sig-err { grid-column:1 / -1; font-family:var(--font-mono); font-size:10px; opacity:.5; margin:2px 0 0; }
.sig-f { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid currentColor;
  font-family: var(--font-mono); font-size: 9px; letter-spacing:.08em; line-height:1.5; opacity:.42; }

/* streamflow drift — amplitude is set inline from the live cfs value */
@keyframes nm-flow { 0%,100% { transform: scaleX(1); } 50% { transform: scaleX(.82); } }

@media (max-width: 520px) { .nm-signal { right: 8px; left: 8px; width:auto; bottom: 8px; } }
@media (prefers-reduced-motion: reduce) {
  .nm-signal { transition: opacity .2s linear; transform: none; }
  .nm-signal.on { transform: none; }
}

/* ============================================================
   MINERAL FIELD — the hero backdrop (replaces the gated portrait slot)
   Five mineral studies at five depths behind the headline. Each turns
   and breathes on its own clock and leans with the cursor, so the field
   never repeats and never resolves into a single readable object.

   The artwork is white-on-black line work, so it is inverted on the light
   theme and left alone in dark — one asset set, both themes.
   ============================================================ */
.nm-min { position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0;
  opacity:0; transition: opacity 1.6s var(--ease) .3s;
  /* Dissolve at the edges. Without this the layer's own rectangle reads as a
     visible panel behind the type — the artwork must appear to have no frame. */
  -webkit-mask-image: radial-gradient(ellipse 62% 58% at 50% 46%, #000 30%, rgba(0,0,0,.55) 62%, transparent 88%);
  mask-image: radial-gradient(ellipse 62% 58% at 50% 46%, #000 30%, rgba(0,0,0,.55) 62%, transparent 88%); }
.nm-hero.in .nm-min { opacity:.62; }

.nm-min { color: var(--ink); }
/* An <img>-referenced SVG is its own document and cannot inherit the page's
   currentColor — it resolves to black in there. Black is correct on paper, so
   light mode needs nothing; dark mode inverts to put white line work on the
   dark ground. Inverting a pure black-on-transparent drawing is exact, not an
   approximation, because there is no colour to distort. */
body.dark .nm-min-l { filter: invert(1); }
.nm-min-l { position:absolute; left:50%; top:50%; width:min(74vw,900px); height:auto;
  /* --px/--py are written by JS from the pointer; the rest is per-layer drift */
  transform: translate3d(calc(-50% + var(--px,0px)), calc(-50% + var(--py,0px)), 0)
             rotate(var(--rot,0deg)) scale(var(--sc,1));
  will-change: transform;
  /* The studies are stroke-only in currentColor, so they take the theme
     directly. The old invert() + blend modes existed only to cope with
     baked black plates and now actively fight the artwork. */
  animation: nm-min-turn var(--dur,44s) linear infinite; }

/* Layers are dark until promoted. The fade is deliberately short — this is
   a cut with just enough softness to avoid a hard strobe, not a dissolve. */
.nm-min-l { opacity:0 !important; transition: opacity .18s linear; }
.nm-min-l.is-live { opacity: var(--live,.30) !important; }
.nm-min-l:nth-child(1){ --dur:52s; --sc:1.18; --live:.30; }
.nm-min-l:nth-child(2){ --dur:64s; --sc:.86;  --live:.26; animation-direction:reverse; }
.nm-min-l:nth-child(3){ --dur:44s; --sc:1.42; --live:.22; }
.nm-min-l:nth-child(4){ --dur:76s; --sc:.68;  --live:.34; animation-direction:reverse; }
.nm-min-l:nth-child(5){ --dur:58s; --sc:1.02; --live:.28; }

/* Rotation is applied to a wrapper transform via CSS var so it composes with
   the pointer offset above rather than overwriting it. */
@keyframes nm-min-turn { to { --rot: 360deg; } }
@property --rot { syntax:"<angle>"; inherits:false; initial-value:0deg; }

@media (max-width:820px){ .nm-min-l { width:110vw; } .nm-min { opacity:.7; } }
@media (prefers-reduced-motion: reduce){
  .nm-min-l { animation:none; }
}

/* ============================================================
   CHAPTER BREAK — entering Selected Work
   A held moment between the manifesto and the case-study stage.
   Progress (--cp, 0..1) is written by v3.js from scroll position and
   drives every value here, so the whole break is one timeline rather
   than a set of independent animations that can fall out of sync.
   ============================================================ */
.nm-chap { position: relative; height: 190vh; }
.nm-chap-pin { position: sticky; top: 0; height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(14px, 2vh, 26px); padding: 0 var(--gutter); text-align: center;
  /* recede on the way out — the chapter card falls back as the stage arrives */
  transform: scale(calc(1 - var(--cp, 0) * 0.12)) translateY(calc(var(--cp, 0) * -4vh));
  opacity: calc(1 - var(--cp, 0) * 1.25); }

.nm-chap-k { font-family: var(--font-mono); font-size: 11px; letter-spacing: .28em;
  text-transform: uppercase; color: var(--quiet); margin: 0;
  opacity: var(--ci, 0); transform: translateY(calc((1 - var(--ci, 0)) * 12px)); }

.nm-chap-t { font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(40px, 9vw, 150px); line-height: .9; letter-spacing: -.03em;
  margin: 0; display: flex; align-items: center; justify-content: center;
  gap: clamp(10px, 1.6vw, 26px); flex-wrap: nowrap; }
/* the two words part around the rule as it draws */
.nm-chap-t .w { display: inline-block;
  transform: translateX(calc(var(--ci, 0) * 0px)); }
.nm-chap-t .w:first-child { transform: translateX(calc((1 - var(--ci, 0)) * 5vw)); }
.nm-chap-t .w:last-child  { transform: translateX(calc((1 - var(--ci, 0)) * -5vw)); }
.nm-chap-t .rule { display: block; height: 2px; background: currentColor;
  width: clamp(40px, 8vw, 130px); transform: scaleX(var(--ci, 0)); transform-origin: center; }

.nm-chap-m { display: flex; gap: clamp(16px, 3vw, 44px); margin: 0;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--quiet);
  opacity: var(--ci, 0); transform: translateY(calc((1 - var(--ci, 0)) * 12px)); }

.nm-chap-s { position: absolute; bottom: clamp(66px, 9vh, 96px);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--quiet);
  opacity: calc(var(--ci, 0) * (1 - var(--cp, 0) * 2));
  animation: nm-chap-nudge 2.4s var(--ease-inout) infinite; }
@keyframes nm-chap-nudge { 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(5px); } }

.nm-chap-k, .nm-chap-m, .nm-chap-t .w, .nm-chap-t .rule {
  transition: transform .9s var(--ease), opacity .9s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .nm-chap { height: 100svh; }
  .nm-chap-pin { transform: none; opacity: 1; }
  .nm-chap-k, .nm-chap-m { opacity: 1; transform: none; }
  .nm-chap-t .w { transform: none; }
  .nm-chap-t .rule { transform: scaleX(1); }
  .nm-chap-s { animation: none; }
}

/* ============================================================
   MOBILE · Selected Work — definitive
   The stacked-mode rules earlier in this file reset the stage's transforms
   but the slide kept `width: min(92vw, calc((100svh - 9rem) * 16/9))` from
   the pinned layout. On a phone that calc collapses to ~190px, so every
   project rendered as a thumbnail scrolled off screen — which read as
   "the images don't load".

   Stated last and explicitly so nothing earlier can win.
   ============================================================ */
@media (max-width: 900px) {
  .wstage { height: auto; }
  .wstage-pin { position: static; height: auto; display: block;
    overflow: visible; place-items: initial; }

  .wslide { width: auto !important; grid-area: auto;
    opacity: 1 !important; visibility: visible !important; pointer-events: auto !important;
    margin: 0 0 clamp(40px, 7vh, 72px); padding-inline: var(--gutter); }

  /* the frame fills the column and keeps its ratio */
  .wframe { width: 100%; aspect-ratio: 16/9; transform: none !important;
    filter: none !important; border-radius: 3px; }
  .wframe video, .wframe img, .wframe .cyc {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; transform: none !important; filter: none !important; }

  /* on touch the media is never revealed by hover, so show it in colour */
  .wframe video, .wframe img { filter: grayscale(0) !important; }

  /* the project name has to come back — .wside is hidden on coarse pointers,
     so without this a stacked slide is an unlabelled picture */
  .wslide::before { content: attr(data-label);
    display: block; margin: 0 0 10px;
    font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
    font-size: clamp(26px, 8vw, 44px); line-height: .9; letter-spacing: -.03em; }

  .wcase-wrap, .wcopy { position: static !important; width: auto !important;
    margin: 14px 0 0; padding: 0; background: none; border: 0;
    box-shadow: none; backdrop-filter: none; }
  .wdots { display: none; }
}

/* On a phone only the still shows (see cycle.js), so it must fill the frame
   the same way the clips would — and nothing else may paint over it. */
@media (max-width: 900px) {
  .wframe .cyc-f { opacity: 0 !important; }
  .wframe .cyc-f.is-on { opacity: 1 !important; }
  .wframe .cyc-still { background-size: cover; background-position: 50% 45%; }
  .wframe video, .wframe img, .wframe .cyc, .wframe .cyc-f {
    border-radius: inherit; }
}

/* stacked-mode per-project copy + link */
.wstack-copy { display: none; }
@media (max-width: 900px) {
  .wstack-copy { display: block; margin-top: 12px; }
  .wstack-p { font-size: 14px; line-height: 1.55; color: var(--quiet);
    margin: 0 0 12px; max-width: 46ch; }
  .wstack-cta, .wstack-soon { display: inline-flex; align-items: center;
    min-height: 44px; padding: 10px 0;
    font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em;
    text-transform: uppercase; text-decoration: none; }
  .wstack-cta { color: var(--ink); border-bottom: 1px solid var(--ink); }
  .wstack-soon { color: var(--quiet); border-bottom: 1px dashed var(--rule); }
  /* the shared desktop card has nothing to say in stacked mode */
  .wcase-wrap { display: none !important; }
}
