/* ============================================================================
   ATELIER 21 — văn phòng kiến trúc
   ----------------------------------------------------------------------------
   CONCEPT
   A drawing sheet, not a website. Everything sits on a 12-column grid whose
   rules stay faintly visible, the way construction paper shows its guides;
   the page is read top-to-bottom like a set of drawings — title block, then
   photographic plates, then the project register, then the method and enquiry.

   PALETTE  (all text pairs verified, ratios from WCAG 2.1 relative luminance)
     --paper        #eceae4  chalk white — page ground
     --paper-2      #e2dfd8  one step deeper — drawing field behind figures
     --ink          #16181b  graphite — body text          14.79:1 on --paper
     --ink-2        #5b6067  muted graphite — meta text     5.27:1 on --paper
                                                            4.76:1 on --paper-2
     --ink-surface  #16181b  inverted ground (contact, footer base)
     --paper-on-ink #eceae4  text on ink                   14.79:1 on --ink
     --ink-2-on-ink #a8a59d  muted on ink                   7.23:1 on --ink
     --signal       #b8280f  the single red: indices, active state, focus
                                                            5.21:1 on --paper
                                                            4.71:1 on --paper-2
     --signal-on-ink #ff5a3c same red lifted for ink grounds 5.74:1 on --ink
     --rule         rgba(22,24,27,.13)  hairline rules, dividers only, never text
     --rule-strong  rgba(22,24,27,.30)  heavier dividers, figure linework
     --rule-ui      rgba(22,24,27,.55)  component boundaries (WCAG 1.4.11)
                                        composites to #767674 = 3.76:1 on --paper
     --rule-on-ink  rgba(236,234,228,.16)  hairlines on ink grounds
     --rule-ui-on-ink rgba(236,234,228,.50) input boundaries on ink
                                        composites to #81817f = 4.56:1 on --ink
     Placeholders: #63686e on --paper 4.67:1; #9a978f on --ink-surface 6.10:1.

   TYPE
     Display  Archivo, variable width axis (wdth 125 = Expanded), wght 500–600
     Text     Inter, 400 / 500 / 600
     display  clamp(3.1rem,10.2vw,9.6rem) / .88 / -.035em / 500 / Expanded
     h1       same as display (the hero is the only h1)
     h2       clamp(2.1rem,5.4vw,4.4rem)  / .92 / -.03em  / 600 / Expanded
     h3       clamp(1.1rem,1.5vw,1.45rem) / 1.15 / -.015em / 600 / Expanded
              (card names and method steps — the compact heading tier)
     plate    clamp(1.55rem,2.9vw,2.75rem) — the pinned elevation titles, which
              are display-scale by design because they sit alone on a plate
     body     1rem (16px)  / 1.62 / 0      / 400 / Inter
     lede     1.125rem     / 1.55 / -.01em / 400 / Inter
     small    .875rem (14px) / 1.55 / 0    / 400
     meta     .75rem  (12px) / 1.4 / .14em / 500 / uppercase

   SPACING
     One section rhythm, one heading rhythm. --space-section between major
     sections, --space-head under every section head, so the page reads as a
     set of equally ruled sheets rather than five hand-tuned blocks.

   LAYOUT
     One 12-column grid, gap clamp(12px,1.6vw,24px), max 1520px, page gutter
     clamp(18px,4.2vw,64px). Column rules are drawn with a repeating gradient
     computed from the same gap, so a rule always falls on a real column edge.
     The structural idea: the page alternates between a *sheet* (rules visible,
     white) and a *plate* (full-bleed, inverted) — the pinned elevation sequence
     and the enquiry sit on ink.

   SIGNATURE MOTION
     A pinned horizontal elevation sequence (#mat-dung). The section is 340svh
     tall and carries a named view timeline (view-timeline-name: --pin); its
     inner sheet is position: sticky and 100svh. The track translates on X with
     a scroll-driven animation — animation-timeline: --pin with
     animation-range: entry 100% exit 0%, which is exactly the window in which
     the sticky sheet is pinned. view-timeline-inset: 0 keeps scroll-padding
     from displacing the timeline. No scroll listener anywhere.
     Under prefers-reduced-motion, or where scroll timelines are unsupported,
     the track is an ordinary vertical stack and the section is auto-height.
     The header's bottom rule draws itself with animation-timeline:
     scroll(root block) — also CSS only.

   PORTFOLIO
     #cong-trinh — nine sample projects with local stock photography, four
     categories, chips with aria-pressed,
     FLIP-reflowed survivors, live count, URL state (?cat=).
   ========================================================================= */

/* ---------------------------------------------------------------- tokens */
:root {
  --paper: #eceae4;
  --paper-2: #e2dfd8;
  --ink: #16181b;
  --ink-2: #5b6067;
  --ink-surface: #16181b;
  --paper-on-ink: #eceae4;
  --ink-2-on-ink: #a8a59d;
  --signal: #b8280f;
  --signal-on-ink: #ff5a3c;
  --rule: rgba(22, 24, 27, .13);
  --rule-strong: rgba(22, 24, 27, .3);
  --rule-ui: rgba(22, 24, 27, .55);
  --rule-on-ink: rgba(236, 234, 228, .16);
  --rule-ui-on-ink: rgba(236, 234, 228, .5);

  --ease-enter: cubic-bezier(.16, 1, .3, 1);
  --ease-expo: cubic-bezier(.19, 1, .22, 1);
  --ease-move: cubic-bezier(.65, 0, .35, 1);
  --ease-exit: cubic-bezier(.7, 0, .84, 0);
  --ease-pop: cubic-bezier(.34, 1.4, .64, 1);
  --dur-micro: 140ms;
  --dur-small: 220ms;
  --dur-med: 320ms;
  --dur-large: 480ms;
  --dur-reveal: 680ms;
  --dur-hero: 900ms;

  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --text: "Inter", "Helvetica Neue", Arial, sans-serif;

  --pad: clamp(18px, 4.2vw, 64px);
  --gap: clamp(12px, 1.6vw, 24px);
  --max: 1520px;
  --header-h: 68px;

  /* one section rhythm, used by every band of the page */
  --space-section: clamp(64px, 9vw, 132px);
  --space-head: clamp(24px, 3vw, 40px);

  --fs-display: clamp(3.1rem, 10.2vw, 9.6rem);
  --fs-h2: clamp(2.1rem, 5.4vw, 4.4rem);
  --fs-h3: clamp(1.1rem, 1.5vw, 1.45rem);
  --fs-lede: 1.125rem;
  --fs-small: .875rem;
  --fs-meta: .75rem;
}

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 8px);
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: 1rem;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* `clip`, not `hidden`: it removes overflow without creating a scroll
   container, which would break the sticky pinned sheet. */
