/* ===========================================================================
   QI-LIGHT — architectural stained glass
   ---------------------------------------------------------------------------
   The whole site is a dark field, because that is what a lightbox is. Every
   photograph here is of glass with light behind it; on a white page the
   colour goes grey and the lead lines disappear. On #0a0a0c the glass does
   the same thing it does in the building — it glows.

   Consequences of that decision, applied consistently:
     · no cards, no shadows, no rounded corners — those are paper devices, and
       a photograph of a dome doesn't need a paper frame around it.
     · the only structural line is 1px at 13% white: the "lead line". It is the
       same device the craft uses to separate one colour from the next.
     · gold (#e0b357) is an accent, never a fill. It marks eyebrows, active
       states and rules — the warm metal note that runs through the archive.
   =========================================================================== */

/* --- fonts ---------------------------------------------------------------- */

@font-face {
  font-family: 'Aleo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/aleo-latin-400-normal.woff2') 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: 'Aleo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/aleo-latin-ext-400-normal.woff2') 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: 'Aleo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/aleo-latin-500-normal.woff2') 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: 'Aleo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/aleo-latin-ext-500-normal.woff2') 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;
}

/* --- tokens --------------------------------------------------------------- */

:root {
  /* The page is a dark field, and the browser needs to be told so. Without it
     the UA still renders its own chrome light: scrollbars, the text caret, and
     Chrome's autofill highlight, which paints a pale block behind an input and
     hides the paper-white text typed into it. */
  color-scheme: dark;

  --ink: #0a0a0c;
  --ink-raised: #101013;
  --ink-deep: #060607;
  --paper: #f4f1ea;
  --muted: rgba(244, 241, 234, 0.64);  /* 7.4:1 on --ink */
  /* 0.44 measured 3.98:1 — under AA, and it carries real content: project
     locations, CV entries, form labels. 0.50 is the first step that clears
     4.5:1 on all three ink values while keeping the hierarchy visible. */
  --faint: rgba(244, 241, 234, 0.52);  /* 5.2:1 */
  --line: rgba(244, 241, 234, 0.14);
  --line-soft: rgba(244, 241, 234, 0.07);
  --gold: #e0b357;
  --gold-dim: rgba(224, 179, 87, 0.42);

  --gutter: clamp(20px, 5.2vw, 88px);
  /* `ch` resolves against the element's own font size, so a 46ch lede at
     18.4px came out 532px while a 62ch body at 16px came out 625px — the
     paragraph that introduces a section was set narrower than the section it
     introduces, and a page carried three or four different text edges. `rem`
     is root-relative, so every block that uses this lands on one edge. */
  --measure: 39rem;        /* 624px, the width 62ch used to give the body */
  --measure-cjk: 38rem;    /* 608px; CJK wants fewer characters per line */
  --shell: 1480px;

  --step: clamp(64px, 9vw, 132px); /* vertical rhythm between sections */

  --serif: 'Aleo', Georgia, 'Times New Roman', serif;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Locale-scoped stacks. Self-hosting CJK display faces would cost several MB
   per script, so each language borrows the best serif already on the reader's
   machine and falls back to its system sans rather than to a wrong-script face. */
html:lang(zh) {
  --serif: 'Aleo', 'Songti SC', 'STSong', 'Noto Serif CJK SC', 'Source Han Serif SC',
    'SimSun', serif;
  --sans: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC',
    system-ui, sans-serif;
}
html:lang(ja) {
  --serif: 'Aleo', 'Hiragino Mincho ProN', 'Yu Mincho', 'Noto Serif JP', 'MS PMincho', serif;
  --sans: 'Hiragino Sans', 'Yu Gothic', 'Noto Sans JP', 'Meiryo', system-ui, sans-serif;
}
html:lang(ko) {
  --serif: 'Aleo', 'Nanum Myeongjo', 'AppleMyungjo', 'Noto Serif KR', 'Batang', serif;
  --sans: 'Apple SD Gothic Neo', 'Pretendard', 'Malgun Gothic', 'Noto Sans KR', system-ui, sans-serif;
}

/* --- reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The header is fixed, so a jump to #spaces (or any in-page link, or the skip
     link) parks the section flush against the top of the viewport and the
     header covers its eyebrow and heading — 69px of it on a phone, 85px on a
     desktop. Stop the scroll below the header, with a little air. */
  scroll-padding-top: clamp(88px, 8vw, 110px);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  /* Every size on the site is rem, so a reader who has set a larger default
     font size in their browser actually gets one. A px scale would ignore that
     setting entirely and only respond to full-page zoom. */
  font-size: 1rem;
  line-height: 1.68;
  /* `text-wrap` is inherited, and it only takes effect on a block container —
     setting it on the spans that hold a card title or a citation would do
     nothing. Inheriting it from here reaches every block on the page and keeps
     a last line from ending on one stranded glyph or a three-letter word.
     Browsers that don't know `pretty` keep their ordinary wrapping. */
  text-wrap: pretty;
  /* Last resort only — it engages when a word has no break opportunity and is
     wider than its column, which is the one case that would otherwise put the
     whole page into sideways scrolling. `break-word`, not `anywhere`: the two
     wrap identically, but `anywhere` also drops min-content to a single
     character, which lets every grid column shrink to its 1fr share and then
     breaks "Privatresidenzen" as "Privatresidenze / n" on a tablet. */
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;  /* fallback for Safari < 16 */
  overflow-x: clip;
}

img, picture, svg { display: block; max-width: 100%; }

/* Author `display` rules outrank the UA stylesheet's `[hidden]`, so `.card`
   and `.arch__t` stayed visible when the filters set `hidden` on them.
   This is the one place !important is the correct tool. */
[hidden] { display: none !important; }
button, input, textarea, select { font: inherit; color: inherit; }

h1, h2, h3, h4 { margin: 0; font-weight: 400; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
/* The UA stylesheet gives <figure> `1em 40px`. The reset had every other
   element but this one, so each gallery plate sat 40px inside the text
   column it was supposed to line up with — at every width, in every
   locale, on all 71 project pages. */
figure { margin: 0; }

a { color: inherit; text-decoration: none; }

::selection { background: var(--gold); color: #100c04; }

:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 3px;
}

/* --- type primitives ------------------------------------------------------ */

.eyebrow {
  font-family: var(--sans);
  font-size: 0.6875rem;
  line-height: 1.4;
  letter-spacing: 0.2em;
  color: var(--gold);
  margin: 0 0 1.4em;
  display: block;
}
html.is-latin .eyebrow { text-transform: uppercase; }
html.is-cjk .eyebrow { font-size: 0.75rem; letter-spacing: 0.16em; }

.display {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.015em;
  /* The vw ceiling matters at a 200% browser font size on a 320px screen:
     without it the rem floor wins and a word like "construisons" is set wider
     than the column, taking the whole page into sideways scrolling. */
  font-size: min(clamp(2.4rem, 6.4vw, 5.2rem), 13vw);
  text-wrap: balance;
}
.h2 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.01em;
  font-size: min(clamp(1.9rem, 3.6vw, 3.1rem), 11vw);
  text-wrap: balance;
}
.h3 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.2;
  font-size: clamp(1.25rem, 1.9vw, 1.6rem);
}
html.is-cjk .display { line-height: 1.2; letter-spacing: 0.005em; }
html.is-cjk .h2 { line-height: 1.32; letter-spacing: 0.005em; }
html.is-cjk .h3 { line-height: 1.5; }

