/* ============================================================
   ROHIT AHUJA DESIGN — rohitahuja.design
   Hand-built static site. No frameworks.
   Type: Archivo (variable, wdth axis) + Roboto Condensed (variable)
   Themes: light (paper) / dark (ink) via [data-theme] + system fallback
   ============================================================ */

@font-face {
  font-family: "Archivo";
  src: url("../fonts/archivo-var.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Roboto Condensed";
  src: url("../fonts/robotocondensed-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  --bg: #F4F2EE;
  --bg-2: #ECE9E2;
  --card: #FBFAF7;
  --ink: #1A1815;
  --ink-2: #45413B;
  --mut: #6F6A61;
  --line: rgba(26, 24, 21, .16);
  --line-soft: rgba(26, 24, 21, .08);
  --accent: #B72025;
  --accent-text: #B72025;
  --scrim: linear-gradient(180deg, rgba(10,10,10,.34), rgba(10,10,10,0) 34%, rgba(10,10,10,0) 55%, rgba(10,10,10,.52));
  --plan-bg: #FFFFFF;
  --shadow: 0 24px 70px -30px rgba(20, 18, 14, .45);
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #121210;
    --bg-2: #181816;
    --card: #1B1B19;
    --ink: #ECE9E2;
    --ink-2: #C9C4BB;
    --mut: #99948A;
    --line: rgba(236, 233, 226, .17);
    --line-soft: rgba(236, 233, 226, .08);
    --accent: #B72025;
    --accent-text: #B72025;
    --plan-bg: #F4F2EE;
    --shadow: 0 24px 70px -30px rgba(0, 0, 0, .8);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #121210;
  --bg-2: #181816;
  --card: #1B1B19;
  --ink: #ECE9E2;
  --ink-2: #C9C4BB;
  --mut: #99948A;
  --line: rgba(236, 233, 226, .17);
  --line-soft: rgba(236, 233, 226, .08);
  --accent: #B72025;
  --accent-text: #B72025;
  --plan-bg: #F4F2EE;
  --shadow: 0 24px 70px -30px rgba(0, 0, 0, .8);
  color-scheme: dark;
}

/* ---------- reset-lite ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* ROOT TYPE SCALE — a gentle bump on wide monitors only.
   Body copy was a flat 1rem (= the 16px browser default) at every width, which reads
   small once the container is allowed to open up past 1920 (see .wrap below). Scaling
   the ROOT rather than individual rules means every rem in this stylesheet — type,
   gutters, section padding — grows together and the page keeps its proportions.

   THE FORMULA IS NOT THE OBVIOUS ONE. `rem` inside the root element's own font-size
   resolves against the INITIAL value (16px), not against the value being computed
   (CSS Values 4). So the constant term is a plain 0.7 x 16 = 11.2px and the whole
   expression is linear in vw:  f(vw) = 0.0025.vw + 11.2, clamped to [16, 18.5].
     390   ->  12.18  -> clamped 16.0px      (phones: untouched)
     1280  ->  14.40  -> clamped 16.0px      (laptops: untouched)
     1920  ->  16.00            16.0px       <- the knee. EXACTLY the old default, so
                                                the client's monitor is unchanged.
     2560  ->  17.60            17.6px       (1440p: +10%)
     3840  ->  20.80  -> clamped 18.5px      (tops out at 2920px wide)
   The lower clamp is what guarantees nothing below 1920 moves by so much as a
   sub-pixel, which is why every px-based media query in this file (all of them are
   <= 980px) is unaffected by construction rather than by inspection.
   NOTE: the 16px floor also means a visitor who has raised their browser's default
   font size above 18.5px would be scaled DOWN. Flagged rather than silently changed —
   the min/max pair here is the brief's. */
html {
  font-size: clamp(16px, 0.25vw + 0.7rem, 18.5px);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 1rem/1.68 "Archivo", "Helvetica Neue", Arial, sans-serif;
  font-variation-settings: "wdth" 100;
  letter-spacing: .005em;
  transition: background-color .35s ease, color .35s ease;
  min-height: 100svh;
  overflow-x: clip;
}
img { max-width: 100%; height: auto; display: block; -webkit-user-drag: none; user-select: none; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .22em; }
a:hover { color: var(--accent-text); }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 3px; border-radius: 2px; }
::selection { background: var(--accent); color: #fff; }

h1, h2, h3 { font-weight: 560; line-height: 1.08; letter-spacing: -.012em; margin: 0 0 .5em; }
h1 { font-size: clamp(2.2rem, 5vw, 3.9rem); font-variation-settings: "wdth" 112; }
h2 { font-size: clamp(1.6rem, 3.2vw, 2.4rem); font-variation-settings: "wdth" 110; }
h3 { font-size: 1.15rem; }
p { margin: 0 0 1.1em; max-width: 64ch; }

/* one container for header, body and footer — everything aligns left and right.
   Width sits between "full bleed" and a hard cap: fluid with generous gutters.

   The fluid part is unchanged and still does all the work up to ~2.77k: the gutter
   clamp saturates at 84px once 4.5vw >= 84 (i.e. from 1867px up), so above that the
   container is simply `100vw - 168`. The CAP was 1760px, which meant it engaged at
   1928px and the site letterboxed on anything wider — on a 2560 monitor it left
   ~800px of dead margin. Raised to 2600px, which:
     - is a no-op at and below 1928px  (at 1920 the fluid term yields 1752px and wins
       under either cap, so the client's 1920 monitor is pixel-identical to before),
     - fills properly on 1440p         (2560 - 168 = 2392px),
     - still stops the line lengths running away on a 4K/ultrawide desktop, where the
       cap now takes over at 100vw - 168 = 2600  ->  2768px and holds at 2600. */
.wrap, .wrap-wide {
  width: min(2600px, calc(100vw - 2 * clamp(28px, 4.5vw, 84px)));
  margin-inline: auto;
}

/* kicker — the drawing-annotation label */
.kicker {
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125;
  font-weight: 700;
  font-size: .8rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--mut);
  display: flex;
  align-items: center;
  gap: .9rem;
  margin: 0 0 1.6rem;
}
.kicker::after { content: ""; height: 1px; background: var(--line); flex: 0 0 3.2rem; }
.kicker.no-rule::after { display: none; }

/* VERIFY badge — draft review marker, removed for production build */
.verify {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .1em;
  color: #fff;
  background: var(--accent);
  padding: .14em .5em .1em;
  border-radius: 2px;
  vertical-align: .18em;
  margin-left: .45em;
  white-space: nowrap;
}

/* ---------- header ---------- */
.site-head {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  transition: background-color .35s ease, border-color .35s ease, transform .35s ease;
  border-bottom: 1px solid transparent;
}
.site-head .bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.05rem 0;
}
.site-head.solid,
.site-head.scrolled {
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--line-soft);
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: .8rem;
  text-decoration: none;
  color: inherit;
}
.brand:hover { color: inherit; }
/* R-logo mark: mono stencil (currentColor through the disc's alpha) that takes its
   red the moment its context takes colour — brand hover, or the hero colourising */
