/* ══ THE TYPEFACE ═══════════════════════════════════════════════════
   Fraunces, served from this site rather than from Google.

   Google Fonts is free and convenient and it also means every visitor
   hands Google their IP address and the fact that they loaded a page
   here, before they have done anything at all. No cookie is involved,
   which is exactly why it is easy to miss — and it had to be disclosed
   in the cookie policy. Serving the file ourselves removes the third
   party completely, and takes the paragraph out of the policy.

   One file, 120KB, carrying all four axes the site actually uses:
   opsz, wght, SOFT and WONK. The latin-ext subset is not shipped
   because nothing on this site needs it — the Japanese comes from the
   system stack, never from Fraunces.

   Fraunces is under the SIL Open Font License, which expressly permits
   this. The licence travels with the file in assets/fonts. ══ */
@font-face{
  font-family:'Fraunces';
  font-style:normal;
  font-weight:100 900;
  /* swap, not block: the page is readable in Georgia while 120KB
     arrives, rather than invisible until it does */
  font-display:swap;
  src:url("fonts/fraunces.woff2") format("woff2");
}
/* ══════════════════════════════════════════════════════════════════════
   NOMI ATELIER — the system
   Shared by every page. The homepage and a concept page differ only in
   content, never in chrome.

   THE CHROMA RULE: typography and grid are fixed. Project colour enters
   only through image slots. The chrome has its own small rotating family
   (below) — never more than one or two live in a single view.

   LIGHT AND DARK is a spatial device, not a page setting: --bg and --fg
   are the ACTIVE ground and ink, and a section can be a dark corridor or
   a light room. Everything downstream reads those two, never --ink and
   --plaster directly.
   ══════════════════════════════════════════════════════════════════════ */
:root{
  --ink:#16212C;
  --plaster:#E7E1D6;
  --plaster-lit:#F1EDE4;

  /* the active ground/ink. JS moves these between the two as you scroll. */
  --bg:#E7E1D6;
  --fg:#16212C;
  --soft:rgba(22,33,44,.62);
  --quiet:rgba(22,33,44,.42);
  --hair:rgba(168,162,154,.5);          /* concrete grey does the hairlines */

  /* ── the rotating chrome family ──
     concrete + beige do the quiet structural work; rust, cobalt and lime
     take turns being the moment of colour. --c is whichever is on duty. */
  --rust:#C4622E;
  --cobalt:#1D5FD6;
  --lime:#B5D34A;
  /* A fourth accent, because --c is painted on TEXT and --lime is
     unusable there: 1.30:1 on plaster. Ember is 4.59:1 on plaster and
     5.11:1 on the lit ground, so it clears AA for body copy, not only
     for display sizes the way rust does at 3.14:1. This file ships to
     the public, so no unannounced project is named in it. */
  --ember:#B23A1E;
  /* Moss, for a project of pale timber and sage. 5.60:1 on plaster.
     Deliberately far darker than --lime so the two never read as a
     near-miss of one another. */
  --moss:#4A5D23;
  --concrete:#A8A29A;
  --beige:#D8CFC0;
  --c:var(--rust);

  --gut:clamp(1.4rem,5vw,4.5rem);
  --strip:clamp(3.2rem,5.5vh,4rem);

  /* one motion: the cut. Fast, directional, no bounce, hard arrival. */
  --blade:cubic-bezier(.25,.86,.3,1);
  --cut-t:.42s;
}
/* whose turn it is, declared per section */
.c-rust  {--c:var(--rust);}
.c-cobalt{--c:var(--cobalt);}
.c-lime  {--c:var(--lime);}
.c-ember {--c:var(--ember);}
.c-moss  {--c:var(--moss);}

*,*::before,*::after{box-sizing:border-box;}
html,body{margin:0;}
html{-webkit-text-size-adjust:100%;overflow-x:clip;}
/* 'Our work' in the hero now goes to the lime field on the same page
   rather than to a concept. A hard jump across two screens loses people:
   they cannot tell whether the page changed. Declined where motion is
   declined — and note that atTop() scrolls INSTANTLY on purpose, because
   this rule would otherwise make its correction visibly slide. */
@media (prefers-reduced-motion:no-preference){
  html{scroll-behavior:smooth;}
}
body{background:var(--bg);color:var(--fg);
  font-family:'Fraunces',Georgia,serif;font-variation-settings:'SOFT' 26,'WONK' 0,'opsz' 14;
  -webkit-font-smoothing:antialiased;
  /* the corridor-to-room change is a cut, not a fade — this only keeps it
     from snapping, it does not soften it into a crossfade */
  transition:background-color .3s var(--blade), color .3s var(--blade);}
img{display:block;max-width:100%;}
a{color:inherit;text-decoration:none;}
button{font:inherit;color:inherit;}
::selection{background:var(--c);color:var(--bg);}
:where(a,button,[tabindex]):focus-visible{outline:2px solid var(--c);outline-offset:4px;}
.sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);}
.skip{position:fixed;left:var(--gut);top:-5rem;z-index:200;background:var(--fg);color:var(--bg);
  padding:.7rem 1.1rem;transition:top .2s var(--blade);}
.skip:focus{top:.6rem;}

.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.24;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");}

/* ── TYPE — one face, doing everything ── */
.d1{font-size:clamp(2.4rem,5.6vw,4.6rem);line-height:.98;letter-spacing:-.034em;margin:0;
  font-variation-settings:'SOFT' 54,'WONK' 1,'opsz' 144;text-wrap:balance;}
.d2{font-size:clamp(1.5rem,2.8vw,2.3rem);line-height:1.06;letter-spacing:-.024em;margin:0;
  font-variation-settings:'SOFT' 46,'WONK' 1,'opsz' 60;text-wrap:balance;}
.d3{font-size:clamp(1.1rem,1.7vw,1.35rem);line-height:1.16;letter-spacing:-.014em;margin:0;
  font-variation-settings:'SOFT' 42,'WONK' 1,'opsz' 32;}
.p{font-size:.92rem;line-height:1.78;margin:0;max-width:34ch;color:var(--soft);
  font-variation-settings:'SOFT' 20,'WONK' 0,'opsz' 14;}
/* The connective tissue of the whole site: labels, the doors, the
   footer, every spec row. It was .61rem — 9.8px — which looks
   deliberate on a large screen and is simply too small to ask someone
   to read on a phone. .68rem is 10.9px: still micro, still the same
   device, and past the point where a browser's own minimum-size
   setting starts overriding it unpredictably.

   The wide letterspacing is doing real work here, not decoration —
   uppercase at .24em is markedly more legible at this size than the
   same size set tight. */
.m{font-size:.68rem;letter-spacing:.24em;text-transform:uppercase;margin:0;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;}
.q{color:var(--quiet);}
/* 8.6px was indefensible at any width */
.slot{display:inline-block;border:1px solid var(--quiet);color:var(--quiet);padding:.22em .5em;
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;}

/* のみ is always lime, everywhere it appears — a colour you see standing
   still, the way a painted door frame is part of a room whether or not
   you are touching it. Nothing here waits for a hover. */
.nomi{font-size:.8rem;letter-spacing:.08em;line-height:1;color:var(--lime);
  font-family:'Hiragino Sans','Yu Gothic','Noto Sans JP','Meiryo',sans-serif;}
.rule{display:flex;align-items:center;gap:1rem;margin:0;color:var(--quiet);}
.rule::before,.rule::after{content:'';flex:1;height:1px;background:var(--hair);}
/* one hairline in the section's colour, at rest */
.rule::before{background:linear-gradient(90deg, transparent, var(--c));}
.rule::after{background:linear-gradient(90deg, var(--c), transparent);}

/* a section's eyebrow carries whichever colour is on duty — permanently */
.lead{color:var(--c);}

.wrap{padding-inline:var(--gut);}
.pad{padding-block:clamp(3.5rem,10vh,7rem);}
main{position:relative;z-index:2;}

/* ══ THE STRIP — wordmark + four doors. Everywhere. Never expands. ══ */
.strip{position:fixed;inset-inline:0;top:0;z-index:100;height:var(--strip);
  display:flex;align-items:center;justify-content:space-between;gap:1.5rem;
  padding-inline:var(--gut);color:var(--fg);
  background:color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border-bottom:1px solid var(--hair);}
.strip .mark{display:flex;align-items:baseline;gap:.5rem;
  font-size:.98rem;font-variation-settings:'SOFT' 44,'WONK' 1,'opsz' 24;letter-spacing:-.012em;}
.doors{display:flex;gap:clamp(.9rem,2.4vw,2.2rem);}
.doors a{position:relative;padding-block:.3rem;transition:color .26s var(--blade);}
.doors a::after{content:'';position:absolute;left:0;right:0;bottom:0;height:1px;background:currentColor;
  clip-path:inset(0 100% 0 0);transition:clip-path .3s var(--blade);}
.doors a:hover::after,.doors a[aria-current]::after{clip-path:inset(0);}
/* each door has its own turn at colour — only ever one live at a time */
.doors a:nth-child(1):hover{color:var(--rust);}
.doors a:nth-child(2):hover{color:var(--cobalt);}
.doors a:nth-child(3):hover{color:var(--lime);}
.doors a:nth-child(4):hover{color:var(--rust);}
@media (max-width:40rem){ .doors{gap:.85rem;} .doors a{font-size:.55rem;} }

/* ══ THE CUT ══ one motion, no opacity — struck, never faded ══ */
.js .cut{clip-path:inset(0 100% 0 0);}
.cut{position:relative;transition:clip-path var(--cut-t) var(--blade);}
.cut.struck{clip-path:inset(0) !important;}
.cut::after{content:'';position:absolute;top:0;bottom:0;width:1px;left:0;
  background:var(--c);opacity:0;pointer-events:none;}
.js .cut.struck::after{animation:blade var(--cut-t) var(--blade) forwards;}
@keyframes blade{0%{left:0;opacity:.6;}92%{opacity:.6;}100%{left:100%;opacity:0;}}
.c1{transition-delay:.07s;} .c1::after{animation-delay:.07s;}
.c2{transition-delay:.14s;} .c2::after{animation-delay:.14s;}
.c3{transition-delay:.21s;} .c3::after{animation-delay:.21s;}
.c4{transition-delay:.28s;} .c4::after{animation-delay:.28s;}

/* ══ THE OPENING — the chisel, typed onto bare ground ═══════════════
   A short read, not a splash. Nomi is named after a tool most people
   have never thought about for more than a second, and the whole
   studio's way of working comes out of what that tool actually does.
   Explaining it once, properly, before anything else is worth more
   than a clever line.

   It is a passage at a time. Each one types, is read, and is cut away
   before the next arrives — so there is only ever one thing on the
   screen, and it always has the same weight as the first.

   Composed on the diagonal, nothing centred: the mark top right, the
   passage left and above the middle, the rail bottom left, the way out
   bottom right.

   THE RULES THIS STILL KEEPS, because it is longer than an intro
   normally has any right to be:
   · The page underneath is fully rendered in the HTML. This is an
     overlay, it is never in a crawler's way, and a visitor without
     JavaScript never sees it at all.
   · The way out is visible from the first frame, not after the typing
     finishes, and any scroll, Esc or press takes it.
   · Any click, tap, arrow or space jumps to the next passage, so a
     fast reader is never waiting on a timer.
   · The rail says how many are left, so it is never an unknown length.
   · Once per session.
   · Under prefers-reduced-motion the whole thing is one static page of
     text with no typing and no timers. ══ */