/* One clause of a CJK heading, emitted by `hd()` in tools/build.py. Making it
   inline-block is the whole mechanism: the line breaker treats the clause as a
   single unit and breaks between clauses in preference to inside one.

   `span.cl` rather than `.cl`, because any `<ancestor> span { display: … }`
   rule elsewhere in this file outranks a bare class and turns every clause into
   its own line. `.marks span` did exactly that: the stat label
   「窑炉、镶嵌台与钢构车间」 stacked two lines deep in a 274px box that fits it
   in 176px, at every width, in zh, ja and ko. */
span.cl { display: inline-block; }

/* Korean is written with spaces, but CSS `word-break: normal` still lets a line
   break between any two Hangul syllables — so 로즈가든 splits as 로즈가/든.
   `keep-all` is the standard fix and costs nothing: the spaces are break
   opportunities already. */
html:lang(ko) { word-break: keep-all; overflow-wrap: break-word; }

/* Balancing works against the clauses: it will pull a glyph across a clause
   boundary to even the line lengths, which is the exact break the markup is
   there to avoid. `pretty` instead spends its effort on the last line, which is
   where a lone stranded glyph shows up. */
html.is-cjk .display,
html.is-cjk .h2 { text-wrap: pretty; }

/* On a phone the Latin clamps bottom out at a fixed rem, so a 320px screen gets
   the same display size as a 430px one. A CJK title is a solid block of
   full-width glyphs with far fewer places to break, so that floor is what
   pushes a lone glyph — or a lone full stop — onto a line of its own. Scale the
   floor with the viewport instead; the ceilings keep the desktop sizes intact. */
@media (max-width: 32.5em) {
  html.is-cjk .display { font-size: clamp(1.6rem, 7.7vw, 2.4rem); }
  html.is-cjk .display--page,
  html.is-cjk .phero__title { font-size: clamp(1.45rem, 6.9vw, 2.1rem); }
  html.is-cjk .h2 { font-size: clamp(1.35rem, 6.1vw, 1.9rem); }
}

.lede {
  /* The vw term does not overtake the floor until the viewport passes about
     1200px, so below that the lede rendered at 16.3px against 16px body — a 2%
     step, which is no step at all. On a phone and on an iPad, the paragraph
     that opens every section read as ordinary body copy. Raise the floor so the
     distinction holds at the sizes this site is actually read at. */
  font-size: clamp(1.15rem, 1.35vw, 1.22rem);
  line-height: 1.62;
  color: var(--muted);
  max-width: var(--measure);
}
html.is-cjk .lede { line-height: 1.85; max-width: var(--measure-cjk); }

.body { color: var(--muted); max-width: var(--measure); }
html.is-cjk .body { line-height: 1.95; max-width: var(--measure-cjk); }
.body + .body { margin-top: 1.1em; }

.small {
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--faint);
  max-width: var(--measure);
}
html.is-cjk .small { max-width: var(--measure-cjk); }

/* --- shell ---------------------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.section { padding-block: var(--step); }
.section--tight { padding-block: calc(var(--step) * 0.62); }
.rule { height: 1px; background: var(--line-soft); border: 0; margin: 0; }

/* Present to a screen reader, absent to the eye. The studio page's people
   section needs a heading so the level does not jump h1 -> h3, but the design
   has no room for a visible one — the section is introduced by the page lede.
   Not `display: none`, which would remove it from the accessibility tree too. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* `fixed`, not `absolute`: absolute positions it against the document, so on a
   page scrolled 1500px down the focused skip link sat at viewport top -1488px —
   off-screen. It is the first tab stop from anywhere on the page, including
   after a mouse user has scrolled, so it has to be pinned to the viewport. */
.skip {
  position: fixed;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--gold);
  color: #100c04;
  padding: 10px 18px;
}
.skip:focus { left: 12px; top: 12px; }

/* --- image primitive ------------------------------------------------------ */

.fig {
  position: relative;
  overflow: hidden;
  background: var(--ink-raised);
}
/* `picture` is inline by default, which breaks the percentage-height chain and
   leaves the image at its intrinsic size inside any container that is sized by
   something other than an aspect-ratio (the hero, the CTA band, project heroes). */
.fig picture { display: block; width: 100%; height: 100%; }
.fig--fill { height: 100%; }
.fig img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The fade-in and the scroll reveal are hidden-until-JS effects. They are
   scoped to .js — set by a one-line inline script in <head> — so that with
   JavaScript blocked the page renders fully visible instead of blank. */
.js .fig img {
  opacity: 0;
  transform: scale(1.014);
  transition: opacity 0.8s var(--ease), transform 1.2s var(--ease);
}
.js .fig img.is-loaded { opacity: 1; transform: none; }
.fig--contain img { object-fit: contain; }

/* Slow, restrained zoom on interactive figures. Photographs of built work
   should not bounce; 1.5s reads as light moving rather than as a UI effect. */
@media (hover: hover) {
  a:hover > .fig img,
  a:focus-visible > .fig img,
  .card:hover .fig img { transform: scale(1.035); transition-duration: 1.5s; }
}

/* --- header --------------------------------------------------------------- */

.head {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease);
  border-bottom: 1px solid transparent;
}
.head::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(6, 6, 7, 0.72), rgba(6, 6, 7, 0));
  pointer-events: none;
  transition: opacity 0.4s var(--ease);
}
.head.is-stuck {
  background: rgba(10, 10, 12, 0.86);
  backdrop-filter: saturate(1.3) blur(14px);
  -webkit-backdrop-filter: saturate(1.3) blur(14px);
  border-bottom-color: var(--line-soft);
}
.head.is-stuck::before { opacity: 0; }

.head__inner {
  position: relative;
  display: flex;
  align-items: center;
  gap: 24px;
  height: 68px;
}
@media (min-width: 56.25em) { .head__inner { height: 84px; } }