.brand .mark, .site-foot .mark {
  position: relative;
  width: 32px;
  height: 32px;
  transition: transform .8s cubic-bezier(.19, 1, .22, 1);
}
.brand .mark::before, .site-foot .mark::before,
.brand .mark::after, .site-foot .mark::after {
  content: "";
  position: absolute;
  inset: 0;
  transition: opacity .5s ease;
}
.brand .mark::before, .site-foot .mark::before {
  background: currentColor;
  -webkit-mask: url("../img/brand/logo-mask.png") center/contain no-repeat;
  mask: url("../img/brand/logo-mask.png") center/contain no-repeat;
}
.brand .mark::after, .site-foot .mark::after {
  background: url("../img/brand/logo-colour.png") center/contain no-repeat;
  opacity: 0;
}
.brand:hover .mark, .site-foot .mark:hover { transform: rotate(360deg); }
.brand:hover .mark::after, .site-foot .mark:hover::after { opacity: 1; }
.brand:hover .mark::before, .site-foot .mark:hover::before { opacity: 0; }
/* index: the mark colourises together with the hero photo */
body:has(.hero:hover) .brand .mark::after { opacity: 1; }
body:has(.hero:hover) .brand .mark::before { opacity: 0; }
@media (hover: none), (pointer: coarse) {
  .brand .mark::after, .site-foot .mark::after { opacity: 1; }
  .brand .mark::before, .site-foot .mark::before { opacity: 0; }
}
.brand .word {
  font-weight: 700;
  font-size: .92rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-variation-settings: "wdth" 125;
  white-space: nowrap;
}
.brand .word span { color: var(--mut); font-weight: 430; }

.site-nav { display: flex; align-items: center; gap: 2rem; }
.site-nav a {
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125;
  font-weight: 700;
  font-size: .84rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  text-decoration: none;
  padding: .3rem 0;
  position: relative;
}
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 100%;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transition: right .3s ease;
}
.site-nav a:hover::after, .site-nav a[aria-current="page"]::after { right: 0; }
.site-nav a[aria-current="page"] { color: var(--accent-text); }

/* header over hero (index, project tops): light-on-image until scrolled */
.site-head.over { color: #F2EFE9; }
.site-head.over:not(.scrolled) .site-nav a[aria-current="page"] { color: #fff; }
.site-head.over:not(.scrolled) .brand .word span { color: rgba(242, 239, 233, .62); }
.site-head.over:not(.scrolled) .theme-btn { border-color: rgba(242, 239, 233, .4); }
.site-head.over.scrolled { color: var(--ink); }

/* theme toggle */
.theme-btn {
  inline-size: 34px;
  block-size: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  transition: border-color .3s, transform .5s cubic-bezier(.19,1,.22,1);
}
.theme-btn:hover { transform: rotate(40deg); border-color: var(--mut); }
.theme-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.theme-btn .moon { display: none; }
:root[data-theme="dark"] .theme-btn .sun { display: none; }
:root[data-theme="dark"] .theme-btn .moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-btn .sun { display: none; }
  :root:not([data-theme="light"]) .theme-btn .moon { display: block; }
}

/* hamburger */
.menu-btn { display: none; width: 34px; height: 34px; position: relative; z-index: 75; }
.menu-btn i, .menu-btn i::before, .menu-btn i::after {
  content: ""; position: absolute; left: 6px; right: 6px; height: 1.6px;
  background: currentColor; transition: transform .3s ease, top .3s ease, opacity .2s;
}
.menu-btn i { top: 50%; }
.menu-btn i::before { top: -6px; }
.menu-btn i::after { top: 6px; }
body.menu-open .menu-btn i { background: transparent; }
body.menu-open .menu-btn i::before { top: 0; transform: rotate(45deg); }
body.menu-open .menu-btn i::after { top: 0; transform: rotate(-45deg); }

/* mobile overlay menu */
.mobile-menu {
  position: fixed; inset: 0; z-index: 70;
  background: var(--bg);
  display: grid;
  place-content: center;
  gap: 1rem;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s ease, visibility .3s;
}
body.menu-open .mobile-menu { opacity: 1; visibility: visible; }
.mobile-menu a {
  font-size: clamp(2rem, 9vw, 3rem);
  font-weight: 580;
  font-variation-settings: "wdth" 112;
  text-decoration: none;
  letter-spacing: -.01em;
}
.mobile-menu a[aria-current="page"] { color: var(--accent-text); }
.mobile-menu .mm-meta { margin-top: 2.4rem; color: var(--mut); font-size: .85rem; }
body.menu-open { overflow: hidden; }

/* Build B: logo-only header — large mark, standard text; page content sits lower */
html[data-hdr="b"] .brand .mark { width: 46px; height: 46px; }
html[data-hdr="b"] main[style] { padding-top: 108px !important; }

/* ---------- B&W → colour hover (the house rule) ---------- */
.bw img {
  filter: grayscale(1) contrast(1.03);
  transition: filter .65s cubic-bezier(.25, .6, .25, 1), transform 1.3s cubic-bezier(.19, 1, .22, 1), opacity .6s;
}
@media (hover: hover) and (pointer: fine) {
  .bw:hover img, .bw:focus-within img { filter: grayscale(0) contrast(1); }
}
/* touch devices: colour, always */
@media (hover: none), (pointer: coarse) {
  .bw img { filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bw img { transition: filter .3s ease; }
}

/* ---------- hero (index) ---------- */
.hero {
  position: relative;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  color: #F5F2EC;
}
.hero img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.hero::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--scrim);
  pointer-events: none;
}
.hero .hero-inner {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  /* wordmark bottom locks to the bottom of the facade's tile grid in the hero
     photo, independent of viewport crop. The line sits at 81.7% of the image;
     with object-fit cover (width-limited, centred) that maps to
     y = 0.2134·vw + 0.5·vh, so the padding below is 50svh − 21.34vw. */
  padding-bottom: max(2.5rem, calc(50svh - 21.34vw));
  pointer-events: none;
}
.hero h1 {
  font-size: clamp(1.5rem, 4.4vw, 3.9rem);
  font-weight: 630;
  font-variation-settings: "wdth" 116;
  letter-spacing: .045em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  margin: 0 0 .9rem;
}
.hero h1 .thin { font-weight: 330; font-variation-settings: "wdth" 116; }
.hero .hero-sub {
  font-family: "Roboto Condensed", sans-serif;
  font-weight: 520;
  letter-spacing: .24em;
  text-transform: uppercase;
  font-size: clamp(1.1rem, 2.2vw, 1.5rem);
  color: rgba(245, 242, 236, .92);
  display: flex; align-items: center; gap: 1rem;
  margin-bottom: .7rem;
}
.hero .hero-sub::before { content: ""; width: 3rem; height: 1px; background: rgba(245,242,236,.65); }
.hero .credit {
  position: absolute;
  right: 4vw; bottom: 1.4rem;
  z-index: 2;
  font-family: "Roboto Condensed", sans-serif;
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(245,242,236,.62);
}
/* portrait crops are height-limited: the tile-grid line stays at 81.7% of view */
@media (max-aspect-ratio: 149/100) {
  .hero .hero-inner { padding-bottom: 19svh; }
}