.open{position:fixed;inset:0;z-index:150;background:var(--plaster);
  overflow:hidden;clip-path:inset(0);cursor:pointer;
  transition:clip-path .52s var(--blade);}
.open[hidden]{display:none;}
.open.shrink{clip-path:inset(0 0 0 100%);pointer-events:none;}

/* the mark, small and quiet, top right */
.open .nomi{position:absolute;top:clamp(1.4rem,5vh,2.4rem);
  right:clamp(1.6rem,8vw,6rem);}

/* the reading area. Left and above centre — never the middle of the
   screen — and tall enough that a long passage and a short one do not
   move the block around as they swap. */
.open .read{position:absolute;left:clamp(1.6rem,12vw,11rem);
  top:clamp(7rem,26vh,13rem);width:min(42ch,78vw);
  min-height:min(46vh,22rem);}
.open .passage{margin:0;color:var(--ink);
  font-size:clamp(1.12rem,1.92vw,1.62rem);line-height:1.52;letter-spacing:-.012em;
  font-variation-settings:'SOFT' 40,'WONK' 1,'opsz' 40;
  /* every passage occupies the same corner, so the swap is a change of
     text rather than a change of layout */
  position:absolute;inset:0 0 auto 0;
  clip-path:inset(0);transition:clip-path .34s var(--blade);}
/* not the current one: clipped away to nothing, and out of the way of
   a screen reader and a mouse */
.open .passage[hidden]{display:none;}
.open .passage.going{clip-path:inset(0 0 0 100%);}
/* The two characters inside the passages. Lime, as the mark is
   everywhere else — and on the same Japanese stack as .nomi, because
   Fraunces has no CJK glyphs at all: without this the kanji renders as
   an empty box. 鰿 is a 28-stroke character, so it also needs a little
   more size than the surrounding text to be legible at all. */
.open .passage .nm{color:var(--lime);font-style:normal;
  font-family:'Hiragino Sans','Yu Gothic','Noto Sans JP','Meiryo',sans-serif;
  font-size:1.15em;line-height:1;letter-spacing:.02em;}

/* an empty marker parked at the end of the typed text, so the caret
   can be positioned off a real measurement rather than a guess from a
   character count — which a line wrap would break every time */
.open .passage .tip{display:inline-block;width:0;height:1em;vertical-align:-.12em;}

/* the green line. The text is never green — only this. */
.open .caret{position:absolute;display:inline-block;
  width:.055em;height:1.05em;background:var(--lime);
  font-size:clamp(1.12rem,1.92vw,1.62rem);
  animation:tick 1.05s steps(1) infinite;}
@keyframes tick{0%,49%{opacity:1;}50%,100%{opacity:0;}}

/* how many are left. Not a loading bar — a contents. */
.open .rail{position:absolute;left:clamp(1.6rem,12vw,11rem);
  bottom:clamp(1.8rem,7vh,3.4rem);display:flex;gap:.42rem;align-items:center;}
.open .rail i{display:block;width:clamp(1.3rem,2.6vw,2.2rem);height:2px;
  background:var(--hair);transition:background-color .3s var(--blade);}
.open .rail i.done{background:rgba(22,33,44,.34);}
.open .rail i.now{background:var(--lime);}

/* diagonally opposite the passage, and there from the first frame:
   a read this long must be leaveable before it starts */
.open .enter{position:absolute;right:clamp(1.6rem,8vw,6rem);
  bottom:clamp(1.6rem,7vh,3.2rem);z-index:4;
  display:inline-flex;align-items:center;gap:.7rem;
  background:none;border:0;padding:.5rem 0;cursor:pointer;color:var(--soft);
  transition:color .24s var(--blade);}
.open .enter:hover{color:var(--ink);}
.open .enter .sq{width:5px;height:5px;background:var(--lime);flex:none;}

/* the film, if footage ever arrives: it takes over and the read hides */
.open .film{position:absolute;inset:0;z-index:1;display:block;
  width:100%;height:100%;object-fit:cover;}
.open:has(.film) .read,.open:has(.film) .rail,.open:has(.film) .nomi{display:none;}

@media (max-width:52rem){
  .open .read{left:var(--gut);right:var(--gut);width:auto;
    top:clamp(5rem,16vh,8rem);min-height:58vh;}
  /* the rail sits ABOVE the way out on a phone rather than beside it:
     at this width nine segments and the press share the same line and
     run into each other */
  .open .rail{left:var(--gut);bottom:clamp(4.4rem,12vh,5.4rem);}
  .open .enter{right:var(--gut);}
}

@media (prefers-reduced-motion:reduce){
  /* no typing, no timers, no blink: one static page of text. The
     passages stack and the visitor leaves when they are ready. */
  .open{transition:none;cursor:auto;}
  .open .caret{display:none;}
  .open .read{position:static;margin:clamp(4.5rem,14vh,7rem) auto 0
    clamp(1.6rem,12vw,11rem);min-height:0;max-height:none;}
  .open .passage{position:static;clip-path:none;transition:none;
    margin-bottom:1.1em;}
  .open .passage.going{clip-path:none;}
  .open .rail{display:none;}
  /* nine passages at once is far taller than a screen, and .open is
     overflow:hidden for the cut it leaves by — without this the last
     six are simply unreachable. The way out has to stop scrolling with
     them too. */
  .open{overflow:auto;}
  .open .read{padding-bottom:clamp(6rem,18vh,9rem);padding-right:var(--gut);}
  .open .enter{position:fixed;background:var(--plaster);padding:.5rem 0 .5rem .9rem;}
  .open .nomi{position:fixed;}
}

/* ══ THE REVEAL — wordmark broken up, doors set loose ═══════════════
   The reference does not set one oversized word off to one side: it
   breaks the word across lines and displaces a single character. Same
   move here, so it reads as composed rather than as confetti. ══ */
/* The hero was min-height:100svh with its content centred, which put
   the wordmark and the doors in the middle of a full screen and left a
   long run of nothing beneath them. It is now as tall as its content
   needs plus breathing room, with a floor so it still owns the first
   screen on a short laptop. The lime field arriving a little sooner is
   the point: the work should be the thing you are heading towards. */
.scatter{position:relative;
  min-height:min(82svh,44rem);display:grid;align-content:center;
  padding-block:clamp(3rem,9vh,5.5rem) clamp(1.6rem,4vh,2.6rem);}

/* ── THE BRAND — the one thing that travels ──────────────────────────
   In the document it is simply the page's h1, in flow, legible, and
   there before any script runs. With JS it lifts out of flow and rides
   the scroll down into the lime, turning lime as it arrives, so it
   disappears into the ground the work sits on. Its space in the hero is
   measured and reserved, so nothing below it moves when it lifts. ── */
/* the slot holds the brand's place in the hero grid once it lifts, so
   nothing below it moves at the moment it goes fixed */
.brandslot{position:relative;}
.brand{position:relative;z-index:40;pointer-events:none;
  display:flex;align-items:flex-start;gap:clamp(.5rem,1.4vw,1.1rem);}
.js .brand{will-change:transform,opacity;}
.brand .nomi{flex:none;margin-top:.5em;}
/* Lime, always, everywhere it appears. Never ink, never a transition:
   it is lime on the cream and lime on the lime, so the only thing that
   changes as it travels is what is behind it. */
.wm{margin:0;line-height:.8;letter-spacing:-.052em;color:var(--lime);
  font-size:clamp(3.2rem,15.5vw,12.5rem);
  font-variation-settings:'SOFT' 62,'WONK' 1,'opsz' 144;}
.wm b{display:block;font-weight:inherit;}
/* exactly as it was: the second line indented, the r displaced */
.wm .b{margin-left:clamp(0rem,7.5vw,7.5rem);}
/* one displaced character — 35deg, as the reference rotates its 2 */
.wm .kick{display:inline-block;margin-left:.07em;transform-origin:50% 62%;
  transform:rotate(35deg);}
/* ── the hero, on a phone ──
   15.5vw is a desktop measure. At 375 it renders the wordmark at 58px
   inside a 666px box containing 210px of content, centred, which left
   248px of nothing above it and a mark with no presence at all. 28vw
   is the ceiling before Atelier breaks to a third line; it lands the
   mark's right edge on the gutter and caps at 7.5rem so a tablet does
   not inherit a phone's proportion. The shorter box is the other half
   of the same fix: the lime field should be visible under the fold,
   because the work is what the visitor is heading towards. */
@media (max-width:52rem){
  /* 28vw was measured against a 375px screen and nothing narrower.
     The wordmark shares its row with the のみ mark, so its real room is
     the hero width minus the gutter, the gap and that mark: 224px at
     320. 28vw needs 233 there, so "Atelier" broke and dropped the r
     onto a third line — and 320 is what an iPhone reports with Display
     Zoom switched on, so it is a real phone, not a museum piece. At 360
     28vw cleared it by 3px, which is not clearing it either.

     26vw cleared 320 by only 8px — 3.7% of the measure, which is
     inside the range two engines can disagree by. 25vw clears it by
     17px (8%) and 375 by 35px, and is still 62% larger than the 58px
     this was before. A wordmark that breaks looks broken, so the four
     pixels buy more than they cost. */
  .wm{font-size:clamp(3.2rem,25vw,7.5rem);}
  .scatter{min-height:min(64svh,34rem);}
}

/* the doors, set loose across the field instead of grouped into a menu.
   With the floating header gone these are the only navigation, so they
   carry a little more weight than a scattered label would. */
.spread{display:grid;grid-template-columns:repeat(12,1fr);
  row-gap:clamp(1rem,3.4vh,2.2rem);position:relative;z-index:50;}
/* A line of small caps is 16-21px tall, which is under the 24px
   minimum target size. The padding creates the target and the
   negative margin hands the space back, so the composition is
   unchanged and the thing is still hittable with a thumb. */
.spread a{display:flex;align-items:baseline;gap:.7rem;width:max-content;
  padding-block:.42rem;margin-block:-.42rem;
  font-size:clamp(.82rem,1.1vw,1.05rem);letter-spacing:.14em;text-transform:uppercase;
  font-variation-settings:'SOFT' 20,'WONK' 0,'opsz' 14;
  transition:color .24s var(--blade);}
/* .68em of the hero label is fine; .68em of the SMALL label on the
   inner pages is 8.9px, which is below anything defensible. max() only
   ever raises it, so the hero proportion is untouched and the small one
   gets a floor. */
.spread a .n{color:var(--c);font-size:max(.68em,.66rem);letter-spacing:.2em;}
.spread a:nth-child(1){grid-column:1/5;}
.spread a:nth-child(2){grid-column:6/10;transform:translateY(clamp(.4rem,2.4vh,1.6rem));}
.spread a:nth-child(3){grid-column:3/7;transform:translateY(clamp(.2rem,1.4vh,1rem));}
.spread a:nth-child(4){grid-column:9/13;justify-self:end;}
.spread a:nth-child(1):hover{color:var(--rust);}
.spread a:nth-child(2):hover{color:var(--cobalt);}
.spread a:nth-child(3):hover{color:var(--lime);}
.spread a:nth-child(4):hover{color:var(--rust);}