.wordmark {
  font-family: var(--serif);
  /* A logotype, not running text: it is set `nowrap`, so at a 200% browser font
     size on a 320px screen it grew to ~360px and pushed the whole page into
     sideways scrolling. The vw term caps it at the width the screen can hold;
     everywhere else the rem value wins and it still answers the reader's text
     size setting. */
  font-size: min(1.1875rem, 5.6vw);
  letter-spacing: 0.16em;
  line-height: 1;
  white-space: nowrap;
}
.wordmark span { color: var(--gold); }

.head__nav { display: none; margin-left: auto; }
@media (min-width: 56.25em) {
  .head__nav { display: flex; align-items: center; gap: 34px; }
}
.navlink {
  position: relative;
  font-size: 0.8125rem;
  letter-spacing: 0.09em;
  color: var(--muted);
  padding-block: 6px;
  transition: color 0.25s var(--ease);
}
html.is-latin .navlink { text-transform: uppercase; letter-spacing: 0.14em; }
.navlink::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--ease);
}
.navlink:hover, .navlink[aria-current] { color: var(--paper); }
.navlink:hover::after, .navlink[aria-current]::after { transform: scaleX(1); }

/* language menu */
.lang { position: relative; }
.lang__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 7px 13px;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.lang__btn:hover { border-color: var(--gold-dim); color: var(--paper); }
.lang__btn svg { width: 9px; height: 9px; flex: none; opacity: 0.7; }
.lang__menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 176px;
  background: rgba(12, 12, 15, 0.97);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--line);
  padding: 6px;
  display: none;
}
.lang__menu.is-open { display: block; }
.lang__menu a {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 12px;
  font-size: 0.8125rem;
  color: var(--muted);
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.lang__menu a:hover { background: rgba(244, 241, 234, 0.06); color: var(--paper); }
.lang__menu a[aria-current] { color: var(--gold); }
.lang__menu em { font-style: normal; opacity: 0.42; font-size: 0.6875rem; letter-spacing: 0.1em; }

/* burger */
.burger {
  margin-left: auto;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0 8px;
  background: none;
  border: 0;
  cursor: pointer;
}
@media (min-width: 56.25em) { .burger { display: none; } }
.burger i {
  display: block;
  height: 1px;
  background: var(--paper);
  transition: transform 0.35s var(--ease), opacity 0.2s var(--ease);
}
body.nav-open .burger i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
body.nav-open .burger i:nth-child(2) { opacity: 0; }
body.nav-open .burger i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.drawer {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--ink-deep);
  padding: 100px var(--gutter) 40px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease), visibility 0.4s;
  overflow-y: auto;
}
body.nav-open .drawer { opacity: 1; visibility: visible; transform: none; }
/* An anchor is inline, and the drawer markup carries no whitespace between the
   five links — left as-is they set as one unbroken run of text ("WorkArchive
   Capabilities…", and in Chinese a wall of glyphs with nothing between them).
   Each one is a row of the menu, so each one is a block. */
.drawer nav { display: flex; flex-direction: column; }
.drawer a.drawer__link {
  display: block;
  font-family: var(--serif);
  font-size: clamp(1.9rem, 8vw, 2.6rem);
  line-height: 1.5;
  border-bottom: 1px solid var(--line-soft);
  padding-block: 14px;
}
.drawer__langs {
  margin-top: auto;
  padding-top: 32px;
  display: flex;
  flex-wrap: wrap;
  /* The rows space themselves now that each link carries 11px of padding; an
     extra row gap on top of that would double-count. */
  gap: 0 20px;
}
/* Six endonyms of very different widths wrap 5 + 1 on a phone, which reads as a
   mistake rather than a list. Three and three, on a grid — sized in em, so a
   reader who turns their text up gets fewer columns instead of a clipped
   "Français". */
@media (max-width: 30em) {
  .drawer__langs { display: grid; grid-template-columns: repeat(auto-fit, minmax(6.5em, 1fr)); }
}
/* A short screen — an iPhone SE upright, or any phone held sideways — cannot
   hold five 30px menu rows plus the language list, and the last row landed just
   past the fold. Tighten the menu instead of asking the reader to scroll a
   five-item nav. */
@media (max-height: 40em) {
  .drawer { padding-top: 84px; }
  .drawer a.drawer__link { font-size: clamp(1.5rem, 6.4vw, 2rem); padding-block: 10px; }
  .drawer__langs { padding-top: 22px; }
}
/* Real padding, not the negative-inset ::after the other small links use. That
   trick is inside `max-width: 51.25em`, so a phone held sideways (844px wide,
   390 tall) fell out of it and got bare 24px targets; and in portrait the insets
   of adjacent rows overlapped across the 8px row gap and stole area from each
   other, measuring 31px instead of the intended 46px. Padding is part of the
   box: it cannot collide and it needs no media query. */
.drawer__langs a { font-size: 0.875rem; color: var(--muted); padding-block: 11px; }
.drawer__langs a[aria-current] { color: var(--gold); }

/* --- hero ----------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100vh;   /* fallback: svh is Safari 15.4+ / Chrome 108+ */
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-top: 120px;
  isolation: isolate;
}
.hero__bg { position: absolute; inset: 0; z-index: -2; }
.hero__bg .fig { height: 100%; }
/* Three overlapping scrims rather than one. The vertical ramp protects the
   headline and the corner radial protects the button block, but the hero title
   is long — in German and Japanese it runs to three lines and climbs into the
   brightest part of the room, where measured contrast fell to 2.0:1 against a
   lit staircase. The horizontal ramp darkens the left column the type actually
   occupies and leaves the right of the frame, where the dome is, alone.
   Hospitality interiors are lit for the room, not for a website. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  --scrim-up: 0.72;   /* vertical ramp, just under half way up */
  --scrim-mid: 0.38;  /* horizontal ramp, 44% across */
  --scrim-end: 78%;   /* where the horizontal ramp reaches zero */
  background:
    linear-gradient(to top, rgba(6, 6, 7, 0.97) 0%, rgba(6, 6, 7, 0.9) 24%,
      rgba(6, 6, 7, var(--scrim-up)) 46%, rgba(6, 6, 7, 0.6) 62%,
      rgba(6, 6, 7, 0.42) 78%, rgba(6, 6, 7, 0.72) 100%),
    linear-gradient(to right, rgba(6, 6, 7, 0.54) 0%, rgba(6, 6, 7, var(--scrim-mid)) 44%,
      rgba(6, 6, 7, 0) var(--scrim-end)),
    radial-gradient(120% 80% at 12% 92%, rgba(6, 6, 7, 0.82), transparent 66%);
}
/* The eyebrow is 11px, so WCAG asks 4.5:1 of it, not the 3:1 the display type
   gets. Gold sits at 0.49 luminance and the brightest patch behind it measures
   0.28 — too close, and no text colour fixes that on its own; even pure white
   only reached 2.8:1 there. Darkening the whole photograph enough for gold
   would have cost the hero its subject, so the scrim carries part of it and the
   hero eyebrow takes a pale warm tone that stays in the gold family. Measured
   worst case across six locales and four widths: 4.57:1. */