.scroll-cue {
  position: absolute;
  left: 50%; bottom: 0;
  width: 1px; height: 74px;
  background: rgba(245,242,236,.5);
  overflow: hidden;
  z-index: 2;
}
.scroll-cue::after {
  content: "";
  position: absolute; left: 0; top: -40%;
  width: 100%; height: 40%;
  background: #fff;
  animation: cue 2.1s cubic-bezier(.65, 0, .35, 1) infinite;
}
@keyframes cue { to { top: 110%; } }
@media (prefers-reduced-motion: reduce) { .scroll-cue::after { animation: none; } }

/* ---------- sections ---------- */
section { padding: clamp(4rem, 9vh, 7rem) 0; }
section.tight { padding-top: clamp(2.5rem, 5vh, 4rem); }
.statement p.big, .big-line {
  font-size: clamp(1.55rem, 3.6vw, 2.75rem);
  line-height: 1.28;
  font-weight: 460;
  font-variation-settings: "wdth" 106;
  letter-spacing: -.012em;
  max-width: 24em;
  margin: 0;
}
.statement .attrib { color: var(--mut); margin-top: 1.4rem; font-size: .95rem; }
.statement em, .big-line em { font-style: normal; color: var(--accent-text); }

/* section head row: kicker + optional link right */
.sec-head { display: flex; align-items: baseline; justify-content: space-between; gap: 2rem; }
.more {
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125;
  font-size: .84rem; font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.more::after { content: "  →"; transition: letter-spacing .3s; }
.more:hover::after { letter-spacing: .3em; }
.more-after { display: none; }

/* ---------- work grid ---------- */
.tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2.2rem, 4vw, 3.6rem) clamp(1.6rem, 2.6vw, 2.4rem);
}
.tiles.tiles-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1080px) {
  .tiles.tiles-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  .tiles, .tiles.tiles-3 { grid-template-columns: 1fr; }
}
.tile { text-decoration: none; color: inherit; display: block; }
.tile figure { margin: 0; }
.tile .ph {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--bg-2);
  position: relative;
}
.tile .ph img { width: 100%; height: 100%; object-fit: cover; }
@media (hover: hover) { .tile:hover .ph img { transform: scale(1.035); } }
.tile figcaption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: .85rem 0 0;
  border-top: 1px solid transparent;
}
.tile .t-name { font-weight: 560; font-size: 1.02rem; letter-spacing: -.005em; }
.tile .t-meta {
  font-family: "Roboto Condensed", sans-serif;
  color: var(--mut);
  font-size: .78rem;
  font-weight: 520;
  letter-spacing: .13em;
  text-transform: uppercase;
  white-space: nowrap;
}
.tile:hover .t-name { color: var(--accent-text); }
/* hover-reveal name plate — grid view keeps images caption-free */
.ph-t {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: 2.4rem 1rem .85rem;
  color: #F4F2EE;
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  background: linear-gradient(180deg, transparent, rgba(10, 10, 9, .55));
  opacity: 0;
  transition: opacity .45s ease;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  body:not([data-view="list"]) .tile figcaption { display: none; }
  .tile:hover .ph-t { opacity: 1; }
}
body[data-view="list"] .ph-t { display: none; }

.tile .tag {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .66rem; font-weight: 640;
  letter-spacing: .12em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  color: var(--mut);
  border-radius: 999px;
  padding: .18em .6em;
  margin-left: .55em;
  vertical-align: .14em;
}

/* group header inside work page */
.work-group { margin-top: clamp(3rem, 7vh, 5rem); }
.work-group:first-of-type { margin-top: 0; }
.group-note { color: var(--mut); font-size: .92rem; max-width: 58ch; margin: -0.6rem 0 2.2rem; }

/* ---------- filters + view toggle ---------- */
.work-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding: 0 0 2.4rem;
}
.chips { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; }
.chip {
  font-family: "Roboto Condensed", sans-serif;
  font-weight: 560;
  font-size: .84rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--mut);
  padding: .35rem 0;
  position: relative;
}
.chip::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 0;
  height: 2px; background: var(--accent); transition: right .3s ease;
}
.chip:hover { color: var(--ink); }
.chip.on { color: var(--ink); }
.chip.on::after { right: 0; }
.view-toggle { display: flex; gap: .3rem; border: 1px solid var(--line); border-radius: 999px; padding: .2rem; }
.view-toggle button {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .74rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  padding: .32rem .9rem; border-radius: 999px; color: var(--mut);
}
.view-toggle button.on { background: var(--ink); color: var(--bg); }