/* ══ THE DOORS, PINNED ══════════════════════════════════
   Once the hero is behind you the four doors gather into a line at the
   top of the screen and stay reachable for the rest of the page; scroll
   back into the hero and they go straight back to where they were set
   loose. It is the SAME <nav> moved, not a second copy — a duplicate
   would give the page two primary navigations, read twice by a screen
   reader and crawled twice.

   The change is a hard cut, not a slide. Everything else on this site
   cuts; a nav that eased between two layouts would be the one soft
   thing on the page, and it would smear whenever someone scrolled fast
   across the threshold.

   It pins LEFT, not across the full width: 'Let's talk' is fixed at the
   top right and the two must never meet. ══ */
/* holds the nav's place in the hero grid while it is pinned, so nothing
   in the hero re-centres at the moment it leaves flow */
.navslot{position:relative;}
/* A GROUND, AT EVERY WIDTH. This was a bare line of type on no
   background, which only works while whatever scrolls under it happens
   to be empty. On the studio band it printed straight over the
   photographs and over the body column; on the 404 it printed over the
   heading. It keeps its left-aligned, content-width composition — it
   has simply stopped being transparent. */
.spread.pinned{position:fixed;z-index:80;
  top:clamp(1.15rem,4.5vh,2.4rem);left:var(--gut);right:auto;
  /* A rule under it, not a box around it. The ground alone is
     invisible against the plaster, which left body copy running right
     up to the band and reading as a collision even though the bar is
     painting cleanly on top of it. One hairline underneath separates
     the two and reads as structure rather than as a widget. */
  background:var(--plaster);padding:.6rem .9rem;
  border-bottom:1px solid rgba(22,33,44,.14);
  display:flex;flex-wrap:wrap;align-items:baseline;
  gap:clamp(.85rem,2.3vw,2.1rem);row-gap:.35rem;
  max-width:calc(100vw - var(--gut) - 11rem);
  font-size:clamp(.72rem,.92vw,.9rem);}
/* the scatter is undone: in a line they are a line */
/* a bigger hit area than the type needs, taken back with a negative
   margin so nothing moves — at this size the label alone is a 14px
   tall target */
.spread.pinned a{grid-column:auto;transform:none;font-size:inherit;
  padding-block:.55rem;margin-block:-.55rem;}
/* On a phone there is not room for the line AND the invitation on one
   row, so the doors drop to their own row underneath it rather than
   wrapping into a four-line stack on top of it. The numbers go: they are
   an editorial device for the hero, not a wayfinding one, and at this
   width they are what pushes the line past the screen. */
@media (max-width:52rem){
  /* A fixed element with no background is only invisible until
     something scrolls under it. On the desktop that is a wide empty
     gutter; on a phone it is the body copy, and this printed over the
     text of every page. So on a phone it becomes what it actually is —
     a header: full bleed, its own ground, one hairline to sit it off
     the page. The mark rides underneath it and is clipped by it,
     which is the right behaviour for a header. */
  .spread.pinned{top:0;left:0;right:0;max-width:none;
    padding:.85rem var(--gut) .8rem;
    background:var(--plaster);
    flex-wrap:nowrap;gap:clamp(.6rem,3.4vw,1.2rem);
    font-size:.68rem;letter-spacing:.1em;}
  .spread.pinned a{gap:0;}
  .spread.pinned .n{display:none;}
}

/* ══ THE FIELD — the lime the work floats on ════════════════════════
   The wordmark arrives here already lime, so it vanishes into the
   ground rather than sitting on top of it. The work is small, loose and
   movable: pick a square up, carry it, and the surface answers. ══ */
.field{position:relative;background:var(--lime);overflow:hidden;
  min-height:118svh;isolation:isolate;}
.field .ripple{position:absolute;inset:0;z-index:0;pointer-events:none;
  display:block;width:100%;height:100%;}
.field .fieldnote{position:relative;z-index:3;color:#2B3A0C;
  display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  padding:clamp(2rem,7vh,4rem) var(--gut) 0;}
/* it is the heading of the section that carries the work, so it is set
   as a heading rather than as another micro-label */
.field .fieldnote p{font-size:clamp(1.05rem,1.9vw,1.6rem);letter-spacing:.12em;
  font-variation-settings:'SOFT' 30,'WONK' 0,'opsz' 24;}

/* ── the squares ──
   Loose coordinates, not a grid. Small: the work is not yet the point,
   the surface is. Each is a real link with its name in the markup. */
/* a definite box: --y is a percentage, and a min-height parent gives
   percentages nothing to resolve against, so every square landed on the
   same line */
.plots{position:absolute;inset:0;z-index:2;}
/* ★ touch-action is NOT set here. It used to be 'none', for the drag,
   and on a phone that meant the browser would not scroll when a touch
   began on a square — five dead zones in the middle of the lime where
   the page simply refused to move. Nobody would have reported that as
   a bug; they would have decided the site was broken.

   Dragging is a mouse gesture here. On a touch screen the field is
   about one screenful and carrying squares around it would fight the
   scroll for no payoff, so touch gets the tap: a ripple, and the
   concept opens. See field() in the script. */
.work{position:absolute;margin:0;width:clamp(5.2rem,9vw,8.5rem);
  left:var(--x);top:var(--y);cursor:grab;
  filter:drop-shadow(0 14px 22px rgba(38,54,8,.28));
  transition:filter .3s var(--blade);}
@media (pointer:fine){ .work{touch-action:none;} }
.work.held{cursor:grabbing;z-index:9;filter:drop-shadow(0 26px 34px rgba(38,54,8,.4));}
.work a{display:block;}
.work .shot{aspect-ratio:1/1;}
.work .name{position:absolute;left:50%;bottom:calc(100% + .55rem);transform:translateX(-50%);
  white-space:nowrap;background:var(--ink);color:var(--plaster);padding:.34rem .55rem;
  opacity:0;transition:opacity .16s linear;pointer-events:none;}
@media (hover:hover) and (pointer:fine){
  .work:hover .name,.work.held .name{opacity:1;}
}
/* No hover on touch, and "nearest the middle" means nothing on a loose
   field — so every square simply carries its name, quietly. */
@media (hover:none),(pointer:coarse){
  .work .name{opacity:1;background:none;color:#2B3A0C;padding:0;
    bottom:auto;top:calc(100% + .4rem);}
}
@media (max-width:52rem){
  /* These were flattened to a left-aligned stack, which is the one
     thing the hero is not. On the desktop the doors are scattered
     across the grid and that asymmetry IS the composition — losing it
     on a phone turns the hero into a list. It survives a narrow screen
     on a tighter spread: two ranged left, two ranged right, the same
     small vertical offsets, nothing near an edge. */
  .spread{row-gap:1.15rem;}
  .spread a:nth-child(1){grid-column:1/9;justify-self:start;transform:none;}
  .spread a:nth-child(2){grid-column:4/13;justify-self:end;
    transform:translateY(clamp(.15rem,1vh,.6rem));}
  .spread a:nth-child(3){grid-column:2/11;justify-self:start;
    transform:translateY(clamp(.1rem,.5vh,.35rem));}
  .spread a:nth-child(4){grid-column:5/13;justify-self:end;transform:none;}
  /* …but the scatter is a HERO composition only. These rules sit later
     in the file than .spread.pinned a, and at equal specificity later
     wins, so the offsets were leaking into the pinned header and
     staggering its baselines. Two classes beats one: in the bar they
     are a line, and a line is level. */
  .spread.pinned a:nth-child(n){grid-column:auto;justify-self:auto;transform:none;}
  /* 'Our work' sat hard under the mark with no air between them */
  .navslot{margin-top:clamp(1.5rem,5vh,2.8rem);}
  .wm .b{margin-left:0;}
  .field{min-height:130svh;}
}
/* ══ THE TWO INVITATIONS ON A WORK PAGE ═══════════════════════════
   Deliberately unequal. The mid-page one is a quiet aside set in the
   small face — it interrupts the project briefly and gets out of the
   way. The closing one is the size of a statement, because by then the
   project has finished and this is the last thing on the page.

   Neither is a button. Nothing on this site is a button except the
   things that do something without leaving the page, and these two go
   somewhere — so they are links, and they look like links. ══ */
.ask-in{padding-block:clamp(2.4rem,8vh,4.5rem) 0;display:grid;gap:.9rem;
  justify-items:start;}
.ask-in .d3{max-width:36ch;color:var(--soft);}
.ask-out{padding-block:clamp(3.2rem,11vh,6rem) clamp(1rem,3vh,2rem);
  display:grid;gap:clamp(1rem,3vh,1.6rem);justify-items:start;}
.ask-out .d2{max-width:24ch;}
/* .go-on is written for the services page, where it sits in a twelve
   column grid and claims columns 6 to 12. These two blocks are single
   column grids, so that rule invented implicit columns and pushed the
   link 34px off the right edge of a 375 screen — 104px at 320. It only
   needs the colour and hover from that rule, not the placement. */
.ask-in .go-on,.ask-out .go-on{grid-column:auto;margin-top:0;}

/* The spam trap. It has to look like a field to a bot and not exist
   to anyone else: display:none keeps it off the screen, tabindex and
   aria-hidden keep it out of the keyboard order and out of a screen
   reader. A real type="hidden" input is the one thing a bot leaves
   alone, which is why this is a text input instead. */
/* !important, deliberately, and it is the right call here. .handover
   input sets display:block at (0,1,1) and beat .trap at (0,1,0), so the
   trap rendered as a visible field in the middle of the enquiry form.
   The failure mode is worse than an ugly box: a real person could fill
   it in, and a filled honeypot means the enquiry is treated as spam and
   silently discarded. This must never be overridable by a later rule in
   any context the trap is ever dropped into. */
.trap{display:none !important;}

/* ══ THE GATES — the three sectors, at the top of Services ═════════
   Three ways in, above everything. Not cards in the boxed sense: no
   border, no panel, no shadow. A hairline between them and the
   generous column gap do the separating, which is the same move the
   spec rows and the footer already make — this page has no other
   boxes on it and did not need its first three here.

   Each one is an anchor to #offer, so it works with no script; the
   script adds the cut on the way. The whole block is the target, not
   just the word, so it is a thumb-sized thing on a phone. ══ */
.gates{display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(1.4rem,3.4vw,3.2rem);
  padding-block:clamp(2.2rem,7vh,4rem) clamp(.6rem,2vh,1.2rem);}
.gate{display:grid;align-content:start;gap:.55rem;
  padding-top:1.1rem;border-top:1px solid rgba(22,33,44,.18);
  color:var(--ink);text-decoration:none;
  transition:border-color .3s var(--blade);}
/* the rule is the thing that answers, not the type: the words are the
   content and should not flicker under a pointer */
.gate:hover,.gate:focus-visible{border-top-color:var(--ink);}
/* Cobalt, not rust. Rust is the homepage doors and the project
   numbering; these three are a different kind of label and reading them
   in the same colour made them look like navigation. */
.gate-k{color:var(--cobalt);}
.gate-l{max-width:18ch;}
.gate-w{color:var(--soft);max-width:34ch;}

/* Three columns of ~110px at 375 would give each sentence a four-word
   measure, so on a phone they stack and become what they already are
   underneath: a short list of the three things the studio does. */