.hero .eyebrow,
.phero .eyebrow { color: #faf0dc; }
/* On a phone the title runs the full width of the frame, so the horizontal ramp
   has to carry past the right edge instead of fading at 72%; the shorter hero
   also crops the photograph higher, putting the lit part of the room directly
   behind the words. Measured worst-case contrast at 320px: 2.2:1 without this,
   3.2:1 with it. */
@media (max-width: 30em) {
  .hero::after { --scrim-up: 0.66; --scrim-mid: 0.44; --scrim-end: 128%; }
}
.hero__inner { width: 100%; padding-bottom: clamp(40px, 7vh, 84px); }
.hero__title { max-width: 17ch; margin-bottom: 0.5em; }
html.is-cjk .hero__title { max-width: 15em; }
.hero .lede { max-width: var(--measure); }
html.is-cjk .hero .lede { max-width: var(--measure-cjk); }

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 2.2em;
}

.btn {
  --btn-fg: var(--paper);
  display: inline-flex;
  align-items: center;
  /* A flex item will not shrink below the longest word in it, so at a 200%
     browser font size "DAS ATELIER KENNENLERNEN" made the button wider than a
     320px screen and took the page sideways with it. `anywhere` is the only
     value that also lowers that minimum, which lets the label wrap and keeps
     the arrow beside it; it only engages once the label cannot fit. */
  max-width: 100%;
  overflow-wrap: anywhere;
  gap: 12px;
  border: 1px solid var(--line);
  color: var(--btn-fg);
  padding: 14px 26px;
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  background: transparent;
  cursor: pointer;
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease),
    color 0.3s var(--ease);
}
html.is-latin .btn { text-transform: uppercase; letter-spacing: 0.14em; }
.btn:hover { border-color: var(--gold); color: var(--gold); }
.btn--solid {
  background: var(--gold);
  border-color: var(--gold);
  color: #100c04;
}
.btn--solid:hover { background: #eec471; border-color: #eec471; color: #100c04; }
.btn__arrow { width: 16px; height: 8px; flex: none; transition: transform 0.3s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

.scrollcue {
  position: absolute;
  right: var(--gutter);
  bottom: clamp(40px, 7vh, 84px);
  display: none;
  align-items: center;
  gap: 12px;
  font-size: 0.625rem;
  letter-spacing: 0.24em;
  color: var(--faint);
  writing-mode: vertical-rl;
}
html.is-latin .scrollcue { text-transform: uppercase; }
@media (min-width: 56.25em) { .scrollcue { display: flex; } }
.scrollcue::after {
  content: '';
  width: 1px;
  height: 56px;
  background: linear-gradient(var(--gold-dim), transparent);
}

/* --- marks strip ---------------------------------------------------------- */

.marks {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--line-soft);
}
@media (min-width: 53.75em) { .marks { grid-template-columns: repeat(4, 1fr); } }
.marks li {
  min-width: 0;
  padding: 26px 0 28px;
  border-bottom: 1px solid var(--line-soft);
  padding-right: 20px;
}
@media (min-width: 53.75em) {
  .marks li { border-bottom: 0; padding-block: 32px 34px; }
  .marks li + li { border-left: 1px solid var(--line-soft); padding-left: 28px; }
}
.marks b {
  display: block;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.6rem, 2.6vw, 2.2rem);
  line-height: 1.1;
  color: var(--paper);
  margin-bottom: 0.35em;
}
/* Direct child only: the label may itself contain `.cl` clause spans. */
.marks > li > span { font-size: 0.7812rem; line-height: 1.5; color: var(--faint); display: block; }

/* --- editorial two-column ------------------------------------------------- */

.duo {
  display: grid;
  gap: clamp(28px, 5vw, 72px);
  align-items: start;
}
@media (min-width: 56.25em) { .duo { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); } }
.duo--wide { }
@media (min-width: 56.25em) { .duo--wide { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); } }

.sechead { margin-bottom: clamp(32px, 5vw, 64px); }
.sechead .lede { margin-top: 1.1em; }

/* --- scenario list -------------------------------------------------------- */

.scenes { border-top: 1px solid var(--line-soft); }
.scene {
  position: relative;
  display: grid;
  gap: 6px 28px;
  padding-block: clamp(24px, 3.4vw, 40px);
  border-bottom: 1px solid var(--line-soft);
  align-items: baseline;
}
@media (min-width: 56.25em) {
  .scene {
    grid-template-columns: 64px minmax(0, 5fr) minmax(0, 6fr);
    align-items: start;
  }
}
.scene__n {
  font-family: var(--serif);
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  color: var(--gold);
  padding-top: 0.45em;
}
.scene__name { font-family: var(--serif); font-size: clamp(1.5rem, 2.6vw, 2.1rem); line-height: 1.2; }
html.is-cjk .scene__name { line-height: 1.4; }
.scene__lede { color: var(--faint); font-size: 0.875rem; margin-top: 0.5em; }
.scene__body { color: var(--muted); font-size: 0.9375rem; max-width: var(--measure); }
html.is-cjk .scene__body { max-width: var(--measure-cjk); }
html.is-cjk .scene__body { line-height: 1.95; }

/* Hover-follow preview: desktop only, and never on touch where it would just
   sit in the corner unexplained. */
.scene__peek {
  position: fixed;
  z-index: 5;
  width: 300px;
  aspect-ratio: 4 / 3;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.96);
  transition: opacity 0.4s var(--ease), transform 0.5s var(--ease);
  border: 1px solid var(--line);
}
@media (hover: hover) and (min-width: 68.75em) {
  .scene.is-peek .scene__peek { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* --- work grid ------------------------------------------------------------ */

.grid {
  display: grid;
  gap: clamp(26px, 3.4vw, 46px) clamp(18px, 2.4vw, 32px);
  grid-template-columns: 1fr;
}
@media (min-width: 40em) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 67.5em) { .grid--3 { grid-template-columns: repeat(3, 1fr); } }

/* The featured grid on the home page is deliberately uneven: a wall of equal
   rectangles reads as a catalogue, and this is meant to read as a portfolio. */
.grid--feature { grid-template-columns: 1fr; }
@media (min-width: 52.5em) {
  .grid--feature { grid-template-columns: repeat(6, 1fr); }
  .grid--feature > :nth-child(1) { grid-column: span 4; }
  .grid--feature > :nth-child(2) { grid-column: span 2; align-self: end; }
  .grid--feature > :nth-child(3) { grid-column: span 2; }
  .grid--feature > :nth-child(4) { grid-column: span 4; }
  .grid--feature > :nth-child(5) { grid-column: span 3; }
  .grid--feature > :nth-child(6) { grid-column: span 3; }
}

.card { display: block; }
.card .fig { aspect-ratio: 4 / 3; margin-bottom: 18px; }
.card--tall .fig { aspect-ratio: 3 / 4; }
.card--wide .fig { aspect-ratio: 16 / 10; }
.card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  color: var(--gold);
  margin-bottom: 0.7em;
}
html.is-latin .card__meta { text-transform: uppercase; }
.card__meta span:not(:last-child)::after { content: ' · '; color: var(--gold-dim); }
.card__title {
  font-family: var(--serif);
  font-size: clamp(1.14rem, 1.6vw, 1.42rem);
  line-height: 1.28;
  transition: color 0.25s var(--ease);
}
html.is-cjk .card__title { line-height: 1.5; }
.card:hover .card__title { color: var(--gold); }
.card__blurb {
  color: var(--faint);
  font-size: 0.875rem;
  line-height: 1.6;
  margin-top: 0.55em;
  max-width: 46ch;
}
html.is-cjk .card__blurb { line-height: 1.85; }
.card__place { color: var(--faint); font-size: 0.8125rem; margin-top: 0.5em; }