/* ---------- list view ---------- */
body[data-view="list"] .tiles { display: block; }
body[data-view="list"] .tile { border-top: 1px solid var(--line-soft); }
body[data-view="list"] .work-group .tiles .tile:last-child { border-bottom: 1px solid var(--line-soft); }
body[data-view="list"] .tile .ph { display: none; }
body[data-view="list"] .tile figcaption {
  padding: 1.05rem .2rem;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
}
body[data-view="list"] .tile .t-name { font-size: clamp(1.15rem, 2.4vw, 1.7rem); font-weight: 500; }
body[data-view="list"] .tile:hover { background: var(--line-soft); }

/* floating list preview */
#list-preview {
  position: fixed;
  z-index: 55;
  width: min(340px, 26vw);
  aspect-ratio: 4 / 3;
  object-fit: cover;
  pointer-events: none;
  opacity: 0;
  transform: scale(.96);
  transition: opacity .25s ease, transform .35s cubic-bezier(.19,1,.22,1);
  box-shadow: var(--shadow);
}
#list-preview.show { opacity: 1; transform: none; }
@media (hover: none) { #list-preview { display: none; } }

/* ---------- project page ---------- */
/* HERO HEIGHT IS PURELY PROPORTIONAL. It used to read `min(78vh, 820px)`, and the 820px
   was the same class of bug as the old fixed container-width cap: an ABSOLUTE ceiling on
   a RELATIVE measure, so the composition changed with the monitor instead of scaling with
   it. Worked through on the client's own two screens —
       1920x1080  viewport ~950px tall   78vh = 741px   cap never reached   hero = 78%
       2560x1440  viewport ~1310px tall  78vh = 1022px  cap bites at 820    hero = 63%
   — i.e. the hero got proportionally SMALLER exactly where the screen got bigger, and the
   project title (which he wants sitting just below the fold) climbed into view on the
   larger monitor. The cap is therefore gone outright rather than raised: any absolute
   number only moves the viewport height at which the same drift starts. A 4K screen gets
   the same 78% as a laptop, which is the whole point.
   THE FLOOR STAYS. `min-height: 420px` is a different kind of limit — it protects a SHORT
   window (a landscape laptop, a browser dragged down to half height) where 78% of very
   little is not enough image to read as a plate. It engages below a 538px viewport
   (420 / .78) and nowhere near any full-screen desktop.
   `vh`, not `svh`/`dvh`: this hero is not the first thing on the page (the fixed header
   sits over it) and it is not trying to fill the phone viewport exactly, so the stable
   large-viewport unit is the one that keeps the box from resizing as mobile URL bars
   retract mid-scroll. */
.p-hero { position: relative; height: 78vh; min-height: 420px; overflow: hidden; background: var(--bg-2); }
.p-hero img { width: 100%; height: 100%; object-fit: cover; }

/* ---- OPT-IN HERO ANCHOR, one project at a time ----
   `object-fit: cover` fills the 78vh box and throws the overflow away symmetrically:
   its default `object-position: 50% 50%` takes equal bites off the top and the bottom of
   a photograph that is wider, relative to its own aspect, than the box. That is the right
   default and it stays the default — this rule is not in `.p-hero img`, it is a class the
   generator only writes when a project asks for it.
   WHY A PROJECT WOULD ASK. On a landscape cover in a 78vh box the crop is deep: at 1920
   x1080 the box is 842px tall and a 4:3 source scaled to 1920 wide is 1440px, so 598px —
   two fifths of the frame — is discarded, ~299 off each edge. Where the subject sits high
   (H-12 Walton Road: the lit ceiling and the head of the glazing) centring cuts the top
   off. `center top` moves the whole 598px bite to the bottom, which on that photograph is
   gym floor.
   SET IT IN `_build_site.py`, NOT HERE: the project dict's `hero_pos` field is the single
   place the decision lives, and the generator validates it against HERO_POS there, so a
   typo is a build error rather than a class with no rule. Adding a value means adding it
   in both files.
   HORIZONTAL AXIS DELIBERATELY LEFT AT `center`, on both values. Which axis actually
   crops flips with the viewport's own aspect: a 1920x1080 desktop gives a 1920x842 box
   (2.28:1) and a 4:3 source overflows vertically, which is the case above; a 390x844
   phone gives 390x658 (0.59:1) and the same source overflows HORIZONTALLY instead, with
   the vertical fitting exactly. On the phone, therefore, `center top` and `center center`
   resolve to the same picture — the rule is inert rather than wrong, and the crop that
   does happen there stays centred, which is what it should be. */
.p-hero img.pos-top    { object-position: center top; }
.p-hero img.pos-bottom { object-position: center bottom; }
.p-head { padding: clamp(2.4rem, 5vh, 4rem) 0 0; }
.p-head .crumb {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .8rem; font-weight: 560;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--mut);
  display: inline-flex; gap: .6rem;
  text-decoration: none;
  margin-bottom: 1.2rem;
}
.p-head .crumb:hover { color: var(--accent-text); }
.p-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(240px, 330px);
  gap: 2.5rem clamp(2.5rem, 6vw, 6rem);
  align-items: start;
  padding: 1.6rem 0 0;
}
.p-facts { position: sticky; top: 90px; }
.p-facts dl { margin: 0; border-top: 1px solid var(--line); }
.p-facts .row {
  display: grid;
  grid-template-columns: 9.5em 1fr;
  gap: 1rem;
  padding: .62rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.p-facts dt {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .72rem; font-weight: 620;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--mut);
  padding-top: .18em;
}
.p-facts dd { margin: 0; font-size: .95rem; }
.p-body p { color: var(--ink-2); }
.p-body p:first-of-type { color: var(--ink); font-size: 1.12rem; line-height: 1.6; }