@media (max-width:52rem){
  .gates{grid-template-columns:1fr;gap:clamp(1.1rem,3vh,1.7rem);}
  .gate-l,.gate-w{max-width:none;}
}

/* Wherever .shot is a real photograph rather than the grey plate, it
   fills its frame and crops — it never stretches. The frames on this
   site are deliberately not the shape of a camera file, so without
   this a 4:3 photograph in a wide opening plate is squashed. Each
   image carries its own object-position so the crop keeps the part of
   the room that matters. */
img.shot{display:block;width:100%;height:100%;object-fit:cover;}

/* ══ IMAGE SLOTS — lit materials, never swatches ══ */
.shot{position:relative;overflow:hidden;isolation:isolate;display:block;
  --a:#8A7F6C; --b:#5C5342;
  background:
    linear-gradient(104deg, transparent 34%, rgba(255,255,255,.30) 43%,
                    rgba(255,255,255,.07) 49%, transparent 56%),
    radial-gradient(56% 46% at 86% 90%, var(--b) 0%, transparent 60%),
    radial-gradient(64% 54% at 22% 10%, rgba(255,255,255,.52), transparent 58%),
    radial-gradient(90% 78% at 94% 98%, rgba(0,0,0,.44), transparent 64%),
    var(--a);}
.shot::before{content:'';position:absolute;inset:0;z-index:1;
  background:radial-gradient(28% 20% at 34% 22%, rgba(255,255,255,.4), transparent 62%),
             linear-gradient(196deg, transparent 62%, rgba(0,0,0,.2) 100%);}
.shot::after{content:'';position:absolute;inset:0;z-index:2;opacity:.26;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23m)'/%3E%3C/svg%3E");}
.shot .tag{position:absolute;left:.75rem;bottom:.75rem;z-index:5;}

