/* WITHIN Design Lab: site styles.

   Two polarities. Paper and onyx alternate down the page, and any section can
   carry either: [data-polarity="dark"] redefines the tokens rather than
   restating component colours, so every component works on both grounds.

   In this stylesheet Scarlet Fire appears in three places only: focus rings,
   text selection and the pointer. The one other place it lives is the entry
   field in site.js, where it is the whole point: colour otherwise belongs to
   the work. */

/* The three faces the site uses, served from this domain rather than from
   Google, so no visitor's address reaches a third party to read a page.
   Inter and Archivo are both under the SIL Open Font License 1.1; the licence
   travels with them in assets/fonts/. Only latin and latin-ext are carried:
   the site is English and Spanish, and a browser fetches the second file only
   if a page actually uses a character from that range. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-stretch: 125%;
  font-display: swap;
  src: url(fonts/archivo-600-125-latin.woff2?v=b05a7ca2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-stretch: 125%;
  font-display: swap;
  src: url(fonts/archivo-600-125-latin-ext.woff2?v=90ce8ff6) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/inter-latin.woff2?v=f11d729b) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/inter-latin-ext.woff2?v=cf3eb50f) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --paper: #F1F1EF;
  --paper-lo: #E8E8E5;
  --onyx: #0A0A0A;
  --ink: #14161A;
  --ink-soft: #3A3E44;
  --grey-1: #5A5F66;
  --grey-2: #8A9099;
  --grey-3: #C7C9CB;
  --grey-4: #DEDEDC;
  --alabaster: #E5E4E2;
  --scarlet: #E42D16;

  --ease: cubic-bezier(.23, 1, .32, 1);
  --dur-fast: 180ms;
  --dur-base: 250ms;

  /* Per-font cap and descender trims, so type sits optically on the rules. */
  --trim-voice-top: .32em;
  --trim-voice-bottom: .36em;
  --trim-label-top: .28em;
  --trim-label-bottom: .28em;

  --font-voice: 'Inter', system-ui, 'Helvetica Neue', Arial, sans-serif;
  --font-shout: 'Archivo', 'Arial Black', Helvetica, sans-serif;
  --font-label: 'Inter', system-ui, 'Helvetica Neue', Arial, sans-serif;

  --size-voice: clamp(14px, 12.61px + .357vw, 18px);
  --size-sub: clamp(23px, 19.18px + .98vw, 34px);
  --size-shout: clamp(46px, 18.89px + 6.95vw, 124px);
  --size-label: clamp(11px, 9.51px + .35vw, 14px);

  --brand-w: clamp(96px, 10vw, 142px);
  /* The wordmark PNG has a 3.5% transparent margin each side, so the visible
     W starts inside its box. Anything that must align with the W, rather than
     with the file, is offset by this. Measured off the file. */
  --brand-ink: calc(var(--brand-w) * .035);

  --gutter: clamp(22px, 3.05vw, 52px);
  --section-space: clamp(80px, 9vw, 150px);
  --band-gap: clamp(10px, 1.2vw, 16px);

  --foreground: var(--ink);
  --canvas: var(--paper);

  /* The parts of the screen the system owns: the Dynamic Island and status bar
     at the top, the home indicator at the bottom, the rounded corners at the
     sides. The entry is full-bleed by design, so the page declares
     viewport-fit=cover and inherits the whole screen; what the system draws on
     top of that has to be kept clear of. These read 0 on every device without
     a safe area, so every rule using them settles back to the design's own
     spacing everywhere else. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

/* One switch flips a whole section. */
[data-polarity="dark"] {
  --paper: #0A0A0A;
  --paper-lo: #17171A;
  --ink: #E5E4E2;
  --ink-soft: #C7C9CB;
  --grey-1: #9AA1A9;
  --grey-2: #767D85;
  --grey-3: #33332F;
  --grey-4: #262626;
  background: var(--onyx);
  color: var(--ink);
}