.gallery {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1rem, 2vw, 1.8rem);
  padding-top: clamp(2.5rem, 6vh, 4rem);
}
.gallery figure { margin: 0; position: relative; }
.gallery .g-full { grid-column: 1 / -1; }
.gallery img { width: 100%; height: auto; cursor: zoom-in; }
.gallery .g-half:not(.plan) img { aspect-ratio: 3 / 4; object-fit: cover; }
.gallery .g-full img { max-height: 82vh; object-fit: cover; }
.gallery .plan { background: var(--plan-bg); border: 1px solid var(--line-soft); padding: clamp(1rem, 3vw, 2.4rem); }
.gallery .plan img { object-fit: contain; cursor: zoom-in; aspect-ratio: auto; }
.gallery figure + figure { margin: 0; }

/* prev / next */
.p-nav {
  margin-top: clamp(3.5rem, 8vh, 6rem);
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.p-nav a {
  padding: clamp(1.8rem, 4vh, 3rem) 0;
  text-decoration: none;
  display: grid;
  gap: .5rem;
}
.p-nav a + a { text-align: right; border-left: 1px solid var(--line-soft); padding-left: 2rem; }
.p-nav a:first-child { padding-right: 2rem; }
.p-nav .lbl {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .76rem; font-weight: 600; letter-spacing: .16em;
  text-transform: uppercase; color: var(--mut);
}
.p-nav .nm { font-size: clamp(1.3rem, 3vw, 2rem); font-weight: 540; font-variation-settings: "wdth" 108; }
.p-nav a:hover .nm { color: var(--accent-text); }

/* ---------- lightbox ---------- */
.lb {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(10, 10, 9, .93);
  display: none;
  align-items: center;
  justify-content: center;
}
.lb.open { display: flex; }
.lb .lb-stage { position: relative; display: inline-block; }
.lb img {
  max-width: 92vw;
  max-height: 86vh;
  width: auto; height: auto;
  box-shadow: 0 40px 120px -40px rgba(0,0,0,.9);
}
/* RAD mark, bottom corner of every opened image */
.lb .lb-w {
  position: absolute;
  right: 14px; bottom: 14px;
  width: 42px; height: 42px;
  background: #F4F2EE;
  -webkit-mask: url("../img/brand/logo-mask.png") center/contain no-repeat;
  mask: url("../img/brand/logo-mask.png") center/contain no-repeat;
  opacity: .6;
  pointer-events: none;
}
/* lightbox is chrome-free: image, arrows, close — no captions */
.lb button {
  position: absolute;
  color: #F4F2EE;
  font-size: 1.6rem;
  width: 52px; height: 52px;
  display: grid; place-items: center;
}
.lb .lb-x { top: 1rem; right: 1.4rem; }
.lb .lb-prev { left: 1rem; top: 50%; transform: translateY(-50%); }
.lb .lb-next { right: 1rem; top: 50%; transform: translateY(-50%); }
.lb button:hover { color: #fff; transform-origin: center; }
.lb .lb-n {
  position: absolute;
  left: 50%; bottom: 1.3rem;
  transform: translateX(-50%);
  color: rgba(244, 242, 238, .6);
  font-family: "Roboto Condensed", sans-serif;
  font-size: .78rem;
  font-weight: 520;
  letter-spacing: .22em;
}

/* ---------- index: practice strip + studio list ---------- */
.tri { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2.5rem; }
.tri h3 { margin-bottom: .5rem; }
.tri p { color: var(--mut); font-size: .95rem; max-width: none; }
.now-list { border-top: 1px solid var(--line); margin-top: .6rem; }
.now-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1rem;
  align-items: baseline;
  padding: 1.1rem .2rem;
  border-bottom: 1px solid var(--line-soft);
}
.now-row .nm { font-size: clamp(1.05rem, 2.2vw, 1.45rem); font-weight: 500; }
.now-row .st {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .76rem; font-weight: 560; letter-spacing: .14em;
  text-transform: uppercase; color: var(--mut);
}

/* ---------- studio page ---------- */
.duo { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2.5rem, 6vw, 5.5rem); align-items: stretch; }
.duo .img-plate { display: flex; flex-direction: column; max-width: 540px; justify-self: end; width: 100%; }
.duo .img-fill { flex: 1 1 auto; position: relative; min-height: 320px; }
.duo .img-fill img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.duo > div:first-child p { font-size: 1.05rem; line-height: 1.75; }
.tenets { counter-reset: tn; border-top: 1px solid var(--line); margin-top: 1rem; }
/* Row rhythm: every body paragraph wraps to EXACTLY two lines, so all five rows are the
   same height and the hairlines between them are evenly spaced. That makes the measure
   below a tuned value, not a taste one. Measured window in which all five bodies are two
   lines: 54ch..81ch. Below 54 tenet 04 (the longest) breaks to three; above 81 tenet 05
   (the shortest) collapses to one.
     Re-measured 07.08.2026 in 1ch steps at 1920/2560/3840 (_qa/tenet-sweep.mjs) after the
   tenet copy changed — the window came back identical (54..81), confirmed independently
   rather than assumed. Client wants the lines shorter, so the value now sits close to the
   54 floor instead of at the midpoint: 57ch, i.e. lower-bound + 3ch of margin — enough that
   a font fallback or a rounding difference cannot tip a row to three, but not so much
   headroom that it drifts back toward the old 68ch measure.
     Why one number covers every desktop width: `ch` scales with the fluid root, and the
   1fr column is far wider than 57ch at every width from 1920 up, so this max-width is
   always the binding constraint and the wrapping is IDENTICAL at 1920 / 2560 / 3840 rather
   than merely verified at each (re-confirmed by the same re-sweep — see report).
     Editing this number or any tenet body re-opens the question — re-measure the line
   counts (_qa/tenet-sweep.mjs) before shipping either. With the row height fixed at two
   lines, the number and heading are centred against it. */