/* project palettes — a pairing per concept, never a site-wide accent */
.pal-oxblood{--a:#6B2020;--b:#6B7A4A;}
.pal-cobalt {--a:#1D5FD6;--b:#B08D57;}
.pal-rust   {--a:#B5502A;--b:#1A1A1A;}
.pal-dusty  {--a:#7C93A8;--b:#C1552A;}
.pal-mint   {--a:#A9C9B8;--b:#C89B6A;}
/* stated explicitly: swapping through a temp var is a dependency cycle
   and CSS discards all three, leaving the slot unpainted */
.pal-oxblood.pal-flip{--a:#6B7A4A;--b:#6B2020;}
.pal-cobalt.pal-flip {--a:#B08D57;--b:#1D5FD6;}
.pal-rust.pal-flip   {--a:#1A1A1A;--b:#B5502A;}
.pal-dusty.pal-flip  {--a:#C1552A;--b:#7C93A8;}
.pal-mint.pal-flip   {--a:#C89B6A;--b:#A9C9B8;}

/* ── AWAITING PHOTOGRAPHY ──────────────────────────────────────────
   A slot that still says "photography to come" has no business being the
   most colourful thing on the page. It stays concrete grey — lit and
   textured, but honestly a placeholder. The palettes above are a promise
   about the real imagery, not the site's colour supply; remove .awaiting
   when a photograph lands and the concept's pairing takes over.
   Declared after the palettes so it wins on source order — it ties on
   specificity with .pal-x.pal-flip. */
.shot.awaiting{--a:#A8A29A;--b:#7E7871;}
.shot.awaiting.deep{--a:#8E8880;--b:#6A655F;}

/* ══ ROOMS — a section can be dark or light, sequenced to the content ══ */
.room-dark{background:#101820;color:#E7E1D6;
  --fg:#E7E1D6;--soft:rgba(231,225,214,.62);--quiet:rgba(231,225,214,.4);
  --hair:rgba(168,162,154,.34);}
.room-light{background:var(--plaster);color:var(--ink);
  --fg:#16212C;--soft:rgba(22,33,44,.62);--quiet:rgba(22,33,44,.42);
  --hair:rgba(168,162,154,.5);}




/* ══ LET'S TALK — the standing invitation ═══════════════════════════
   Fixed, horizontally centred, low. Centred because the doors put
   CONTACT hard right, and anything living in that corner collides with
   it the moment the page is at the top. It never leaves the homepage.

   Ink, not lime: it passes over the cream AND over the lime field, and
   lime type on the lime field would simply vanish. Ink reads on both. ══ */
.talk{position:fixed;top:clamp(1.3rem,4.5vh,2.4rem);right:clamp(2.2rem,11vw,8rem);
  z-index:90;display:inline-flex;align-items:center;gap:.5rem;
  /* same reasoning as the pinned nav: it is fixed over a scrolling
     page, so it is over a photograph sooner or later. on-dark still
     flips both, so the pill inverts with the type. */
  /* A GROUND, BUT NOT A BOX. The brackets are the frame — an outline
     as well would be two frames. On the plaster this ground is
     invisible and the thing still reads as bare brackets, which is
     the design; over a photograph it becomes the panel it needs to
     be. It only shows up where it is doing work. */
  background:var(--plaster);padding:.4rem .75rem;color:var(--ink);
  transition:color .3s var(--blade);}
/* Held in brackets rather than a box. They stay still while the letters
   turn — a frame does not fidget — and they open a little on hover. */
.talk .br{font-size:clamp(1.7rem,2.1vw,2.1rem);line-height:1;
  font-variation-settings:'SOFT' 30,'WONK' 1,'opsz' 40;
  transition:transform .3s var(--blade),color .3s var(--blade);}
.talk:hover .br{color:var(--lime);}
.talk:hover .br:first-of-type{transform:translateX(-3px);}
.talk:hover .br:last-of-type{transform:translateX(3px);}
/* No background means no guaranteed contrast: ink is invisible the
   moment this passes over a dark ground, and Wanted IS a dark ground.
   So it reads what is behind it, the way the wordmark does. */
.talk.on-dark{color:var(--plaster);background:var(--ink);}

/* The letters tumble top-over-bottom on one horizontal axis running
   through their middles — threaded on a balancing pole.

   A steady trickle, not a burst. Each turn is quick — about 0.85s — and
   the schedule is continuous: one letter is tumbling somewhere in the
   word at almost any moment, and then the next, with no long pause in
   between. The delays are set per letter in the markup rather than
   stepped off an index, so the order is scattered AND the spacing is
   irregular — a metronome would read as a mechanism. */
.talk .tw{display:inline-flex;perspective:460px;
  font-size:clamp(.82rem,1.05vw,.95rem);letter-spacing:.22em;text-transform:uppercase;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;}
.talk .tw i{display:inline-block;font-style:normal;transform-style:preserve-3d;
  animation:spindle 8s cubic-bezier(.45,.05,.3,1) infinite;
  animation-delay:calc(var(--d) * 1s);}
.talk .tw i.gap{width:.42em;animation:none;}
@keyframes spindle{0%,89.4%{transform:rotateX(0deg);}100%{transform:rotateX(-360deg);}}
/* on hover the whole word turns over, quickly */
.talk:hover .tw i{animation-duration:2.6s;}

/* ══ WANTED — the notice that becomes their brief ═══════════════════
   Beige travertine, black wording, and the site's own green doing the
   marking. Framed and set off-centre: the panel is inset from the left
   and stops well short of the right edge, so a wide empty field of beige
   sits beside it rather than the block being centred in the page.

   The frame is drawn as a hairline with the label sitting astride its
   top edge, the way a legend sits on a fieldset — a slightly technical
   device, which suits a studio that works to drawings.

   Why the green is a FILL and never text or a hairline: lime on beige
   measures 1.10, which is invisible as type. As a solid block the hue
   difference still reads, and ink on lime is 9.61 — so the green works
   behind the words, never as the words. That is also why the send button
   is a lime field with black on it. ══ */
/* No background of its own. It was painting --plaster over the page,
   which is the same hex as the body ground — but the grain overlay sits
   BELOW <main>, so a section that paints its own ground covers the grain
   and one that does not lets it through. Same colour, visibly different
   surface, which is exactly what read as "slightly different". */
.wanted{position:relative;color:var(--ink);
  /* room above for the label, which hangs outside the frame's top edge */
  padding-block:clamp(3.8rem,10vh,7rem) clamp(3rem,8vh,5rem);
  --c:var(--lime);
  --fg:#16212C;--soft:rgba(22,33,44,.62);--quiet:rgba(22,33,44,.46);
  --hair:rgba(168,162,154,.5);}
.wanted .inner{position:relative;z-index:2;border:1px solid var(--ink);
  margin-left:clamp(0rem,5vw,4.5rem);
  margin-right:clamp(0rem,20vw,17rem);
  padding:clamp(2.2rem,5.5vh,3.6rem) clamp(1.3rem,3.4vw,2.8rem)
          clamp(2.8rem,7vh,4.2rem);}
/* The label sits ABOVE the frame with clear air, not astride its top
   rule. Straddling the line is as close to the box as a label can
   physically get, and it read as cramped both times it was tried —
   including once the panel had been given extra padding to breathe. Set
   free of the box entirely, aligned to the panel's left edge. */
.frame{margin:0 0 .5rem clamp(0rem,5vw,4.5rem);color:var(--ink);
  /* Bigger than a micro-label: it opens the section, so it should be
     read before the sentence rather than found afterwards. Still well
     under the sentence itself, and under what it grows into. */
  font-size:clamp(.78rem,1.15vw,.95rem);letter-spacing:.2em;
  text-transform:uppercase;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 11;}
/* and underneath it, quietly, what to do about it */
.aid{margin:0 0 clamp(1.1rem,3vh,1.9rem) clamp(0rem,5vw,4.5rem);
  color:var(--soft);letter-spacing:.14em;}
.aid[hidden]{display:none;}
.notice{font-size:clamp(1.4rem,3.2vw,2.5rem);line-height:1.42;letter-spacing:-.018em;
  margin:0;
  font-variation-settings:'SOFT' 46,'WONK' 1,'opsz' 60;}

.sw{position:relative;display:inline-block;}
/* One element, two states: a band of green sitting under the phrase at
   rest, filling the whole word on hover. The words stay black either
   way — the green is the ground behind them, never the ink. */
.pick{position:relative;z-index:0;background:none;border:0;
  padding:0 .12em;margin:0;cursor:pointer;
  font:inherit;letter-spacing:inherit;color:var(--ink);
  /* the dance is the invitation — it is how a visitor knows these words
     are theirs rather than ours. It stops once the word has been
     answered, so the page shows what is still open. */
  animation:dance 2.8s ease-in-out infinite;
  animation-delay:calc(var(--i) * .3s);
  transform-origin:50% 90%;}
.pick::before{content:'';position:absolute;inset:0 0 -.04em;z-index:-1;
  background:var(--lime);
  transform:scaleY(.14);transform-origin:50% 100%;
  transition:transform .26s var(--blade);}
.pick:hover::before{transform:scaleY(1);}
/* answered: the band drops to a quiet hairline, so what is still green
   is what is still open */
.pick.chosen{animation:none;}
.pick.chosen::before{background:var(--concrete);transform:scaleY(.05);}
.pick.chosen:hover::before{background:var(--lime);transform:scaleY(1);}
@keyframes dance{
  0%,100%{transform:translateY(0) rotate(0deg);}
  22%    {transform:translateY(-.07em) rotate(-2.4deg);}
  48%    {transform:translateY(.035em) rotate(1.8deg);}
  74%    {transform:translateY(-.045em) rotate(-1deg);}
}

.opts{position:absolute;top:calc(100% + .5rem);left:0;z-index:30;
  min-width:15rem;max-width:min(24rem,86vw);
  background:var(--ink);color:var(--plaster);padding:.5rem;
  box-shadow:0 18px 44px rgba(22,33,44,.34);}
.opts[hidden]{display:none;}
.sw.right .opts{left:auto;right:0;}
.opts button{display:block;width:100%;text-align:left;background:none;border:0;
  padding:.5rem .6rem;cursor:pointer;color:inherit;
  font-size:.82rem;line-height:1.35;letter-spacing:0;
  font-variation-settings:'SOFT' 20,'WONK' 0,'opsz' 14;
  transition:background-color .16s linear,color .16s linear;}
.opts button:hover{background:var(--lime);color:var(--ink);}
.opts .own{display:block;width:100%;margin-top:.35rem;
  background:none;border:0;border-top:1px solid rgba(231,225,214,.26);
  padding:.6rem;color:var(--plaster);font:inherit;font-size:.82rem;letter-spacing:0;
  font-variation-settings:'SOFT' 20,'WONK' 0,'opsz' 14;}
.opts .own::placeholder{color:rgba(231,225,214,.5);}
.opts .own:focus{outline:none;border-top-color:var(--lime);}

/* the hand-over: only ever after all five have been set */
.handover{margin-top:clamp(2rem,6vh,3.4rem);max-width:34rem;}
.handover[hidden]{display:none;}
.hrow{display:flex;gap:.8rem;flex-wrap:wrap;margin-top:1rem;}
.handover label{display:block;flex:1 1 12rem;}
.handover input{display:block;width:100%;margin-top:.4rem;background:none;
  border:0;border-bottom:1px solid var(--hair);padding:.55rem 0;
  color:var(--fg);font:inherit;font-size:.95rem;
  font-variation-settings:'SOFT' 20,'WONK' 0,'opsz' 14;}
.handover input:focus{outline:none;border-bottom-color:var(--ink);}
/* black on the green — 9.61, and it puts the green where it carries */
.send{margin-top:1.6rem;background:var(--lime);color:var(--ink);border:0;
  padding:.85rem 1.5rem;cursor:pointer;
  font-size:.7rem;letter-spacing:.22em;text-transform:uppercase;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;
  transition:background-color .26s var(--blade),color .26s var(--blade);}
.send:hover{background:var(--ink);color:var(--plaster);}
.thanks{margin-top:1.2rem;}
.thanks[hidden]{display:none;}

@media (prefers-reduced-motion:reduce){
  .talk .tw i,.pick{animation:none;}
}
@media (max-width:52rem){
  .talk{gap:.6rem;padding:.7rem 1.1rem;}
  .opts{position:fixed;left:1.4rem;right:1.4rem;top:auto;bottom:1rem;
    max-width:none;min-width:0;}
  .sw.right .opts{left:1.4rem;right:1.4rem;}
}
@media (prefers-reduced-motion:reduce){
  .js .cut{clip-path:inset(0);}
  .cut,.doors a::after{transition:none;}
  .js .cut.struck::after{animation:none;}
  /* nobody who asked not to receive motion is shown the throw: the light
     is already at rest, in its final colour, and the prompt is there. */
  .open .enter{animation:none;opacity:1;}
  .open .enter .sq{animation:none;}
  .open,.open .scene{transition:none;}
  *{scroll-behavior:auto;}
}

/* ══ THE CURSOR ═════════════════════════════════════════════════════
   No custom cursor. A chisel was drawn twice — solid at 32px, then
   outlined and detailed at 48px — and neither landed: depicting a
   physical object at cursor size, with no control over antialiasing,
   reads as a blob or a pin however carefully it is built. The pointer
   is also the one element on screen every second of the visit, so a
   flourish that is merely acceptable is worse than none.

   The site's character is carried by things that can actually hold it:
   the tumbling invitation, the dancing phrases, the chrome rules, the
   drifting squares. Draggable work keeps grab/grabbing, because there
   the cursor is doing a job rather than decorating. The grab/grabbing
   rules live with the squares themselves, in THE FIELD above. ══ */

/* ══ THE DRIFT ══════════════════════════════════════════════════════
   Left alone, the field does not sit perfectly still. One square at a
   time eases a short way across the lime and rings the surface as it
   goes — never all of them together, which would read as a screensaver
   rather than as something settling. Any touch of the field stops it. ══ */
.work.drift{transition:transform 3.8s cubic-bezier(.38,0,.28,1),
                       filter .3s var(--blade);}
@media (prefers-reduced-motion:reduce){
  .work.drift{transition:none;}
}

/* the frame's turn — it grows out of a small label into a statement once
   the whole sentence belongs to the visitor, so the change is something
   you see happen rather than a word quietly swapping */
/* at statement size it is no longer a label, so it stops looking like one */
.frame.turned{font-size:clamp(1.25rem,2.4vw,1.8rem);letter-spacing:.005em;
  text-transform:none;color:var(--ink);
  font-variation-settings:'SOFT' 46,'WONK' 1,'opsz' 40;
  /* No fill mode. With 'both' the element takes the keyframe 0% state
     (opacity:0) whenever the animation has not run yet — a throttled or
     background tab suspends CSS animations, and the turned frame would
     sit there invisible. Without a fill it renders at its normal style
     until the animation actually plays. Same rule as the one that bit
     the entrance reveals. */
  animation:arrive .72s var(--blade);}
@keyframes arrive{
  0%  {opacity:0;transform:translateY(.45em) scale(.86);}
  60% {opacity:1;}
  100%{opacity:1;transform:none;}
}
@media (prefers-reduced-motion:reduce){ .frame.turned{animation:none;} }

/* Declared last on purpose: the general max-width:52rem block sits above
   the WANTED rules, so an override placed in it loses on source order. */
@media (max-width:52rem){
  /* the off-centre inset is a desktop luxury — on a phone a 20vw empty
     margin left the panel 237px wide and the sentence wrapped to bits */
  .wanted .inner{margin-right:clamp(0rem,6vw,3rem);}
}

/* のみ keeps its place beside the wordmark and then recurs in lime down
   the page — the statement, the corner of the Wanted frame, the footer.
   Never on the lime field itself, where lime on lime is nothing. */
.wanted .seal{position:absolute;right:clamp(1rem,2.6vw,2rem);
  bottom:clamp(.9rem,2.4vh,1.6rem);z-index:3;}

/* ══ THE PROJECT PAGE ═══════════════════════════════════════════════
   Composed the way the reference pages are: almost nothing is centred
   and almost nothing starts at the left margin. A wide empty left
   column runs the length of the page and the content hangs off it, so
   the page reads as a sheet with a margin rather than as a column of
   blocks. Plates leave the frame on the right instead of sitting
   politely inside the gutter.

   Everything here is a slot. The images are the same placeholder
   rectangles the homepage uses — no cut geometry dressing up a hole
   where a photograph will go.

   Chrome, type and ink are the homepage's. Only the arrangement is new.
   ══ */

/* the opening band — wordmark and the four doors, which then pin out of
   here exactly as they pin out of the hero on the homepage */
.band{padding-block:clamp(1.1rem,3vh,1.7rem) clamp(1.5rem,4vh,2.4rem);
  display:grid;gap:clamp(1rem,2.6vh,1.5rem);}
.band .mark{display:inline-flex;align-items:baseline;gap:.5rem;width:max-content;
  padding-block:.3rem;margin-block:-.3rem;
  font-size:clamp(1rem,1.5vw,1.25rem);letter-spacing:-.03em;color:var(--ink);
  font-variation-settings:'SOFT' 54,'WONK' 1,'opsz' 48;}
.band .mark .nomi{color:var(--lime);}

/* 1 · the opening plate. Inset hard on the left and off the page on the
   right: an image centred in its gutter reads as a slide, one that
   leaves the frame reads as something you are standing in front of. */
/* margin-inline:0 first — a <figure> carries a 40px default inline
   margin, which was holding the plate 40px off the right edge it is
   supposed to run off. */
.plate-open{margin:0 0 0 clamp(1.4rem,26vw,25rem);}
/* Height from the VIEWPORT, not from an aspect ratio. A ratio ties the
   plate's height to its width, so the wider the screen the taller it
   got — on a large monitor it took the whole first screen on its own
   and pushed the project's name below the fold. The first screen has a
   job: the name, the paragraph and the three facts, all readable
   without scrolling. The plate gets what is left. */
/* Raised from 36vh once a real photograph went in. At 36vh the frame
   was 932x259 on a laptop — a 3.6:1 slot holding a 4:3 file, which
   cropped away about seventy per cent of the room and showed mostly
   ceiling. 46vh gives roughly 2.4:1: still a wide, deliberate band
   rather than a photograph printed whole, but enough of the room to be
   worth looking at. The cap keeps the name and the three facts on the
   first screen, which is what this clamp has always been protecting. */
.plate-open .shot{aspect-ratio:auto;height:clamp(13rem,46vh,30rem);}

/* 2 · the title row — place, name, and the one paragraph that says what
   this is. Three unequal columns, none of them starting at the margin. */
.pname{display:grid;grid-template-columns:repeat(12,1fr);
  gap:clamp(1rem,2.4vw,2rem);align-items:start;
  padding-block:clamp(2.2rem,6vh,3.6rem) clamp(1.2rem,3vh,2rem);}
.pname .where{grid-column:2/4;justify-self:end;text-align:right;
  color:var(--soft);line-height:2.1;}
.pname .title{grid-column:4/9;}
.pname .blurb{grid-column:9/13;}
.ptitle{margin:0;text-transform:uppercase;line-height:.9;letter-spacing:-.028em;
  font-size:clamp(2.1rem,5.6vw,4.6rem);
  font-variation-settings:'SOFT' 62,'WONK' 1,'opsz' 144;}
.pname .ritual{margin-top:1.1rem;color:var(--soft);max-width:26ch;}
/* A project can open with more than one paragraph — the break is the
   client's and is kept. Only the ones after the first take a margin, so
   a single-paragraph page is unchanged. */
.blurb + .blurb{margin-top:.85em;}
.blurb{margin:0;max-width:30ch;line-height:1.62;color:var(--ink);
  font-size:clamp(.98rem,1.3vw,1.15rem);
  font-variation-settings:'SOFT' 34,'WONK' 0,'opsz' 24;}

/* 3 · the spec line. Three facts spread the full width, label over
   value, the last one ranged right off the far edge. */
.spec{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1rem,2.4vw,2rem);
  padding-block:clamp(1.2rem,3.4vh,2.2rem) clamp(2.5rem,8vh,4.5rem);}
.spec div{display:flex;flex-direction:column;gap:.55rem;}
.spec div:nth-child(1){grid-column:4/7;}
.spec div:nth-child(2){grid-column:7/11;}
.spec div:nth-child(3){grid-column:11/13;align-items:flex-end;text-align:right;}
.spec dd{margin:0;}

/* 4 · a big plate and a small one beside it, tops aligned. The size gap
   is the point: they are not two of a kind. */
/* ── AS FOUND ────────────────────────────────────────────────────────
   A contact sheet, not a gallery. These are estate-agent record shots
   and the whole point is that they stay small: evidence under the
   paragraph, at the size evidence deserves, never competing with the
   work above and below them.

   Four across, two on a phone. The label sits on the same baseline
   grid as the spec strip above it so the two read as one family of
   annotation rather than two separate devices. */
   ★ THE ROW IS .found-row, NOT .strip. It was .strip for about ten
   minutes, and .strip is already the fixed top navigation bar — so the
   four photographs inherited position:fixed and flew to the top of the
   viewport, above the spec line and detached from their own label,
   while .asfound collapsed to the height of the label alone. One
   stylesheet serves the whole site, so a new component with a generic
   name is a collision waiting to happen: grep the name first. */
.asfound{margin-block:clamp(2.6rem,7vh,4.5rem);}
.asfound .lede{color:var(--c);margin:0 0 .9rem;}
.asfound .found-row{display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(.5rem,1.2vw,1rem);}
.asfound figure{margin:0;}
.asfound .shot{aspect-ratio:3/2;width:100%;}
@media (max-width:46rem){
  .asfound .found-row{grid-template-columns:repeat(2,1fr);}
}

.duo{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1rem,2.4vw,2rem);
  align-items:start;}
.duo figure{margin:0;}
.duo .big{grid-column:4/10;}
.duo .big .shot{aspect-ratio:16/11;}
.duo .small{grid-column:10/13;}
.duo .small .shot{aspect-ratio:16/10;}
.duo figcaption{margin-top:.6rem;}

/* 5 · the run — the facts hold still in the margin while the plates go
   past them. The sheet stays readable for the whole sequence instead of
   being something you scrolled away from in the first screen. */
.run{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.4rem,3vw,2.6rem);
  align-items:start;}
.run .sheet{grid-column:1/4;position:sticky;
  /* clear of the pinned doors, which sit about 54px down */
  top:clamp(5rem,13vh,7rem);
  display:grid;gap:clamp(.9rem,2.4vh,1.4rem);}
.run .sheet div{display:flex;flex-direction:column;gap:.35rem;}
.run .sheet dd{margin:0;}
.run .plates{grid-column:4/13;display:grid;gap:clamp(1.4rem,4vh,2.6rem);}
.run .plates figure{margin:0;}
.run .plates figcaption{margin-top:.6rem;}
/* every third plate pulls back off the right edge, so the run has a
   rhythm rather than being a stack of identical rectangles */
.run .plates figure:nth-child(3n){margin-right:clamp(0rem,14vw,12rem);}

.next{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  flex-wrap:wrap;border-top:1px solid var(--hair);padding-top:1.2rem;
  margin-top:clamp(2.5rem,7vh,4.5rem);}
.next a .d3{transition:transform .34s var(--blade);display:inline-block;}
.next a:hover .d3{transform:translateX(10px);}

/* On a phone the wide margin is just lost width. Everything comes back
   to the gutter, the sheet stops sticking and becomes an ordinary block
   above the plates, and nothing bleeds off the right. */
@media (max-width:52rem){
  .plate-open{margin-left:0;}
  /* same reasoning on a phone, where the plate is full width and a
     26vh band was a 375x211 letterbox */
  .plate-open .shot{aspect-ratio:auto;height:clamp(11rem,34vh,19rem);}
  .pname,.spec,.duo,.run{display:block;}
  .pname{padding-block:clamp(2rem,7vh,3.5rem) clamp(1.2rem,4vh,2rem);}
  .pname .where{text-align:left;line-height:1.9;margin-bottom:.9rem;}
  .pname .title{margin-bottom:1.4rem;}
  .blurb{max-width:none;}
  .spec div{margin-bottom:1.4rem;}
  .spec div:nth-child(3){align-items:flex-start;text-align:left;}
  .duo figure + figure{margin-top:1.4rem;}
  .duo .small .shot{aspect-ratio:16/11;}
  .run .sheet{position:static;margin-bottom:2.2rem;
    grid-template-columns:1fr 1fr;display:grid;gap:1.1rem;}
  .run .plates figure:nth-child(3n){margin-right:0;}
}

/* On a project page the doors are a header, not the composition. The
   scatter earns its space on the homepage, where it IS the hero; here it
   would take a third of the first screen before the project has said
   anything. Same element, same pin behaviour — set in a line. */
.band .spread{display:flex;flex-wrap:wrap;align-items:baseline;
  gap:clamp(.9rem,2.4vw,2.2rem);row-gap:.4rem;grid-template-columns:none;}
.band .spread a{grid-column:auto;transform:none;}

/* ══ THE STUDIO ═════════════════════════════════════════════════════
   A full-bleed band of three columns: portraits flush to the screen
   edges on either side, the studio's own words down the middle. No
   gutters. The structure reads from the tone difference between the
   plates and the plaster, so it needs no rules, no cards and no gaps.

   Taken from the reference: the arrangement. Not taken: its black
   ground, and its column of awards, rankings and institutional posts —
   Nomi has none of those to claim, so the middle column carries what
   the studio actually thinks instead.

   Hovering or pressing a portrait brings up that person's biography.
   Everything is in the HTML: the reveal is an enhancement, never the
   only way to read it. ══ */
.crew{display:grid;grid-template-columns:1fr 1.16fr 1fr;align-items:start;}
/* Each plate is SQUARE, and the column is as tall as its plates make
   it. They used to divide the height of the text column between them,
   which gave six wide letterbox slots — fine for a grey rectangle and
   wrong for a face. A portrait wants roughly a square.

   The columns are therefore taller than the words beside them, which is
   what the reference does too: the text finishes and the plates carry
   on down the page. */
.crew .stack{display:grid;grid-auto-rows:auto;align-content:start;}
.face,.plate-in{aspect-ratio:1;}

/* The plates run a long way past the words, so the middle column is
   held at BOTH ends: the text at the top, the geography line at the
   foot, and quiet plaster between them. Sticky was the first attempt
   and it is wrong here — a column of text taller than the screen
   cannot stick without cutting off its own last paragraphs. */
.saying{padding:clamp(1.8rem,4vw,3.4rem) clamp(1.6rem,3.4vw,3rem);
  align-self:stretch;display:flex;flex-direction:column;
  justify-content:space-between;gap:2rem;}
.saying .words{display:grid;gap:1.1em;}
.saying .p{max-width:42ch;}

.face{position:relative;overflow:hidden;}
/* the portrait itself is the control: a real button, so a press works on
   a phone, a trackpad and a keyboard, not only a mouse hover */
.faceon{display:block;position:relative;width:100%;height:100%;
  padding:0;border:0;background:none;cursor:pointer;text-align:left;}
/* the portrait fills the square. object-position is set per person in
   the markup rather than once here, because a blind centre crop puts
   faces in odd places — the frames in the set range from 0.80 to 1.04
   and each one wants its own answer. */
.faceon .shot{display:block;width:100%;height:100%;object-fit:cover;
  background:var(--concrete);}
/* the CSP allows img-src from self only, so nothing here can be
   swapped for a remote image by accident */
.faceon .who{position:absolute;left:.85rem;bottom:.85rem;z-index:5;
  background:var(--ink);color:var(--plaster);padding:.34rem .6rem;
  display:inline-flex;gap:.5rem;align-items:baseline;max-width:calc(100% - 1.7rem);}
.faceon .who b{font-weight:inherit;}
.faceon .who i{font-style:normal;color:rgba(231,225,214,.62);}

/* the biography, over the plate. It arrives on the site's one motion —
   the cut — rather than a fade, and it is dark so the swap registers as
   a change of state and not as a caption appearing. */
.bio{position:absolute;inset:0;z-index:6;background:#101820;color:#E7E1D6;
  --fg:#E7E1D6;--soft:rgba(231,225,214,.66);--quiet:rgba(231,225,214,.44);
  padding:clamp(1.1rem,2.2vw,1.8rem);
  display:grid;align-content:end;gap:.45rem;pointer-events:none;
  clip-path:inset(0 100% 0 0);transition:clip-path .4s var(--blade);}
.bio .nm{color:var(--lime);}
.bio .p{color:var(--soft);max-width:36ch;}
.face:hover .bio,
.face:focus-within .bio,
.face.told .bio{clip-path:inset(0);}
.face:hover .who,
.face:focus-within .who,
.face.told .who{opacity:0;}
.faceon .who{transition:opacity .22s var(--blade);}

/* ── no hover: the biography is something you go and get ──
   This used to print the biography underneath the portrait, which
   made the band a column of photograph-then-paragraph: a staff list,
   not a studio. Tapping the portrait reveals it over the plate
   instead, on the site's one motion, exactly as hovering does on a
   desktop. The .told handler already existed for keyboard users — all
   that was needed was to stop the CSS undoing it.

   :hover is unreliable on touch — it sticks after a tap — so it is
   explicitly neutralised here and .told is left as the only switch. */
@media (hover:none){
  .face:hover .bio{clip-path:inset(0 100% 0 0);}
  .face:hover .who{opacity:1;}
  .face.told .bio{clip-path:inset(0);}
  .face.told .who{opacity:0;}
  /* It has a whole square to live in, so it can be set smaller and
     still read comfortably. The longest biography on the page is ~340
     characters, which lands well inside a full-width plate. */
  .bio{padding:1rem;gap:.35rem;}
  .bio .p{font-size:.78rem;line-height:1.52;max-width:none;}
}

@media (max-width:52rem){
  .face{min-height:0;}
}

/* ══ THE STUDIO BAND ON A PHONE ═══════════════════════════════════
   Not one column, and not the desktop's three. Three would give the
   copy a ~100px measure at 375 — fourteen characters a line — so the
   arrangement holds down to 46rem (a tablet keeps it, with a ~34
   character measure) and below that becomes two.

   The two authored columns dissolve with display:contents so every
   plate flows into a single two-up grid rather than two independent
   stacks that drift out of step. The copy spans the full width and
   comes first: a grid of faces before a word of explanation is a
   staff list, not a studio.

   Tapping a face is the whole mechanic here. A 160px square cannot
   hold a 340-character biography at any readable size, so the tapped
   plate takes the full width and drops its square — small portrait,
   tap, it opens. That IS the interaction, not a fallback for it. ══ */
@media (max-width:46rem){
  .crew{grid-template-columns:1fr 1fr;}
  .crew .stack{display:contents;}
  /* --o is set per item by the generator: the flattened DOM order puts
     the people down one side, this puts them back in step. */
  .crew .face,.crew .plate-in{order:var(--o,0);}
  .saying{grid-column:1/-1;order:-1;display:block;
    padding:clamp(1.4rem,5vw,2.2rem) var(--gut);}
  .saying .p{max-width:none;}
  .saying .words{display:grid;gap:1.1em;margin-bottom:1.4rem;}
  .plate-in{align-self:start;}
  /* the opened plate */
  .face.told{grid-column:1/-1;aspect-ratio:auto;min-height:62vw;}
  .bio{padding:1rem;gap:.35rem;}
  .bio .p{font-size:.82rem;line-height:1.55;max-width:none;}
  /* the opened panel reads from the top — align-content:end is for a
     caption sitting on a photograph, not for a paragraph in a panel */
  .face.told .bio{align-content:start;}
}

/* The role on a closed plate, and only where it will not fit.
   On a 187px plate at 375 it wrapped to four broken lines (CREATIVE /
   DIRECTO / R CO- / FOUNDER) and shrinking it would have taken it under
   10px, so the plate carries the name alone and the role is the first
   thing you see when it opens. But the two-up grid runs all the way to
   46rem, where a plate is ~365px and has room to spare — hiding it
   there was the rule overreaching. 34rem is where a plate drops below
   ~270px and the wrap starts. */
@media (max-width:34rem){
  .faceon .who i{display:none;}
}

/* a plate between the people — an image, or a piece of film once there
   is one. Inert: no name strip, no biography, nothing to press. */
.plate-in{position:relative;overflow:hidden;margin:0;}
.plate-in .shot{display:block;width:100%;height:100%;}
/* A film in a plate. Square like every other plate, so object-fit does
   the cropping — the source is 4:3 and nothing needs re-encoding to sit
   in a 1:1 hole. Focal point slightly above centre, which is where the
   subject of a wide shot usually is.

   Plain black underneath rather than the grey placeholder gradient, so
   the first frame does not arrive over a texture that is about to be
   replaced. */
.plate-in .reel{display:block;width:100%;height:100%;
  object-fit:cover;object-position:50% 42%;background:#101820;}
@media (prefers-reduced-motion:reduce){
  /* a looping film is exactly the kind of movement somebody turns this
     on to avoid. It holds on its first frame instead. */
  .plate-in .reel{animation:none;}
}

/* the section label above the brief on the homepage. The standalone
   page has a <h1> doing this job; here it is a heading for a section
   in a longer page, so it is the same micro device one size up. */
.start-label{margin:0 0 clamp(1rem,3vh,1.6rem) clamp(0rem,5vw,4.5rem);
  color:var(--ink);font-size:clamp(.82rem,1.25vw,1.02rem);letter-spacing:.2em;}
.wanted{padding-block:clamp(2.5rem,7vh,4.5rem) clamp(3rem,8vh,5rem);}

/* ══ CONTACT ════════════════════════════════════════════════════════
   The plain route. Stacked, ranged left, nothing boxed: a label over a
   rule, the way a specification sheet is filled in rather than the way
   a web form usually looks. The aside beside it points at the Wanted
   sentence, which is the better enquiry — this page exists for the
   people who would simply rather type into a form. ══ */
.reach{display:grid;grid-template-columns:repeat(12,1fr);
  gap:clamp(1.6rem,4vw,3.5rem);align-items:start;}
.ask{grid-column:2/8;display:grid;gap:clamp(1.4rem,3.4vh,2.2rem);}
.aside{grid-column:9/13;display:grid;gap:.5rem;align-content:start;}
.aside .p{max-width:34ch;}
.aside a{color:var(--ink);}
.aside a:hover{color:var(--lime-ink,var(--rust));}

.fld{margin:0;display:grid;gap:.5rem;}
.fld label{color:var(--soft);}
/* no boxes: a hairline is the field, and it takes the section's colour
   the moment you are in it */
.ask input,.ask textarea{
  font:inherit;font-size:clamp(1rem,1.5vw,1.25rem);line-height:1.5;
  font-variation-settings:'SOFT' 34,'WONK' 0,'opsz' 24;
  color:var(--ink);background:none;border:0;border-bottom:1px solid var(--hair);
  padding:.4rem 0;width:100%;border-radius:0;
  transition:border-color .24s var(--blade);}
.ask textarea{resize:vertical;min-height:6.5em;}
.ask input::placeholder,.ask textarea::placeholder{color:var(--quiet);}
.ask input:focus,.ask textarea:focus{outline:none;border-bottom-color:var(--lime);border-bottom-width:2px;}
/* and a real focus ring for anyone arriving by keyboard */
.ask input:focus-visible,.ask textarea:focus-visible{outline:2px solid var(--lime);outline-offset:3px;}

.go{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin-top:.6rem;}
.ask .send{background:var(--lime);color:var(--ink);border:0;cursor:pointer;
  padding:.85rem 1.7rem;font:inherit;font-size:.68rem;letter-spacing:.24em;
  text-transform:uppercase;font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;
  transition:background-color .24s var(--blade);}
.ask .send:hover{background:var(--ink);color:var(--plaster);}
.sent{color:var(--ink);}
.sent[hidden]{display:none;}

@media (max-width:52rem){
  .reach{display:block;}
  .aside{margin-top:clamp(2.4rem,7vh,4rem);}
  .aside .p{max-width:none;}
}

/* ══ SERVICES — SUBTRACTION ═════════════════════════════════════════
   The page opens with everything the studio does, in the order a
   project runs. Say what the building is and the lines that do not
   apply are cut out with the same hard directional wipe everything else
   on this site arrives on, and the list closes up.

   This is the one page where the studio's own sentence — take away what
   isn't it — is the interface rather than a caption on it.

   The chosen word carries a lime FIELD behind it, never lime type: the
   same rule as the Wanted sentence and for the same reason.

   Nothing is hidden by default. The .js class, set during parse, is
   what permits anything to be taken away, so a visitor with no script
   reads the full offer. ══ */
.offer{padding-block:clamp(.5rem,2vh,1.4rem) clamp(2.5rem,7vh,4.5rem);
  display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.4rem,4vw,3rem);
  align-items:start;}

/* the control, in the margin, holding still while the list is cut */
.cutline{grid-column:1/5;display:grid;gap:.9rem;align-content:start;
  position:sticky;top:clamp(5rem,13vh,7rem);}
.picks{display:flex;flex-direction:column;align-items:flex-start;gap:.35rem;}
.mark-word{font:inherit;position:relative;z-index:0;background:none;border:0;
  padding:.08em .3em;margin-left:-.3em;cursor:pointer;color:var(--ink);
  font-size:clamp(1.3rem,2.3vw,1.95rem);line-height:1.12;letter-spacing:-.022em;
  font-variation-settings:'SOFT' 46,'WONK' 1,'opsz' 48;text-align:left;}
/* a band of green under the word at rest, the whole word once it is the
   one you have chosen */
.mark-word::before{content:'';position:absolute;inset:0 0 .06em;z-index:-1;
  background:var(--lime);transform:scaleY(.12);transform-origin:50% 100%;
  opacity:0;transition:transform .3s var(--blade),opacity .3s var(--blade);}
.mark-word:hover::before{opacity:1;transform:scaleY(.2);}
.mark-word[aria-pressed="true"]::before{opacity:1;transform:scaleY(1);}
.mark-word:focus-visible{outline:2px solid var(--lime);outline-offset:3px;}
/* 'Everything' is the way back, so it is quieter than the three choices */
.mark-word.all{font-size:clamp(.95rem,1.4vw,1.15rem);color:var(--soft);
  font-variation-settings:'SOFT' 30,'WONK' 0,'opsz' 20;margin-top:.5rem;}
.whofor{max-width:30ch;min-height:3em;color:var(--quiet);}

/* the offer itself */
/* block + margin, not grid + gap. A grid gap is still drawn either side
   of a row that has been cut to nothing, so removing nine lines left
   nine gaps of dead air behind them. A margin belongs to the row and
   goes when the row goes. */
.offer-list{grid-column:6/12;list-style:none;margin:0;padding:0;}
.offer-list li + li{margin-top:.5em;}
.offer-list li{font-size:clamp(1.02rem,1.5vw,1.3rem);line-height:1.42;
  font-variation-settings:'SOFT' 40,'WONK' 1,'opsz' 32;color:var(--ink);
  display:flex;gap:.9rem;align-items:baseline;
  clip-path:inset(0);transition:clip-path .38s var(--blade),opacity .3s var(--blade);}
/* .6em of the row label is 9.8px — same floor as the door numerals */
.offer-list li .n{color:var(--quiet);font-size:max(.6em,.66rem);letter-spacing:.14em;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;flex:none;}
/* cut away — the site's one motion, used here to mean removal */
.js .offer-list li.gone{clip-path:inset(0 0 0 100%);opacity:0;
  font-size:0;line-height:0;margin:0;}
.tally{grid-column:6/12;margin:clamp(1.4rem,4vh,2.2rem) 0 0;color:var(--quiet);}

.go-on{grid-column:6/12;margin:clamp(1.2rem,3vh,2rem) 0 0;}
.go-on a{color:var(--ink);}
.go-on a:hover{color:var(--rust);}

@media (max-width:52rem){
  .offer{display:block;}
  .cutline{position:static;margin-bottom:2rem;}
  .picks{flex-direction:row;flex-wrap:wrap;gap:.2rem 1rem;}
  .whofor{max-width:none;min-height:0;}
  .tally,.go-on{max-width:none;}
}

/* ══ CONTACT ════════════════════════════════════════════════════════
   The plain route. Stacked, ranged left, nothing boxed: a label over a
   rule, the way a specification sheet is filled in rather than the way
   a web form usually looks. The aside beside it points at the Wanted
   sentence, which is the better enquiry — this page exists for the
   people who would simply rather type into a form. ══ */
.reach{display:grid;grid-template-columns:repeat(12,1fr);
  gap:clamp(1.6rem,4vw,3.5rem);align-items:start;}
.ask{grid-column:2/8;display:grid;gap:clamp(1.4rem,3.4vh,2.2rem);}
.aside{grid-column:9/13;display:grid;gap:.5rem;align-content:start;}
.aside .p{max-width:34ch;}
.aside a{color:var(--ink);}
.aside a:hover{color:var(--lime-ink,var(--rust));}

.fld{margin:0;display:grid;gap:.5rem;}
.fld label{color:var(--soft);}
/* no boxes: a hairline is the field, and it takes the section's colour
   the moment you are in it */
.ask input,.ask textarea{
  font:inherit;font-size:clamp(1rem,1.5vw,1.25rem);line-height:1.5;
  font-variation-settings:'SOFT' 34,'WONK' 0,'opsz' 24;
  color:var(--ink);background:none;border:0;border-bottom:1px solid var(--hair);
  padding:.4rem 0;width:100%;border-radius:0;
  transition:border-color .24s var(--blade);}
.ask textarea{resize:vertical;min-height:6.5em;}
.ask input::placeholder,.ask textarea::placeholder{color:var(--quiet);}
.ask input:focus,.ask textarea:focus{outline:none;border-bottom-color:var(--lime);border-bottom-width:2px;}
/* and a real focus ring for anyone arriving by keyboard */
.ask input:focus-visible,.ask textarea:focus-visible{outline:2px solid var(--lime);outline-offset:3px;}

.go{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin-top:.6rem;}
.ask .send{background:var(--lime);color:var(--ink);border:0;cursor:pointer;
  padding:.85rem 1.7rem;font:inherit;font-size:.68rem;letter-spacing:.24em;
  text-transform:uppercase;font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;
  transition:background-color .24s var(--blade);}
.ask .send:hover{background:var(--ink);color:var(--plaster);}
.sent{color:var(--ink);}
.sent[hidden]{display:none;}

@media (max-width:52rem){
  .reach{display:block;}
  .aside{margin-top:clamp(2.4rem,7vh,4rem);}
  .aside .p{max-width:none;}
}

/* ══ SERVICES — SUBTRACTION ═════════════════════════════════════════
   The page opens with everything the studio does, in the order a
   project runs. Say what the building is and the lines that do not
   apply are cut out with the same hard directional wipe everything else
   on this site arrives on, and the list closes up.

   This is the one page where the studio's own sentence — take away what
   isn't it — is the interface rather than a caption on it.

   The chosen word carries a lime FIELD behind it, never lime type: the
   same rule as the Wanted sentence and for the same reason.

   Nothing is hidden by default. The .js class, set during parse, is
   what permits anything to be taken away, so a visitor with no script
   reads the full offer. ══ */
.offer{padding-block:clamp(.5rem,2vh,1.4rem) clamp(2.5rem,7vh,4.5rem);
  display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.4rem,4vw,3rem);
  align-items:start;}

/* the control, in the margin, holding still while the list is cut */
.cutline{grid-column:1/5;display:grid;gap:.9rem;align-content:start;
  position:sticky;top:clamp(5rem,13vh,7rem);}
.picks{display:flex;flex-direction:column;align-items:flex-start;gap:.35rem;}
.mark-word{font:inherit;position:relative;z-index:0;background:none;border:0;
  padding:.08em .3em;margin-left:-.3em;cursor:pointer;color:var(--ink);
  font-size:clamp(1.3rem,2.3vw,1.95rem);line-height:1.12;letter-spacing:-.022em;
  font-variation-settings:'SOFT' 46,'WONK' 1,'opsz' 48;text-align:left;}
/* a band of green under the word at rest, the whole word once it is the
   one you have chosen */
.mark-word::before{content:'';position:absolute;inset:0 0 .06em;z-index:-1;
  background:var(--lime);transform:scaleY(.12);transform-origin:50% 100%;
  opacity:0;transition:transform .3s var(--blade),opacity .3s var(--blade);}
.mark-word:hover::before{opacity:1;transform:scaleY(.2);}
.mark-word[aria-pressed="true"]::before{opacity:1;transform:scaleY(1);}
.mark-word:focus-visible{outline:2px solid var(--lime);outline-offset:3px;}
/* 'Everything' is the way back, so it is quieter than the three choices */
.mark-word.all{font-size:clamp(.95rem,1.4vw,1.15rem);color:var(--soft);
  font-variation-settings:'SOFT' 30,'WONK' 0,'opsz' 20;margin-top:.5rem;}
.whofor{max-width:30ch;min-height:3em;color:var(--quiet);}

/* the offer itself */
/* block + margin, not grid + gap. A grid gap is still drawn either side
   of a row that has been cut to nothing, so removing nine lines left
   nine gaps of dead air behind them. A margin belongs to the row and
   goes when the row goes. */
.offer-list{grid-column:6/12;list-style:none;margin:0;padding:0;}
.offer-list li + li{margin-top:.5em;}
.offer-list li{font-size:clamp(1.02rem,1.5vw,1.3rem);line-height:1.42;
  font-variation-settings:'SOFT' 40,'WONK' 1,'opsz' 32;color:var(--ink);
  display:flex;gap:.9rem;align-items:baseline;
  clip-path:inset(0);transition:clip-path .38s var(--blade),opacity .3s var(--blade);}
/* .6em of the row label is 9.8px — same floor as the door numerals */
.offer-list li .n{color:var(--quiet);font-size:max(.6em,.66rem);letter-spacing:.14em;
  font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;flex:none;}
/* cut away — the site's one motion, used here to mean removal */
.js .offer-list li.gone{clip-path:inset(0 0 0 100%);opacity:0;
  font-size:0;line-height:0;margin:0;}
.tally{grid-column:6/12;margin:clamp(1.4rem,4vh,2.2rem) 0 0;color:var(--quiet);}

.go-on{grid-column:6/12;margin:clamp(1.2rem,3vh,2rem) 0 0;}
.go-on a{color:var(--ink);}
.go-on a:hover{color:var(--rust);}

@media (max-width:52rem){
  .offer{display:block;}
  .cutline{position:static;margin-bottom:2rem;}
  .picks{flex-direction:row;flex-wrap:wrap;gap:.2rem 1rem;}
  .whofor{max-width:none;min-height:0;}
  .tally,.go-on{max-width:none;}
}

/* ══ THE PROMPT ═════════════════════════════════════════════════════
   What is left on the homepage now the mechanic lives at its own URL.
   The shortest honest version: what it is, one worked example, the way
   in. Not a teaser — a real sentence, with the five changeable words
   marked exactly as they are marked on the page it leads to, so the
   thing is legible before you click it.

   It borrows .frame and .aid from that page on purpose. Two labels that
   do the same job should not look like two different devices. ══ */
/* No background of its own — it would paint the page ground over the
   grain, which sits below <main>. Same rule as .wanted and .scatter:
   only a section that genuinely CHANGES colour declares one. */
/* the utility pages — a policy is a document, so it is set as one:
   one column, generous measure, nothing decorative */
.legal{max-width:62ch;padding-bottom:clamp(2rem,6vh,4rem);}
.legal h2{margin:clamp(1.8rem,5vh,2.6rem) 0 .5rem;color:var(--soft);}
.legal h2:first-child{margin-top:0;}
.legal .p{max-width:none;}
.foot a{color:inherit;display:inline-block;padding-block:.4rem;margin-block:-.4rem;}
.foot a:hover{color:var(--ink);}

/* ── the consent bar ──
   Bottom of the screen, plain, and the two answers are the same size as
   each other. A banner where "no" is a link in the small print and
   "yes" is a button is not consent, it is a funnel. Only ever appears
   when there is a measurement ID set — see consent() in the script. */
.consent{position:fixed;left:0;right:0;bottom:0;z-index:120;
  background:var(--ink);color:var(--plaster);
  display:flex;align-items:center;justify-content:space-between;
  gap:clamp(1rem,3vw,2.5rem);flex-wrap:wrap;
  padding:clamp(.9rem,2.4vh,1.3rem) var(--gut);}
.consent p{margin:0;max-width:62ch;color:rgba(231,225,214,.82);}
.consent a{color:var(--lime);}
.consent-go{display:flex;gap:.6rem;flex:none;}
.consent button{font:inherit;font-size:.68rem;letter-spacing:.24em;
  text-transform:uppercase;font-variation-settings:'SOFT' 16,'WONK' 0,'opsz' 9;
  padding:.72rem 1.3rem;cursor:pointer;border:1px solid var(--plaster);
  background:none;color:var(--plaster);
  transition:background-color .2s var(--blade),color .2s var(--blade);}
.consent button.yes{background:var(--lime);border-color:var(--lime);color:var(--ink);}
.consent button:hover{background:var(--plaster);color:var(--ink);}
.consent button.yes:hover{background:var(--plaster);}
.consent button:focus-visible{outline:2px solid var(--lime);outline-offset:3px;}
@media (max-width:52rem){ .consent{flex-direction:column;align-items:flex-start;} }

/* ══ TARGET SIZE ════════════════════════════════════════════════════
   WCAG 2.2 asks for 24×24 CSS pixels. Most of this site's links are a
   single line of small caps, which lands at 19–23px tall — close enough
   to look fine on a mouse and not close enough for a thumb.

   Padding makes the target, a negative margin hands the space back, so
   every one of these stays exactly where it was on the page. Measured
   at eight widths from 375 to 1900 after the change: nothing moved.

   Kept as one rule rather than scattered through the file so the next
   new link can be added to the list in one place. ══ */
.next a,
.go-on a,
.aside a,
.legal a,
.foot a,
.talk,
.mark-word,
.pills .pill,
.open .enter{
  padding-block:.42rem;
  margin-block:-.42rem;
}
/* inline-block so the padding is actually honoured on an inline link */
.next a,.go-on a,.aside a,.legal a,.foot a{display:inline-block;}
/* the fixed invitation is its own flex row, so it takes the padding
   without needing the display change */
.talk{padding-inline:.3rem;margin-inline:-.3rem;}

/* ── the invitation, on a small screen ──
   Enlarging it put it into the header: at 375 and 414 it sat on top of
   the doors, and at 768 it cleared them by 15px, which is not clearing
   them. A fixed top-right control and a pinned top-left navigation are
   two things fighting for the same corner on a phone.

   So it moves to the bottom right, where a thumb actually reaches and
   where nothing else lives. It ends up more prominent rather than less,
   which was the point of making it bigger. */
@media (max-width:52rem){
  .talk{top:auto;bottom:clamp(1rem,3vh,1.7rem);right:var(--gut);
    /* It is fixed over a scrolling page, so sooner or later it is over
       body copy, and brackets with no ground are unreadable there.
       on-dark already flips the type for the dark sections; the ground
       has to follow it or the pill goes cream on ink. */
    background:var(--plaster);padding:.5rem .8rem;margin-inline:0;}
  .talk.on-dark{background:var(--ink);}
}

/* ── the range ──
   What the studio will actually take on, and how far. It used to be the
   last line on the page, in soft grey, after the list — a footnote.

   Studio deliberately carries no practical information now, which makes
   this the only place on the site that states the concept-to-
   installation range. The most important commercial fact about the
   studio cannot also be the quietest line on the page.

   It sits above the list instead, where it explains what the list is:
   the order a project runs in. Full-strength ink, not --soft. */
.range{grid-column:6/12;margin:0 0 clamp(1.6rem,4.5vh,2.6rem);max-width:46ch;
  font-size:clamp(.98rem,1.3vw,1.12rem);line-height:1.62;color:var(--ink);
  font-variation-settings:'SOFT' 34,'WONK' 0,'opsz' 24;}
@media (max-width:52rem){ .range{max-width:none;} }

/* ══ THE FOOTER ═════════════════════════════════════════════════════
   The only place on the site that has to work for somebody who reached
   the bottom of a page without finding what they came for. So it
   carries the whole navigation rather than a wordmark and a slogan, and
   it is where Privacy and Cookies live so nothing above has to make
   room for them.

   Four groups on a hairline, ranged left, unequal widths — the same
   asymmetry as everything else rather than four tidy columns. ══ */
.foot{border-top:1px solid var(--hair);
  margin-top:clamp(3rem,9vh,6rem);
  padding-block:clamp(2rem,6vh,3.4rem) clamp(2.2rem,6vh,3.6rem);
  display:grid;grid-template-columns:1.3fr 1fr 1.2fr 1fr;
  gap:clamp(1.6rem,4vw,3rem);align-items:start;color:var(--quiet);}

.foot-name{margin:0 0 .5rem;font-size:clamp(1rem,1.4vw,1.2rem);
  letter-spacing:-.03em;color:var(--ink);
  font-variation-settings:'SOFT' 54,'WONK' 1,'opsz' 48;}
.foot-name .nomi{color:var(--lime);}

.foot-doors{display:grid;gap:.5rem;justify-items:start;}
.foot-doors a{color:var(--soft);}
.foot-doors a:hover{color:var(--ink);}
.foot-doors a[aria-current]{color:var(--ink);}

.foot-talk .d3{color:var(--ink);}
.foot-talk .d3:hover{color:var(--rust);}
.foot-reach{margin-top:.9rem;display:flex;gap:.4rem;flex-wrap:wrap;}

.foot-legal{display:grid;gap:.5rem;justify-items:start;}
.foot-legal p{margin:0;}
.foot-legal a{color:var(--soft);}
.foot-legal a:hover{color:var(--ink);}

@media (max-width:64rem){
  .foot{grid-template-columns:1fr 1fr;row-gap:clamp(1.8rem,5vh,2.6rem);}
}
@media (max-width:40rem){
  .foot{grid-template-columns:1fr;}
}