html, body { overflow-x: clip; }

[hidden] { display: none !important; }

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

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

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-radius: 0;
  -webkit-appearance: none;
  appearance: none;
}

button { cursor: pointer; }

ul, ol { list-style: none; padding: 0; }

::selection {
  background: var(--signal);
  color: var(--paper);
}

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}

.contact :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--signal-on-ink);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  padding: 14px 20px;
  min-height: 44px;
  background: var(--ink);
  color: var(--paper);
  font-size: var(--fs-meta);
  letter-spacing: .12em;
  text-transform: uppercase;
  transform: translateY(-120%);
  transition: transform var(--dur-small) var(--ease-exit);
}
.skip:focus-visible {
  transform: translateY(0);
  outline-offset: -4px;
  outline-color: var(--signal-on-ink);
}

/* ------------------------------------------------------------- primitives */
.shell {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* The drawing-sheet grid. Twelve column edges drawn at 13% ink. The period
   is exact: for content width W, 12 columns and gap g, a column edge repeats
   every (W + g) / 12 — so the rules land precisely on real grid lines at any
   viewport width without a single extra element. Decorative, so aria-hidden
   is handled by ::before and it never intercepts pointer events. */
.shell--rules { position: relative; }
.shell--rules::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset-block: 0;
  inset-inline: var(--pad);
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to right,
    var(--rule) 0 1px,
    transparent 1px calc((100% + var(--gap)) / 12)
  );
}
.shell--rules > * { position: relative; z-index: 1; }

.idx {
  font-family: var(--text);
  font-size: var(--fs-meta);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--signal);
  font-variant-numeric: tabular-nums;
}

.sec-tag {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: var(--fs-meta);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.sec-tag .idx { color: var(--signal); }
.sec-tag--light { color: var(--ink-2-on-ink); }
.sec-tag--light .idx { color: var(--signal-on-ink); }

/* buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 24px;
  border: 1px solid var(--ink);
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  transition:
    background-color var(--dur-small) var(--ease-move),
    color var(--dur-small) var(--ease-move),
    border-color var(--dur-small) var(--ease-move),
    transform var(--dur-micro) var(--ease-move);
}
.btn:active { transform: translateY(1px); transition-duration: 90ms; }

.btn--solid {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.btn--line {
  background: transparent;
  color: var(--ink);
  /* component boundary, so it must clear 3:1 (WCAG 1.4.11) — --rule-ui
     composites to 3.76:1 on --paper, where --rule-strong was 1.91:1 */
  border-color: var(--rule-ui);
}

@media (hover: hover) and (pointer: fine) {
  .btn--solid:hover { background: var(--signal); border-color: var(--signal); }
  .btn--line:hover { border-color: var(--ink); }
}

/* figures -------------------------------------------------------------- */
.fig { width: 100%; height: auto; overflow: visible; }
.fig__line path,
.fig__line rect {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1;
  stroke-linecap: square;
  vector-effect: non-scaling-stroke;
}
.fig__line .fig__thin { stroke: var(--rule-strong); }
.fig__line .fig__ground { stroke-width: 2; }
/* needs a two-class selector to beat `.fig__line path { fill: none }` */
.fig__line path.fig__fill {
  fill: var(--ink);
  fill-opacity: .07;
  stroke: var(--ink);
}
.fig__dim path {
  fill: none;
  stroke: var(--ink-2);
  stroke-width: .75;
  opacity: .8;
  vector-effect: non-scaling-stroke;
}
.fig__hatch path {
  fill: none;
  stroke: var(--ink-2);
  stroke-width: .75;
  opacity: .5;
  vector-effect: non-scaling-stroke;
}
.fig__mark { fill: var(--signal); stroke: none; }

/* --------------------------------------------------------------- header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
/* the "reacts to scroll" cue: a 2px ink rule that draws itself along the
   bottom edge. transform only — no height, no shadow. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
.site-header__inner {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 48px);
  min-height: var(--header-h);
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-height: 44px;
  padding-block: 10px;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -.02em;
  line-height: 1;
}
.brand__num {
  color: var(--signal);
  font-variant-numeric: tabular-nums;
}

.site-header__place {
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-right: auto;
  white-space: nowrap;
}
.site-header__place span { padding-inline: 4px; }

.nav {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.4vw, 40px);
}
.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2vw, 34px);
}
.nav__list a {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  min-height: 44px;
  padding-block: 12px;
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-small) var(--ease-move);
}
.nav__idx {
  font-size: var(--fs-meta);
  letter-spacing: .06em;
  color: var(--signal);
  font-variant-numeric: tabular-nums;
}
.nav__cta { min-height: 44px; padding: 0 18px; white-space: nowrap; }

@media (hover: hover) and (pointer: fine) {
  .nav__list a:hover { border-bottom-color: var(--ink); }
}

.nav-toggle { display: none; }

/* ---------------------------------------------------------------- hero */
.hero {
  padding-block: clamp(28px, 5vw, 64px) clamp(32px, 4vw, 56px);
}
.hero__shell { position: relative; }

.hero__grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: clamp(24px, 3.4vw, 44px);
  align-items: start;
}

.hero__eyebrow {
  grid-area: 1 / 1 / 2 / 7;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  padding-top: 10px;
  border-top: 1px solid var(--rule-strong);
  font-size: var(--fs-meta);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.hero__stamp {
  grid-area: 1 / 10 / 2 / 13;
  justify-self: end;
  padding-top: 10px;
  border-top: 1px solid var(--rule-strong);
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-align: right;
}
.hero__stamp .idx { margin-right: 8px; }

.hero__title {
  grid-area: 2 / 1 / 3 / 9;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 500;
  font-size: var(--fs-display);
  line-height: .88;
  letter-spacing: -.035em;
  margin-top: clamp(4px, 1vw, 14px);
}
.hero__kicker {
  display: block;
  font-family: var(--text);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: clamp(14px, 2vw, 26px);
}
.hero__line {
  display: block;
  /* The mask must clear Vietnamese diacritics both above and below the
     baseline (ở, ạ, ộ, ữ). Padding widens the clip box; the matching negative
     margin keeps the .9 line rhythm unchanged. */
  overflow: hidden;
  padding-block: .06em .16em;
  margin-block: -.06em -.16em;
}

.hero__figure {
  grid-area: 2 / 9 / 4 / 13;
  align-self: start;
  margin-top: clamp(0px, 1vw, 12px);
  background: var(--paper-2);
  border: 1px solid var(--rule-strong);
  padding: clamp(14px, 1.6vw, 22px) clamp(14px, 1.6vw, 22px) 12px;
}
.hero__figure .fig { max-width: 340px; margin-inline: auto; }
.hero__caption {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--ink-2);
  letter-spacing: .02em;
}
.hero__caption .idx { margin-right: 6px; }