.tenet {
  display: grid;
  grid-template-columns: 3.4rem 16rem 1fr;
  gap: 1.5rem;
  padding: 1.5rem .2rem;
  border-bottom: 1px solid var(--line-soft);
  align-items: center;
}
.tenet::before {
  counter-increment: tn;
  content: counter(tn, decimal-leading-zero);
  font-family: "Roboto Condensed", sans-serif;
  font-weight: 300; font-size: 1.5rem;
  color: var(--mut);
}
.tenet h3 { font-size: 1.06rem; margin: 0; }
.tenet p { color: var(--mut); font-size: .95rem; margin: 0; max-width: 57ch; }
.xp { border-top: 1px solid var(--line); }
.xp-row {
  display: grid;
  grid-template-columns: 10rem 1fr auto;
  gap: 1.5rem;
  padding: 1.05rem .2rem;
  border-bottom: 1px solid var(--line-soft);
  align-items: baseline;
}
.xp-row .yrs { font-family: "Roboto Condensed", sans-serif; font-size: .8rem; letter-spacing: .1em; color: var(--mut); }
.xp-row .role { font-weight: 540; }
.xp-row .org { color: var(--mut); font-size: .92rem; }
.cap-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 2.5rem; max-width: 760px; }
.cap-cols ul { list-style: none; margin: .8rem 0 0; padding: 0; }
.cap-cols li { padding: .45rem 0; border-bottom: 1px solid var(--line-soft); font-size: .95rem; color: var(--ink-2); }

/* ---------- contact ---------- */
.contact-hero { padding-top: clamp(7rem, 16vh, 10rem); }
.contact-big a { text-decoration: none; border-bottom: 2px solid var(--line); transition: border-color .3s; }
.contact-big a:hover { border-color: var(--accent); color: inherit; }
.contact-rows { border-top: 1px solid var(--line); margin-top: 3rem; }
.c-row {
  display: grid;
  grid-template-columns: 10rem 1fr;
  gap: 1.5rem;
  padding: 1.1rem .2rem;
  border-bottom: 1px solid var(--line-soft);
}
.c-row .k {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .74rem; font-weight: 620; letter-spacing: .14em;
  text-transform: uppercase; color: var(--mut); padding-top: .25em;
}
.contact-lead {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(280px, .7fr);
  gap: clamp(2rem, 4vw, 4.5rem);
  align-items: stretch;
}
.careers {
  border: 1px solid var(--line);
  padding: clamp(1.3rem, 2.5vw, 2rem) clamp(1.4rem, 2.5vw, 2.2rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .5rem;
}
.careers .c-k {
  font-family: "Archivo", sans-serif;
  font-variation-settings: "wdth" 125;
  font-weight: 700;
  font-size: .78rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--mut);
  margin: 0;
}
.careers p { margin: 0; color: var(--ink-2); line-height: 1.7; }
.careers a { text-decoration-thickness: 1px; text-underline-offset: .22em; }
@media (max-width: 1080px) {
  .contact-lead { grid-template-columns: 1fr; }
  .careers { max-width: 560px; }
}

/* ---------- footer ---------- */
.site-foot {
  margin-top: clamp(3rem, 8vh, 6rem);
  border-top: 1px solid var(--line);
  padding: clamp(2.2rem, 5vh, 3.5rem) 0 2rem;
  font-size: .92rem;
}
.foot-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 2.5rem;
  align-items: start;
}
.foot-brand .word {
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  font-size: .85rem; font-variation-settings: "wdth" 125;
}
.foot-brand p { color: var(--mut); font-size: .88rem; margin-top: .9rem; }
.site-foot .mark {
  width: 44px; height: 44px;
  margin-bottom: 1rem;
}
.foot-col ul { list-style: none; margin: 0; padding: 0; }
.foot-col li { padding: .28rem 0; }
.foot-col a { text-decoration: none; color: var(--mut); }
.foot-col a:hover { color: var(--accent-text); }
.foot-col .ig { display: inline-block; padding-top: .35rem; }
.foot-col .ig svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.foot-col .ig .dot { fill: currentColor; stroke: none; }
.foot-col .ig:hover { color: var(--ink); }
.foot-col .k {
  font-family: "Roboto Condensed", sans-serif;
  font-size: .72rem; font-weight: 620; letter-spacing: .16em;
  text-transform: uppercase; color: var(--mut);
  margin-bottom: .8rem;
}
.legal {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem;
  color: var(--mut);
  font-size: .78rem;
  border-top: 1px solid var(--line-soft);
  margin-top: 2.6rem; padding-top: 1.4rem;
}

/* ---------- back to top: due north ----------
   WHOLE PIXELS, DELIBERATELY. The offsets here were `clamp(1rem, 3vh, 2rem)` and
   `clamp(1rem, 3vw, 2rem)`. Both terms of each clamp are viewport- or root-relative, and
   the root font-size is itself fluid (see the ROOT TYPE SCALE note at the top of this
   file): 16px at 1920, 17.6px at 2560, 18.5px at 3840. So `2rem` resolved to a whole
   32px at 1920 but 35.2px at 2560, and the button landed on a fractional pixel ROW —
   measured y 998 at 1920 (integer) against 1354.8125 at 2560. A `mask` re-sampled across
   pixel boundaries loses exactly what this mark is made of: the arrow's strokes are thin
   and mostly axis-aligned, so half a pixel of offset spreads every edge over two rows
   and the whole glyph reads soft. The button is a fixed 50x50 at every viewport and its
   distance from the corner carries no meaning that has to scale with type, so these are
   plain px and always land on integers.
   Only the <1200px branch uses these two; from 1200 up the rail below positions the
   button and its `bottom` is the one that counts. 24px matches what the old clamp
   actually resolved to on a phone (3vh of a 700-950px viewport = 21-28.5px) and 32px is
   what it resolved to on a laptop, so nothing moves perceptibly at any width. */
.to-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .4s ease, transform .4s ease, visibility .4s;
}
.to-top::before {
  content: "";
  width: 100%;
  height: 100%;
  background: currentColor;
  -webkit-mask: url("../img/brand/north-arrow-black.png") center/contain no-repeat;
  mask: url("../img/brand/north-arrow-black.png") center/contain no-repeat;
  transition: transform .8s cubic-bezier(.19, 1, .22, 1);
}
body.scrolled-far .to-top { opacity: 1; visibility: visible; transform: none; }
.to-top:hover::before { transform: rotate(360deg); }