/* Every layout grid on the site. A grid item's default `min-width: auto` takes
   its floor from the widest word inside it, so one German compound — or one
   unspaced Chinese address — sets the column wider than its share and carries
   the whole page into sideways scrolling. At a 200% browser font size that hit
   the home, studio, capabilities, contact and project pages. Zero lets the
   column keep its share; `overflow-wrap` on the body then breaks the word. */
.marks > *, .duo > *, .scene > *, .grid > *, .filters > *, .meta > *,
.plates > *, .pager > *, .steps > *, .row > *, .cv li > *, .person > *,
.form > *, .contactblock > div > *, .foot__grid > *, .chooser__list > *,
.sheet > * { min-width: 0; }

/* --- filters -------------------------------------------------------------- */

.filters {
  display: grid;
  /* The row gap has to beat the gap the chips wrap at, or the two groups read
     as one block: on a phone «按空间» wraps to three rows and «按类型» to four,
     11px apart inside each group, and only 18px between them. */
  gap: 30px 40px;
  padding-block: 22px 26px;
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: clamp(34px, 4vw, 56px);
}
@media (min-width: 53.75em) { .filters { grid-template-columns: 1fr 1fr; } }
/* A chip carries 12px of side padding, so on a phone — where the chips wrap to
   three or four rows — every row after the first started its text 12px inside
   the label above it, and the block read as accidentally ragged. Pulling the
   row back by that padding and giving it to the label instead puts the label
   and every wrapped chip on one left edge. */
.filters__group {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 6px;
  margin-left: -12px;
}
.filters__label {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  color: var(--faint);
  margin-left: 12px;
  margin-right: 12px;
  flex: none;
}
html.is-latin .filters__label { text-transform: uppercase; }
.chip {
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  font-size: 0.8125rem;
  padding: 5px 12px;
  cursor: pointer;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.chip:hover { color: var(--paper); }
.chip[aria-pressed='true'] { color: var(--gold); border-color: var(--gold-dim); }
.count { font-size: 0.8125rem; color: var(--faint); margin-bottom: 22px; }
.empty { color: var(--faint); padding-block: 60px; text-align: center; }

/* --- project detail ------------------------------------------------------- */

.phero { position: relative; height: min(84svh, 860px); min-height: 420px; }
.phero .fig { position: absolute; inset: 0; }
.phero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(6, 6, 7, 0.95) 0%, rgba(6, 6, 7, 0.68) 26%,
    rgba(6, 6, 7, 0.14) 60%, rgba(6, 6, 7, 0.6) 100%);
}
.phero__inner {
  position: absolute;
  inset: auto 0 0;
  padding-bottom: clamp(28px, 5vh, 56px);
  z-index: 1;
}
.phero__title { max-width: 20ch; font-size: clamp(2rem, 4.8vw, 3.8rem); }

.meta {
  display: grid;
  /* Four short facts — place, room, type, count. Stacked one per row they cost
     most of a phone screen before the reader reaches the description, so pair
     them up until there is room for the full row. */
  column-gap: 20px;
  border-top: 1px solid var(--line-soft);
}
/* Four columns need ~900px: at iPad-portrait width they come out 148px wide,
   narrower than "Privatresidenzen", and the word breaks across two lines. */
@media (min-width: 22.5em) { .meta { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 56em) { .meta { grid-template-columns: repeat(4, 1fr); column-gap: 0; } }
.meta div {
  padding: 20px 0 22px;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 56em) {
  .meta div { border-bottom: 0; padding-right: 24px; }
  .meta div + div { border-left: 1px solid var(--line-soft); padding-left: 26px; }
}
.meta dt {
  font-size: 0.6562rem;
  letter-spacing: 0.2em;
  color: var(--faint);
  margin-bottom: 0.9em;
}
html.is-latin .meta dt { text-transform: uppercase; }
.meta dd { margin: 0; font-family: var(--serif); font-size: 1.05rem; line-height: 1.4; }

/* Gallery: alternating full-bleed and paired plates so a long project page
   keeps a rhythm instead of turning into a scroll of identical thumbnails. */
.plates { display: grid; gap: clamp(18px, 2.6vw, 34px); }
.plate .fig { aspect-ratio: 3 / 2; }
.plate--portrait .fig { aspect-ratio: 3 / 4; }
.plate__cap { font-size: 0.7812rem; color: var(--faint); margin-top: 10px; max-width: 60ch; }
@media (min-width: 47.5em) {
  .plates { grid-template-columns: repeat(2, 1fr); }
  .plate--full { grid-column: 1 / -1; }
  .plate--full .fig { aspect-ratio: 16 / 9; }
}

.pager {
  display: grid;
  gap: 20px;
  border-top: 1px solid var(--line-soft);
  padding-top: 28px;
}
@media (min-width: 47.5em) { .pager { grid-template-columns: 1fr auto 1fr; align-items: center; } }
.pager__label { font-size: 0.6875rem; letter-spacing: 0.18em; color: var(--faint); display: block; margin-bottom: 0.6em; }
html.is-latin .pager__label { text-transform: uppercase; }
.pager__title { font-family: var(--serif); font-size: 1.1rem; transition: color 0.25s var(--ease); }
.pager a:hover .pager__title { color: var(--gold); }
.pager__next { text-align: right; }

/* --- process -------------------------------------------------------------- */

.steps { display: grid; gap: 0; border-top: 1px solid var(--line-soft); }
@media (min-width: 47.5em) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 70em) { .steps { grid-template-columns: repeat(3, 1fr); } }
.step {
  padding: 28px 0 30px;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 47.5em) {
  .step { padding: 32px 28px 36px 0; }
  .step:nth-child(2n) { padding-left: 28px; border-left: 1px solid var(--line-soft); }
}
@media (min-width: 70em) {
  .step { padding: 34px 30px 40px 0; }
  .step:nth-child(2n) { padding-left: 0; border-left: 0; }
  .step:not(:nth-child(3n + 1)) { padding-left: 30px; border-left: 1px solid var(--line-soft); }
}
.step__n {
  font-family: var(--serif);
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  color: var(--gold);
  display: block;
  margin-bottom: 1.2em;
}
.step__t { font-family: var(--serif); font-size: 1.22rem; line-height: 1.3; margin-bottom: 0.6em; }
/* Was 0.9062rem — half a pixel from .scene__body's 0.9375rem, which reads as
   the same size set slightly wrong rather than as a level of its own. */