.hero__aside {
  grid-area: 3 / 1 / 4 / 7;
  margin-top: clamp(8px, 1.6vw, 22px);
}
.hero__lede {
  font-size: var(--fs-lede);
  line-height: 1.58;
  letter-spacing: -.008em;
  max-width: 40ch;
  color: var(--ink);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: clamp(20px, 2.4vw, 32px);
}

.hero__rail {
  grid-area: 4 / 1 / 5 / 13;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--gap);
  border-top: 1px solid var(--rule-strong);
  padding-top: 14px;
  margin-top: clamp(10px, 2vw, 26px);
}
.hero__rail > div {
  padding-right: var(--gap);
  border-right: 1px solid var(--rule);
}
.hero__rail > div:last-child { border-right: 0; }
.hero__rail dt {
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 6px;
}
.hero__rail dd {
  font-size: var(--fs-small);
  line-height: 1.5;
  letter-spacing: -.005em;
}
.hero__cue { text-align: right; }
.hero__cue dt { color: var(--signal); }
/* Static: the pinned elevation sequence is the single signature moment, and a
   looping arrow in the title block competed with it for the same attention. */
.hero__arrow {
  display: inline-block;
  margin-left: 6px;
}

/* ==========================================================================
   SIGNATURE — pinned horizontal elevation sequence
   --------------------------------------------------------------------------
   The BASE state is a plain vertical stack of the three elevations. That is
   also the no-support fallback and the reduced-motion state, so a browser
   without scroll-driven animations never sees a half-applied pin.
   Inside @supports the section becomes 340svh tall, its sheet sticky at
   100svh, and the track translates on X across exactly the pinned window —
   animation-range `entry 100% exit 0%` on a view timeline named on the tall
   section, which is precisely the interval in which the sticky sheet is
   pinned. No JS scroll code exists anywhere on this page.
   ========================================================================== */
.pinned {
  position: relative;
  background: var(--ink-surface);
  color: var(--paper-on-ink);
  padding-block: clamp(40px, 6vw, 72px);
}

/* The pinned band is a full-bleed plate, so it keeps its own tighter padding;
   its head rule lines up with the other three section heads. */
.pinned__sticky {
  background: var(--ink-surface);
  border-top: 1px solid var(--rule-on-ink);
}

.pinned__head {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 12px;
  align-items: end;
  padding-block: clamp(18px, 2.4vw, 30px) clamp(10px, 1.4vw, 18px);
  border-bottom: 1px solid var(--rule-on-ink);
}
.pinned__head .sec-tag {
  grid-column: 1 / 4;
  color: var(--ink-2-on-ink);
}
.pinned__head .sec-tag .idx { color: var(--signal-on-ink); }
.pinned__title {
  grid-column: 4 / 11;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: clamp(1.4rem, 2.4vw, 2.6rem);
  line-height: 1;
  letter-spacing: -.03em;
}
.pinned__count {
  grid-column: 11 / 13;
  justify-self: end;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 500;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  letter-spacing: .02em;
  color: var(--ink-2-on-ink);
  font-variant-numeric: tabular-nums;
}
.pinned__num { color: var(--signal-on-ink); margin-right: 2px; }

.pinned__viewport { position: relative; }

.pinned__track {
  display: grid;
  gap: 0;
  padding-inline: var(--pad);
}

.pane {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(20px, 3vw, 32px);
  padding-block: clamp(28px, 4vw, 44px);
  border-top: 1px solid var(--rule-on-ink);
}
.pane:first-child { border-top: 0; }

/* the vertical (fallback / reduced-motion) reading still pairs text with its
   elevation once there is room — a full-width empty plate would read as a bug */
@media (min-width: 760px) {
  .pane {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
    align-items: center;
    gap: clamp(24px, 3.4vw, 48px);
  }
}

.pane__text { max-width: 52ch; }
.pane__idx {
  color: var(--signal-on-ink);
  margin-bottom: clamp(10px, 1.4vw, 18px);
}
.pane__title {
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  /* plate scale: the elevation titles sit alone on an ink plate, so they take
     the display tier rather than the compact h3 tier used inside cards */
  font-size: clamp(1.55rem, 2.9vw, 2.75rem);
  line-height: 1.02;
  letter-spacing: -.03em;
  margin-bottom: clamp(12px, 1.6vw, 20px);
}
.pane__body {
  font-size: clamp(.9375rem, 1.05vw, 1.0625rem);
  line-height: 1.62;
  color: var(--ink-2-on-ink);
}
.pane__facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  margin-top: clamp(18px, 2.2vw, 30px);
  padding-top: 12px;
  border-top: 1px solid var(--rule-on-ink);
}
.pane__facts dt {
  font-size: var(--fs-meta);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2-on-ink);
  margin-bottom: 4px;
}
.pane__facts dd {
  font-size: var(--fs-small);
  line-height: 1.4;
  color: var(--paper-on-ink);
}

.pane__fig {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--rule-on-ink);
  padding: clamp(14px, 1.8vw, 24px) clamp(14px, 1.8vw, 24px) 12px;
  color: var(--ink);
  max-width: 420px;
}
.pane__fig .fig { width: 100%; max-width: 300px; margin-inline: auto; }
.pane__cap {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-size: var(--fs-meta);
  letter-spacing: .02em;
  color: var(--ink-2);
  text-align: left;
}
.pane__cap .idx { margin-right: 6px; }

.pinned__foot {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 10px;
  align-items: center;
  padding-block: clamp(14px, 1.8vw, 20px);
  border-top: 1px solid var(--rule-on-ink);
}
.pinned__progress {
  display: none;
  grid-column: 1 / 6;
  height: 2px;
  background: var(--rule-on-ink);
  overflow: hidden;
}
.pinned__bar {
  display: block;
  height: 100%;
  background: var(--signal-on-ink);
  transform-origin: 0 50%;
}
.pinned__note {
  grid-column: 1 / 13;
  font-size: var(--fs-meta);
  letter-spacing: .04em;
  color: var(--ink-2-on-ink);
  max-width: 62ch;
  /* stops the right-aligned note from dropping a single word onto its own
     line in the pinned footer */
  text-wrap: pretty;
}