*, *::before, *::after { box-sizing: border-box; }
html { background: var(--paper); scroll-padding-top: 110px; scrollbar-width: thin; scrollbar-color: var(--grey-3) var(--paper); }
body {
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  font: 400 var(--size-voice)/1.5 var(--font-voice);
  letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: 'ss01' 1, 'cv05' 1, 'tnum' 1;
}
a { color: inherit; text-decoration: none; }
button { border: 0; padding: 0; border-radius: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
button:disabled { opacity: .35; cursor: default; }
a, button { -webkit-tap-highlight-color: transparent; }
h1, h2, h3, p, figure, dl, dd, ul, ol { margin: 0; }
h1, h2, h3 { font-weight: 500; }
ul, ol { padding: 0; list-style: none; }
img, video, svg { display: block; max-width: 100%; }
[hidden] { display: none !important; }
::selection { background: var(--scarlet); color: #fff; }
:focus-visible { outline: 2px solid var(--scarlet); outline-offset: 5px; }
/* Regions are focused only so a jump (index.html#work, the skip link) lands
   the next Tab in the right place. They are not controls, and on a full-width
   section the ring is a scarlet rule across the top of the page. */
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }

/* Rag. No line of text may end on one word left by itself. site.js binds the
   last two words of every paragraph and of every line before a break, which
   holds in every browser; these properties refine the rest of the rag where
   the browser supports them. Titles and leads are balanced; running text is
   set 'pretty', which also avoids short last lines. */
p, li, td, figcaption { text-wrap: pretty; }
h1, h2, h3, .statement, .brief-lead, .prose p.first, .stack-item .lead, .page-sub, .section-head .note, .contact-copy p { text-wrap: balance; }

.label { font: 500 var(--size-label)/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .085em; }
.shout { font-family: var(--font-shout); font-stretch: 125%; font-weight: 600; font-size: var(--size-shout); line-height: .9; text-transform: uppercase; letter-spacing: -.035em; }
.trim-voice { padding-block: var(--trim-voice-top) var(--trim-voice-bottom); margin-block: calc(-1 * var(--trim-voice-top)) calc(-1 * var(--trim-voice-bottom)); }
.trim-label { padding-block: var(--trim-label-top) var(--trim-label-bottom); margin-block: calc(-1 * var(--trim-label-top)) calc(-1 * var(--trim-label-bottom)); }
.icon { width: 20px; height: 20px; flex: 0 0 auto; }
.text-link { display: inline-flex; align-items: center; gap: 14px; min-height: 44px; }
.text-link .icon { width: 15px; height: 15px; transition: transform var(--dur-base) var(--ease); }
.text-link:hover .icon { transform: translate(3px, -3px); }
.skip-link { position: fixed; z-index: 200; top: calc(14px + var(--safe-top)); left: calc(var(--gutter) + var(--safe-left)); padding: 12px 20px; color: var(--ink); background: var(--paper); transform: translateY(-180%); }
.skip-link:focus { transform: translateY(0); }
.wrap { padding-inline: var(--gutter); }
@media (min-width: 1700px) {
  .wrap, .work, .practice, .position, .gap, .contact { padding-inline: max(var(--gutter), calc((100vw - 1700px) / 2)); }
}

/* ---------- the wordmark ---------- */
/* One black PNG of the studio's own logotype. On onyx it is inverted rather
   than swapped, so there is only ever one file to keep current. */
.wordmark { width: 100%; height: auto; aspect-ratio: 4 / 1; }
.in-volume .site-header .wordmark, [data-polarity="dark"] .wordmark { filter: invert(1); }
.brand { display: flex; align-items: center; gap: 12px; }
.brand .wordmark { width: var(--brand-w); }
/* The rule matches the visible cap height of the wordmark (letters fill the
   middle 73.2% of the 4:1 PNG), so it reads as part of the logotype. */
.brand-lab { position: relative; font: 500 clamp(8.5px, var(--brand-w) * .07, 10px)/1.2 var(--font-label); text-transform: uppercase; letter-spacing: .1em; color: var(--grey-2); padding-left: 11px; }
.brand-lab::before { content: ""; position: absolute; left: 0; top: 50%; width: 1px; height: calc(var(--brand-w) * .183); background: var(--grey-3); transform: translateY(-50%); }
.in-volume .brand-lab { color: #767D85; }
.in-volume .brand-lab::before { background: #33332F; }

/* ---------- header ---------- */
.site-header { position: absolute; z-index: 30; top: 0; left: 0; width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: calc(26px + var(--safe-top)) calc(var(--gutter) + var(--safe-right)) 26px calc(var(--gutter) + var(--safe-left)); color: var(--foreground); }
/* The wordmark's ink starts 3.5% inside its own file, so left to itself the
   mark stands a few pixels inside the line every section below it keeps. It is
   pulled back by exactly that frame, and the header then reads against the
   same left edge as the page. Everything that aligns with the W measures that
   line off this element (site.js), so the entry follows the header. */
.brand { margin-left: calc(-1 * var(--brand-ink)); }
.js .site-header { position: fixed; }
.nav { display: flex; align-items: center; flex-wrap: wrap; gap: 4px clamp(18px, 2.6vw, 44px); font-size: clamp(13px, 10.9px + .34vw, 17px); letter-spacing: -.015em; }
.nav a { display: flex; align-items: center; gap: 7px; min-height: 44px; padding-inline: 8px; margin-inline: -8px; transition: opacity var(--dur-fast); }
.nav .icon { width: 13px; height: 13px; }
/* Presence by subtraction: the current section recedes. */
.nav a[aria-current] { opacity: .35; }
.nav a:hover { opacity: .55; }

/* The two languages close the navigation, set as a label rather than as a
   seventh page: they are a state of this page, not another one. The language
   you are already reading recedes, the same way the current page does. */
.lang { display: flex; align-items: center; gap: 9px; }
.nav .lang a { font: 500 var(--size-label)/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .085em; gap: 0; padding-inline: 6px; margin-inline: -6px; }
.lang-sep { color: var(--grey-2); font: 500 var(--size-label)/1.4 var(--font-label); }

/* Over a navigation word the pointer is drawn into it: the dot shrinks away
   where it is, and its colour spreads through the letters from that same
   point. The fill is a radial gradient clipped to the glyphs, grown by a
   registered property so the browser can interpolate it. The base colour is
   still currentColor, so the effect works on paper and on onyx alike. */
@property --nav-fill { syntax: '<length>'; inherits: false; initial-value: 0px; }
@media (hover: hover) and (pointer: fine) {
  .nav a {
    --fill-x: 50%; --fill-y: 50%;
    background-image: radial-gradient(circle at var(--fill-x) var(--fill-y), var(--scarlet) calc(var(--nav-fill) - 1px), currentColor var(--nav-fill));
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
    /* Leaving drains a little faster than arriving fills. */
    transition: --nav-fill 420ms cubic-bezier(.5, 0, .2, 1), opacity var(--dur-fast);
  }
  .nav a .icon { transition: color 320ms var(--ease); }
  .nav a:hover { opacity: 1; }
  .nav a.is-drawn, .nav a:focus-visible { --nav-fill: 150px; transition: --nav-fill 680ms cubic-bezier(.65, 0, .25, 1), opacity var(--dur-fast); }
  .nav a.is-drawn .icon, .nav a:focus-visible .icon { color: var(--scarlet); transition-delay: 220ms; }
}
.site-header.scrolled { background: var(--paper); border-bottom: 1px solid var(--grey-4); }
html.in-volume { --foreground: var(--alabaster); --canvas: var(--onyx); }
html.in-volume .site-header.scrolled { background: var(--onyx); border-color: #262626; }

/* ---------- the menu mark ---------- */
/* Three rules on a phone, closing into a cross while the pages are listed.
   Above the breakpoint it is not shown and the navigation is the header. */
.nav-toggle { display: none; align-items: center; justify-content: center; position: relative; z-index: 2; width: 46px; height: 46px; margin-right: -12px; color: var(--foreground); }
.nav-bars { position: relative; display: block; width: 25px; height: 13px; }
.nav-bars > span { position: absolute; left: 0; width: 100%; height: 1.5px; border-radius: 999px; background: currentColor; transition: transform 340ms var(--ease), opacity 160ms ease; }
.nav-bars > span:nth-child(1) { top: 0; }
.nav-bars > span:nth-child(2) { top: 50%; margin-top: -.75px; }
.nav-bars > span:nth-child(3) { bottom: 0; }
.nav-open .nav-bars > span:nth-child(1) { transform: translateY(5.75px) rotate(45deg); }
.nav-open .nav-bars > span:nth-child(2) { opacity: 0; }
.nav-open .nav-bars > span:nth-child(3) { transform: translateY(-5.75px) rotate(-45deg); }
@keyframes menu-in { from { transform: translateY(12px); } to { transform: none; } }

/* ---------- preloader ---------- */
/* The word fills in as the assets land. No spinner: the percentage is it. */
.preloader { display: none; position: fixed; inset: 0; z-index: 100; background: var(--paper); pointer-events: none; --fill: 0%; }
.js:not(.reduced-motion) .preloader { display: grid; place-items: center; }
/* The paper dissolves rather than cutting: the word stays exactly where it
   was and only what lives inside it is new. */
.preloader.finished { opacity: 0; transition: opacity 620ms var(--ease); }
.loader-counter { position: absolute; top: 44px; right: var(--gutter); color: var(--grey-2); font-size: 10px; }
/* These dimensions mirror the mask so the loader hands over without a jump.
   The loader always spells the whole word, so on a phone, where the entry is
   the W alone, it keeps its own width and the paper crossfades instead. */
.loader-word { position: absolute; top: 66.7%; left: 50%; width: var(--loader-w, var(--word-w, 100vw)); transform: translate(-50%, -50%); }
.loader-word .ghost { opacity: .14; }
.loader-word .fill { position: absolute; inset: 0; clip-path: inset(0 calc(100% - var(--fill)) 0 0); transition: clip-path 180ms linear; }

/* ---------- entry ---------- */
.entry { position: relative; height: 100svh; min-height: 620px; }
.cinematic .entry { height: calc(100svh + 3000px); }
.hero-stage { position: sticky; top: 0; width: 100%; height: 100svh; min-height: 620px; overflow: hidden; isolation: isolate; background: var(--canvas); color: var(--foreground); }
.hero-art, .portal-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* --word-w and --word-left are measured once in site.js and read by the loader,
   this flat word and the WebGL mask alike. Keeping the three together means the
   W, the claim and the animated window always share their leading edge. */
.fallback-art { position: absolute; top: 66.7%; left: 50%; width: var(--word-w, 100vw); transform: translate(-50%, -50%); transition: opacity 200ms ease; }
.portal-canvas { opacity: 0; transition: opacity 200ms ease; }
/* The letterforms, cut by the browser's own vector rasteriser from the same SVG
   the flat wordmark is drawn with, rather than by a texture the shader has to
   magnify. A texture has a resolution and the dive runs past it: at 175x one
   texel spans a thousand pixels and the contour becomes a chain of chords. The
   mask-image, -size and -position are set from site.js, which also stands the
   shader's own mask down while this one is cutting, so the two never fight. */
.portal-canvas { -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.webgl-ready .portal-canvas { opacity: 1; }
.webgl-ready .fallback-art { opacity: 0; }
.hero-bottom { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.hero-bottom > * { pointer-events: auto; }
.hero-thesis { position: absolute; top: 38%; left: var(--word-left, 3.5vw); max-width: 620px; font-size: clamp(22px, 1.17vw, 24px); line-height: 1.3; letter-spacing: -.025em; }
.hero-thesis .soft { color: var(--grey-1); }
.nowrap { white-space: nowrap; }
.scroll-in { position: absolute; right: calc(var(--word-left, 3.5vw) + var(--safe-right)); bottom: calc(38px + var(--safe-bottom)); display: flex; align-items: center; gap: 20px; min-height: 46px; white-space: nowrap; }
.scroll-in .icon { width: 17px; height: 29px; animation: nudge 3s var(--ease) infinite; }
@keyframes nudge { 0%, 45%, 100% { transform: translateY(0); } 65% { transform: translateY(6px); } }
.flight-copy { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center; padding: 0 var(--gutter); pointer-events: none; opacity: 0; color: var(--alabaster); }
/* The statement is set in the same alabaster as the shouts that follow it.
   It is a <p>, so the claim's grey must not reach it: the claim has a class. */
.flight-copy .shout { font-size: clamp(44px, 10.5vw, 160px); color: var(--alabaster); }
.flight-copy .flight-claim { margin-top: 28px; font-size: clamp(16px, 1.35vw, 22px); color: #9AA1A9; }
.flight-status { position: absolute; bottom: 42px; right: var(--gutter); opacity: 0; color: var(--alabaster); }

/* ---------- the arrival ---------- */
/* Nothing is on the page until the loader hands over. The word itself wipes in
   inside the scene; everything written around it rises afterwards, in the
   order you would read it. */
.js:not(.is-ready):not(.reduced-motion) .hero-bottom > *,
.js:not(.is-ready):not(.reduced-motion) .site-header .brand,
.js:not(.is-ready):not(.reduced-motion) .nav a { opacity: 0; }
@keyframes arrive { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: none; } }
.is-ready .site-header .brand { animation: arrive 760ms var(--ease) 140ms both; }
.is-ready .nav a { animation: arrive 680ms var(--ease) both; }
.is-ready .nav a:nth-child(1) { animation-delay: 250ms; }
.is-ready .nav a:nth-child(2) { animation-delay: 300ms; }
.is-ready .nav a:nth-child(3) { animation-delay: 350ms; }
.is-ready .nav a:nth-child(4) { animation-delay: 400ms; }
.is-ready .nav a:nth-child(5) { animation-delay: 450ms; }
.is-ready .nav a:nth-child(6) { animation-delay: 500ms; }
.is-ready .hero-thesis { animation: arrive 900ms var(--ease) 620ms both; }
.is-ready .scroll-in { animation: arrive 820ms var(--ease) 780ms both; }

/* ---------- section furniture ---------- */
.kicker { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding-top: 16px; border-top: 1px solid var(--grey-3); color: var(--grey-1); }
.section-head { display: grid; grid-template-columns: 1fr .38fr; align-items: end; gap: 45px; margin-top: 50px; }
.section-head .note { max-width: 260px; justify-self: end; font-size: clamp(17px, 1.48vw, 24px); letter-spacing: -.025em; line-height: 1.35; padding-bottom: 4px; color: var(--ink-soft); }

/* ---------- work index ---------- */
.work { position: relative; z-index: 3; padding: 80px var(--gutter) var(--section-space); }
.cinematic .work { margin-top: -100svh; padding-top: 128px; }
.work .shout { font-size: min(var(--size-shout), 11.9vw); }
.filter-bar { display: flex; justify-content: space-between; align-items: center; gap: 20px; margin: 62px 0 22px; padding-bottom: 12px; border-bottom: 1px solid var(--grey-3); }
.filters { display: flex; gap: clamp(24px, 3vw, 44px); }
.filter { font-size: 13px; min-height: 36px; transition: opacity var(--dur-fast); }
.filter[aria-pressed="true"] { opacity: .35; }
.filter sup { font: 500 8px var(--font-label); letter-spacing: .08em; margin-left: 5px; position: relative; top: -3px; }
.filter:hover { opacity: .55; }
.filter-bar > .label { color: var(--grey-1); }
html:not(.ui-ready) .filters, html:not(.ui-ready) .copy-email { display: none; }

.project-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 44px 24px; }
.project-card { min-width: 0; display: block; }
.project-media { position: relative; display: block; overflow: hidden; aspect-ratio: 4 / 3; }
.project-media.square { aspect-ratio: 1; }
.project-media img, .project-media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* The index. Two equal columns and nothing wider: every plate is either
   landscape or portrait, and the columns are stacked so that a landscape over
   a portrait on one side meets a portrait over a landscape on the other. No
   plate ever runs the full width of the page. site.js deals the cards into the
   columns, shortest column first, so the stagger holds under a filter; without
   it they sit in a plain two-column grid, in reading order. */
.index-grid { align-items: start; }
.index-grid .project-media.landscape { aspect-ratio: 3 / 2; }
.index-grid .project-media.portrait { aspect-ratio: 4 / 5; }
/* A square plate closes the odd column: two landscapes and a square stand
   level with two portraits, gaps included. */
.index-grid .project-media.square { aspect-ratio: 1; }
.project-col { display: flex; flex-direction: column; gap: 44px; min-width: 0; }
.project-col:empty { display: none; }
.index-grid.is-dealt { row-gap: 0; }
/* The living room is wide; its lamp sits left of centre. */
/* A 3:2 film in a wide plate: held a little low, where the product is. */
.project-media .card-film { object-position: 50% 62%; }
.project-media .alt-frame { opacity: 0; transition: opacity var(--dur-base) ease; }
.project-card:hover .alt-frame, .project-card:focus-visible .alt-frame { opacity: 1; }
.project-media .image-arrow { position: absolute; z-index: 2; right: 20px; bottom: 18px; width: 22px; height: 22px; color: #fff; mix-blend-mode: difference; }
/* Metadata and caption swap inside one fixed slot: no layout shift, and the
   two are set identically, so hovering changes the words and nothing else. */
.meta-slot { position: relative; display: block; height: 26px; margin-top: 9px; }
.meta-slot > span { position: absolute; inset: 0; display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity var(--dur-base) ease; font: 500 clamp(11px, .86vw, 13px)/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .08em; }
/* Name, discipline, year, material: set apart by space rather than a mark.
   Where the line runs out, the material gives way first. */
.meta-line { gap: 0 clamp(14px, 1.6vw, 28px); }
.meta-line > span { flex: 0 0 auto; }
.meta-line > span:last-child { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.meta-caption { opacity: 0; }
.project-card:hover .meta-line, .project-card:focus-visible .meta-line { opacity: 0; }
.project-card:hover .meta-caption, .project-card:focus-visible .meta-caption { opacity: 1; }

/* ---------- the line (statement over photography) ---------- */
.gap { position: relative; z-index: 3; padding: 0 var(--gutter) clamp(70px, 8vw, 130px); }
.gap-media { margin-top: 44px; }
.gap-media img { width: 100%; height: auto; aspect-ratio: 1700 / 721; object-fit: cover; }
.gap-shout { position: relative; margin-top: -.34em; font-size: clamp(40px, 7.6vw, 122px); }
.gap-note { max-width: 54ch; margin-top: clamp(26px, 3vw, 44px); margin-left: auto; font-size: clamp(15px, 1.25vw, 20px); line-height: 1.55; color: var(--grey-1); }
.gap-note .key { color: var(--ink); }

/* ---------- practice ---------- */
.practice { position: relative; z-index: 3; padding: 0 var(--gutter) var(--section-space); }
.practice-intro { display: grid; grid-template-columns: .45fr 1fr; gap: 60px; padding: 62px 0 96px; }
.practice-intro .label { max-width: 180px; color: var(--grey-1); margin-top: 12px; }
/* Section titles, set exactly as the pillar titles below them are. */
.statement { font: 600 clamp(24px, 15px + 2.3vw, 50px)/.98 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; max-width: 1100px; }
.statement .soft { color: var(--grey-2); }
.pillar { border-top: 1px solid var(--grey-3); display: grid; grid-template-columns: .45fr 1fr; gap: 60px; padding: 28px 0 34px; }
.pillar-index { display: flex; justify-content: space-between; gap: 20px; padding-top: 5px; padding-right: 12%; }
.pillar-index > span:last-child { color: var(--grey-1); }
.pillar h2, .pillar h3 { font: 600 clamp(24px, 15px + 2.3vw, 50px)/.9 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; }
.pillar p { max-width: 580px; color: var(--grey-1); font-size: clamp(14px, 1.05vw, 17px); line-height: 1.6; margin-top: 20px; }
.practice-end { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-top: 30px; border-top: 1px solid var(--grey-3); }
.practice-end p { font-size: clamp(15px, 1.5vw, 23px); letter-spacing: -.02em; max-width: 34ch; }

/* ---------- position ---------- */
.position { position: relative; z-index: 3; padding: 0 var(--gutter) var(--section-space); }
.position-head { padding: 62px 0 40px; }
.position-head p { max-width: 60ch; margin-top: 26px; color: var(--grey-1); font-size: clamp(14px, 1.05vw, 17px); line-height: 1.65; }
.argument { counter-reset: a; max-width: 62rem; }
/* Hanging number, not a grid. A grid container blockifies its inline children,
   so the <span> in a line became a third grid item and dropped into the 3rem
   number column, one word per line. The number hangs in the padding instead and
   the sentence stays a single run of text. */
.argument li { counter-increment: a; position: relative; padding: 18px 0 18px 4rem; border-top: 1px solid var(--grey-3); font-size: clamp(17px, 1.85vw, 27px); line-height: 1.3; letter-spacing: -.025em; }
.argument li:last-child { border-bottom: 1px solid var(--grey-3); }
.argument li::before { content: counter(a, decimal-leading-zero); position: absolute; left: 0; top: 18px; font: 500 var(--size-label) var(--font-label); color: var(--grey-2); padding-top: .55em; font-variant-numeric: tabular-nums; }
.argument .soft { color: var(--grey-2); }
.argument strong { font-weight: 500; }

/* ---------- contact ---------- */
.contact { position: relative; z-index: 3; padding: 56px var(--gutter) 0; }
.contact-main { display: grid; grid-template-columns: 1fr .48fr; gap: 50px; align-items: end; padding: 86px 0 100px; }
.contact .shout { font-size: clamp(58px, 8.6vw, 140px); }
.contact-copy p { max-width: 320px; font-size: clamp(18px, 1.65vw, 27px); line-height: 1.35; letter-spacing: -.025em; margin-bottom: 28px; }
@media (min-width: 1001px) {
  /* Beside the title the note stands exactly as tall as it: its first capital
     level with the B of "Build", its last baseline on the baseline of
     "future.". Both are sized from one value and the note is set to a measure
     in em, so it keeps its six lines, and the fit, at every width. The factors
     come from the fonts' own metrics: Archivo caps sit .095em into a .9 line,
     Inter's .365em into a 1.4 line, and six lines of Inter span the two
     lines of Archivo at .2057 of its size. */
  .contact { --foot-shout: clamp(58px, 8.6vw, 140px); }
  .contact .shout { font-size: var(--foot-shout); }
  .contact-main { align-items: start; }
  .contact-copy p { max-width: 11.25em; margin-top: calc(var(--foot-shout) * .026); font-size: calc(var(--foot-shout) * .2057); line-height: 1.4; }
}
.email-link { display: flex; align-items: center; justify-content: space-between; gap: 20px; min-height: 49px; padding-bottom: 9px; border-bottom: 1px solid var(--grey-1); font-size: clamp(16px, 1.4vw, 24px); }
/* Shown only when a mailto link found no email app (assets/site.js). Always
   onyx, whichever ground it opens over. */
.mail-note { position: fixed; left: 50%; bottom: calc(24px + var(--safe-bottom)); z-index: 120; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 20px; width: max-content; max-width: calc(100vw - 32px); padding: 10px 12px 10px 20px; background: #0A0A0A; color: #E5E4E2; border: 1px solid #2E2E2E; opacity: 0; transform: translate(-50%, 12px); transition: opacity 240ms var(--ease), transform 240ms var(--ease); }
.mail-note.is-on { opacity: 1; transform: translate(-50%, 0); }
.mail-note > span { color: #8A9099; }
.mail-note a, .mail-note button { display: inline-flex; align-items: center; min-height: 36px; font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; text-decoration: underline; text-underline-offset: .3em; }
.mail-note [data-mail="close"] { min-width: 36px; justify-content: center; font-size: 18px; text-decoration: none; color: #8A9099; }
.copy-email { display: inline-flex; align-items: center; min-height: 44px; margin-top: 4px; color: var(--grey-1); }
.foot-rows { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 22px 40px; border-top: 1px solid var(--grey-3); padding-top: 22px; }
.foot-item { display: flex; flex-direction: column; gap: 6px; }
.foot-item .lbl { color: var(--grey-1); }
/* The mark runs from the left edge of the text to the right edge of it, so
   the W stands on the same line as the column above and the N ends where the
   right-hand column does. The PNG carries transparent margins, measured off
   the file: 3.5% before the W and 4.075% after the N. The image is widened by
   the ink's share of its width and pulled left by its left margin; what hangs
   into the gutter is transparent, and is clipped so the page cannot scroll. */
.foot-mark { display: grid; grid-template-columns: minmax(0, 1fr); padding: 70px 0 48px; overflow: hidden; }
/* The entry field, inside the letters (site.js). Canvas and image share one
   cell; the image keeps the name, and stands in wherever the field cannot. */
.foot-mark > * { grid-area: 1 / 1; width: calc(100% / .92425); max-width: none; margin-left: calc(-3.5% / .92425); }
.foot-field {
  display: block; height: auto; aspect-ratio: 4 / 1; opacity: 0;
  transition: opacity 600ms var(--ease);
  -webkit-mask: url(wordmark.png?v=44d8df42) center / 100% 100% no-repeat;
  mask: url(wordmark.png?v=44d8df42) center / 100% 100% no-repeat;
}
.foot-mark.is-lit .foot-field { opacity: 1; }
.foot-mark.is-lit .wordmark { opacity: 0; transition: opacity 0s 600ms; }
.foot-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 15px 30px; border-top: 1px solid var(--grey-3); min-height: 74px; color: var(--grey-1); }
.foot-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0 28px; }
.foot-controls a, .foot-controls button { display: flex; align-items: center; gap: 8px; min-height: 44px; }
.foot-controls .icon { width: 12px; height: 12px; }

/* ---------- interior pages ---------- */
.page { padding-top: 104px; }
.page-head { padding: 26px 0 0; }
.page-lead { display: grid; grid-template-columns: 1fr .42fr; align-items: end; gap: 45px; margin-top: 44px; }
.page-title { font-family: var(--font-shout); font-stretch: 125%; font-weight: 600; font-size: clamp(40px, 7.4vw, 112px); line-height: .88; text-transform: uppercase; letter-spacing: -.038em; }
.page-sub { max-width: 34ch; justify-self: end; font-size: clamp(17px, 1.48vw, 24px); line-height: 1.35; letter-spacing: -.025em; color: var(--ink-soft); padding-bottom: 6px; }
.banner { margin-top: 46px; }
.banner img { width: 100%; height: auto; aspect-ratio: 1800 / 764; object-fit: cover; }

.brief { display: grid; grid-template-columns: 1fr .42fr; gap: clamp(30px, 5vw, 80px); padding: clamp(48px, 6vw, 96px) 0 clamp(30px, 4vw, 60px); }
.brief-lead { font-size: clamp(20px, 2.2vw, 32px); line-height: 1.3; letter-spacing: -.028em; margin-bottom: 34px; max-width: 24ch; }
.brief-body p { max-width: 62ch; margin-bottom: 18px; color: var(--grey-1); line-height: 1.7; font-size: clamp(14px, 1.05vw, 17px); }
/* Emphasis by dimming: key phrases stay ink, the rest recedes. */
.brief-body .key { color: var(--ink); }
.brief-side { display: flex; flex-direction: column; gap: 30px; }
.brief-side .lbl { color: var(--grey-2); padding-bottom: 10px; border-bottom: 1px solid var(--grey-3); margin-bottom: 12px; }
.brief-side li, .brief-side .val { font-size: 13px; line-height: 1.85; color: var(--ink-soft); }

.plates { display: flex; flex-direction: column; gap: var(--band-gap); padding-bottom: clamp(60px, 8vw, 120px); }
.band { display: grid; gap: var(--band-gap); }
.band.span { grid-template-columns: 1fr; }
.band.pair { grid-template-columns: 1fr 1fr; }
.band.trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.band.left { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
.band.right { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.band.right > figure { grid-column: 2; }
.band figure { margin: 0; }
.band.trio { --band-gap: clamp(8px, .9vw, 14px); }
.band.quad { grid-template-columns: repeat(4, minmax(0, 1fr)); --band-gap: clamp(8px, .9vw, 14px); }
.band.aside { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: center; gap: clamp(26px, 3vw, 54px); }
.band-note .label { display: block; margin-bottom: 16px; color: var(--grey-1); }
.band-note p { max-width: 42ch; margin-bottom: 16px; font-size: clamp(15px, 1.15vw, 18px); line-height: 1.65; color: var(--grey-1); }
.band-note p.first { font-size: clamp(18px, 1.7vw, 24px); line-height: 1.3; letter-spacing: -.028em; color: var(--ink); }
.band-note .key { color: var(--ink); }
.band-note a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .2em; }
.band-note a:hover { text-decoration-color: var(--scarlet); }
/* Lab study pages: small label text reads at full contrast, not grey. */
.study-page main .kicker,
.study-page main .prose > .label,
.study-page main .band-note .label,
.study-page main figcaption { color: var(--ink); }
/* Study pages open straight on an image, so buy back the breathing room
   a prose block would normally give under the title. */
.study-page .page-head + .plates { padding-top: clamp(56px, 7.5vw, 130px); }
.band img, .band video { width: 100%; height: auto; }
.band figcaption { display: flex; justify-content: space-between; gap: 16px; padding-top: 9px; color: var(--grey-2); font: 500 var(--size-label)/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .085em; }

.next-work { padding: clamp(40px, 5vw, 70px) 0 clamp(70px, 9vw, 130px); }
.next-work .project-grid { margin-top: 34px; }

/* editorial pages: process, services, mission, studio */
.prose { padding: clamp(46px, 6vw, 90px) 0; display: grid; grid-template-columns: .45fr 1fr; gap: 60px; }
.prose .label { max-width: 190px; color: var(--grey-1); }
.prose p { max-width: 66ch; margin-bottom: 20px; font-size: clamp(15px, 1.15vw, 18px); line-height: 1.65; color: var(--grey-1); }
.prose p.first { font-size: clamp(20px, 2.2vw, 32px); line-height: 1.3; letter-spacing: -.028em; color: var(--ink); max-width: 26ch; }
.prose h3 { font: 500 clamp(18px, 1.6vw, 24px)/1.25 var(--font-voice); letter-spacing: -.022em; color: var(--ink); margin: 36px 0 12px; }
.prose .key { color: var(--ink); }
/* ---------- process: the four stages, drawn ----------
   The drawing is pinned for the length of the section while the four steps
   scroll over it, one screen each; assets/process.js draws it. The steps pass
   over the left of the frame and the object stands on the right. The last
   step is two screens long and its copy holds beside the object while the
   drawing gives way to the photograph; then both leave together. Without the
   script the stage is not shown and the steps read as a plain dark list. */
.process-film { position: relative; margin-top: clamp(20px, 3vw, 50px); background: var(--onyx); }
.process-stage { display: none; }
.process-drawn .process-stage { display: block; position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; }
.process-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.process-render { position: absolute; max-width: none; height: auto; opacity: 0; transform: translate(-50%, -50%); pointer-events: none; }
.process-meter { position: absolute; right: var(--gutter); bottom: 32px; display: flex; gap: 8px; color: var(--grey-1); }
.process-meter span:first-child { color: var(--ink); }
.process-steps { position: relative; padding: clamp(60px, 8vw, 120px) var(--gutter); }
.process-drawn .process-steps { margin-top: -100vh; margin-top: -100svh; padding-block: 0; }
.process-step + .process-step { margin-top: clamp(50px, 6vw, 90px); }
.process-drawn .process-step { min-height: 100vh; min-height: 100svh; margin-top: 0; display: flex; align-items: center; }
.process-drawn .process-step.is-held { min-height: 200vh; min-height: 200svh; align-items: flex-start; }
.process-drawn .process-hold { position: sticky; top: 0; width: 100%; height: 100vh; height: 100svh; display: flex; align-items: center; }
.process-copy { max-width: min(30rem, 34vw); }
.process-copy > .label, .intel-copy > .label { color: var(--grey-1); }
.process-title, .intel-title { margin: 20px 0 18px; font: 600 clamp(28px, 14px + 2.2vw, 50px)/.95 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; color: var(--ink); }
.process-copy p, .intel-copy p { max-width: 38ch; color: var(--grey-1); font-size: clamp(15px, 1.15vw, 18px); line-height: 1.65; }
@media (min-width: 1700px) {
  .process-steps { padding-inline: max(var(--gutter), calc((100vw - 1700px) / 2)); }
}
@media (max-width: 900px) {
  /* One column: the object sits in the upper part of the frame and the copy
     settles at the foot of each screen, on a fade of the ground. */
  .process-copy { max-width: none; }
  .process-drawn .process-step { align-items: flex-end; padding-bottom: 9svh; }
  .process-drawn .process-step.is-held { align-items: flex-start; padding-bottom: 0; }
  .process-drawn .process-hold { align-items: flex-end; padding-bottom: 9svh; }
  .process-drawn .process-copy { width: calc(100% + 2 * var(--gutter)); margin-inline: calc(-1 * var(--gutter)); padding: 70px var(--gutter) 0; background: linear-gradient(to top, var(--onyx) 62%, rgba(10, 10, 10, 0)); }
  .process-meter { bottom: auto; top: 96px; }
}

/* A case study that cites its evidence: set small, numbered, and quiet. */
.sources { list-style: decimal; padding-left: 1.3em; max-width: 72ch; color: var(--grey-1); font-size: clamp(12px, .95vw, 14px); line-height: 1.6; }
.sources li { padding-left: .4em; margin-bottom: 10px; }
.sources li::marker { color: var(--grey-2); font: 500 var(--size-label) var(--font-label); }
.next-close { margin-bottom: clamp(50px, 6vw, 90px); }
.next-close p { max-width: 42ch; }

.stack { border-top: 1px solid var(--grey-3); }
.page > .stack:last-child { margin-bottom: clamp(70px, 9vw, 140px); }
.stack-item { border-bottom: 1px solid var(--grey-3); display: grid; grid-template-columns: .45fr 1fr; gap: 60px; padding: 30px 0 36px; }
.stack-item .lead { font-size: clamp(19px, 1.8vw, 28px); line-height: 1.25; letter-spacing: -.025em; max-width: 22ch; }
.stack-item p { color: var(--grey-1); font-size: clamp(14px, 1.05vw, 17px); line-height: 1.65; max-width: 62ch; }
.stack-item p + p { margin-top: 14px; }
.stack-index { display: flex; justify-content: space-between; gap: 20px; padding-top: 6px; padding-right: 12%; }
.stack-index > span:last-child { color: var(--grey-1); }

/* privacy policy: the stack, with lists and links that read as such */
.policy { margin-top: clamp(46px, 6vw, 90px); }
.policy .stack-item p + .policy-list, .policy .policy-list + p { margin-top: 14px; }
.policy-list { max-width: 62ch; color: var(--grey-1); font-size: clamp(14px, 1.05vw, 17px); line-height: 1.65; }
.stack-item .lead + .policy-list { margin-top: 18px; }
.policy-list li { position: relative; padding-left: 1.5em; }
.policy-list li + li { margin-top: 10px; }
/* A short rule for a marker: drawn, not typed. */
.policy-list li::before { content: ''; position: absolute; left: 0; top: .82em; width: .6em; height: 1px; background: currentColor; }
.policy .key { color: var(--ink); }
.policy a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .2em; }
.policy a:hover { text-decoration-color: var(--scarlet); }
.policy-end { margin-bottom: clamp(70px, 9vw, 140px); border-top: 0; padding-top: clamp(30px, 4vw, 56px); }

.ledger { width: 100%; border-collapse: collapse; margin: 10px 0 0; font-size: clamp(14px, 1.05vw, 17px); }
.ledger th { text-align: left; padding: 0 24px 10px 0; border-bottom: 1px solid var(--grey-3); color: var(--grey-2); font: 500 var(--size-label)/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .085em; }
.ledger td { padding: 14px 24px 14px 0; border-bottom: 1px solid var(--grey-3); vertical-align: top; }
.ledger td:last-child { color: var(--grey-1); }

/* contact: the footer's invitation, given a page of its own on onyx */
.contact-page { min-height: 100svh; padding-bottom: clamp(40px, 5vw, 80px); }
.contact-direct { padding: clamp(64px, 9vw, 140px) 0 clamp(36px, 5vw, 70px); }
.contact-address { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-bottom: clamp(14px, 1.6vw, 24px); border-bottom: 1px solid var(--grey-3); font-size: clamp(24px, 5.6vw, 96px); line-height: 1.05; letter-spacing: -.04em; overflow-wrap: anywhere; }
.contact-address .icon { width: clamp(20px, 3.4vw, 56px); height: clamp(20px, 3.4vw, 56px); transition: transform var(--dur-base) var(--ease); }
.contact-address:hover .icon { transform: translate(6px, -6px); }
.contact-direct-row { display: flex; justify-content: space-between; align-items: center; gap: 20px; margin-top: 8px; color: var(--grey-1); }
.contact-direct-row .copy-email { margin-top: 0; }
.contact-direct-row a { display: inline-flex; align-items: center; min-height: 44px; }
.contact-lines { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 44px clamp(24px, 3vw, 48px); padding: clamp(30px, 4vw, 60px) 0 clamp(64px, 8vw, 120px); }
.contact-line { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; padding-top: 16px; border-top: 1px solid var(--grey-3); }
.contact-line .label { color: var(--grey-1); }
.contact-line p { max-width: 32ch; font-size: clamp(16px, 1.3vw, 21px); line-height: 1.4; letter-spacing: -.02em; color: var(--ink-soft); }
.contact-line .text-link { margin-top: auto; }
.contact-foot { padding-top: 0; }

/* Mission: the beliefs are the process page's film, drawn by assets/mission.js
   from the shapes in assets/img/mission. */
.beliefs { padding-top: clamp(20px, 3vw, 40px); }
/* One measure for everything in the text column, so the large lines and the
   running text share both edges, and two sizes only: the lead and the body.
   Leads run to the measure rather than balancing short of it. */
.prose.even > div > *, .prose.even p.first { max-width: 46rem; }
.prose.even .first { font: 400 clamp(20px, 2.2vw, 32px)/1.3 var(--font-voice); letter-spacing: -.028em; color: var(--ink); text-wrap: pretty; }
.prose.even h3.first { margin: clamp(40px, 4.5vw, 68px) 0 20px; }
/* ---------- services: how Within Intelligence works ----------
   The process page's film, symmetrical: the drawing (assets/services.js)
   holds the middle of the frame and the five steps scroll past it, their
   copy alternating left and right. Without the script the stage is not shown
   and the steps read as a dark list, still alternating. */
.intel-film { position: relative; margin-top: clamp(10px, 2vw, 30px); background: var(--onyx); color: var(--ink); }
.intel-stage { display: none; }
.intel-drawn .intel-stage { display: block; position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; }
.intel-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.intel-meter { position: absolute; left: 50%; bottom: 32px; display: flex; gap: 8px; color: var(--grey-1); transform: translateX(-50%); }
.intel-meter span:first-child { color: var(--ink); }
.intel-steps { position: relative; margin: 0; padding: clamp(60px, 8vw, 120px) max(var(--gutter), calc((100vw - 1700px) / 2)); list-style: none; }
.intel-drawn .intel-steps { margin-top: -100vh; margin-top: -100svh; padding-block: 0; }
.intel-step + .intel-step { margin-top: clamp(50px, 6vw, 90px); }
.intel-drawn .intel-step { min-height: 100vh; min-height: 100svh; margin-top: 0; display: flex; align-items: center; }
.intel-copy { max-width: min(26rem, 28vw); }
@media (min-width: 901px) {
  .intel-step:nth-child(even) .intel-copy { margin-left: auto; }
}
@media (max-width: 900px) {
  /* As on the process page: the drawing high in the frame, the copy at the
     foot of each screen on a fade of the ground, all on one side. */
  .intel-copy { max-width: none; }
  .intel-drawn .intel-step { align-items: flex-end; padding-bottom: 9svh; }
  .intel-drawn .intel-copy { width: calc(100% + 2 * var(--gutter)); margin-inline: calc(-1 * var(--gutter)); padding: 70px var(--gutter) 0; background: linear-gradient(to top, var(--onyx) 62%, rgba(10, 10, 10, 0)); }
  .intel-meter { bottom: auto; top: 96px; }
}

/* ---------- services: the gap, closed ----------
   Without assets/gap.js this is a plain dark panel: the line whole over the
   last photograph, the copy beneath. With it the panel is pinned for three
   screens while the two halves of the line travel in from the sides. */
.gap-film { position: relative; margin-top: clamp(20px, 3vw, 50px); background: var(--onyx); color: var(--ink); }
.gap-stage { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(36px, 6vh, 72px); min-height: 100vh; min-height: 100svh; padding: clamp(90px, 12vh, 140px) max(var(--gutter), calc((100vw - 1700px) / 2)); overflow: hidden; }
.gap-hero { display: grid; place-items: center; width: 100%; }
.gap-frame { grid-area: 1 / 1; position: relative; width: min(clamp(150px, 19vw, 330px), 32vh); aspect-ratio: 4 / 5; }
.gap-frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.gap-frame img:last-child { opacity: 1; }
.gap-title { grid-area: 1 / 1; position: relative; z-index: 1; font: 600 min(6.5vw, 124px)/.9 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; white-space: nowrap; }
.gap-line { display: flex; justify-content: center; gap: .28em; }
.gap-word { display: block; }
/* The copy under the line sits on the page grid, label and text column, as
   every other passage on the site does. */
.gap-stage .gap-copy { width: 100%; padding: 0; }
.gap-copy p:last-child { margin-bottom: 0; }
.gap-drawn .gap-film { height: 300vh; height: 300svh; }
.gap-drawn .gap-stage { position: sticky; top: 0; height: 100vh; height: 100svh; min-height: 0; }
.gap-drawn .gap-frame { will-change: transform; }
.gap-drawn .gap-frame img, .gap-drawn .gap-frame img:last-child { opacity: 0; }
.gap-drawn .gap-frame img.is-shown { opacity: 1; }
.gap-drawn .gap-word, .gap-drawn .gap-copy { will-change: transform; }
.services-body { padding-top: clamp(60px, 8vw, 120px); }

/* The service index: every service in one list, set on the pillar grid so the
   names start where the text column does. On a fine pointer a photograph of
   the service follows beside the pointer (assets/services.js). */
.service-index { padding-bottom: clamp(50px, 7vw, 110px); }
.service-index .statement { margin: clamp(30px, 4vw, 56px) 0 clamp(34px, 4.5vw, 64px); }
.service-list { list-style: none; margin: 0; padding: 0; border-bottom: 1px solid var(--grey-3); }
.service { display: grid; grid-template-columns: .45fr 1fr; gap: 60px; align-items: baseline; padding: clamp(18px, 2vw, 28px) 0; border-top: 1px solid var(--grey-3); }
.service > .label { justify-self: start; color: var(--grey-2); font-variant-numeric: tabular-nums; }
.service-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .9fr); gap: 12px 40px; align-items: baseline; }
.service h3 { font: 600 clamp(22px, 12px + 1.9vw, 40px)/.95 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; color: var(--ink); transition: color 260ms var(--ease); }
.service p { color: var(--grey-1); font-size: clamp(15px, 1.15vw, 18px); line-height: 1.55; }
@media (hover: hover) and (pointer: fine) {
  .service-list:hover .service:not(:hover) h3 { color: var(--grey-3); }
}
.service-peek { position: fixed; left: 0; top: 0; z-index: 60; width: clamp(200px, 17vw, 280px); aspect-ratio: 4 / 5; pointer-events: none; opacity: 0; scale: .9; transition: opacity 220ms var(--ease), scale 360ms var(--ease); }
.service-peek.is-on { opacity: 1; scale: 1; }
.service-peek img, .service-peek video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.service-peek .is-shown { opacity: 1; }
@media (hover: none), (pointer: coarse) { .service-peek { display: none; } }

/* The closing line, shouted, with the note beneath it in the text column. */
.closing { padding-top: clamp(46px, 6vw, 90px); }
.closing-title { margin-top: clamp(30px, 4vw, 56px); font: 600 clamp(28px, 10px + 3.9vw, 72px)/.92 var(--font-shout); font-stretch: 125%; text-transform: uppercase; letter-spacing: -.035em; }
.closing .prose { padding-top: clamp(26px, 3vw, 44px); }

/* ---------- cursor, reveal, motion ---------- */
/* The pointer. A dot in the accent colour, centred on the actual hotspot;
   the old mark was drawn down and right of it, so you aimed at a link and
   clicked next to it. Over anything carrying data-cursor it opens into a pill
   and says what the thing does. */
.word-cursor {
  position: fixed; z-index: 150; left: 0; top: 0; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  height: 11px; min-width: 11px; padding: 0; border-radius: 999px;
  background: var(--scarlet); color: #fff;
  font: 500 10px/1 var(--font-label); text-transform: uppercase; letter-spacing: .09em; white-space: nowrap;
  opacity: 0;
  transition: opacity 200ms ease, height 320ms var(--ease), min-width 320ms var(--ease), padding 320ms var(--ease), scale 460ms var(--ease);
}
/* Drawn into a navigation word. `scale` composes with the inline transform
   that places the dot, so it shrinks exactly where the pointer is. */
.word-cursor.absorbed { scale: 0; transition: opacity 200ms ease, height 320ms var(--ease), min-width 320ms var(--ease), padding 320ms var(--ease), scale 380ms cubic-bezier(.55, 0, .75, .2); }
.word-cursor .cursor-text { display: block; max-width: 0; overflow: hidden; opacity: 0; transition: max-width 340ms var(--ease), opacity 200ms ease; }
.word-cursor.labelled { height: 28px; min-width: 28px; padding-inline: 14px; }
.word-cursor.labelled .cursor-text { max-width: 240px; opacity: 1; }
/* On press it tightens, so a click has a reply even where nothing else moves. */
.word-cursor.pressed { height: 8px; min-width: 8px; }
.word-cursor.labelled.pressed { height: 25px; min-width: 25px; }
@media (hover: hover) and (pointer: fine) {
  .cursor-ready, .cursor-ready a, .cursor-ready button { cursor: none; }
  .word-cursor.visible { opacity: 1; }
}
@media (pointer: coarse) { .word-cursor { display: none !important; } }
.can-reveal .reveal:not(.is-visible) { opacity: 0; transform: translateY(24px); }
.can-reveal .reveal { transition: opacity 720ms var(--ease), transform 720ms var(--ease); }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

.reduced-motion .entry, .reduced-motion .preloader { display: none; }
.reduced-motion .work, .reduced-motion .cinematic .work { margin-top: 0; padding-top: 150px; opacity: 1 !important; pointer-events: auto !important; }
.reduced-motion *, .reduced-motion *::before, .reduced-motion *::after { animation: none !important; transition-duration: 200ms !important; }
.reduced-motion .reveal { opacity: 1 !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .entry, .preloader { display: none !important; }
  .work, .cinematic .work { margin-top: 0; padding-top: 150px; opacity: 1 !important; pointer-events: auto !important; }
  *, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition-duration: 200ms !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
}

/* ---------- responsive ---------- */
@media (max-width: 1000px) {
  .band.quad { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .section-head { grid-template-columns: 1fr; gap: 30px; }
  .section-head .note { justify-self: start; max-width: 320px; }
  .page-lead { grid-template-columns: 1fr; gap: 26px; }
  .service { grid-template-columns: .34fr 1fr; gap: 30px; }
  .service-body { grid-template-columns: 1fr; }
  .page-sub { justify-self: start; }
  .brief { grid-template-columns: 1fr; }
  .prose, .practice-intro, .pillar, .stack-item { grid-template-columns: .34fr 1fr; gap: 30px; }
  .pillar-index, .stack-index { flex-direction: column; gap: 10px; padding-right: 0; }
  .contact-main { grid-template-columns: 1fr; gap: 52px; padding-block: 66px; }
  .contact-lines { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* With the script the pages stand behind one mark in the corner. Without it
     the navigation stays the full-width taxonomy it already is, because a
     button nothing can open is not a navigation. */
  html:not(.js) .site-header { flex-direction: column; align-items: stretch; gap: 14px; padding-block: 18px 12px; }
  html:not(.js) .nav { justify-content: space-between; gap: 8px 10px; font: 500 11px/1.4 var(--font-label); text-transform: uppercase; letter-spacing: .035em; }
  html:not(.js) .nav a { min-height: 32px; gap: 5px; padding-inline: 4px; margin-inline: -4px; }
  html:not(.js) .nav .icon { display: none; }
  html:not(.js) .site-header.scrolled { padding-bottom: 10px; }
  html:not(.js) .page { padding-top: 132px; }

  .js .nav-toggle { display: flex; margin-block: -11px; }
  .js .site-header { padding-block: calc(18px + var(--safe-top)) 18px; }
  /* The wordmark and the mark stay above the sheet the pages are listed on. */
  .js .site-header .brand { position: relative; z-index: 2; }
  /* The pages listed down the screen, on the ground the header is already
     standing on: opening the menu changes what is written, not where you are. */
  .js .nav {
    position: fixed; inset: 0; z-index: 1;
    flex-direction: column; flex-wrap: nowrap; align-items: stretch; justify-content: flex-start;
    gap: 0; padding: calc(102px + var(--safe-top)) calc(var(--gutter) + var(--safe-right)) calc(var(--gutter) + 24px + var(--safe-bottom)) calc(var(--gutter) + var(--safe-left));
    background: var(--canvas);
    font-size: clamp(26px, 7.6vw, 38px); letter-spacing: -.03em;
    overflow-y: auto; overscroll-behavior: contain;
    visibility: hidden; opacity: 0;
    transition: opacity 280ms var(--ease), visibility 0s 280ms;
  }
  .js.nav-open .nav { visibility: visible; opacity: 1; transition: opacity 280ms var(--ease), visibility 0s; }
  .js .nav > a { justify-content: space-between; gap: 14px; min-height: 0; padding: 19px 0; margin-inline: 0; border-top: 1px solid var(--grey-3); line-height: 1; }
  .js .nav > a:last-child { border-bottom: 1px solid var(--grey-3); }
  .js .nav .icon { width: .46em; height: .46em; color: var(--grey-2); }
  /* The languages close the sheet, under the last page rather than among them:
     one row on the same rule, at the size the rest of the small type is set. */
  .js .nav .lang { gap: 16px; padding: 21px 0; border-block: 1px solid var(--grey-3); letter-spacing: .085em; }
  .js .nav .lang a { padding: 4px 6px; margin: -4px -6px; }
  /* The list arrives from the top down, in the order you would read it. */
  .js.nav-open .nav > a, .js.nav-open .nav .lang { animation: menu-in 460ms var(--ease) both; }
  .js.nav-open .nav > a:nth-child(1) { animation-delay: 40ms; }
  .js.nav-open .nav > a:nth-child(2) { animation-delay: 80ms; }
  .js.nav-open .nav > a:nth-child(3) { animation-delay: 120ms; }
  .js.nav-open .nav > a:nth-child(4) { animation-delay: 160ms; }
  .js.nav-open .nav > a:nth-child(5) { animation-delay: 200ms; }
  .js.nav-open .nav > a:nth-child(6) { animation-delay: 240ms; }
  .js.nav-open .nav .lang { animation-delay: 280ms; }
  /* Nothing scrolls behind the sheet. */
  .js.nav-open, .js.nav-open body { overflow: hidden; }
}
@media (max-width: 370px) {
  /* The narrowest phones keep the whole navigation on one line. */
  html:not(.js) .nav { font-size: 10.5px; }
}
@media (max-width: 620px) {
  /* One size for every title. The display faces are set from a single token
     here, so no line runs into the gutter and the page speaks in one voice
     instead of four. */
  :root { --size-title: clamp(27px, 8.2vw, 38px); }
  .shout, .work .shout, .contact .shout, .gap-shout, .flight-copy .shout,
  .page-title, .statement, .pillar h2, .pillar h3, .service h3,
  .process-title, .intel-title, .gap-title, .closing-title { font-size: var(--size-title); }
  /* Passages that carry the argument on a wide frame and only crowd a phone. */
  .wide-only { display: none; }
  /* The beliefs are five, not numbered. */
  #beliefs-film .process-meter { display: none; }
  .brand-lab { display: none; }
  .entry, .hero-stage { min-height: 560px; }
  .hero-thesis { top: 34%; max-width: 330px; font-size: var(--size-sub); }
  /* On a phone the entry is not the word but one letter of it: the W blown up
     until about three quarters of it reaches the right edge, its stem standing
     on the same line as the header wordmark. site.js measures that box, and
     the mask in the scene is cut from the same numbers, so the flat image and
     the animated one are never in two places. The claim rides on the W's cap
     rather than on the frame, which is why its edge comes from there too. */
  .js .fallback-art { top: var(--word-box-top); left: var(--word-box-left); transform: none; }
  /* 9em holds the claim to the three lines it is written in: the ampersand
     travels down to the design it belongs to, and nothing reaches the gutter.
     The measure is cut to the sentence, so a translation sets its own: Spanish
     runs about a fifth longer and needs the room to stand in three lines too. */
  .js .hero-thesis { top: auto; bottom: var(--hero-text-bottom); max-width: 9em; font-size: 15.33px; /* 11.5pt */ line-height: 1.31; }
  .js[lang="es"] .hero-thesis { max-width: 12em; }
  .scroll-in { left: calc(var(--word-left, 3.5vw) + var(--safe-left)); right: calc(var(--word-left, 3.5vw) + var(--safe-right)); bottom: calc(22px + var(--safe-bottom)); justify-content: space-between; padding-top: 14px; border-top: 1px solid var(--grey-3); }
  .scroll-in .icon { height: 23px; }
  .work { padding-top: 54px; }
  .kicker > span:last-child { max-width: 130px; text-align: right; }
  .kicker > span:only-child { max-width: none; text-align: left; }
  .section-head { margin-top: 32px; }
  .filter-bar { margin-top: 38px; }
  .filter-bar > .label { display: none; }
  .project-grid { grid-template-columns: 1fr; gap: 30px; }
  .project-col { gap: 30px; }
  .project-media { aspect-ratio: 4 / 3; }
  /* On a phone the index is one column of identical portrait plates. */
  .index-grid .project-media.landscape, .index-grid .project-media.portrait, .index-grid .project-media.square { aspect-ratio: 4 / 5; }
  .index-grid .lumen-room { object-position: 50% 50%; }
  .index-grid .card-film { object-position: 46% 50%; }
  .gap-shout { margin-top: -.18em; }
  .gap-note { margin-left: 0; }
  .prose, .practice-intro, .pillar, .stack-item { grid-template-columns: 1fr; gap: 22px; }
  .practice-intro { padding: 36px 0 52px; }
  .pillar-index, .stack-index { flex-direction: row; justify-content: space-between; }
  .practice-end { flex-direction: column; align-items: flex-start; gap: 14px; }
  .argument li { padding-left: 3.2rem; }
  .band.pair, .band.left, .band.right, .band.trio, .band.quad, .band.aside { grid-template-columns: 1fr; }
  .band.right > figure { grid-column: 1; }
  .service { grid-template-columns: 2.2rem minmax(0, 1fr); gap: 0 12px; }
  .service-body { grid-template-columns: 1fr; }
  .prose > .label:empty, .pillar-index:empty { display: none; }
  /* On a phone the line stands in two rows, one word pair arriving from each side. */
  .gap-line { flex-direction: column; align-items: center; gap: .08em; }
  .ledger th:last-child, .ledger td:last-child { display: none; }
  .foot-bottom { padding-block: 16px; }
  .flight-status { bottom: 28px; }
}
@media (max-height: 620px) and (min-width: 700px) {
  .entry, .hero-stage { min-height: 100svh; }
  .hero-thesis { top: 28%; }
  .scroll-in { bottom: calc(20px + var(--safe-bottom)); }
}

@media print {
  .entry, .site-header, .preloader, .word-cursor, .filters, .foot-controls, .foot-field { display: none !important; }
  .foot-mark .wordmark { opacity: 1 !important; }
  .work, .cinematic .work { margin-top: 0; padding-top: 20px; opacity: 1 !important; }
  .project-card, .pillar, .band, .stack-item { break-inside: avoid; }
  .reveal { opacity: 1 !important; transform: none !important; }
  [data-polarity="dark"] { background: #fff; color: #000; }
}