.step__b { color: var(--muted); font-size: 0.9375rem; line-height: 1.68; }
html.is-cjk .step__b { line-height: 1.95; }

/* --- definition rows (materials, client info, credentials) ---------------- */

.rows { border-top: 1px solid var(--line-soft); }
.row {
  display: grid;
  gap: 6px 32px;
  padding-block: 22px 24px;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 53.75em) { .row { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); } }
.row__t { font-family: var(--serif); font-size: 1.12rem; line-height: 1.35; }
/* The row's text column is 7/11 of the shell, which comes to 801px on a
   laptop — about 95 characters a line, well past the point where the eye
   loses its place returning to the left edge. Hold it on the site measure. */
.row__b {
  color: var(--muted);
  font-size: 0.9062rem;
  line-height: 1.7;
  max-width: var(--measure);
}
html.is-cjk .row__b { line-height: 1.95; max-width: var(--measure-cjk); }

.cv { border-top: 1px solid var(--line-soft); }
.cv li {
  display: grid;
  gap: 2px 28px;
  padding-block: 16px 18px;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 43.75em) { .cv li { grid-template-columns: 4.4em minmax(0, 1fr); } }
.cv__y { font-family: var(--serif); color: var(--gold); font-size: 0.8125rem; letter-spacing: 0.06em; padding-top: 0.28em; }
/* The credential list's text cell fills the shell — a citation title then
   runs the full 1290px. Same edge as everything else. */
.cv li > span:last-child { max-width: var(--measure); }
html.is-cjk .cv li > span:last-child { max-width: var(--measure-cjk); }
/* `block`, or the work and the venue share a line with nothing between them —
   «Xia Yu Ge14th National Exhibition of Fine Arts». Both were plain inline
   spans, so the `margin-top` below (which says plainly that the venue was meant
   to sit on its own line) was being dropped on the floor. Six rows, six
   locales, every width. */
.cv__w { display: block; font-family: var(--serif); font-size: 1.02rem; line-height: 1.4; }
.cv__e { color: var(--faint); font-size: 0.875rem; line-height: 1.6; margin-top: 0.15em; }
html.is-cjk .cv__e { line-height: 1.8; }

/* --- people --------------------------------------------------------------- */

.person {
  display: grid;
  gap: clamp(22px, 3.4vw, 52px);
  padding-block: clamp(34px, 5vw, 64px);
  border-bottom: 1px solid var(--line-soft);
  align-items: start;
}
@media (min-width: 56.25em) { .person { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); } }
.person .fig { aspect-ratio: 4 / 5; }
.person__role {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  color: var(--gold);
  display: block;
  margin-bottom: 1em;
}
html.is-latin .person__role { text-transform: uppercase; }
.person__name { font-family: var(--serif); font-size: clamp(1.6rem, 2.6vw, 2.15rem); line-height: 1.15; margin-bottom: 0.7em; }
html.is-cjk .person__name { line-height: 1.35; }
.link-out {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: 1.6em;
  font-size: 0.8438rem;
  color: var(--gold);
  border-bottom: 1px solid var(--gold-dim);
  padding-bottom: 3px;
  transition: border-color 0.25s var(--ease);
}
.link-out:hover { border-bottom-color: var(--gold); }
.link-out svg { width: 11px; height: 11px; }

/* --- contact -------------------------------------------------------------- */

.form { display: grid; gap: 20px; }
@media (min-width: 43.75em) { .form { grid-template-columns: 1fr 1fr; } }
/* Grid items default to `min-width: auto`, whose automatic minimum is the
   input's intrinsic width (~20 characters). At a large browser font size that
   is wider than half the form, and the second column pushes the page sideways.
   The same trap as the lightbox's min-height. */
.field { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.field--full { grid-column: 1 / -1; }
.field label { font-size: 0.6875rem; letter-spacing: 0.16em; color: var(--faint); }
html.is-latin .field label { text-transform: uppercase; }
.field input, .field textarea {
  width: 100%;
  min-width: 0;
  background: rgba(244, 241, 234, 0.035);
  border: 1px solid var(--line);
  padding: 13px 15px;
  font-size: 0.9375rem;
  color: var(--paper);
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease);
}
/* Placeholders are hints, not content, but 0.30 was unreadable in a dim room. */
.field input::placeholder, .field textarea::placeholder { color: rgba(244, 241, 234, 0.42); }
/* The old rule removed the outline and leaned on a 42%-opacity border to show
   focus, which is far too quiet for a keyboard user. The global :focus-visible
   ring is left in place and the field is tinted on top of it. */
.field input:focus, .field textarea:focus {
  border-color: var(--gold);
  background: rgba(244, 241, 234, 0.055);
}
.field textarea { resize: vertical; min-height: 132px; }

.checklist li {
  position: relative;
  padding-left: 26px;
  padding-block: 9px;
  border-bottom: 1px solid var(--line-soft);
  color: var(--muted);
  font-size: 0.9062rem;
}
.checklist li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 1.15em;
  width: 12px;
  height: 1px;
  background: var(--gold-dim);
}
.contactblock { border-top: 1px solid var(--line-soft); }
.contactblock > div {
  padding-block: 18px 20px;
  border-bottom: 1px solid var(--line-soft);
  display: grid;
  gap: 4px 28px;
}
@media (min-width: 40em) { .contactblock > div { grid-template-columns: 10em minmax(0, 1fr); } }
.contactblock dt { font-size: 0.6875rem; letter-spacing: 0.16em; color: var(--faint); padding-top: 0.35em; }
html.is-latin .contactblock dt { text-transform: uppercase; }
.contactblock dd { margin: 0; font-size: 0.9375rem; line-height: 1.62; }
.contactblock a { border-bottom: 1px solid var(--line); transition: border-color 0.25s var(--ease); }
.contactblock a:hover { border-bottom-color: var(--gold); color: var(--gold); }

/* --- full-bleed CTA ------------------------------------------------------- */