/* --- pinned behaviour, only where scroll-driven animation exists ---------
   Gated on a wide viewport as well: below 1180px a horizontally travelling
   track would squeeze the elevations and the header nav, so those widths
   keep the vertical stack, which is also the fallback. ------------------- */
@media (min-width: 1180px) {
@supports (animation-timeline: view()) {
  .pinned {
    height: 340svh;
    padding-block: 0;
    view-timeline-name: --pin;
    view-timeline-axis: block;
    /* html carries scroll-padding-top for anchor links; without this the
       timeline would start that many pixels early and the track would be
       already displaced at the instant the sheet pins. */
    view-timeline-inset: 0;
  }
  .pinned__sticky {
    position: sticky;
    top: 0;
    height: 100svh;
    padding-top: var(--header-h);
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    overflow: hidden;
  }
  .pinned__head {
    padding-block: clamp(12px, 1.4vw, 18px) clamp(10px, 1.2vw, 14px);
  }
  .pinned__viewport {
    display: flex;
    align-items: stretch;
    overflow: hidden;
    min-height: 0;
  }
  .pinned__track {
    display: flex;
    align-items: stretch;
    gap: clamp(20px, 3vw, 56px);
    width: max-content;
    padding-inline: max(var(--pad), calc((100vw - var(--max)) / 2 + var(--pad)));
    will-change: transform;
    animation: pinned-track linear both;
    animation-timeline: --pin;
    /* The pin window: section top reaches the viewport top (entry 100%) until
       the section bottom reaches the viewport bottom (exit 0%). `contain`
       would be zero-length here, because a 340svh section is never contained
       by a 100svh viewport. */
    animation-range: entry 100% exit 0%;
  }
  @keyframes pinned-track {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(min(0px, calc(100vw - 100%)), 0, 0); }
  }

  .pane {
    grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
    gap: clamp(20px, 3vw, 48px);
    align-items: center;
    width: min(78vw, 980px);
    padding-block: clamp(10px, 1.4vw, 18px);
    padding-left: clamp(16px, 2.4vw, 36px);
    border-top: 0;
    border-left: 1px solid var(--rule-on-ink);
  }
  .pane:first-child { border-left: 0; padding-left: 0; }
  .pane__text { max-width: 46ch; }
  .pane__fig {
    /* natural height, vertically centred against the text column. Stretching
       the plate to the row height would letterbox the drawing. */
    align-self: center;
    max-width: none;
    width: 100%;
  }
  .pane__fig .fig {
    width: 100%;
    height: auto;
    max-width: 100%;
    max-height: 58svh;
    /* the drawing scales to the plate's width, capped by viewport height */
    aspect-ratio: 300 / 420;
    object-fit: contain;
  }

  .pinned__progress { display: block; }
  .pinned__bar {
    transform: scaleX(.02);
    animation: pinned-progress linear both;
    animation-timeline: --pin;
    animation-range: entry 100% exit 0%;
  }
  @keyframes pinned-progress {
    from { transform: scaleX(.02); }
    to   { transform: scaleX(1); }
  }
  .pinned__note { grid-column: 6 / 13; justify-self: end; text-align: right; }
}
}

/* ------------------------------------------------------------- portfolio */
.port {
  padding-block: var(--space-section) clamp(56px, 8vw, 112px);
  border-bottom: 1px solid var(--rule);
}

.port__head {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 18px;
  align-items: end;
  padding-bottom: var(--space-head);
  border-bottom: 1px solid var(--rule-strong);
}
.port__head .sec-tag { grid-column: 1 / 4; }
.port__title {
  grid-column: 4 / 9;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: var(--fs-h2);
  line-height: .92;
  letter-spacing: -.03em;
}
.port__intro {
  grid-column: 9 / 13;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 42ch;
}

/* filters ------------------------------------------------------------- */
/* The bar is ~168px tall on a desktop (two rows: chips + search). Pinning it
   under the 68px header left 26–33% of a laptop viewport as permanent chrome
   and, being opaque, masked the top 168px of every card scrolling beneath it.
   It is short enough not to need pinning, so it scrolls away with the sheet. */