/* RAIL (>=1200px only) — centres the button in the empty gutter beside .wrap instead
   of pinning it to the viewport corner. Below 1200 the gutter (~28-34px, see .wrap
   above) is narrower than the 50px button, so the corner placement stays exactly as
   shipped (the rule above, untouched) and the rail below is simply inert.

   The rail's OWN box is positioned with left:0/right:0 (not width:100vw) so its outer
   edges sit at the layout viewport's edges — the same scrollbar-EXCLUDED box that
   getBoundingClientRect() and position:fixed both key off.

   The middle track is `100vw`, not `100%`, even though the rail's own box above is
   deliberately scrollbar-excluded — this one has to match .wrap's width FORMULA
   (further up this file), vw and all, or the two stop agreeing on where the
   container's right edge is. .wrap's width is computed from 100vw (scrollbar
   INCLUDED) and then centred with auto-margins inside a parent that's only as wide as
   the scrollbar-EXCLUDED viewport — so below the 2600px cap, .wrap's real right
   margin is short of the naive "gutter" value by half a scrollbar-width. A
   `100%`-based middle track (the more obvious-looking choice, and literally what an
   earlier pass of this rule used) doesn't share that shortfall, so it silently
   disagrees with .wrap about where the container ends: measured 9.5px (button to
   .wrap) vs 17px (button to the viewport edge) at 1920 with this machine's 15px
   scrollbar — visibly off-centre on any page that actually scrolls, at every width
   below the 2600px cap (1920, 2560; 3840 is immune, because both versions saturate to
   the same flat 2600px regardless of scrollbar). Matching .wrap's own `100vw` fixes
   that: re-measured 13.25px on both sides, every time, at every tested width
   (1920/2560/3840) — exact, not approximate.

   One residual, and it belongs to .wrap, not to this rail: because .wrap's real
   gutter is itself a little scrollbar-dependent (measured 76.5px with a 15px
   scrollbar vs 84px without — a pre-existing property of the `100vw` width formula
   above, nothing to do with this button), a button held exactly centred in that real
   gutter necessarily inherits HALF of its wobble — measured 13.25px vs 17px (3.75px)
   at 1920 depending on whether the page has a scrollbar. That is the smallest this
   number can be made while still satisfying "centred on .wrap", which is the actual
   spec; it also reads as a rounding error next to the bug it replaces (the old
   right:vw offset had no relationship to .wrap at all, so its miss scaled with
   viewport width, not with half a scrollbar). Fixing it outright would mean changing
   .wrap's own width formula from `100vw` to `100%`, site-wide, well outside this
   rail. */
@media (min-width: 1200px) {
  .to-top-rail {
    position: fixed;
    left: 0; right: 0;                 /* NOT 100vw — excludes the scrollbar, which is the point */
    bottom: 32px;                      /* px, not rem — see the whole-pixel note above .to-top */
    z-index: 50;
    pointer-events: none;              /* the rail must never intercept clicks */
    display: grid;
    grid-template-columns: 1fr min(2600px, calc(100vw - 2 * clamp(28px, 4.5vw, 84px))) 1fr;
  }
  .to-top { grid-column: 3; justify-self: center; pointer-events: auto; position: static; }
}

/* ---------- reveal ---------- */
.rv { opacity: 0; transform: translateY(16px); transition: opacity .7s ease, transform .7s cubic-bezier(.19, 1, .22, 1); }
.rv.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- 404 ---------- */
.oops { min-height: 72svh; display: grid; place-content: center; text-align: center; gap: .6rem; }
.oops .mark { margin-inline: auto; width: 66px; height: 66px; background: currentColor;
  -webkit-mask: url("../img/brand/north-arrow-black.png") center/contain no-repeat;
  mask: url("../img/brand/north-arrow-black.png") center/contain no-repeat;
  transform: rotate(137deg); opacity: .85; }
.oops h1 { font-size: clamp(2rem, 6vw, 3.4rem); }
.oops p { color: var(--mut); margin-inline: auto; }