.cta {
  position: relative;
  isolation: isolate;
  padding-block: clamp(80px, 13vw, 190px);
  text-align: center;
}
.cta__bg { position: absolute; inset: 0; z-index: -2; }
.cta__bg .fig { height: 100%; }
.cta::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Dark enough for AA-contrast text, light enough that the photograph is
     still a photograph rather than a black rectangle. The archive is shot in
     dim interiors, so anything above ~0.6 flattens it to nothing. */
  background: linear-gradient(rgba(6, 6, 7, 0.6), rgba(6, 6, 7, 0.42) 45%, rgba(6, 6, 7, 0.72));
}
.cta .lede { margin-inline: auto; }
.cta .h2 { margin-bottom: 0.5em; }
.cta .btn { margin-top: 2.4em; }

/* --- footer --------------------------------------------------------------- */

.foot {
  border-top: 1px solid var(--line-soft);
  padding-block: clamp(48px, 6vw, 76px) 34px;
  background: var(--ink-deep);
}
.foot__grid {
  display: grid;
  gap: 36px clamp(24px, 4vw, 64px);
}
/* Four columns need about 1000px. Forcing them from 760px gave iPad portrait
   four ~128px columns — narrower than the word "LANGUAGE" once a reader turns
   their text size up, and cramped even at the default. Two columns first. */
@media (min-width: 47.5em) { .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 62.5em) { .foot__grid { grid-template-columns: minmax(0, 5fr) repeat(3, minmax(0, 3fr)); } }
.foot__t {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  color: var(--faint);
  margin-bottom: 1.5em;
}
html.is-latin .foot__t { text-transform: uppercase; }
.foot li + li { margin-top: 0.75em; }
.foot a { color: var(--muted); font-size: 0.875rem; transition: color 0.25s var(--ease); }
.foot a:hover { color: var(--gold); }
.foot a[aria-current] { color: var(--gold); }
.foot__blurb { color: var(--faint); font-size: 0.875rem; max-width: 34ch; margin-top: 1.4em; }
.foot__base {
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: 22px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 26px;
  justify-content: space-between;
  font-size: 0.75rem;
  color: rgba(244, 241, 234, 0.52);  /* was 0.34 -> 2.76:1, under AA */
}

/* --- language chooser (root page) ---------------------------------------- */

.chooser {
  min-height: 100vh;   /* fallback, as above */
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 40px var(--gutter);
  text-align: center;
}
.chooser__list {
  display: grid;
  gap: 1px;
  margin-top: 40px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  width: min(560px, 100%);
}
@media (min-width: 35em) { .chooser__list { grid-template-columns: 1fr 1fr; } }
.chooser__list a {
  background: var(--ink);
  padding: 20px 24px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  transition: background-color 0.3s var(--ease);
}
.chooser__list a:hover { background: var(--ink-raised); }
.chooser__list strong { font-family: var(--serif); font-weight: 400; font-size: 1.15rem; }
.chooser__list em { font-style: normal; font-size: 0.6875rem; letter-spacing: 0.16em; color: var(--faint); text-transform: uppercase; }


/* --- archive mosaic ------------------------------------------------------- */

/* Columns, not a grid: the archive is 430 photographs at every aspect ratio
   from 4032x3024 phone shots to 3815x2461 panoramas. Cropping them all to one
   rectangle would throw away the thing the page is for — the range of the work.
   Column flow keeps every plate whole and still fills the page edge to edge. */
.arch {
  columns: 2;
  column-gap: clamp(8px, 1.1vw, 14px);
}
@media (min-width: 40em) { .arch { columns: 3; } }
@media (min-width: 62.5em) { .arch { columns: 4; } }
@media (min-width: 87.5em) { .arch { columns: 5; } }

.arch__t {
  position: relative;
  margin: 0 0 clamp(8px, 1.1vw, 14px);
  break-inside: avoid;
  cursor: zoom-in;
  display: block;
}
.arch__t .fig img { height: auto; }
.arch__t::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid transparent;
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease);
  pointer-events: none;
}
@media (hover: hover) {
  .arch__t:hover::after { border-color: var(--gold-dim); background: rgba(6, 6, 7, 0.18); }
}

/* --- home archive strip --------------------------------------------------- */

/* Deliberately full-bleed and slightly clipped at both ends: it should read as
   a slice of something much longer, which is exactly what it is. */
.strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: clamp(112px, 13vw, 176px);
  grid-template-rows: repeat(2, clamp(112px, 13vw, 176px));
  gap: 4px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  padding-inline: var(--gutter);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
}
.strip::-webkit-scrollbar { display: none; }
.strip__t { display: block; height: 100%; }
.strip__t .fig { height: 100%; }

/* --- evidence sheet (workshop photographs) -------------------------------- */