.filters {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 12px;
  align-items: center;
  padding-block: 16px;
  border-bottom: 1px solid var(--rule);
  position: relative;
  background: var(--paper);
}
.filters__label {
  grid-column: 1 / 3;
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.filters__scroll {
  grid-column: 3 / 10;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  padding-block: 2px;
  margin-block: -2px;
  /* without this a flex row with overflow squeezes chips instead of
     scrolling them; nowrap keeps each chip at its intrinsic width */
  scroll-snap-type: x proximity;
}
.chip { scroll-snap-align: start; }
/* When the row is actually scrollable (roughly below 595px), the last visible
   chip is cut mid-word and nothing signals more content to the right. The
   fade is keyed off a class main.js sets from real scrollWidth, not off a
   breakpoint: between ~595 and 620px the chips already fit and a mask there
   would dim a chip the user can see in full. */
.filters__scroll.is-scrollable {
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
.filters__count {
  grid-column: 11 / 13;
  justify-self: end;
  font-size: var(--fs-meta);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.filters__count span { color: var(--ink); font-weight: 600; }

/* search — a ruled line, not a pill */
.search {
  grid-column: 1 / 13;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
  row-gap: 2px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
}
.search__label {
  grid-column: 1 / 3;
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.search__box {
  position: relative;
  display: flex;
  align-items: center;
  /* the underline is the field's only boundary: --rule-ui, 3.76:1 */
  border-bottom: 1px solid var(--rule-ui);
  transition: border-color var(--dur-small) var(--ease-move);
}
.search__box:focus-within { border-bottom-color: var(--ink); }
.search__input {
  flex: 1;
  min-height: 44px;
  padding: 8px 0;
  font-size: 1rem;
  color: var(--ink);
  background: transparent;
}
.search__input::placeholder { color: #63686e; opacity: 1; }
.search__input::-webkit-search-cancel-button { -webkit-appearance: none; }
/* The magnifier is drawn as SVG, not as a circle border, so the design keeps
   its promise of no rounded corners anywhere. */
.search__icon {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  margin-right: 2px;
  fill: none;
  stroke: var(--ink-2);
  stroke-width: 1.25;
  stroke-linecap: square;
}
.search__hint {
  grid-column: 2 / 3;
  justify-self: end;
  font-size: var(--fs-meta);
  letter-spacing: .04em;
  color: var(--ink-2);
  white-space: nowrap;
}
.search__hint [data-search-count] { color: var(--ink); font-weight: 600; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--rule-ui);
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: .04em;
  white-space: nowrap;
  color: var(--ink);
  background: transparent;
  transition:
    background-color var(--dur-small) var(--ease-move),
    color var(--dur-small) var(--ease-move),
    border-color var(--dur-small) var(--ease-move),
    transform var(--dur-micro) var(--ease-move);
}
.chip:active { transform: translateY(1px); transition-duration: 90ms; }
.chip__n {
  font-size: var(--fs-meta);
  letter-spacing: .04em;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur-small) var(--ease-move);
}
.chip[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.chip[aria-pressed="true"] .chip__n { color: var(--signal-on-ink); }

@media (hover: hover) and (pointer: fine) {
  .chip:hover { border-color: var(--ink); }
  .chip[aria-pressed="true"]:hover { border-color: var(--signal); background: var(--signal); }
  /* on the signal ground the lifted --signal-on-ink count drops to 2.02:1 */
  .chip[aria-pressed="true"]:hover .chip__n { color: var(--paper); }
}

/* cards --------------------------------------------------------------- */
/* Rules are drawn on the cards themselves, not as a gap background: a gap
   background would leave a coloured void wherever the last row is short
   (e.g. two results in a three-column grid). */
.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
  margin-top: 1px;
}
/* With every card hidden the frame rules would be an orphan 1px line above
   the empty state, so the grid drops its border entirely. */
.grid.is-empty { border: 0; }

.card {
  background: var(--paper);
  min-width: 0;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.card__link {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: 0;
  height: 100%;
  padding: clamp(16px, 1.8vw, 26px);
  min-height: 100%;
  transition: background-color var(--dur-med) var(--ease-move);
}
.card__top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--rule);
}
.card__year {
  font-size: var(--fs-meta);
  letter-spacing: .12em;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.card__art {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  margin-block: clamp(14px, 1.8vw, 22px);
  background: var(--paper-2);
  border: 1px solid var(--rule);
  padding: clamp(12px, 1.4vw, 20px);
  transition: transform var(--dur-large) var(--ease-expo);
}
.card__art .fig { width: 100%; height: 100%; }
.card__art .fig__line path,
.card__art .fig__line rect { stroke-width: 1; }

.card__body { display: block; }
.card__name {
  display: block;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.15;
  letter-spacing: -.015em;
  margin-bottom: 6px;
}
.card__meta {
  display: block;
  font-size: var(--fs-meta);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.card__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: clamp(16px, 2vw, 24px);
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  font-size: var(--fs-meta);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.card__arrow {
  display: inline-block;
  font-size: 1rem;
  line-height: 1;
  color: var(--signal);
  transition: transform var(--dur-small) var(--ease-move);
}

@media (hover: hover) and (pointer: fine) {
  .card__link:hover { background: var(--paper-2); }
  .card__link:hover .card__art { transform: scale(1.022); }
  .card__link:hover .card__arrow { transform: translate(3px, -3px); }
}
.card__link:focus-visible .card__art { transform: scale(1.022); }
.card__link:focus-visible .card__arrow { transform: translate(3px, -3px); }
.card__link:focus-visible { outline-offset: -3px; }

/* filtering: survivors FLIP from their old slot to the new one */
.card.is-flipping { transition: transform var(--dur-large) var(--ease-expo); }

.empty {
  display: grid;
  justify-items: start;
  gap: 10px;
  padding-block: clamp(40px, 6vw, 80px);
  border-bottom: 1px solid var(--rule);
}
.empty__title {
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: clamp(1.25rem, 2.2vw, 1.75rem);
  line-height: 1.1;
  letter-spacing: -.02em;
}
.empty__body {
  font-size: var(--fs-small);
  color: var(--ink-2);
  max-width: 46ch;
  margin-bottom: 10px;
}

/* ---------------------------------------------------------------- method */
.method {
  padding-block: var(--space-section);
  background: var(--paper);
  /* Deliberately NOT content-visibility: auto. Measured, the estimated
     intrinsic height differed from the rendered height by up to 367px, which
     moves the scrollbar as the section paints — a worse trade than the paint
     it saves on a page with no images. */
}
.method__head {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 16px;
  align-items: end;
  padding-bottom: var(--space-head);
  border-bottom: 1px solid var(--rule-strong);
}
.method__head .sec-tag { grid-column: 1 / 4; }
.method__title {
  grid-column: 4 / 9;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: var(--fs-h2);
  line-height: .92;
  letter-spacing: -.03em;
}
.method__intro {
  grid-column: 9 / 13;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 40ch;
}

.method__list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}
.step {
  background: var(--paper);
  padding: clamp(20px, 2.4vw, 34px) clamp(16px, 1.8vw, 26px) clamp(28px, 3.4vw, 46px);
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.step__n {
  color: var(--signal);
  padding-bottom: 14px;
  margin-bottom: clamp(16px, 2vw, 28px);
  border-bottom: 1px solid var(--rule);
}
.step__name {
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.15;
  letter-spacing: -.015em;
  margin-bottom: 12px;
}
.step__body {
  font-size: var(--fs-small);
  line-height: 1.62;
  color: var(--ink-2);
}

/* --------------------------------------------------------------- contact */
.contact {
  background: var(--ink-surface);
  color: var(--paper-on-ink);
  padding-block: var(--space-section);
  border-top: 1px solid var(--rule-on-ink);
}
.contact__shell {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(20px, 3.4vw, 56px);
  row-gap: clamp(36px, 5vw, 56px);
  align-items: start;
}
.contact__intro { grid-column: 1 / 6; }

.contact__title {
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 600;
  font-size: clamp(2.2rem, 5vw, 4rem);
  line-height: .94;
  letter-spacing: -.032em;
  margin-block: clamp(16px, 2vw, 26px) clamp(18px, 2.2vw, 28px);
}
.contact__lede {
  font-size: var(--fs-lede);
  line-height: 1.58;
  color: var(--ink-2-on-ink);
  max-width: 40ch;
}

.contact__facts {
  margin-top: clamp(28px, 3.4vw, 44px);
  border-top: 1px solid var(--rule-on-ink);
}
.contact__facts > div {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr);
  gap: 16px;
  padding-block: 12px;
  border-bottom: 1px solid var(--rule-on-ink);
}
.contact__facts dt {
  font-size: var(--fs-meta);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2-on-ink);
  padding-top: 2px;
}
.contact__facts dd {
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--paper-on-ink);
}

.contact__note {
  margin-top: clamp(22px, 2.6vw, 32px);
  font-size: var(--fs-meta);
  line-height: 1.65;
  letter-spacing: .02em;
  color: var(--ink-2-on-ink);
  max-width: 46ch;
  padding-left: 14px;
  border-left: 2px solid var(--signal-on-ink);
}
.contact__note .idx {
  display: block;
  color: var(--signal-on-ink);
  margin-bottom: 4px;
}

.contact__form-wrap { grid-column: 7 / 13; }

.form {
  display: grid;
  gap: clamp(18px, 2vw, 26px);
  border-top: 1px solid var(--paper-on-ink);
  padding-top: clamp(22px, 2.6vw, 32px);
}

.field { display: grid; gap: 6px; }
.field__label {
  font-size: var(--fs-meta);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--paper-on-ink);
}
.field__req { color: var(--signal-on-ink); margin-left: 2px; }