/* ---------- responsive ---------- */
@media (max-width: 1080px) {
  .p-grid { grid-template-columns: 1fr; }
  .p-facts { position: static; order: 2; max-width: 560px; }
  .tri, .cap-cols { grid-template-columns: 1fr; gap: 1.8rem; }
  .duo { grid-template-columns: 1fr; }
  .duo .img-plate { justify-self: start; }
  /* stacked layout: heading and body sit in one column beside the number, so the
     desktop centring and the widened measure are both handed back — this block renders
     pixel-identical to before the two-line row work.
       Number and heading sit in row 1 of this 2-column grid (body is alone in row 2,
     under grid-column:2, so it isn't part of row 1's alignment group and keeps its own
     position). Two things were tried and rejected here before landing on centring:
     `align-items: start` top-aligned their BOXES (the number is 1.5rem/1.68, a ~2.5rem
     line box; the heading is 1.06rem/1.08, a ~1.15rem line box), which left the heading's
     glyph sitting visibly higher than the number's. `align-items: baseline` fixed that
     but overcorrected: digits have no descender, so a baseline-aligned heading sits level
     with the BOTTOM of the number, not its middle — the client flagged this as reading
     "bottom-aligned", not centred, and asked for the heading optically centred on the
     number glyph instead.
       Fix: no override at all — just `align-items: center`, inherited from the base
     `.tenet` rule (line 843-850, above 1080px), which the client already reviewed and
     approved. That rule is safe to reuse as-is rather than re-deriving a narrow-specific
     value: align-items:center places every row-1 item's OWN box centre on the row's
     midline regardless of the row's height, so the residual gap between an item's
     box-centre and its glyph-centre (cap-height-band midpoint) is purely a function of
     that item's own font-size/line-height/font metrics — it does not depend on what sets
     the row height. Wide layout's row is tall (set by the 2-line body paragraph) and
     narrow layout's row is short (set by the number's own ~2.5rem line box); the box-vs-
     glyph relationship for the number and for the heading is identical in both, so the
     wide layout's already-approved centring carries over unchanged. Measured via CDP +
     canvas actualBoundingBoxAscent (cap-height band = baseline − ascent .. baseline, using
     each element's leading capital letter/lining digits so lowercase ascenders and
     descenders elsewhere in a heading can't skew it) at 390/500/768/900/1000 for tenets
     01+04 (_qa/tenet-centre-measure.mjs): a constant |glyph-centre diff| of 0.5px (heading
     0.5px above the number's true centre — root font-size is pinned at its 16px floor
     across this whole width range, so the offset doesn't vary with width), well inside the
     1.5px tolerance — align-items:center alone is enough and no nudge was added. The old
     compensating `.tenet h3 { margin-top: .15em }`
     is still correctly gone; the base rule's `.tenet h3 { margin: 0 }` (line 858) is what
     centring should act on, and reintroducing that margin would re-open the old offset. */
  .tenet { grid-template-columns: 3rem 1fr; }
  .tenet p { grid-column: 2; max-width: 56ch; }
  /* Row rhythm, take 2: client flagged the gap ABOVE the heading vs BELOW the body as
     lopsided and asked to "move the body up" until the two match. Measured via CDP + ink
     (_qa/tenet-spacing-before.mjs — same cap-height-ink technique as the centring fix above,
     mirrored with a matching last-line technique: a zero-size marker APPENDED as the
     paragraph's last child, vertical-align:baseline, gives the last line's baseline the same
     way a first-child marker gives the heading's) BEFORE this change, at all 5 widths
     (390/500/768/900/1000) x all 5 tenets:
       TOP GAP (rule -> heading cap-top ink)         = 38.00px (tenet 01) / 37.00px (others),
                                                         constant across every width.
       BOTTOM GAP (body baseline -> rule), baseline   = 33.53px, constant across every width
       convention                                       AND every tenet regardless of its
                                                         last line's own text/descenders.
     -> BOTTOM was the SMALLER of the two by 3.47-4.47px, not bigger — the opposite of how it
     first read on screen. The stated "global p margin" cause did not hold (VERIFIED, not
     assumed): `.tenet p` already zeroes it (margin: 0, line 859), and .tenet's own
     padding-top/padding-bottom were already symmetric (24px/24px). The real, confirmed
     mechanism is upstream of any margin: row 1's align-items:center (kept for the centring
     fix above) centres the SHORT heading line-box inside the number's TALL one (2.52rem vs
     1.08 x 1.06rem = 40.3px vs 18.3px, an 11.0px slack split above+below the heading purely
     within row 1 — confirmed directly off the number pseudo's box model and the heading's own
     rect, independent of the ink measurement above). Row 2 has no such slack (the body
     paragraph is the sole occupant of its row, so the track fits it exactly) — its own
     line-height is looser than the heading's (1.68 vs 1.08) but not loose enough to close an
     11px head start the number gave row 1. Net effect measured above: row 1's slack
     outweighs row 2's looser leading, so the RULE-to-heading distance ends up bigger than the
     body-to-RULE distance, by a near-constant ~4px regardless of width or which tenet (the
     row 1 mechanism doesn't depend on column width, and baseline position is a pure function
     of font metrics + padding, not of how many lines the body wrapped to — confirmed by the
     BEFORE numbers above being flat across all 25 width x tenet combinations).
       Fix: `.tenet` padding-bottom 1.5rem -> 1.75rem (+4px) here only. Chosen over touching
     row 1 (padding-top, or anything upstream of the centring fix) because the client's own
     framing anchors the FIX to the body side ("move the body up") and because this row's
     TOP gap must stay exactly as-is — nothing above it, including the centring, should move.
     A flat +4px was enough to bring every tenet at every width to within 0.53px of equal
     (measured after: see _qa/tenet-spacing/after-real.json) — comfortably inside the 1.5px
     tolerance — so no per-tenet or per-width tuning was needed. Baseline was the target, not
     true ink-bottom: equalising on ink-bottom would chase each last line's own descender (a
     'g' in "building." vs a bare period in "thought."/"outside."), making the fix -- and the
     rows' rhythm -- depend on which letters happen to end each tenet's copy. Both numbers
     are still measured and reported for comparison. Row heights below 1080px are already
     content-driven (number of wrapped lines varies by tenet/width) and change slightly with
     this padding bump; per the brief that's expected and is not itself a regression. */
  .tenet { padding-bottom: 1.75rem; }
}
@media (max-width: 760px) {
  .site-nav { display: none; }
  .menu-btn { display: block; }
  .foot-grid { grid-template-columns: 1fr; gap: 2rem; }
  .gallery { grid-template-columns: 1fr; }
  .gallery .g-half { grid-column: 1 / -1; }
  .p-nav { grid-template-columns: 1fr; }
  .p-nav a + a { border-left: 0; border-top: 1px solid var(--line-soft); text-align: left; padding-left: 0; }
  .xp-row { grid-template-columns: 1fr; gap: .2rem; }
  .brand .word span { display: none; }
  .p-facts .row { grid-template-columns: 8em 1fr; }
  .hero .credit { display: none; }
  .hero .wrap { display: flex; flex-direction: column; align-items: center; text-align: center; }
  .hero h1 { order: 1; }
  .hero .hero-sub { order: 2; margin: .45rem 0 0; font-size: .8rem; }
  .hero .hero-sub::before { display: none; }
  .tile .t-name { font-size: .95rem; }
  .tile .tag { font-size: .6rem; }
  .tile .t-meta { font-size: .68rem; letter-spacing: .1em; }
  .t-pl { display: none; }
  .sec-head .more { display: none; }
  .more-after { display: inline-block; margin-top: 1.9rem; }
  .sec-head .kicker { white-space: nowrap; }
  body[data-view="list"] .tile .t-name { font-size: 1.02rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; min-width: 0; }
  body[data-view="list"] .tile figcaption { gap: .6rem; padding: .95rem .1rem; }
  .chips { gap: .3rem .95rem; }
  .chip { font-size: .7rem; letter-spacing: .12em; }
  .lb .lb-w { width: 21px; height: 21px; }
  .c-row { grid-template-columns: 1fr; gap: .15rem; padding: .9rem .1rem; }
  .c-row .k { padding-top: 0; }
  .contact-big { font-size: clamp(1.35rem, 6vw, 1.55rem); }
}