.sheet {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(8px, 1.1vw, 14px);
}
@media (min-width: 40em) { .sheet { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 62.5em) { .sheet { grid-template-columns: repeat(4, 1fr); } }
.sheet__t { margin: 0; }
.sheet__t .fig { aspect-ratio: 1 / 1; }

/* --- lightbox ------------------------------------------------------------- */

/* The point of opening an image is to see the whole thing large. The stage is
   an absolutely-positioned box inset from the viewport edges, and the image
   fills it with object-fit: contain — so a 4:3 dome and a 1:3 panorama both
   arrive at the largest size that still fits, with the caption clear of them. */
.lb {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(4, 4, 5, 0.97);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.lb[hidden] { display: none; }

.lb__stage {
  position: absolute;
  inset: clamp(56px, 7vh, 84px) clamp(52px, 6vw, 104px) clamp(84px, 11vh, 116px);
}
/* The cloned <picture> is pinned to the stage rather than laid out inside it.
   As a grid/flex child it would inherit `min-height: auto`, whose automatic
   minimum is the image's intrinsic height — a 720x960 plate then renders 1500px
   tall and overflows instead of fitting. Absolute positioning has no such rule. */
.lb__stage picture { position: absolute; inset: 0; display: block; }
.lb__stage img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}
.lb__stage img.is-shown { opacity: 1; }

/* A soft ramp under the caption: some plates are bright right down to the
   bottom edge, and the caption has to stay readable over any of them. */
.lb::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: clamp(110px, 16vh, 180px);
  background: linear-gradient(rgba(4, 4, 5, 0), rgba(4, 4, 5, 0.9));
  pointer-events: none;
}
.lb__bar {
  position: absolute;
  z-index: 1;
  left: clamp(18px, 4vw, 56px);
  right: clamp(18px, 4vw, 56px);
  bottom: clamp(18px, 3vh, 34px);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 22px;
}
.lb__cap {
  font-family: var(--serif);
  font-size: clamp(0.95rem, 1.4vw, 1.15rem);
  line-height: 1.4;
  color: var(--paper);
}
.lb__go {
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  color: var(--gold);
  border-bottom: 1px solid var(--gold-dim);
  padding-bottom: 2px;
  transition: border-color 0.25s var(--ease);
}
.lb__go:hover { border-bottom-color: var(--gold); }
.lb__go[hidden] { display: none; }
.lb__n {
  margin-left: auto;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.lb__x, .lb__nav {
  position: absolute;
  background: none;
  border: 1px solid var(--line);
  color: var(--paper);
  width: 44px;
  height: 44px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.lb__x:hover, .lb__nav:hover { border-color: var(--gold); color: var(--gold); }
.lb__x { top: clamp(12px, 2vh, 22px); right: clamp(14px, 4vw, 56px); font-size: 1.375rem; }
.lb__nav { top: 50%; transform: translateY(-50%); font-size: 1.625rem; }
.lb__nav--prev { left: clamp(6px, 1.4vw, 22px); }
.lb__nav--next { right: clamp(6px, 1.4vw, 22px); }

@media (max-width: 43.75em) {
  /* Three stacked bands, so nothing can land on anything else: the plate, then
     the caption, then the two paging buttons on the bottom row where a thumb
     reaches them. Side-anchored arrows would sit *behind* the caption here — a
     two-line German title runs the full width of the screen. */
  .lb__stage { inset: 52px 10px 168px; }
  .lb__nav { top: auto; bottom: 16px; transform: none; }
  .lb__nav--prev { left: 14px; }
  .lb__nav--next { right: 14px; }
  .lb__bar { bottom: 74px; left: 14px; right: 14px; }
  .lb__n { margin-left: 0; width: 100%; }
  .lb::after { height: 210px; }
}
/* A phone held sideways is 390px tall. The default stage reserves 56px above
   and 84px below, which is more than a third of that height, and the plate ends
   up a postage stamp in the middle of a black screen. Give the picture the room
   instead — the caption still clears it, and the arrows sit outside the stage. */
@media (max-height: 32em) and (min-width: 43.76em) {
  .lb__stage { inset: 34px clamp(52px, 6vw, 104px) 52px; }
  /* Only the position moves. This used to shrink the button to 38px to spare
     the picture, but the picture's room comes from the stage inset above — and
     measured at 844x390, 932x430 and 1024x600 the 38px button overlaps the
     image anyway, so the 6px bought nothing and cost the 44px thumb target the
     rest of the site keeps. A phone held sideways is still a finger. */
  .lb__x { top: 8px; }
  .lb__bar { bottom: 10px; }
  .lb::after { height: 88px; }
}
body.lb-open { overflow: hidden; }


/* --- touch and small-screen legibility ------------------------------------ */

/* Two things the desktop layout gets wrong on a phone, found by tools/dev/audit.html:
   the 10–11px label sizes are below comfortable reading size, and several text
   links have a hit area around 27px tall — well under the 44px a thumb expects.
   The hit area is grown with a pseudo-element so the underline stays welded to
   the text instead of drifting away from it. */
@media (max-width: 51.25em) {
  .link-out, .wordmark, .navlink, .foot a, .drawer__langs a,
  .contactblock a, .lb__go, .pager a {
    position: relative;
  }
  .link-out::after, .wordmark::after, .foot a::after, .drawer__langs a::after,
  .contactblock a::after, .lb__go::after {
    content: '';
    position: absolute;
    /* Vertical only. These links are already 70–140px wide; it was their 17–27px
       height that fell short. Extending sideways as well pushed 6px of scrollable
       overflow into every ancestor for no benefit. */
    inset: -11px 0;
  }
  .chip { padding: 9px 14px; }
  .lang__btn { padding: 10px 14px; }
}

@media (max-width: 37.5em) {
  .eyebrow { font-size: 0.75rem; }
  .card__meta { font-size: 0.75rem; }
  .meta dt, .pager__label, .filters__label, .person__role,
  .foot__t, .field label, .contactblock dt { font-size: 0.75rem; }
  .small, .marks span { font-size: 0.8438rem; }
  .card__blurb, .scene__lede { font-size: 0.9375rem; }
  .foot__base { font-size: 0.8125rem; }
  .lb__n { font-size: 0.8125rem; }
}

/* --- reveal on scroll ----------------------------------------------------- */

.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .js .fig img { opacity: 1; transform: none; transition: none; }
  a:hover > .fig img, .card:hover .fig img { transform: none; }
  .scene__peek { display: none; }
}

/* --- print ---------------------------------------------------------------- */

/* A prospect will print this or save it as a PDF — that is what a BD site is
   for. The screen palette is paper-white type on a dark field, so setting only
   `body { color: #111 }` left every token pointing the wrong way: the ledes,
   body copy, project locations and footer were paper-white at 52–64% alpha on a
   white sheet, i.e. invisible, and the gold eyebrows measured 1.95:1. Printing
   the Chinese home page produced headings and almost nothing else.

   Flip the whole token set instead of patching rules one at a time. */
@media print {
  :root {
    --ink: #fff;
    --ink-raised: #fff;
    --ink-deep: #fff;
    --paper: #111;
    --muted: #333;                    /* 12.6:1 on white */
    --faint: #565656;                 /* 7.0:1 */
    --gold: #8a6714;                  /* 5.2:1; #e0b357 is 1.95:1 on paper */
    --gold-dim: #a8842c;
    --line: rgba(0, 0, 0, 0.3);
    --line-soft: rgba(0, 0, 0, 0.16);
  }
  body { background: #fff; color: #111; }
  @page { margin: 16mm; }

  /* Screen furniture: nothing here survives being printed. */
  .head, .drawer, .scrollcue, .filters, .scene__peek, .lb, .strip,
  .hero__cta, .cta { display: none !important; }
  .js .fig img, .js [data-reveal] { opacity: 1 !important; transform: none !important; }

  /* Both heroes set light type over a photograph held behind it. On paper the
     scrim is gone and the type is now dark, so stack them instead: the
     photograph prints as a plate, the words sit on white underneath it. */
  .hero, .phero {
    min-height: 0;
    height: auto;
    display: block;
    padding-top: 0;
  }
  .hero::after, .phero::after { display: none; }
  .hero__bg, .phero > .fig { position: static; inset: auto; }
  .hero__bg .fig, .phero > .fig { height: auto; }
  .hero__bg img, .phero > .fig img { height: auto; max-height: 92mm; object-fit: cover; }
  .hero__inner, .phero__inner { position: static; inset: auto; padding: 8mm 0 0; }

  /* A printed link is worth nothing without its target. Internal hrefs are
     relative and useless on paper, so only external ones carry it. */
  a[href^="http"]:not(.btn)::after {
    content: ' (' attr(href) ')';
    font-size: 0.82em;
    color: #565656;
    word-break: break-all;
  }

  .card, .scene, .step, .person, .cv li, .row, figure { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
  .arch { columns: 3; }
  .arch__t { break-inside: avoid; }
}