.field__input {
  width: 100%;
  min-height: 48px;
  padding: 12px 0;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--paper-on-ink);
  background: transparent;
  /* --rule-ui-on-ink composites to 4.56:1 on --ink: the underline is the
     field's only boundary before focus (--rule-on-ink was 1.56:1) */
  border-bottom: 1px solid var(--rule-ui-on-ink);
  transition: border-color var(--dur-small) var(--ease-move);
}
.field__input::placeholder {
  color: #9a978f; /* 6.10:1 on --ink-surface */
  opacity: 1;
}
.field__input--area {
  min-height: 108px;
  padding: 12px 0;
  resize: vertical;
  border: 1px solid var(--rule-ui-on-ink);
  padding-inline: 12px;
  margin-top: 4px;
}
@media (hover: hover) and (pointer: fine) {
  .field__input:hover { border-bottom-color: var(--paper-on-ink); }
}
.field__input:focus-visible {
  outline: 2px solid var(--signal-on-ink);
  outline-offset: 2px;
  border-bottom-color: transparent;
}

.field__select { position: relative; }
.field__select select {
  padding-right: 28px;
  cursor: pointer;
}
.field__select select option {
  background: var(--ink-surface);
  color: var(--paper-on-ink);
}
.field__caret {
  position: absolute;
  right: 2px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 1px solid var(--paper-on-ink);
  border-bottom: 1px solid var(--paper-on-ink);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

.field__suffix { position: relative; display: flex; align-items: center; }
.field__unit {
  position: absolute;
  right: 0;
  font-size: var(--fs-small);
  color: var(--ink-2-on-ink);
  pointer-events: none;
}

.field__hint {
  font-size: var(--fs-meta);
  line-height: 1.5;
  letter-spacing: .02em;
  color: var(--ink-2-on-ink);
}
.field__err {
  display: flex;
  gap: 6px;
  font-size: var(--fs-meta);
  line-height: 1.5;
  letter-spacing: .02em;
  color: var(--signal-on-ink);
  font-weight: 500;
}
.field__err::before { content: "—"; }

.field__input[aria-invalid="true"] {
  border-bottom-color: var(--signal-on-ink);
  border-bottom-width: 2px;
}
.field__input--area[aria-invalid="true"] { border-color: var(--signal-on-ink); }

.form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: 4px;
}
.form__submit {
  background: var(--paper-on-ink);
  color: var(--ink-surface);
  border-color: var(--paper-on-ink);
  min-width: 200px;
}
@media (hover: hover) and (pointer: fine) {
  .form__submit:hover {
    background: var(--signal-on-ink);
    border-color: var(--signal-on-ink);
    color: var(--ink-surface);
  }
}
.form__legal {
  flex: 1 1 240px;
  font-size: var(--fs-meta);
  line-height: 1.55;
  color: var(--ink-2-on-ink);
  max-width: 46ch;
}

.form__status {
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--paper-on-ink);
  border-top: 1px solid var(--rule-on-ink);
  padding-top: 14px;
  min-height: 0;
}
.form__status:empty { display: none; }
.form__status[data-state="ok"] { color: var(--paper-on-ink); }
.form__status[data-state="ok"]::before {
  content: "✓";
  color: var(--signal-on-ink);
  margin-right: 8px;
  font-weight: 600;
}
.form__status[data-state="err"] { color: var(--signal-on-ink); }

/* ---------------------------------------------------------------- footer */
.site-footer {
  background: var(--ink-surface);
  color: var(--paper-on-ink);
  padding-block: clamp(40px, 5vw, 64px) clamp(28px, 3vw, 40px);
  border-top: 1px solid var(--rule-on-ink);
}
.site-footer__top {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: 20px;
  padding-bottom: clamp(24px, 3vw, 36px);
  border-bottom: 1px solid var(--rule-on-ink);
}
.brand--footer {
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  color: var(--paper-on-ink);
}
.site-footer__claim {
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 500;
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  line-height: 1.3;
  letter-spacing: -.015em;
  text-align: right;
  color: var(--ink-2-on-ink);
}

.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  row-gap: 28px;
  padding-block: clamp(26px, 3vw, 40px);
}
/* nth-child, not nth-of-type: the three blocks are <nav>, <div>, <div>, so
   only the first ever matched by type and the other two stacked in column 1. */
.site-footer__nav:nth-child(1) { grid-column: 1 / 4; }
.site-footer__nav:nth-child(2) { grid-column: 4 / 7; }
.site-footer__nav:nth-child(3) { grid-column: 7 / 10; }
.site-footer__mark { grid-column: 11 / 13; justify-self: end; }

.site-footer__h {
  font-size: var(--fs-meta);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--signal-on-ink);
  margin-bottom: 12px;
}
.site-footer__nav ul { display: grid; gap: 0; }
.site-footer__nav li {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink-2-on-ink);
}
.site-footer__nav a {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--paper-on-ink);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-small) var(--ease-move);
}
.site-footer__nav li:not(:has(a)) { min-height: 44px; display: flex; align-items: center; }
@media (hover: hover) and (pointer: fine) {
  .site-footer__nav a:hover { border-bottom-color: var(--paper-on-ink); }
}
.site-footer__mark .fig { width: clamp(64px, 7vw, 96px); }
.site-footer__mark .fig__line path { stroke: var(--ink-2-on-ink); }
.site-footer__mark .fig__mark { fill: var(--signal-on-ink); }

.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  padding-top: 18px;
  border-top: 1px solid var(--rule-on-ink);
  font-size: var(--fs-meta);
  line-height: 1.6;
  letter-spacing: .02em;
  color: var(--ink-2-on-ink);
}
.site-footer__base a {
  color: var(--paper-on-ink);
  /* The credit link was the only 107x16 target on the page. The 44px box is
     bought with padding and paid back with an equal negative margin, so the
     visual line height does not move. The underline is a text-decoration, not
     a border, so it stays under the words instead of under the padded box. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-block: 12px;
  margin-block: -12px;
  text-decoration: underline;
  text-decoration-color: var(--rule-on-ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color var(--dur-small) var(--ease-move);
}
@media (hover: hover) and (pointer: fine) {
  .site-footer__base a:hover { text-decoration-color: var(--signal-on-ink); }
}
.site-footer__meta { margin-left: auto; }
.site-footer__meta .idx { margin-right: 4px; }

/* ==========================================================================
   MOTION — reveals
   Content is visible by default. Only when JS is present *and* the user has
   not asked for reduced motion does the inline head script add .js-motion,
   which is the sole selector that hides [data-reveal].
   ========================================================================== */
html.js-motion [data-reveal] {
  opacity: 0;
  translate: 0 22px;
}
html.js-motion [data-reveal].is-revealed {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity var(--dur-reveal) var(--ease-enter),
    translate var(--dur-reveal) var(--ease-enter);
  transition-delay: var(--reveal-delay, 0ms);
}

/* hero: a painted mask, never opacity on the LCP node */
.hero__line > span {
  display: inline-block;
}
html.js-motion .hero__line > span {
  animation: line-rise var(--dur-hero) var(--ease-expo) both;
}
html.js-motion .hero__line:nth-child(2) > span { animation-delay: 60ms; }
html.js-motion .hero__line:nth-child(3) > span { animation-delay: 120ms; }
html.js-motion .hero__line:nth-child(4) > span { animation-delay: 180ms; }
@keyframes line-rise {
  from { transform: translateY(105%); }
  to   { transform: translateY(0); }
}

/* sticky header reaction — CSS only. A 2px rule draws along the bottom edge
   as the page leaves the title block. No scroll listener, no layout property. */
@supports (animation-timeline: scroll()) {
  .site-header::after {
    animation: header-rule linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 240px;
  }
  @keyframes header-rule {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
  }
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 1180px) {
  .hero__title { grid-area: 2 / 1 / 3 / 10; }
  .hero__figure { grid-area: 2 / 10 / 4 / 13; }
  .hero__aside { grid-area: 3 / 1 / 4 / 8; }
  .port__title, .method__title, .pinned__title { grid-column: 4 / 10; }
  .port__intro, .method__intro { grid-column: 9 / 13; }
  .contact__intro { grid-column: 1 / 7; }
  .contact__form-wrap { grid-column: 7 / 13; }
}

@media (max-width: 1100px) {
  :root { --header-h: 60px; }

  .site-header__place { display: none; }
  .site-header__inner { gap: 14px; }
  .nav { gap: 14px; }
  .nav__list { gap: 14px; }
  .nav__list a { font-size: var(--fs-meta); letter-spacing: .06em; gap: 5px; }
  .nav__cta { padding: 0 12px; font-size: var(--fs-meta); letter-spacing: .06em; }

  /* Without JS the toggle is not rendered at all and the nav stays a plain
     inline strip; JS adds .has-js-nav to swap in the disclosure pattern. */
  html:not(.has-js-nav) .nav-toggle { display: none; }
  html:not(.has-js-nav) .nav {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
  }
  html:not(.has-js-nav) .nav__list { gap: 16px; }
  html:not(.has-js-nav) .nav__cta { display: none; }

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    min-width: 44px;
    margin-left: auto;
    padding: 0 2px;
    font-size: var(--fs-meta);
    letter-spacing: .12em;
    text-transform: uppercase;
    transition: transform var(--dur-micro) var(--ease-move);
  }
  .nav-toggle:active { transform: translateY(1px); transition-duration: 90ms; }
  .nav-toggle__bars,
  .nav-toggle__bars::before,
  .nav-toggle__bars::after {
    display: block;
    width: 22px;
    height: 1px;
    background: var(--ink);
    /* --ease-pop is reserved for elements under 40px; these bars are 22x1, so
       the X gets a small overshoot that the larger surfaces never use. */
    transition: transform var(--dur-small) var(--ease-pop),
                background-color var(--dur-micro) var(--ease-move);
  }
  .nav-toggle__bars { position: relative; }
  .nav-toggle__bars::before,
  .nav-toggle__bars::after {
    content: "";
    position: absolute;
    left: 0;
  }
  .nav-toggle__bars::before { top: -6px; }
  .nav-toggle__bars::after { top: 6px; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars { background: transparent; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars::after { transform: translateY(-6px) rotate(-45deg); }

  html.has-js-nav .nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: grid;
    gap: 0;
    padding: 8px var(--pad) 20px;
    background: var(--paper);
    border-bottom: 1px solid var(--rule-strong);
    transform-origin: 50% 0;
    /* The closed panel is moved and hidden, never faded: the design has no
       opacity-0 state outside html.js-motion, and a hard sheet drop suits the
       drawing-board language better than a cross-fade. */
    translate: 0 -6px;
    visibility: hidden;
    transition:
      translate var(--dur-small) var(--ease-exit),
      visibility 0s linear var(--dur-small);
  }
  html.has-js-nav .nav[data-open="true"] {
    translate: 0 0;
    visibility: visible;
    transition:
      translate var(--dur-med) var(--ease-enter),
      visibility 0s;
  }
  html.has-js-nav .nav__list {
    display: grid;
    gap: 0;
  }
  html.has-js-nav .nav__list li { border-bottom: 1px solid var(--rule); }
  html.has-js-nav .nav__list a {
    width: 100%;
    min-height: 52px;
    padding-block: 16px;
    font-size: .9375rem;
    letter-spacing: .04em;
  }
  html.has-js-nav .nav__cta { margin-top: 16px; width: 100%; }

  .hero__title { grid-area: 2 / 1 / 3 / 13; }
  .hero__figure {
    grid-area: 3 / 1 / 4 / 13;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
    gap: clamp(14px, 2vw, 24px);
    align-items: center;
  }
  .hero__figure .fig { max-width: 240px; }
  .hero__caption { margin-top: 0; border-top: 0; padding-top: 0; }
  .hero__aside { grid-area: 4 / 1 / 5 / 11; }

  .hero__rail { grid-area: 5 / 1 / 6 / 13; grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 18px; }
  .hero__rail > div:nth-child(2) { border-right: 0; }
  .hero__cue { text-align: left; }

  .pinned__head { row-gap: 10px; }
  .pinned__head .sec-tag,
  .pinned__title,
  .pinned__count { grid-column: 1 / 13; }
  .pinned__count { justify-self: start; }
  .pinned__note { grid-column: 1 / 13; }

  .port__head .sec-tag,
  .port__title,
  .port__intro,
  .method__head .sec-tag,
  .method__title,
  .method__intro { grid-column: 1 / 13; }

  .filters { position: static; }
  .filters__label { grid-column: 1 / 13; }
  .filters__scroll { grid-column: 1 / 13; }
  .filters__count { grid-column: 1 / 13; justify-self: start; }
  .search { grid-column: 1 / 13; }

  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .method__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .contact__intro,
  .contact__form-wrap { grid-column: 1 / 13; }

  .site-footer__nav:nth-child(1) { grid-column: 1 / 5; }
  .site-footer__nav:nth-child(2) { grid-column: 5 / 9; }
  .site-footer__nav:nth-child(3) { grid-column: 9 / 13; }
  .site-footer__mark { display: none; }
}

@media (max-width: 620px) {
  :root {
    --pad: 18px;
    --gap: 10px;
  }

  .hero { padding-block: 20px 28px; }
  .hero__eyebrow { grid-area: 1 / 1 / 2 / 13; }
  .hero__stamp {
    grid-area: 2 / 1 / 3 / 13;
    justify-self: start;
    text-align: left;
    border-top: 0;
    padding-top: 0;
  }
  .hero__title { grid-area: 3 / 1 / 4 / 13; font-size: clamp(2.85rem, 15vw, 4rem); }
  .hero__figure { grid-area: 4 / 1 / 5 / 13; display: block; padding: 14px 14px 10px; }
  .hero__aside { grid-area: 5 / 1 / 6 / 13; }
  .hero__lede { font-size: 1rem; }
  .hero__figure .fig { max-width: 100%; }
  .hero__caption { margin-top: 10px; border-top: 1px solid var(--rule); padding-top: 8px; }
  .hero__rail { grid-area: 6 / 1 / 7 / 13; grid-template-columns: minmax(0, 1fr); }
  .hero__rail > div { border-right: 0; border-bottom: 1px solid var(--rule); padding-block: 12px; }
  .hero__rail > div:last-child { border-bottom: 0; }

  .pinned__title { font-size: clamp(1.5rem, 7vw, 2rem); }
  .pane__facts { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .pane__facts > div { display: flex; justify-content: space-between; gap: 16px; }
  .pane__facts dt { margin-bottom: 0; }

  .port__title { font-size: clamp(2.4rem, 13vw, 3.2rem); }
  .grid { grid-template-columns: minmax(0, 1fr); }
  .card__art { aspect-ratio: 16 / 10; }
  .method__list { grid-template-columns: minmax(0, 1fr); }

  .contact__facts > div {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
  }
  .form__submit { width: 100%; }

  .search { grid-template-columns: minmax(0, 1fr); }
  .search__hint { grid-column: 1 / 2; justify-self: start; }

  .site-footer__top { flex-direction: column; align-items: flex-start; }
  .site-footer__claim { text-align: left; }
  .site-footer__nav:nth-child(1),
  .site-footer__nav:nth-child(2),
  .site-footer__nav:nth-child(3) { grid-column: 1 / 13; }
  .site-footer__cols { row-gap: 22px; }
  .site-footer__base { flex-direction: column; gap: 6px; }
  .site-footer__meta { margin-left: 0; }
}

/* ==========================================================================
   REDUCED MOTION — the pinned track becomes a vertical stack; all travel,
   marquee and clip reveals are removed. Crossfades stay ≤ 140 ms.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html.js-motion [data-reveal] {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
  }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  .pinned__track,
  .pinned__bar,
  .site-header::after {
    animation: none !important;
    transform: none !important;
  }
  .pinned { height: auto !important; padding-block: clamp(40px, 6vw, 72px); }
  .pinned__sticky { position: static; height: auto; display: block; }
  .pinned__viewport { display: block; overflow: visible; }
  .pinned__track {
    display: grid;
    width: auto;
    gap: 0;
    padding-inline: var(--pad);
    will-change: auto;
  }
  .pane {
    width: auto;
    padding-block: clamp(28px, 4vw, 44px);
    border-left: 0;
    border-top: 1px solid var(--rule-on-ink);
    padding-left: 0;
  }
  .pane:first-child { border-top: 0; }
  .pane__fig { height: auto; max-width: 420px; }
  .pane__fig .fig { max-height: none; max-width: 300px; }
  .pinned__progress { display: none !important; }
  .pinned__note { grid-column: 1 / 13; justify-self: start; text-align: left; }
  html.js-motion .hero__line > span { animation: none !important; transform: none !important; }
  .card__link { transition: none !important; }
}

/* more contrast: hairline rules darken, muted text goes to full ink */
@media (prefers-contrast: more) {
  :root {
    --rule: rgba(22, 24, 27, .38);
    --rule-strong: rgba(22, 24, 27, .68);
    --rule-on-ink: rgba(236, 234, 228, .42);
    --ink-2: #3c4046;
    --ink-2-on-ink: #eceae4;
  }
  .btn--line { border-width: 2px; }
  .chip { border-width: 2px; }
}

/* no glass is used anywhere; the fallback is simply the opaque surface */
@media (prefers-reduced-transparency: reduce) {
  .site-header,
  .filters,
  .nav { background: var(--paper); }
}

/* Local stock photography replaces the schematic building plates. */
.photo-real { display: block; max-width: 100%; object-fit: cover; }
.hero__figure .photo-real { width: 100%; height: auto; max-width: none; aspect-ratio: 320 / 460; }
.pane__fig .photo-real { width: 100%; height: auto; max-width: none; max-height: 58svh; aspect-ratio: 300 / 420; min-height: 0; flex: 1 1 auto; }
.card__art { position: relative; padding: 0; overflow: hidden; }
.card__art .photo-real { position: absolute; inset: 0; width: 100%; height: 100%; }
.photo-note { margin: 0 0 24px; max-width: 65ch; font-size: var(--fs-meta); line-height: 1.6; color: var(--ink-2); }
@media (prefers-reduced-motion: reduce) { .pane__fig .photo-real { flex: none; max-height: none; aspect-ratio: 4 / 3; } }
