/* Rummer Development · Phase 1 homepage. Tokens compile from DESIGN.md. */

/* ---------- fonts ---------- */
@font-face {
  font-family: 'Young Serif';
  src: url('../assets/fonts/youngserif.woff2') format('woff2');
  font-weight: 400; font-display: swap;
}
/* Direction A, Aubrey's pick 2026-08-26, promoted from preview to the default
   build 2026-09-03. Cabinet Grotesk carries display, Switzer carries body and
   every label surface. Young Serif survives on Bob Rummer's quotes ONLY, which
   is the rule she set: "keep it strictly to his words. If serif starts appearing
   in pull quotes or section headers, it stops meaning Bob and becomes
   decoration." Archivo and Fragment Mono are retired.

   These two are shipped EXACTLY as Fontshare delivers them. The ITF Free Font
   License v2.0 (bundled beside them) forbids modifying the font software, and
   names SUBSETTING and format conversion as modification, so the house
   subset-to-woff2 step must never be pointed at these files. Vendor woff2 is
   ~42KB each, so there is nothing to gain by trying. */
@font-face {
  font-family: 'Cabinet Grotesk';
  src: url('../assets/fonts/vendor/CabinetGrotesk-Variable.woff2') format('woff2');
  font-weight: 100 900; font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('../assets/fonts/vendor/Switzer-Variable.woff2') format('woff2');
  font-weight: 100 900; font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  --ink-0: oklch(0.17 0.008 60);
  --ink-1: oklch(0.22 0.010 58);
  --ink-2: oklch(0.32 0.012 58);
  --bone: oklch(0.92 0.010 75);
  --bone-dim: oklch(0.76 0.012 70);
  --walnut: oklch(0.62 0.050 55);

  --bay: 76px;
  --margin: 112px;
  --container: 1216px;
  --s1: 19px; --s2: 38px; --s3: 76px; --s4: 152px; --s5: 304px;

  /* Display knobs. w500 set larger is the direction Phillip picked 2026-09-03:
     scale carries the emphasis instead of weight, per Aubrey's own note. */
  --disp-w: 500;
  --disp-scale: 1.14;

  --leg0: 1448px; --leg1: 1440px; --leg2: 1694px; --leg3: 960px; --leg4: 1352px;
}
@media (min-width: 1600px) {
  :root { --bay: 88px; --margin: 144px; --container: 1408px; }
}
@media (max-width: 1366px) {
  :root { --bay: 68px; --margin: 96px; --container: 1088px; }
}
@media (max-width: 820px) {
  :root { --margin: 24px; --container: calc(100% - 48px); }
}

/* ---------- reset-ish ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: auto; }
body {
  background: var(--ink-0);
  color: var(--bone);
  font-family: 'Switzer', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}
img, video, canvas { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
:focus-visible { outline: 2px solid var(--walnut); outline-offset: 2px; }
::selection { background: var(--walnut); color: var(--ink-0); }
@view-transition { navigation: auto; }
/* The shell is the same object on both documents, so it must not be part of
   the root cross-fade: without its own snapshot the nav and footer fade out
   and back in on every navigation, which reads as a flicker on furniture that
   never moved. Naming them animates each against its own twin instead. */
.nav { view-transition-name: shell-nav; }
.footer { view-transition-name: shell-footer; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation: none !important;
  }
}

.container { width: min(var(--container), calc(100% - 2 * var(--margin))); margin-inline: auto; }

/* ---------- type ---------- */
.t-hero  { font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w); font-size: calc(95px * var(--disp-scale)); line-height: 1.02; letter-spacing: -0.02em; }
.t-motion{ font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w); font-size: calc(57px * var(--disp-scale)); line-height: 1.1; letter-spacing: -0.02em; }
.t-h2    { font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w); font-size: calc(76px * var(--disp-scale)); line-height: 1.05; letter-spacing: -0.02em; }
.t-h3    { font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w); font-size: calc(38px * var(--disp-scale)); line-height: 1.2; letter-spacing: -0.02em; }
.t-quote { font-family: 'Young Serif', serif; font-weight: 400; font-size: 57px; line-height: 1.15; }
.t-lede  { font-size: 23.75px; line-height: 34px; }
.t-body  { font-size: 19px; line-height: 28.5px; max-width: 62ch; }
.t-label {
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 16px; line-height: 22px;
  letter-spacing: 0.10em; font-variant-caps: all-small-caps;
}
@media (max-width: 1366px) {
  .t-hero { font-size: calc(84px * var(--disp-scale)); line-height: 1.03; }
  .t-motion { font-size: calc(50px * var(--disp-scale)); }
  .t-h2 { font-size: calc(66px * var(--disp-scale)); }
  .t-h3 { font-size: calc(34px * var(--disp-scale)); }
  .t-quote { font-size: 50px; }
  .t-lede { font-size: 22px; line-height: 32px; }
}
@media (max-width: 820px) {
  .t-hero { font-size: calc(44px * var(--disp-scale)); line-height: 1.08; }
  .t-motion { font-size: calc(30px * var(--disp-scale)); }
  .t-h2 { font-size: calc(38px * var(--disp-scale)); line-height: 1.1; }
  .t-h3 { font-size: calc(27px * var(--disp-scale)); line-height: 1.25; }
  .t-quote { font-size: 30px; line-height: 1.2; }
  .t-lede { font-size: 19px; line-height: 28px; }
  .t-body { font-size: 17px; line-height: 25.5px; }
  .t-label { font-size: 14px; line-height: 20px; }
}

.skiplink {
  position: absolute; left: -9999px; z-index: 100;
  background: var(--bone); color: var(--ink-0); padding: 10px 19px;
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 14px;
}
.skiplink:focus { left: var(--s1); top: var(--s1); }
.cue { color: var(--bone); }

/* ---------- nav ---------- */
/* Height is pinned rather than derived from padding, so the wordmark can carry
   its proper rank without moving the hairline that sits on the nav's bottom
   edge. 93px is exactly what the padded version measured. */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 10;
  display: flex; align-items: center; gap: var(--s2);
  height: 93px; padding: 0 var(--margin);
  transition: opacity 400ms ease;
}
.nav::before {
  content: ''; position: absolute; inset: 0; height: 150%; z-index: -1;
  background: linear-gradient(to bottom, oklch(0.17 0.008 60 / 0.55), transparent);
  pointer-events: none; opacity: 1; transition: opacity 300ms ease;
}
/* 0.92 let body copy read straight through the bar: an H2 was legible under
   ABOUT / INSIGHTS, and on mobile a bone CTA printed through the wordmark.
   The bar itself has to be opaque; the gradient only softens the edge below it. */
body.scrolled .nav::before,
body.static .nav::before, body.no-js .nav::before {
  background: linear-gradient(to bottom,
    oklch(0.17 0.008 60) 0%,
    oklch(0.17 0.008 60) 55%,
    oklch(0.17 0.008 60 / 0.55) 72%,
    transparent 100%);
}
/* granite lives here now: one textured hairline under the nav, in after first scroll.
   screen-blended over ink-2 so the 18-48 stone tones actually read at 2px. */
.nav::after {
  content: ''; position: absolute; left: var(--margin); right: var(--margin); bottom: 0;
  height: 2px;
  background: var(--ink-2) url('../assets/granite-base-1024.webp');
  background-size: 512px 24px;
  background-blend-mode: screen;
  opacity: 0; transform: scaleX(0); transform-origin: left;
  transition: opacity 500ms ease, transform 900ms cubic-bezier(0.22, 1, 0.36, 1);
}
body.scrolled .nav::after { opacity: 1; transform: scaleX(1); }
/* the wordmark holds the top-left at all times: the hero is type on the meadow,
   and the architecture is never covered by a lockup */
.nav-logo { display: block; }
/* the wordmark outranks the nav items: it was set below them at 20px */
.nav-logo img { height: 32px; width: auto; transition: opacity 220ms ease; }
.nav-logo:hover img { opacity: 0.72; }

.nav-links { display: flex; gap: var(--s2); margin-left: auto; }
.nav-links a {
  position: relative;
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 16px; letter-spacing: 0.10em;
  font-variant-caps: all-small-caps; text-decoration: none;
  color: var(--bone); padding: 11px 0; min-height: 44px;
  display: inline-flex; align-items: center;
  transition: opacity 200ms ease;
  opacity: 0.78;
}
.nav-links a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: var(--walnut); transform: scaleX(0); transform-origin: right;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-links a:hover { opacity: 1; }
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }
.nav-links a[aria-current="page"] { opacity: 1; }
.nav-links a[aria-current="page"]::after {
  transform: scaleX(1); background: var(--bone-dim); transform-origin: left;
}
.nav-cta {
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 16px; letter-spacing: 0.10em;
  font-variant-caps: all-small-caps; text-decoration: none; color: var(--bone);
  border: 1px solid oklch(0.92 0.010 75 / 0.5); padding: 12px 22px; min-height: 44px;
  display: inline-flex; align-items: center;
  background: oklch(0.17 0.008 60 / 0.30);
  transition: background 220ms ease, color 220ms ease, border-color 220ms ease;
}
.nav-cta:hover { background: var(--bone); color: var(--ink-0); border-color: var(--bone); }
/* 0.35 with a :hover-only restore made the nav unreadable for the longest leg
   of the scroll and unrecoverable on a touch screen, which has no hover. */
body.atrium .nav { opacity: 0.6; }
body.atrium .nav:hover, body.atrium .nav:focus-within { opacity: 1; }
@media (hover: none) { body.atrium .nav { opacity: 1; } }

/* ---------- mobile menu ---------- */
.nav-menu { display: none; width: 44px; height: 44px; margin-left: 8px; position: relative; }
.nav-bars, .nav-bars::before, .nav-bars::after {
  position: absolute; left: 12px; width: 20px; height: 1px; background: var(--bone);
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1), opacity 180ms ease;
}
.nav-bars { top: 21px; }
.nav-bars::before { content: ''; left: 0; top: -6px; }
.nav-bars::after { content: ''; left: 0; top: 6px; }
.nav-menu[aria-expanded="true"] .nav-bars { transform: rotate(45deg); }
.nav-menu[aria-expanded="true"] .nav-bars::before { transform: translateY(6px) rotate(-90deg); }
.nav-menu[aria-expanded="true"] .nav-bars::after { opacity: 0; }

.drawer {
  position: fixed; inset: 0; z-index: 9; display: flex;
  flex-direction: column; justify-content: center; gap: var(--s3);
  /* 0.97 ghosted the hero H1 through the drawer, so "since 1975" collided with
     the mail link. A menu is not a scrim; make it opaque. */
  padding: 0 24px; background: oklch(0.17 0.008 60);
}
.drawer[hidden] { display: none; }
.drawer-nav { display: flex; flex-direction: column; gap: 6px; }
.drawer-nav a {
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500;
  font-size: 34px; line-height: 1.35;
  text-decoration: none; color: var(--bone); padding: 6px 0; min-height: 44px;
}
.drawer-mail { color: var(--bone-dim); padding: 12px 0; min-height: 44px; }
body.menu-open { overflow: hidden; }

@media (max-width: 820px) {
  /* the outlined CTA box was 142px against a 138px wordmark with a 12px gap, so
     the call to action outweighed the brand and nearly touched it. Drop the box,
     keep the 44px touch target, and give the wordmark room. */
  .nav { height: 68px; padding: 0 24px; gap: 24px; }
  .nav-logo img { height: 26px; }
  .nav-links { display: none; }
  .nav-menu { display: block; }
  .nav-cta {
    margin-left: auto; white-space: nowrap; font-size: 12px;
    padding: 12px 0; min-height: 44px; border-color: transparent; background: none;
  }
  .nav::after { left: 24px; right: 24px; }
}
@media (min-width: 821px) { .drawer { display: none; } }

/* ---------- walk index (right edge, desktop, JS modes only) ---------- */
.walk {
  position: fixed; right: 26px; top: 50%; transform: translateY(-50%);
  z-index: 9; display: flex; flex-direction: column; gap: 22px;
  /* pointer-events alone leaves the five bay buttons focusable, so tabbing off
     "Join the list" walked through Approach, Fire, Atrium, Front and Retreat
     with nothing on screen. visibility takes them out of the tab order too. */
  opacity: 0; pointer-events: none; visibility: hidden;
  transition: opacity 400ms ease, visibility 0s linear 400ms;
}
body.walking .walk {
  opacity: 1; pointer-events: auto; visibility: visible;
  transition: opacity 400ms ease, visibility 0s;
}
body.atrium .walk { opacity: 0.35; }
body.atrium .walk:hover { opacity: 1; }
.walk button {
  display: flex; align-items: center; justify-content: flex-end; gap: 12px;
  padding: 4px 0; color: var(--bone-dim);
}
.walk .w-tick {
  width: 22px; height: 1px; background: var(--bone-dim); opacity: 0.6;
  transition: width 320ms cubic-bezier(0.22, 1, 0.36, 1), background 200ms ease, opacity 200ms ease;
}
.walk .w-t {
  font-size: 11px; opacity: 0; transform: translateX(6px);
  transition: opacity 220ms ease, transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.walk button.is-here .w-t { opacity: 0.5; transform: none; }
.walk button:hover .w-t { opacity: 1; transform: none; }
.walk button:hover .w-tick { width: 38px; background: var(--bone); opacity: 1; }
.walk button.is-here { color: var(--bone); }
.walk button.is-here .w-tick { width: 38px; background: var(--bone); opacity: 1; }
@media (max-width: 1100px) { .walk { display: none; } }
body.static .walk, body.no-js .walk { display: none; }

/* ---------- the spine + stage ---------- */
.spine { position: relative; }
.stage {
  position: sticky; top: 0; height: 100svh;
  margin-bottom: -100svh;
  overflow: clip; z-index: 0;
  background: var(--ink-0);
}

/* frame-box: a cover-fitted 1916:1080 box so % positions map to footage pixels */
.frame-box {
  position: absolute; left: 50%; top: 50%;
  aspect-ratio: 1916 / 1080;
  min-width: 100%; min-height: 100%;
  transform: translate(-50%, -50%);
  z-index: 2;
  transition: opacity 200ms linear;
}
.hero-still img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: fill; }
/* iOS draws a native play glyph on any paused inline video; these are decor, never controls */
video::-webkit-media-controls, video::-webkit-media-controls-start-playback-button, video::-webkit-media-controls-panel, video::-webkit-media-controls-overlay-play-button { display: none !important; -webkit-appearance: none; opacity: 0; }
.smoke {
  position: absolute; left: 42.01%; top: 0; width: 24.03%; height: 42.04%;
  object-fit: fill;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent),
                      linear-gradient(to bottom, #000, #000 78%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent),
              linear-gradient(to bottom, #000, #000 78%, transparent);
  mask-composite: intersect;
}
.scrub { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.cinema { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; display: none; }

/* B8 finish layers: the toggle re-skins the whole scene in place */
.finish-layers {
  position: absolute; inset: 0; z-index: 3;
  opacity: 0; pointer-events: none;
}
.finish-layers img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
#lyCedar { opacity: 0; transition: opacity 380ms ease; }
body.cedar-on #lyCedar { opacity: 1; }

.closer {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  z-index: 3; opacity: 0; transition: opacity 200ms linear; pointer-events: none;
}
.closer.is-on { opacity: 1; }
.hero-box { transition: opacity 200ms linear; }
body.scrubbing .hero-box { opacity: 0; }

.scrim-grade {
  position: absolute; inset: 60% 0 0 0; z-index: 4; pointer-events: none;
  background: linear-gradient(to bottom, transparent, oklch(0.17 0.008 60 / 0.55) 70%);
}

/* B0: the headline sits on the meadow, below the roofline, so the glass
   facade and the lit interior stay whole. The tag anchors the opposite
   corner, which gives the frame its width. */
.hero-lock {
  position: absolute; z-index: 5;
  left: max(var(--margin), calc((100% - var(--container)) / 2));
  right: max(var(--margin), calc((100% - var(--container)) / 2));
  bottom: 21svh;
}
.lock-tag {
  position: absolute; right: 0; bottom: -38px;
  color: var(--bone); text-align: right;
}
.cue {
  position: absolute; z-index: 5; left: 50%; bottom: 24px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 10px; color: var(--bone-dim);
}
.cue-line { width: 1px; height: 57px; background: var(--bone-dim); opacity: 0.7; }
@media (max-width: 820px) {
  /* a corner-to-corner tag needs width the phone does not have: it tucks
     back under the headline */
  .hero-lock { bottom: 22svh; }
  .lock-tag { position: static; text-align: left; margin-top: var(--s1); }
}

/* motion lines (opacity/transform driven by JS from leg progress) */
.mline, .mquote {
  position: absolute; z-index: 5; opacity: 0; pointer-events: none;
  will-change: opacity, transform; color: var(--bone);
}
.mline#m1a { left: max(var(--margin), calc((100% - var(--container)) / 2 + var(--bay))); top: 63svh; }
.mline.mline-b { left: max(var(--margin), calc((100% - var(--container)) / 2 + var(--bay))); top: 70svh; }
.mline.mline-c { left: 50%; top: 60svh; transform: translateX(-50%); text-align: center; white-space: nowrap; }
.mquote {
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(calc(var(--bay) * 12), calc(100% - 2 * var(--margin)));
  text-align: center;
}
/* The attribution is a label, not Bob speaking, so it stays in Switzer with the
   other labels. It carried the serif briefly and that broke her rule twice over:
   serif stopped meaning "his words", and the same name was set two ways on one
   page (this cite in serif, the landing figcaption's .t-label in Switzer). */
.mquote cite {
  display: block; margin-top: var(--s1); font-style: normal; color: var(--bone-dim);
}
@media (max-width: 820px) {
  .mline.mline-c { white-space: normal; width: calc(100% - 48px); }
  .mline#m1a { top: 68svh; }
  .mline.mline-b { top: 76svh; }
}

/* ---------- legs (scroll spacers; landing stills only in static mode) ---------- */
.leg { position: relative; z-index: 1; }
#leg0 { height: var(--leg0); }
#leg1 { height: var(--leg1); }
#leg2 { height: var(--leg2); }
#leg3 { height: var(--leg3); }
#leg4 { height: var(--leg4); }
body.js .landing { display: none; }
body.js.static .landing, body.no-js .landing { display: block; }
.landing { position: relative; height: 100svh; }
.landing img { width: 100%; height: 100%; object-fit: cover; }
.landing figcaption {
  position: absolute; left: var(--margin); bottom: 12svh; z-index: 2;
  max-width: 18ch; color: var(--bone);
}
.landing::after {
  content: ''; position: absolute; inset: 55% 0 0 0;
  background: linear-gradient(to bottom, transparent, oklch(0.17 0.008 60 / 0.55) 70%);
}
.landing figcaption { z-index: 2; }
body.js.static .leg, body.no-js .leg { height: auto; }

/* ---------- scenes: content lives IN the scene, over the held frame ----------
   A scene is a tall scroll region whose inner pin is a transparent 100svh
   overlay. Nothing has a surface; legibility comes from soft light-falloff
   pools and the stage grade. Elements materialize (opacity/rise) driven by
   pin progress, hold, and dissolve before the walk resumes. */
/* Once the pin releases it still has to scroll its own 100svh away, which was
   ~900px of "nothing is happening" between every beat. Pulling the next leg up
   by that height, the same trick the stage uses, makes the walk resume the
   moment the content finishes lifting out. #join is exempt: Insights follows
   it, and that is real content, not a transparent spacer. */
.scene { position: relative; z-index: 2; height: 170svh; margin-bottom: -100svh; }
/* A flick should come to rest ON a beat, not halfway past one. The anchor is a
   zero-size box parked at the same 42% dwell the in-page links already target,
   so momentum settles exactly where the scene is fully lit. `proximity` rather
   than `mandatory` on purpose: the five walking legs carry no anchor and stay
   free to scrub at whatever speed the thumb asks for, and the snap only bites
   near a beat. `scroll-snap-stop: always` is the part that actually answers the
   complaint, since without it a hard flick is allowed to sail straight past. */
.snap { position: absolute; left: 0; top: calc((100% - 100svh) * 0.42); width: 1px; height: 1px; pointer-events: none; }
@media (hover: none) and (pointer: coarse) {
  html { scroll-snap-type: y proximity; }
  .scene .snap { scroll-snap-align: start; scroll-snap-stop: always; }
}
/* the static page is a photo essay with no dwells to land on */
body.static .snap, body.no-js .snap { scroll-snap-align: none; }
/* feel is a device judgement, so both weaker settings stay reachable on the
   phone: ?snap=soft still lands on a beat but lets a hard flick cross several,
   ?snap=off is the old free scroll */
html.snap-soft .scene .snap { scroll-snap-stop: normal; }
html.snap-off { scroll-snap-type: none; }
@media (prefers-reduced-motion: reduce) { html { scroll-snap-type: none; } }
.scene-tall { height: 260svh; }
#finish { height: 220svh; }
#join { height: 215svh; }
/* #join keeps a tail because Insights is real content that would otherwise
   slide over the pin, but a full 100svh of it was frozen frame. Half is enough
   to carry the closer out, and the seam below is masked rather than cut. */
/* -40, was -50 (v4): ten more svh of tail keeps the feather off the submit button
   at the 42% dwell now that the form is a row taller. */
#join { margin-bottom: -40svh; }
.pin {
  position: sticky; top: 0; height: 100svh;
  overflow: clip;
}
.band {
  position: absolute;
  left: max(var(--margin), calc((100% - var(--container)) / 2));
  right: max(var(--margin), calc((100% - var(--container)) / 2));
  top: 93px; bottom: 0;
  display: flex; align-items: center;
  pointer-events: none;
}
.band > .group, .band > .legacy-split, .band > .steward-split, .band > .doors { position: relative; }
/* Unlit groups are transparent but still occupy the layer, so leaving them
   clickable puts invisible controls over whatever IS lit: a hidden film-play
   button sat on the "His plan, to today's standard." headline and swallowed the
   click. Opacity is written inline by the scroll engine, so the hit target has
   to follow it there too, in setOpacity(). This is only the unlit start state. */
.group { position: absolute; pointer-events: auto; }
body.js:not(.static) .group { opacity: 0; pointer-events: none; }
/* type over film carries its own falloff, so a bright window behind a label
   never costs contrast. Shadow, not a plate. */
body.js:not(.static) .group :is(h2, h3, p, a, dt, dd, legend, label, span, cite) {
  text-shadow: 0 1px 2px oklch(0.17 0.008 60 / 0.55), 0 2px 26px oklch(0.17 0.008 60 / 0.72);
}

/* the wash: one full-bleed light falloff per pin. No box, no edge, no panel.
   Opacity rides the scene envelope so the walk is never dimmed between beats. */
.wash {
  position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: 0;
  background:
    radial-gradient(76% 92% at 8% 56%,
      oklch(0.17 0.008 60 / 0.76), oklch(0.17 0.008 60 / 0.36) 48%, transparent 78%),
    linear-gradient(to top, oklch(0.17 0.008 60 / 0.46), transparent 42%);
}
/* B2: four cards run the full width of the foot, so the falloff is a band across
   the bottom rather than a corner pool. Feathered, so it stays a wash. */
.wash-wide {
  background:
    linear-gradient(to top,
      oklch(0.17 0.008 60 / 0.86), oklch(0.17 0.008 60 / 0.72) 34%,
      oklch(0.17 0.008 60 / 0.40) 56%, transparent 78%);
}
.wash-right {
  background:
    radial-gradient(76% 92% at 8% 56%,
      oklch(0.17 0.008 60 / 0.72), oklch(0.17 0.008 60 / 0.34) 48%, transparent 78%),
    radial-gradient(60% 84% at 74% 56%,
      oklch(0.17 0.008 60 / 0.60), oklch(0.17 0.008 60 / 0.28) 48%, transparent 78%),
    linear-gradient(to top, oklch(0.17 0.008 60 / 0.46), transparent 42%);
}

/* The original plan, shown as evidence rather than as decoration. This is NOT the
   stencil elevation, which stays an accent on sub-pages only: it is a real Rummer
   plan sheet, cropped past its title block, so the section's claim ("the plan is
   his") has the drawing sitting next to it. Bone lines on transparent, so it
   inherits the scene wash instead of punching a white box through the film. */
/* Beside the text, not beneath it. .g-light is anchored by its BOTTOM edge, so
   stacking the figure under the copy grew the group upward and drove the H2 to
   48px, clipping its first line under the 93px nav. Pinned right, the group's
   height is the taller of the two columns instead of their sum. */
.planfig {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  margin: 0; width: calc(var(--bay) * 7);
}
/* the sheet's foot: a scale bar in the 6'4" module, north, and the caption.
   Drafting furniture, so the drawing reads as a document and not a picture. */
.planfoot { display: flex; align-items: center; gap: var(--s1); margin-top: 14px; color: var(--bone-dim); }
.planfoot .ic { width: 18px; height: 18px; flex: none; }
.scalebar {
  display: block; flex: none; height: 8px; width: calc(var(--bay) * 2); opacity: 0.8;
  background:
    repeating-linear-gradient(to right, var(--bone-dim) 0 1px, transparent 1px calc(var(--bay) / 2)),
    linear-gradient(var(--bone-dim), var(--bone-dim)) 0 100% / 100% 1px no-repeat;
}
.planfoot figcaption { margin: 0; }
.planfoot .bay-lab { color: var(--bone-dim); white-space: nowrap; }
.planfig img {
  display: block; width: 100%; height: auto;
  opacity: 0.82; mix-blend-mode: screen;
}
.planfig figcaption { color: var(--bone-dim); }
@media (max-width: 820px) {
  /* back into flow on mobile, where there is no room for a second column.
     .g-light.group: the desktop .g-light rule is declared after this block, so
     on equal specificity its 5.7-bay padding-right won and the copy collapsed to
     one word per line on a phone (caught on device 2026-09-13). */
  .g-light.group { padding-right: 0; width: auto; display: block; min-height: 0; }
  .planfig { position: static; transform: none; }
  /* the sheet's foot: the caption takes its own line under the bar and north */
  .planfoot { flex-wrap: wrap; row-gap: 8px; }
  .planfoot figcaption { flex-basis: 100%; }
  .planfig { position: static; width: 68vw; max-width: 320px; margin-top: var(--s1); }
}

/* the sprite is a definition, never a box: the site's own svg rule beat the
   UA's [hidden] and the sheet sat 150px tall in flow under the nav */
.ic-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
/* ---------- the icon set ----------
   Drawn here, not imported: the four marks the form already carried (house,
   bracket, steps, letter) set the language, one-pixel lines on a 22px grid
   with square caps, and the rest of the set extends it. Buy, Founders Circle
   and Invest wear the same mark in the doors and in the form, so a symbol
   means one thing on the page. */
.ic {
  width: 26px; height: 26px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.25;
  stroke-linecap: square; stroke-linejoin: miter;
}
.door .ic { display: block; width: 30px; height: 30px; color: var(--bone); opacity: 0.9; margin-bottom: 12px; }
/* the standard, itemised: what the paragraph claims, set out as a schedule */
.spec {
  display: grid; grid-template-columns: repeat(4, calc(var(--bay) * 2)); justify-content: start;
  column-gap: var(--s1); row-gap: var(--s1); margin-top: var(--s2);
}
.spec div { display: flex; flex-direction: column; gap: 12px; color: var(--bone-dim); }
.spec .ic { color: var(--bone); }
.spec b { display: block; font-weight: 500; color: var(--bone); }
/* the Founders Circle mark: homes around a shared centre, the atrium plan */
/* the siding glyph inside the finish chips */
.chip .ic { width: 14px; height: 14px; stroke-width: 1.4; }
/* Bob's print is a photograph, not a pixel: it lifts and settles like paper in
   a draught. Transform only, so it costs the compositor nothing, and it stops
   with reduced motion. The sheen is a second layer that breathes with it. */
.paper { position: relative; display: block; }
/* perspective belongs on the parent: it projects the children, not the element itself */
.print-bob { perspective: 1100px; }
.paper { transform-origin: 50% 50%; animation: flutter 12s ease-in-out infinite; will-change: transform; }
.paper img { display: block; }
.paper::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(112deg, transparent 38%, oklch(0.92 0.010 75 / 0.22) 50%, transparent 62%);
  mix-blend-mode: screen; opacity: 0; animation: sheen 12s ease-in-out infinite;
}
@keyframes flutter {
  0%, 100% { transform: rotateY(0deg) rotateX(0deg) translateY(0); }
  27% { transform: rotateY(-1.7deg) rotateX(0.8deg) translateY(-2px); }
  54% { transform: rotateY(1.1deg) rotateX(-0.5deg) translateY(1px); }
  79% { transform: rotateY(-0.7deg) rotateX(0.6deg) translateY(-1px); }
}
@keyframes sheen {
  0%, 100% { opacity: 0; transform: translateX(-12%); }
  27% { opacity: 0.9; transform: translateX(6%); }
  54% { opacity: 0.25; transform: translateX(-4%); }
  79% { opacity: 0.6; transform: translateX(3%); }
}
@media (prefers-reduced-motion: reduce) { .paper, .paper::after { animation: none; } }

/* the drafted rule: a hairline that draws in with each group */
.rule {
  display: block; width: calc(var(--bay) * 2); height: 1px;
  background: var(--bone-dim); opacity: 0.85;
  margin-bottom: var(--s1);
  transform: scaleX(0); transform-origin: left;
}
body.static .rule, body.no-js .rule { transform: none; }

.eyebrow { color: var(--bone-dim); }
.group .eyebrow { margin-bottom: var(--s1); }

/* B2 · the opportunity block over the kitchen floor (v4, 2026-09-13).
   The plan index and its --plan-base/--plan-h arithmetic came off with it. One
   bottom-anchored column: eyebrow, then four equal cards on the bay grid. */
.doors {
  position: absolute; inset: 0; pointer-events: none;
  display: flex; flex-direction: column; justify-content: center;
}
.doors .group { position: static; }
.g-doors { margin-bottom: var(--s2); }
/* the 6'4" module, drawn once: a measured scale, not stray marks */
.g-doors .ticks {
  display: none; height: 10px; width: calc(var(--bay) * 3);
  margin-bottom: 14px; opacity: 0.8;
  background:
    repeating-linear-gradient(to right, var(--bone-dim) 0 1px, transparent 1px var(--bay)),
    linear-gradient(var(--bone-dim), var(--bone-dim)) 0 100% / 100% 1px no-repeat;
}
.door-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s2); align-items: stretch; }
.door { display: flex; flex-direction: column; min-width: 0; }
/* four across, so the heading steps down from the two-door 38px; two lines
   reserved so the four bodies start on one line */
.door .t-h3 { font-size: 30px; line-height: 1.15; min-height: calc(30px * 1.15 * 2); }
.door .t-body { margin-top: 10px; color: var(--bone-dim); }
/* the four bodies differ by five lines; the actions still sit on one baseline */
.door .cta { margin-top: auto; align-self: flex-start; }
.door .t-body { margin-bottom: var(--s1); }
.door-brief { display: none; }
.cta {
  display: inline-block; margin-top: var(--s1); padding: 13px 26px;
  background: var(--bone); color: var(--ink-0); text-decoration: none; border: 1px solid var(--bone);
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 16px; letter-spacing: 0.10em;
  font-variant-caps: all-small-caps;
  transition: background 220ms ease, border-color 220ms ease, color 220ms ease;
}
.cta.ghost {
  background: none; border: 0; padding: 13px 0 11px;
  color: var(--bone); border-bottom: 1px solid var(--bone-dim);
}
.cta:hover { background: var(--bone-dim); border-color: var(--bone-dim); }
.cta.ghost:hover { background: none; color: var(--bone); border-bottom-color: var(--bone); }
.cta.ghost::after { content: ' \2192'; }
.cta:disabled {
  background: none; color: var(--bone-dim);
  border-color: oklch(0.76 0.012 70 / 0.4); cursor: default;
}

/* B4 · Bob, text left over the shelves, print right over the sofa */
.legacy-split {
  position: absolute; inset: 0; pointer-events: none;
  display: grid; grid-template-columns: calc(var(--bay) * 8) 1fr; column-gap: var(--s3);
  align-items: center;
}
.legacy-split .group { position: relative; }
.g-bob-text { width: auto; }
.g-bob-text .t-body { margin-top: var(--s1); color: var(--bone); max-width: 46ch; }
/* second beat of B4 (v4): the standard, low-left where Bob's text was */
.g-standard { position: relative; width: calc(var(--bay) * 11); }
.g-standard .t-body { margin-top: var(--s1); color: var(--bone); max-width: 46ch; }
/* prints are photographs PLACED in the scene: a quiet hairline and a seating
   shadow of light, never a filled panel. Feathering was tried and rejected:
   Bob's high-key backdrop bloomed, Aubrey's low-key frame vanished. */
.print { border: 0; }
.print img {
  /* the portrait now carries width/height attributes for layout stability, so
     the height has to be released here or it holds the attribute's 1125px */
  display: block; width: 100%; height: auto;
  border: 1px solid oklch(0.92 0.010 75 / 0.22);
  box-shadow: 0 14px 34px oklch(0.17 0.008 60 / 0.42);
}
.print figcaption {
  display: flex; align-items: center; gap: 10px;
  margin-top: 12px; color: var(--bone-dim);
}
.print figcaption::before {
  content: ''; width: 19px; height: 1px; background: var(--bone-dim); opacity: 0.7;
}
/* scoped to the scene: /about/ shares .print-bob inside a 4-bay grid column,
   and the 5-bay width rode over its body copy at 1920 (judge, 2026-09-23) */
.legacy-split .print-bob { width: calc(var(--bay) * 5); justify-self: end; }
.print-bob img { filter: grayscale(1); }

/* B6 · statement low-left, then Aubrey right */
.g-light {
  /* a flow column for the words, the sheet pinned beside them: a grid spread
     the sheet's height across the text rows and opened a gap under the eyebrow */
  position: relative; width: 100%; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: center;
  padding-right: calc(var(--bay) * 8); min-height: calc(var(--bay) * 7.2);
}
.g-light .t-body { margin-top: var(--s1); max-width: 52ch; }
.steward-split {
  position: absolute; inset: 0; pointer-events: none;
  display: grid; grid-template-columns: calc(var(--bay) * 8) 1fr; column-gap: var(--s3);
  align-items: center;
}
.steward-split .group { position: relative; }
/* The film, where the through-glass photo used to be. A facade: nothing loads
   from YouTube until it is clicked, and it wears none of YouTube's chrome. */
.film { width: auto; }
.film-btn {
  display: block; position: relative; width: 100%; padding: 0;
  text-decoration: none; color: inherit; cursor: pointer;
  border: 1px solid oklch(0.92 0.010 75 / 0.22);
  box-shadow: 0 14px 34px oklch(0.17 0.008 60 / 0.42);
  overflow: clip;
}
.film-btn img {
  width: 100%; height: auto; display: block;
  filter: saturate(0.82) contrast(1.04) brightness(0.94);
  transition: filter 400ms ease;
}
.film-btn::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, oklch(0.17 0.008 60 / 0.55), transparent 52%);
}
.film-btn:hover img, .film-btn:focus-visible img {
  filter: saturate(0.94) contrast(1.04) brightness(1.02);
}
/* The mark is the logo's own bracket, one bay square, drawn as two corner
   rules. A circle around a triangle is the universal play button and would
   have been the only rounded shape on an otherwise all-square site. */
.film-mark {
  position: absolute; left: 50%; top: 50%; z-index: 2;
  width: var(--bay); height: var(--bay);
  margin: calc(var(--bay) / -2) 0 0 calc(var(--bay) / -2);
  display: grid; place-items: center;
}
.film-mark::before, .film-mark::after {
  content: ''; position: absolute; width: 22px; height: 22px;
  border: 1px solid var(--bone); opacity: 0.85;
  transition: opacity 220ms ease;
}
.film-mark::before { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.film-mark::after { bottom: 0; right: 0; border-left: 0; border-top: 0; }
.film-tri {
  width: 0; height: 0; margin-left: 3px;
  border-left: 16px solid var(--bone);
  border-top: 10px solid transparent; border-bottom: 10px solid transparent;
}
.film-btn:hover .film-mark::before, .film-btn:hover .film-mark::after,
.film-btn:focus-visible .film-mark::before, .film-btn:focus-visible .film-mark::after { opacity: 1; }
/* the runtime is the label; the mark already says "play" */
.film-cue {
  position: absolute; left: 14px; bottom: 10px; z-index: 2; color: var(--bone);
}
.film figcaption {
  display: flex; align-items: center; gap: 10px;
  margin-top: 12px; color: var(--bone-dim);
}
.film figcaption::before {
  content: ''; width: 19px; height: 1px; background: var(--bone-dim); opacity: 0.7;
}

/* ---------- the film lightbox ---------- */
/* native <dialog>: real focus containment that still reaches the player's own
   controls, plus Escape and an inert background, none of it hand-rolled */
.lightbox {
  border: 0; padding: max(6svh, 48px) var(--margin);
  width: 100%; max-width: 100%; height: 100%; max-height: 100%;
  background: transparent; color: var(--bone);
  display: grid; place-items: center;
}
.lightbox:not([open]) { display: none; }
.lightbox::backdrop { background: oklch(0.17 0.008 60 / 0.94); }
/* the close sits on the frame's own right edge, clear of the nav row */
.lb-box { width: min(100%, calc(var(--bay) * 15)); }
.lb-frame {
  position: relative; width: 100%;
  aspect-ratio: 16 / 9; border: 1px solid var(--ink-2); background: #000;
}
.lb-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.lb-close {
  display: block; margin: 0 0 14px auto;
  color: var(--bone-dim); padding: 12px 0; min-height: 44px;
  border-bottom: 1px solid transparent; transition: color 200ms ease, border-color 200ms ease;
}
.lb-close:hover { color: var(--bone); border-bottom-color: var(--walnut); }
body.lb-open { overflow: hidden; }
@media (max-width: 820px) {
  .film { width: auto; }
  .film-mark { width: 58px; height: 58px; margin: -29px 0 0 -29px; }
  .lb-inner { padding: 0 24px; }
  .lb-box { width: 100%; }
}
.g-steward { width: auto; max-width: calc(var(--bay) * 7.5); }
.g-steward .t-h3 { margin-top: 4px; }
.g-steward .t-body { margin-top: 10px; max-width: 44ch; }
.pressrow { margin-top: var(--s1); color: var(--bone-dim); }
.g-steward .tracelink { display: inline-block; margin-top: 12px; padding: 8px 0; }
a.tracelink { color: var(--bone); text-decoration: none; border-bottom: 2px solid var(--walnut); }
a.tracelink:hover { border-bottom-color: var(--bone); }

/* B8 · the finish: chips re-skin the scene; content low over the drive */
.g-finish { position: relative; width: calc(var(--bay) * 12); }
.rule.beam { width: calc(var(--bay) * 3); }
.finish-row { display: flex; align-items: baseline; gap: var(--s2); margin-top: var(--s1); flex-wrap: wrap; }
/* The old pair was two outlined boxes set in the same tracked mono as every
   caption on the site, so it read as a label describing the photo rather than as
   a control. Three changes: a real segmented track with a sliding indicator, the
   text face instead of the label face, and a stated instruction above it. */
.finishctl { display: block; }
/* flex, not inline-flex: two inline-level boxes put the hint on the same line as
   the control instead of above it. width:fit-content keeps the underline tight. */
.finish-hint {
  display: flex; width: fit-content; align-items: center; gap: 8px;
  color: var(--walnut); margin-bottom: 12px;
}
.swap-ic { flex: none; }
.chips {
  position: relative; display: inline-flex; gap: 0; padding: 3px;
  border: 1px solid oklch(0.92 0.010 75 / 0.42);
  background: oklch(0.17 0.008 60 / 0.55);
}
/* one indicator that slides, rather than two states that repaint. Equal-width
   chips are what make the 100% translate land exactly on the second one. */
.chip-ind {
  position: absolute; left: 3px; top: 3px; bottom: 3px; width: calc(50% - 3px);
  background: var(--bone); pointer-events: none;
  transition: transform 280ms cubic-bezier(0.22, 1, 0.36, 1);
}
.chips.on-cedar .chip-ind { transform: translateX(100%); }
.chip {
  position: relative; z-index: 1; flex: 1 1 0; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font-family: 'Switzer', system-ui, sans-serif; font-size: 15px; letter-spacing: 0.02em;
  color: var(--bone-dim); padding: 12px 24px; min-height: 44px;
  border: 0; background: none;
  transition: color 220ms ease;
}
.chip:hover { color: var(--bone); }
.chip.is-on { color: var(--ink-0); }
.chip.is-on:hover { color: var(--ink-0); }
@media (prefers-reduced-motion: reduce) { .chip-ind { transition: none; } }
.finish-note { color: var(--bone-dim); max-width: 40ch; }
.finish-copy { min-width: 0; }
.finish-sub { color: var(--bone-dim); margin-top: 10px; }
/* the scene itself is the toggle in JS modes, so the pair only ships static */
.toggle-static { display: none; }
.ts-fig figcaption { margin-top: 10px; color: var(--bone-dim); }

/* expandable: depth without occlusion */
.exp {
  display: inline-flex; align-items: center; gap: 10px;
  margin-top: var(--s1); padding: 12px 0; min-height: 44px; color: var(--bone-dim);
  transition: color 200ms ease;
}
.exp:hover { color: var(--bone); }
.exp-ic { position: relative; width: 11px; height: 11px; }
.exp-ic::before, .exp-ic::after {
  content: ''; position: absolute; background: currentColor;
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}
.exp-ic::before { left: 0; right: 0; top: 5px; height: 1px; }
.exp-ic::after { top: 0; bottom: 0; left: 5px; width: 1px; }
.exp[aria-expanded="true"] .exp-ic::after { transform: scaleY(0); }
.exp-panel { overflow: clip; }
.exp-panel .services {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2);
  padding-top: var(--s1);
}
.services dt { color: var(--bone-dim); }
.services dd { margin-top: 6px; }
.services .t-body { max-width: 34ch; color: var(--bone); }

/* B10 · the ask, low-left where the film leaves you */
/* Sits higher than the other beats on purpose. Insights leads in with a 414px
   dissolve that paints OVER this pin (both are z-index 2, Insights comes
   later), so anything in the bottom band of this frame gets darkened, and the
   lowest element here is the primary action. Measured at mid-dwell: the CTA
   plate rendered 172,169,163 at bottom:9svh against 232,228,221 clear. The
   dissolve stays as it is (it fixes the hard image seam); the group moves up
   out of it instead. */
/* B9b (v4): the Founders Circle, its own dwell. Wash, rule, eyebrow and a
   headline one step down the ladder, so the waypoint never outranks the ask that
   follows it; the filled CTA is what makes the beat distinct. */
.g-circle { position: relative; width: calc(var(--bay) * 9); }
.g-circle .t-h3 { font-size: 48px; }
.g-circle .t-lede { margin-top: var(--s1); color: var(--bone-dim); max-width: 34ch; }
.g-circle .cta { margin-top: var(--s2); }
.g-join { position: relative; width: calc(var(--bay) * 11); }
.g-join .t-lede { margin-top: var(--s1); max-width: 48ch; color: var(--bone-dim); }
/* the headline wants 11 bays, the form does not: a 400px rule under an email
   field reads as a mistake */
.joinform { margin-top: var(--s2); max-width: calc(var(--bay) * 7); }
.joinform .fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s1) var(--s2); }
.field label { display: block; color: var(--bone-dim); margin-bottom: 0; }
.field .opt { opacity: 0.6; }
/* Bordered plate, not a bare underline: the form has to read as a block of
   inputs so the one filled button at its foot is unmistakably the action. */
.field input {
  width: 100%; background: oklch(0.17 0.008 60 / 0.45);
  border: 1px solid oklch(0.92 0.010 75 / 0.28);
  color: var(--bone); padding: 11px 14px; margin-top: 6px;
  caret-color: var(--walnut); font-family: inherit;
  transition: border-color 200ms ease, background 200ms ease;
}
.field input:hover { border-color: oklch(0.92 0.010 75 / 0.55); }
.field input:focus-visible {
  outline: none; border-color: var(--walnut); background: oklch(0.17 0.008 60 / 0.62);
}
/* The join scene is the only form sitting on footage rather than flat ink, and a
   0.45 fill behind a 0.28 edge vanished into a bright roof: scene-contrast
   measured 1.2:1 at the worst sample, so the input rectangles were untraceable.
   Scope the heavier treatment here; /contact/ sits on ink and keeps the lighter one. */
#join .field input {
  background: oklch(0.17 0.008 60 / 0.78);
  border-color: oklch(0.92 0.010 75 / 0.46);
}
#join .field input:hover { border-color: oklch(0.92 0.010 75 / 0.72); }
/* the ID rule above outranks .field input:focus-visible, so the walnut ring is restated here */
#join .field input:focus-visible { background: oklch(0.17 0.008 60 / 0.88); border-color: var(--walnut); }

.field-err { color: var(--walnut); margin-top: 6px; }
/* A native radio dot is the one control on the page wearing the browser's
   look. These are real radios with the input visually hidden, so the keyboard
   and screen-reader behaviour is untouched and the chip carries the state. */
/* the gap before the action is bigger than the gap between the groups: rank
   carried by rhythm as well as by fill */
/* the fields want 7 bays; the three chips want 9 and are allowed to have them,
   because a choice broken over two rows reads as two questions */
.wants { border: 0; margin-top: var(--s1); width: calc(var(--bay) * 11); }
.wants legend { color: var(--bone-dim); margin-bottom: 12px; padding: 0; }
.want-set { display: flex; flex-wrap: wrap; gap: 10px; }
.want { display: inline-flex; cursor: pointer; }
.want input {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* A chosen chip is a STATE, never a fill: bone fill is reserved for the one
   action in the beat. Selection reads as the site's own square mark going
   solid, plus the rule and label coming up to full bone. */
.want span {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 11px 18px;
  color: var(--bone-dim);
  border: 1px solid oklch(0.92 0.010 75 / 0.28);
  background: oklch(0.17 0.008 60 / 0.45);
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease;
}
/* Each option carries its own drawn mark, in the same one-weight line as the
   logo: the low Rummer roof, a post-and-beam frame on its site line, and a
   letter. Selection fills the mark's base, so the chip is read at a glance
   without any of them competing with the one filled button below. */
.want-mark {
  width: 22px; height: 22px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.25;
  stroke-linecap: square; stroke-linejoin: miter;
}
.want-mark .fill { stroke: none; fill: none; transition: fill 200ms ease; }
.want:hover span { color: var(--bone); border-color: oklch(0.92 0.010 75 / 0.55); }
.want input:checked + span {
  color: var(--bone); border-color: var(--bone);
  box-shadow: inset 0 0 0 1px var(--bone);
  background: oklch(0.17 0.008 60 / 0.62);
}
.want input:checked + span .want-mark .fill { fill: var(--walnut); }
.want input:focus-visible + span { outline: 2px solid var(--walnut); outline-offset: 2px; }
/* The action: the only bone fill in the beat, larger than any chip, and the
   one element lifted above the wash. `.wash` is positioned and `.band` is not,
   so the wash paints OVER the band and was knocking the bone plate down to
   ~60% (measured 174,170,164 instead of 240,235,226). The chips stay under it
   and stay quiet; a positioned CTA sits above it because it comes later in the
   DOM. It needs no wash of its own: it is ink type on a bone plate. */
.joinform .cta {
  position: relative; z-index: 1;
  margin-top: 32px; padding: 17px 40px; font-size: 18px;
}
.form-done { margin-top: var(--s1); color: var(--bone); }
.form-done .tracelink { display: inline-block; margin-top: 8px; }
/* A laptop is short before it is narrow. This beat carries the most content of
   any scene, so on a short desktop viewport its internal gaps close rather than
   let the group run up behind the fixed nav (measured: 638px of group in an
   800px frame with the nav ending at 93). */
@media (min-width: 821px) and (max-height: 920px) {
  .g-join .t-h2 { font-size: calc(76px * var(--disp-scale) * 0.82); }
  #join .joinform, #join .wants { margin-top: var(--s1); }
  /* one row of four fields buys back a whole row's height on a short laptop.
     bottom stays at 15svh: lower than that and the button sits in the Insights
     feather at the dwell (measured 130/255 at 6svh, judge-v4). Scoped to #join:
     unscoped it reached the /contact/ form, which sits on flat ink and needs none of it. */
  #join .joinform { max-width: none; }
  #join .joinform .fields { grid-template-columns: repeat(4, 1fr); gap: var(--s1); }
  #join .joinform .cta { margin-top: 24px; }
}
@media (min-width: 821px) and (max-height: 820px) {
  .g-join .t-h2 { font-size: calc(76px * var(--disp-scale) * 0.7); }
  .g-join .t-lede { font-size: 17px; }
}
@media (min-width: 821px) and (max-height: 760px) {
  .g-join { bottom: 8svh; }
}

/* ---------- scenes on mobile ----------
   Bay-width absolute placement is a desktop composition tool. On a phone each
   band collapses to one bottom-anchored stack; bands overlap in space and are
   separated in time by their own in/out windows, so nothing ever collides. */
@media (max-width: 820px) {
  .scene { height: 200svh; }
  .scene-tall { height: 300svh; }
  #finish { height: 220svh; }
#join { height: 215svh; }

  /* `safe flex-end` is the guarantee: bottom-anchored while the stack fits, and
     the moment it does not, it falls back to top-aligned so the heading is
     never the thing that gets cut. Without it, flex-end pushes the overflow off
     the TOP of the pin and the section title disappears under the nav, which is
     exactly what happens on any phone whose visible height is under ~780px
     (every Safari window with the address bar showing).
     The svh caps below keep the stack inside the pin in the first place: they
     are no-ops on a tall phone and only bite as the frame gets shorter. */
  .band {
    /* the desktop band centres its row vertically; in a column that same
       align-items centres horizontally and shrink-wraps the stack, which put
       the standard's spec row 150px off both edges of a 390px phone */
    /* TOP-anchored on phones (2026-09-24). Bottom-anchoring trusted the pin's
       100svh to equal what the phone actually shows, and on Phillip's iPhone it
       did not: every beat ran off the foot of the screen behind the toolbar.
       Anchored to the nav, the stack is on screen whatever the browser decides
       the viewport is; the footage shows below it instead of above it. */
    top: 0; align-items: stretch;
    display: flex; flex-direction: column; justify-content: flex-start;
    gap: min(var(--s2), 5svh);
    padding: calc(68px + var(--s2)) 0 var(--s2);
  }
  /* the join beat carries the most content of any scene; on a phone it has to
     clear the fixed nav, so its spacing tightens rather than its copy */
  /* the join beat carries the most content on the page, so its own budget is
     tighter again: it has to fit a headline, a lede, two fields, three chips
     and the button inside one pinned frame */
  /* Same slack trick, applied to every soft measurement in the beat. Each one
     resolves to its full desktop-of-mobile value on a tall phone and gives
     ground only as the frame shortens. Tap targets and the 44px minimum are
     NOT on the table. */
  /* the top floor is the nav, never less: 68px of fixed bar plus air */
  #join .band { padding: calc(68px + var(--s1)) 0 var(--s1); }
  #join .t-h2 { font-size: clamp(24px, calc(100svh - 634px), 38px); }
  #join .t-lede { font-size: clamp(14px, calc(100svh - 645px), 19px); line-height: 1.42; }
  #join .joinform, #join .wants { margin-top: clamp(10px, calc((100svh - 700px) * 0.8), 38px); }
  #join .joinform .cta { margin-top: clamp(10px, calc((100svh - 700px) * 0.6), 22px); }
  #join .want span { min-height: 44px; padding: 10px 16px; }
  #join .want-set { gap: min(8px, 1.2svh); }
  #join .fields { gap: min(var(--s1), 2.4svh); }
  /* One column while there is room, two the moment there is not. The multiplier
     turns the slack into a switch: above 820px of frame the minimum track is
     100% and the fields stack, below it the minimum collapses and they sit side
     by side, which buys back a whole field's height for the button. */
  /* four fields now (v4): two columns at every phone height, or the chips and the
     button fall out of the bottom of the pin */
  #join .fields { grid-template-columns: 1fr 1fr; }
  /* Side by side, "First name · optional" wraps to two lines and "Email" does
     not, which walked the two inputs out of line. The label reserves both lines
     in exactly the case where the columns are side by side, using the same
     slack switch inverted, so the single-column layout keeps its one-line
     labels and nothing else moves. */
  #join .field label { min-height: 20px; }
  /* margins for these live in the slack block above; only the button's own
     proportions are set here */
  #join .joinform .cta { padding: 14px 32px; font-size: 17px; }
  /* the plate stays, its padding does not: the join group already runs 712px
     of an 844px viewport and has to clear the 68px fixed nav */
  #join .field input { padding: 8px 12px; margin-top: 4px; }
  /* the phone stack is full-width and tall, so the desktop corner pool does not
     reach it: the wash becomes a bottom-up falloff over the whole column */
  /* the stack is at the top now, so the wash falls from the top */
  .wash, .wash-right {
    background: linear-gradient(to bottom,
      oklch(0.17 0.008 60 / 0.84), oklch(0.17 0.008 60 / 0.66) 48%,
      oklch(0.17 0.008 60 / 0.30) 74%, transparent 94%);
  }
  .group { position: static; width: auto; }
  .doors, .legacy-split, .steward-split {
    position: static; display: flex; flex-direction: column; gap: var(--s2);
  }
  .door .t-body, .g-bob-text .t-body, .g-light .t-body,
  .g-steward .t-body, .finish-note, .g-join .t-lede { max-width: none; }
  /* The portrait gives up height before the words do, and it gives it up in
     proportion to how short the frame actually is. `100svh - 505px` is slack,
     not a size: on a tall phone it resolves well above the image's natural
     height and changes nothing, and it only starts cutting once the frame can
     no longer hold the stack. */
  /* the portrait was a 66% thumbnail that shrank further on short phones and
     ended up a postage stamp at the top of the frame. A full-width cover crop
     keeps his face at real size and costs the stack less height. */
  .print-bob { width: 100%; }
  /* Her v4 Bob paragraph runs nine lines on a phone at the base 19px, which drove
     the beat's heading under the nav at 844 and 896. The paragraph gives the height
     back, not the portrait: at 17px it is still comfortably above the 16px floor. */
  .g-bob-text .t-body { font-size: 17px; line-height: 1.45; }
  /* 610, was 530 then 580: the v4 Bob paragraph is two lines longer, and at 580 the
     heading still measured 11px UNDER the nav at 414x896 and 390x844. */
  .scene .print img {
    width: 100%; height: clamp(150px, calc(100svh - 560px), 250px);
    object-fit: cover; object-position: 50% 14%;
  }
  .legacy-split, .steward-split, .doors { gap: min(var(--s2), 4svh); }
  .rule { width: calc(var(--bay) * 1.5); }
  .rule.beam { width: calc(var(--bay) * 2); }

  .g-doors .ticks { width: calc(var(--bay) * 3); }
  /* A horizontal snap row was wrong here (caught on device 2026-09-13): the plan
     strip could bleed because six small drawings read as "there are more", but four
     routing cards with actions read as a broken layout with three of them missing.
     On a phone this is a four-row index instead, the same language as the Insights
     rows: a hairline, a heading, one line, and the action. The whole row is tappable
     via the CTA's stretched hit area, so there is no nested link. */
  .doors { position: static; padding: 0; gap: 0; }
  .g-doors { margin-bottom: var(--s1); }
  .door-grid { display: flex; flex-direction: column; gap: var(--s1); overflow: visible; margin: 0; padding: 0; }
  .door { position: relative; flex: none; padding-top: 14px; }
  /* the mark moves to the row's right end, the way the Insights rows carry
     their arrow: it costs the stack no height on a short phone */
  .door .ic { position: absolute; right: 0; top: 22px; margin: 0; width: 22px; height: 22px; }
  /* the standard's schedule: two by two, mark beside its words */
  .spec { grid-template-columns: 1fr 1fr; column-gap: var(--s1); row-gap: 14px; margin-top: var(--s1); }
  .spec div { flex-direction: row; align-items: center; gap: 10px; }
  .spec .ic { width: 22px; height: 22px; }
  .door .rule { width: calc(var(--bay) * 1.5); }
  .door .t-h3 { font-size: 24px; line-height: 1.15; min-height: 0; margin-top: 10px; }
  /* the paragraph is the desktop composition's; a row gets its one line */
  .door .t-body { display: none; }
  .door-brief {
    display: block; margin-top: 6px; color: var(--bone-dim);
    font-family: 'Switzer', system-ui, sans-serif; font-size: 15px; line-height: 1.4;
  }
  .door .cta, .door .cta.ghost {
    margin-top: 10px; padding: 6px 0 0; font-size: 13px; letter-spacing: 0.08em;
    background: none; border: 0; border-bottom: 0; color: var(--bone-dim);
  }
  .door .cta::after { content: ' \2192'; }
  /* the row, not the label, is the tap target */
  .door .cta::before { content: ''; position: absolute; inset: 0; }
  .finish-row { gap: var(--s1); }
  .joinform .fields { grid-template-columns: 1fr; gap: var(--s1); }
  /* the desktop 9-bay chip row is 684px: on a phone it has to go back to the
     column width and wrap, or the third chip is clipped by the pin */
  .wants { width: auto; }
  .want-set { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .want, .want span { width: 100%; box-sizing: border-box; justify-content: center; }
  .wants legend { float: none; margin: 0 0 6px; }
  .exp-panel .services { grid-template-columns: 1fr; gap: var(--s1); }
  .services .t-body { max-width: none; }
}

/* ---------- short phones (iPhone SE / mini class, 667px and under) ----------
   The v4 beats carry more copy (Bob's paragraph grew two lines, the form gained
   a row of fields and a chip), so on a short phone each stack gives ground in
   type size and in the one image that can afford it, never in tap targets. */
@media (max-width: 820px) and (max-height: 720px) {
  .g-bob-text .t-body, .g-standard .t-body, .g-light .t-body, .g-steward .t-body { font-size: 16px; line-height: 1.45; }
  .g-bob-text .t-h2, .g-standard .t-h2, .g-light .t-h2 { font-size: calc(32px * var(--disp-scale)); }
  /* Bob IS the beat, so on a short frame the portrait does not go: the split goes
     back to the desktop composition, text at the measure and the print beside it,
     which costs height only once instead of stacking two full-width blocks.
     (It was briefly hidden here, and an in-app browser at 430 wide is short enough
     to hit this tier, so Bob vanished on a normal phone. Never again.) */
  #legacy .legacy-split {
    display: grid; grid-template-columns: 1fr 38%; gap: var(--s1);
    align-items: start;
  }
  #legacy .print-bob { width: 100%; }
  #legacy .print img { max-height: none; width: 100%; }
  #legacy .g-bob-text .t-h2 { font-size: 28px; line-height: 1.1; }
  #legacy .g-bob-text .t-body { font-size: 16px; line-height: 1.45; }
  /* the print column is ~120px on the narrowest phones and the full caption wraps
     to two lines under it; the place is on the About page, the name is what matters here */
  #legacy .print figcaption { font-size: 10px; margin-top: 8px; }
  #legacy .print .cap-tail { display: none; }
  .planfig { width: 50vw; max-width: 240px; }
  /* the film + Aubrey stack is the tallest beat on a short phone: the facade
     gives up a sixth of its width rather than the copy giving up a line */
  #light .film { width: 84%; }
  #join .t-h2 { font-size: 28px; }
  #join .t-lede { font-size: 15px; line-height: 1.4; }
  #join .want span { min-height: 44px; padding: 8px 12px; }
  /* the four-row index is the tallest block on a short phone: 132px a row does not
     fit a 667px frame, 94px does, and no tap target goes under 44px */
  .door-grid { gap: 10px; }
  .door { padding-top: 8px; }
  .door .t-h3 { font-size: 20px; margin-top: 7px; }
  .door-brief { font-size: 13px; margin-top: 4px; }
  .door .cta, .door .cta.ghost { margin-top: 6px; font-size: 12px; }
  .g-doors { margin-bottom: 10px; }
  #join .joinform .cta { padding: 12px 28px; font-size: 16px; }
  .g-circle .t-h3 { font-size: 34px; }
}

/* ---------- 640px-tall phones ----------
   The join beat is the tallest stack on the site (headline, lede, four fields,
   four chips, button) and at 640 it ran 36px past the frame. Everything here is
   type and spacing; the 44px tap minimum is never traded. The lede's second
   sentence goes rather than the button. */
@media (max-width: 820px) and (max-height: 660px) {
  #join .t-h2 { font-size: 24px; }
  #join .t-lede { font-size: 14px; }
  #join .lede-tail { display: none; }
  #join .field input { padding: 6px 10px; }
  #join .field label { font-size: 11px; }
  #join .joinform .cta { padding: 10px 24px; font-size: 15px; }
  #join .want span { padding: 6px 10px; font-size: 11px; }
  #join .want-mark { width: 18px; height: 18px; }
}

/* ---------- B11 insights: an editorial index, no cards, no plates ---------- */
.insights { position: relative; z-index: 2; background: var(--ink-0); padding: var(--s4) 0; }
.insights::before {
  content: ''; position: absolute; left: 0; right: 0; top: -46svh; height: 46svh;
  pointer-events: none;
  background: linear-gradient(to bottom,
    transparent 0%, oklch(0.17 0.008 60 / 0.10) 16%, oklch(0.17 0.008 60 / 0.26) 30%,
    oklch(0.17 0.008 60 / 0.46) 44%, oklch(0.17 0.008 60 / 0.66) 58%,
    oklch(0.17 0.008 60 / 0.82) 71%, oklch(0.17 0.008 60 / 0.93) 84%,
    oklch(0.17 0.008 60 / 0.985) 94%, var(--ink-0) 100%);
}
/* Measured 2026-08-24: the real footer is 601px desktop and 946px mobile, so
   the 700px placeholder grew the page by 246px on a phone the moment the
   footer rendered. On the four sub-pages the skip buys nothing anyway, since
   the footer is a large fraction of a short page, so it stands down there. */
.footer { content-visibility: auto; contain-intrinsic-size: auto 601px; }
body.sub .footer { content-visibility: visible; }
@media (max-width: 820px) {
  .footer { contain-intrinsic-size: auto 946px; }
}
.insights .eyebrow { margin-bottom: var(--s2); }
.index { border-top: 1px solid var(--ink-2); }
.irow {
  /* the thumbnail column is new, so the index number gives up its bay-and-a-half
     to pay for it. At the old width the longest headline broke to five lines. */
  display: grid; grid-template-columns: 40px 184px minmax(0, 1fr) auto;
  gap: var(--s2); align-items: baseline;
  padding: var(--s2) 0; border-bottom: 1px solid var(--ink-2);
  text-decoration: none;
}
.irow-n { color: var(--bone-dim); }
/* Small thumbnail, not a card image. Her own news page runs 540x338 cards; on a
   horizontal row that scale would outweigh the headline, so this sits at 184px
   and reads as a marker beside the type. 16:10 matches the Insights index crops. */
.irow-thumb { display: block; width: 184px; align-self: start; overflow: hidden; }
.irow-thumb img {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover;
  filter: grayscale(1) contrast(1.04); opacity: 0.72;
  transition: filter 260ms ease, opacity 260ms ease, transform 260ms ease;
}
.irow:hover .irow-thumb img { filter: none; opacity: 1; }
/* min-width:0 or the grid track refuses to shrink below its longest word and the
   headline runs out past the viewport on mobile. Grid items default to auto. */
.irow-main { display: block; min-width: 0; }
.irow-main .t-h3 {
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.irow:hover .irow-main .t-h3 { transform: translateX(12px); }
.irow-line { color: var(--bone-dim); margin-top: 8px; max-width: 58ch; }
.irow-meta {
  color: var(--bone-dim); display: inline-flex; align-items: center; gap: 14px;
  white-space: nowrap;
}
.irow-arrow {
  color: var(--bone-dim); opacity: 0; transform: translateX(-8px);
  transition: opacity 220ms ease, transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.irow:hover .irow-arrow { opacity: 1; transform: none; color: var(--bone); }
@media (max-width: 820px) {
  /* thumb keeps its own column and spans both rows, so the meta line sits under
     the headline rather than under the image. Explicit placement, because the
     hidden number would otherwise shift every item one cell to the left. */
  /* Stacked on mobile, like the cards on her own news page. Beside the thumb the
     headline had a 222px column and ran to seven lines at 27px; full width it is
     three. minmax(0,1fr) regardless, because a bare fr track keeps a min-content
     floor and the longest word pushed the row 83px past the viewport. */
  .irow { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .irow-n { display: none; }
  .irow-thumb { width: 160px; grid-column: 1; grid-row: auto; }
  .irow:hover .irow-main .t-h3 { transform: none; }
}

/* ---------- B12 footer ---------- */
.footer { background: var(--ink-0); position: relative; z-index: 2; }
/* the module, drawn once more: bay ticks hanging off one granite hairline */
.foot-ticks { position: relative; height: 11px; }
.foot-ticks::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--ink-2) url('../assets/granite-base-1024.webp');
  background-size: 512px 24px;
  background-blend-mode: screen;
}
.foot-ticks::after {
  content: ''; position: absolute; inset: 2px 0 0 0;
  background: repeating-linear-gradient(to right, var(--ink-2) 0 1px, transparent 1px var(--bay));
}
.footer .container { padding: var(--s3) 0 var(--s2); }
.foot-head { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
.foot-logo { height: 34px; width: auto; }
.foot-tag { color: var(--bone-dim); margin-left: auto; }
.foot-grid {
  display: grid; grid-template-columns: repeat(2, calc(var(--bay) * 3)) 1fr;
  gap: var(--s2); margin-top: var(--s3);
}
.foot-col { display: flex; flex-direction: column; gap: 10px; }
.foot-lab { color: var(--bone-dim); margin-bottom: 4px; }
.foot-col a {
  position: relative; align-self: flex-start;
  color: var(--bone-dim); text-decoration: none; padding: 11px 0; min-height: 44px;
  transition: color 200ms ease;
}
.foot-col a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 8px; height: 1px;
  background: var(--walnut); transform: scaleX(0); transform-origin: right;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.foot-col a:hover { color: var(--bone); }
.foot-col a:hover::after { transform: scaleX(1); transform-origin: left; }
.foot-write { justify-self: end; text-align: right; }
/* One line, at reading size. It was set in the display serif at h3, which broke
   an address that is already 26 characters over three lines and made it the
   only thing setting the footer's height. */
.foot-mail {
  display: inline-block; color: var(--bone); text-decoration: none;
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 17px; letter-spacing: 0.04em;
  border-bottom: 1px solid var(--walnut); padding-bottom: 6px;
  max-width: 100%; white-space: nowrap;
  transition: border-color 220ms ease, color 220ms ease;
}
.foot-mail:hover { border-bottom-color: var(--bone); }
.foot-legal {
  display: flex; justify-content: space-between; gap: var(--s2); flex-wrap: wrap;
  margin-top: var(--s3); padding-top: var(--s1);
  border-top: 1px solid var(--ink-2); color: var(--bone-dim);
}
.foot-legal a { color: inherit; }
@media (max-width: 820px) {
  .foot-grid { grid-template-columns: 1fr 1fr; }
  .foot-write { justify-self: start; text-align: left; grid-column: 1 / -1; }
  .foot-tag { margin-left: 0; }
}

/* ---------- static page (no-JS / reduced-motion / frames-blocked) ---------- */
body.no-js .stage, body.static .stage {
  position: relative; margin-bottom: 0; height: 100svh;
}
body.no-js .scrub, body.static .scrub,
body.no-js .cinema, body.static .cinema,
body.no-js .closer, body.static .closer,
body.no-js .finish-layers, body.static .finish-layers,
body.no-js .mline, body.static .mline,
body.no-js .mquote, body.static .mquote { display: none; }
body.static .hero-box, body.no-js .hero-box { opacity: 1; }
body.reduced .smoke { display: none; }

/* scenes flatten into the photo essay.
   The leg and #finish heights are set by ID, so the static overrides must be
   ID-qualified too or the scroll spacers survive as black gaps. */
body.no-js .scene, body.static .scene { height: auto; margin-bottom: 0; }
body.no-js #finish, body.static #finish { height: auto; }
body.no-js #leg0, body.static #leg0,
body.no-js #leg1, body.static #leg1,
body.no-js #leg2, body.static #leg2,
body.no-js #leg3, body.static #leg3,
body.no-js #leg4, body.static #leg4 { height: auto; }
body.no-js .pin, body.static .pin { position: static; height: auto; overflow: visible; }
body.no-js .band, body.static .band {
  position: static; pointer-events: auto;
  width: min(var(--container), calc(100% - 2 * var(--margin)));
  margin-inline: auto;
  padding: var(--s3) 0;
  display: flex; flex-direction: column; gap: var(--s2);
}
body.no-js .scene > .pin > .band:first-of-type,
body.static .scene > .pin > .band:first-of-type { padding-top: var(--s4); }
body.no-js .scene > .pin > .band:last-of-type,
body.static .scene > .pin > .band:last-of-type { padding-bottom: var(--s4); }
/* display type sets the column here, not a 62ch measure meant for body copy */
body.no-js .group, body.static .group { position: static; opacity: 1; width: auto; max-width: none; }
body.no-js .wash, body.static .wash { display: none; }
body.no-js .legacy-split, body.static .legacy-split,
body.no-js .steward-split, body.static .steward-split {
  position: static; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); align-items: start;
}
/* the eyebrow sits above a 2x2 of cards in the static essay (judge-v4 item 6) */
body.no-js .doors, body.static .doors { position: static; display: flex; flex-direction: column; padding: 0; gap: var(--s2); }
body.no-js .door-grid, body.static .door-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3) var(--s2); overflow: visible; margin: 0; padding: 0; }
body.no-js .door, body.static .door { flex: none; }
body.no-js .print img, body.static .print img { max-width: calc(var(--bay) * 4); }
body.no-js .film, body.static .film { max-width: calc(var(--bay) * 7); }
body.no-js .rule, body.static .rule { transform: none; }
/* no toggle without JS, so the two finishes stand side by side and the claim
   still proves itself. The charcoal landing still sits directly above, so the
   pair is labelled rather than left to be inferred. */
body.no-js .chips, body.static .chips { display: none; }
body.no-js .toggle-static, body.static .toggle-static {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s1);
  margin-top: var(--s2); max-width: none;
}
body.no-js .toggle-static img, body.static .toggle-static img {
  width: 100%; opacity: 1; position: static;
  border: 1px solid oklch(0.92 0.010 75 / 0.22);
}
body.no-js .finish-note, body.static .finish-note { max-width: 62ch; }
@media (max-width: 820px) {
  body.no-js .toggle-static, body.static .toggle-static { grid-template-columns: 1fr; }
  /* body.static .doors outranks the phone column rule on specificity, so without
     this the two doors render as two 128px columns on a 390px screen and the
     CTA wraps to three lines inside its own button. */
  body.no-js .legacy-split, body.static .legacy-split,
  body.no-js .steward-split, body.static .steward-split,
  body.no-js .door-grid, body.static .door-grid {
    grid-template-columns: 1fr; gap: var(--s2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .smoke { display: none; }
  * { transition-duration: 0.01ms !important; }
}

/* ---------- the elevation drawing, in the footer ----------
   Her own line elevation, lifted off the current site and pulled to an alpha
   matte so it tints to bone instead of carrying its old dark slab. Aubrey's
   note holds: accent, never a full-width picture. So it runs off the right
   edge behind the write column rather than sitting centred as a band, which is
   also what keeps it from reading as a reskin of the current site.
   Tried and rejected: the doors beat. Over a photograph that busy, linework
   quiet enough to belong reads as dirt on the lens. It wants a flat ink ground,
   which on this page means here, and on the sub-pages means their heroes. */
.foot-sketch {
  position: absolute; right: 0; bottom: 0;
  width: min(58%, calc(var(--bay) * 13)); aspect-ratio: 1920 / 440;
  background: var(--bone-dim);
  -webkit-mask: url('../assets/img/sketch-elevation.webp') right bottom / cover no-repeat;
  mask: url('../assets/img/sketch-elevation.webp') right bottom / cover no-repeat;
  opacity: 0.17; pointer-events: none;
}
@media (max-width: 820px) {
  .foot-sketch { width: 86%; opacity: 0.14; }
  .foot-mail { font-size: 15px; }
}

/* Honeypot. Off-screen rather than display:none, which some bots skip. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form-err{color:var(--bone);opacity:.9;margin-top:.9rem}

/* ============================================================================
   SUB-PAGE SHELL
   Four pages inherit this: /about/, /insights/, /contact/, /policies/. The nav,
   drawer and footer markup is carried across from the homepage unchanged, so
   everything below only has to solve what the homepage never needed: a page
   that opens on flat ink instead of footage.
   ========================================================================== */

/* The homepage nav floats over a film. Here it floats over the page, so the
   content owes it its own height back. */
.subpage { padding-top: 93px; }
@media (max-width: 820px) { .subpage { padding-top: 68px; } }

/* ---------- the stencil hero band (PLAN-copy-and-completion S2) ----------
   The drawing was buried in the footer and was tried over a photograph in the
   doors beat, where linework quiet enough to belong read as dirt on the lens.
   It wants a flat ink ground, which is exactly what a sub-page opens on.
   Aubrey's constraint holds: an accent, never a full-width picture. So it is
   the carport bay crop, run off the right edge at a third of the band, faded
   out before it reaches the type, and closed by the same granite hairline the
   nav and the footer use. Three appearances of that rule on the site, and the
   band is the only place the drawing is allowed to be large. */
.phead { position: relative; background: var(--ink-0); overflow: hidden; }
/* --s4 put 152px of empty ink between the nav and the kicker on all five
   sub-pages. That is the pinned-logo-to-hero dead band, and it read as a gap
   rather than as breathing room. Let the sketch and the H1 own that space. */
.phead .container { position: relative; z-index: 1; padding: var(--s3) 0 var(--s3); }
.phead-kicker { color: var(--bone-dim); }
.phead h1 { margin-top: var(--s1); text-wrap: balance; }
.phead-lede { color: var(--bone-dim); margin-top: var(--s2); max-width: 54ch; }

/* Two nested masks rather than mask-composite: the outer element fades the
   drawing out towards the type, the inner one is the drawing. Compositing two
   mask layers is the tidier CSS and the worse cross-browser bet. */
.phead-sketch {
  position: absolute; right: 0; bottom: 0; z-index: 0;
  width: min(46%, calc(var(--bay) * 9)); aspect-ratio: 900 / 607;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 72%);
  mask-image: linear-gradient(to right, transparent, #000 72%);
  pointer-events: none;
}
.phead-sketch::before {
  content: ''; position: absolute; inset: 0;
  background: var(--bone-dim);
  -webkit-mask: url('../assets/img/sketch-bay.webp') right bottom / cover no-repeat;
  mask: url('../assets/img/sketch-bay.webp') right bottom / cover no-repeat;
  opacity: 0.16;
}
/* One drawing, one hero. Repeating it in the footer of the same page turns a
   motif into wallpaper, so the footer copy stands down on sub-pages. */
body.sub .foot-sketch { display: none; }

/* the band closes on the granite hairline, drawn a third time */
.phead-rule {
  position: relative; z-index: 1; height: 2px;
  margin: 0 var(--margin);
  background: var(--ink-2) url('../assets/granite-base-1024.webp');
  background-size: 512px 24px;
  background-blend-mode: screen;
}

@media (max-width: 1100px) {
  .phead-sketch { width: 54%; }
}
/* It stays an accent on a phone too. Stacking it above the title as a
   full-width band was tried and is exactly the "full-width picture" Aubrey
   ruled out, so it keeps its corner and only loses scale. */
@media (max-width: 820px) {
  .phead .container { padding: var(--s3) 0 var(--s3); }
  /* A page title is a sentence here, not a word. At the full t-h2 the About
     headline ran six lines and ate a 600px phone whole, lede included. */
  .phead h1 { font-size: 32px; line-height: 1.14; }
  .phead-sketch { width: 66%; }
  .phead-sketch::before { opacity: 0.15; }
  .phead-rule { margin: 0 24px; }
}

/* ---------- sub-page body: the bay grid, with a drafting margin ----------
   3 bays of index, 1 bay of gutter, the measure in the rest. The index is the
   only navigation a long legal page gets, and on the homepage's own vocabulary
   it is the walk index sitting still. */
.pbody { padding: var(--s3) 0 var(--s4); }
.pgrid {
  display: grid;
  grid-template-columns: calc(var(--bay) * 3) 1fr;
  gap: var(--s3);
  align-items: start;
}
.pindex { position: sticky; top: calc(93px + var(--s2)); display: flex; flex-direction: column; gap: 10px; }
.pindex .foot-lab { margin-bottom: 4px; }
.pindex a {
  position: relative; color: var(--bone-dim); text-decoration: none;
  padding: 6px 0; min-height: 32px; display: inline-flex; align-items: center;
  transition: color 200ms ease;
}
.pindex a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px;
  background: var(--walnut); transform: scaleX(0); transform-origin: right;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.pindex a:hover { color: var(--bone); }
.pindex a:hover::after { transform: scaleX(1); transform-origin: left; }

/* ---------- prose ----------
   The measure is 66ch, but the COLUMN is not: the section rules run the full
   width of the bay so the page has a drawn right edge instead of a ragged one,
   and the text sits inside it at a length that can actually be read. That is
   the drafting margin doing a job, rather than a void left over from capping
   the whole block. */
.prose { max-width: none; }
.prose > * + * { margin-top: var(--s1); }
.prose :is(p, ul) { max-width: 66ch; }
:is(.prose, .pcol) > h3 { max-width: 66ch; }
.prose h2 {
  font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w);
  font-size: calc(38px * var(--disp-scale)); line-height: 1.2; letter-spacing: -0.02em;
  margin-top: var(--s3); padding-top: var(--s2);
  border-top: 1px solid var(--ink-2);
}
.prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* Direct children only. A descendant selector here silently repainted the
   `.t-h3` inside the reused Insights rows as a mono micro-label. */
:is(.prose, .pcol) > h3 {
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500;
  font-size: 14px; line-height: 20px; letter-spacing: 0.14em;
  font-variant-caps: all-small-caps;
  color: var(--bone-dim);
  margin-top: var(--s2);
}
.prose p { font-size: 19px; line-height: 28.5px; }
.prose ul { list-style: none; margin-top: var(--s1); }
.prose li { position: relative; padding-left: var(--s2); font-size: 19px; line-height: 28.5px; }
.prose li + li { margin-top: 12px; }
/* the 6'4" tick, once more: a measured mark, not a bullet glyph */
.prose li::before {
  content: ''; position: absolute; left: 0; top: 14px;
  width: 19px; height: 1px; background: var(--ink-2);
}
.prose li strong { font-variation-settings: 'wght' 600; }
/* Running-text links only. As a bare `.prose a` this repainted every link in a
   reused component: it turned the Insights row separators walnut and set the
   `.cta` label to bone on a bone fill, which renders as an empty button. */
.prose :is(p, li, h2, h3) a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid var(--walnut);
  transition: border-color 200ms ease, color 200ms ease;
}
.prose :is(p, li, h2, h3) a:hover { border-bottom-color: var(--bone); }
.prose .stamp { color: var(--bone-dim); margin-top: var(--s3); }

@media (max-width: 1100px) {
  .pgrid { grid-template-columns: 1fr; gap: var(--s2); }
  .pindex { position: static; flex-direction: row; flex-wrap: wrap; gap: var(--s1) var(--s2); align-items: baseline; }
  .pindex .foot-lab { margin-bottom: 0; }
}
@media (max-width: 820px) {
  .pbody { padding: var(--s2) 0 var(--s4); }
  .prose h2 { font-size: 27px; margin-top: var(--s2); }
  .prose p, .prose li { font-size: 17px; line-height: 25.5px; }
  .prose li::before { top: 12px; }
}

/* ---------- the founder story (/about/) ----------
   Long-form on the sub-page shell. The section rule moves off the `h2` and onto
   the section itself, so it still spans the full bay when a portrait sits
   beside the measure; a rule that stopped at the text edge would make the
   portrait look pasted on rather than placed. */
.psect { border-top: 1px solid var(--ink-2); padding-top: var(--s2); }
.prose > .psect { margin-top: var(--s3); }
.prose > .psect:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
.psect h2 { border-top: 0; margin-top: 0; padding-top: 0; }
.pcol > * + * { margin-top: var(--s1); }

/* Text at the measure, the print in the last four bays: the homepage's own
   B4 composition, standing still. */
.psplit { display: grid; grid-template-columns: minmax(0, 1fr) calc(var(--bay) * 4); gap: var(--s3); align-items: start; }
.psplit .print { margin-top: 0; }
.psplit .print img { width: 100%; height: auto; }

/* Her sentences, and Bob's, carry more than the paragraphs around them, so they
   break the measure instead of sitting inside it. */
.pquote { margin: var(--s2) 0; padding-left: var(--s2); border-left: 1px solid var(--ink-2); max-width: none; }
.pquote p {
  font-family: 'Cabinet Grotesk', system-ui, sans-serif; font-weight: var(--disp-w);
  font-size: calc(38px * var(--disp-scale)); line-height: 1.2; letter-spacing: -0.02em;
  max-width: 24ch;
}
/* Bob's own words, and only his. The modifier is on the blockquote so a quote
   from Aubrey in the same component cannot pick the serif up by accident. */
.pquote.bob p {
  font-family: 'Young Serif', serif; font-weight: 400;
  font-size: 38px; letter-spacing: 0;
}
.pquote cite { display: block; margin-top: var(--s1); font-style: normal; color: var(--bone-dim); }

/* The press list is the homepage Insights index, reused. It runs in a column
   roughly three quarters as wide, so the title steps down a size and the row
   number goes: at 38px in this measure every headline wrapped to five lines. */
.psect .index { margin-top: var(--s2); }
.psect .irow { grid-template-columns: 1fr auto; }
.psect .irow-n { display: none; }
.psect .irow .t-h3 { font-size: 27px; line-height: 1.25; }
.psect .irow-line { max-width: 52ch; }

.pclose { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }
.pclose .t-body { margin: 0; }
/* two actions side by side (v4): one filled, one traced, baseline-aligned */
.ctarow { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }
.ctarow .cta { margin-top: 0; }
/* the second pair on a page is text, not buttons: one filled pair per page */
.linkrow { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }
.linkrow a.tracelink { display: inline-block; padding: 8px 0; }
.linkrow a.tracelink::after { content: ' \2192'; }
/* her instruction: the last sentence of the promise stands alone */
.prose p.standalone { margin-top: var(--s2); font-variation-settings: 'wght' 500; }
/* the press rows carry a thumbnail now, like the Insights index they reuse */
.psect .irow { grid-template-columns: 184px minmax(0, 1fr) auto; }
/* Insights: the closing ask above the footer (v4) */
.ctablock { position: relative; margin-top: var(--s3); padding-top: var(--s2); border-top: 1px solid var(--ink-2); }
.ctablock .rule { position: absolute; top: -1px; left: 0; width: calc(var(--bay) * 2); height: 1px; background: var(--bone-dim); transform: none; }
.ctablock .linkrow { margin-top: var(--s1); }
.ctablock-note { color: var(--bone-dim); margin-top: var(--s2); }
.ctablock-note a { color: var(--bone); text-decoration: none; border-bottom: 1px solid var(--walnut); }
.ctablock-note a:hover { border-bottom-color: var(--bone); }

@media (max-width: 1100px) {
  .psplit { grid-template-columns: 1fr; gap: var(--s2); }
}
@media (max-width: 820px) {
  .prose > .psect { margin-top: var(--s2); }
  .pquote { padding-left: var(--s1); }
  .pquote p { font-size: 27px; line-height: 1.25; }
  /* the .psect override above outranks the base row's own mobile collapse, so
     it has to hand the column back explicitly */
  .psect .irow { grid-template-columns: 1fr; }
  .ctarow { gap: var(--s1); }
  .psect .irow .t-h3 { font-size: 24px; }
}

/* ---------- Insights: the index and the essay template ----------
   The essay is deliberately NOT the About layout. `copy/insights-longevity-essay.md`
   sets the constraint: an editorial page, byline at the top, sources at the foot,
   and no sales CTA next to the body, because that adjacency is most of what
   separates an opinion piece under her byline from housing advertising. So the
   drafting margin carries the byline instead of a section index, and the section
   rules come off: this is continuous prose, not a document with clauses. */
:is(.pindex, .pmeta) { }
.pmeta { position: sticky; top: calc(93px + var(--s2)); display: flex; flex-direction: column; gap: 10px; }
.pmeta .foot-lab { margin-bottom: 4px; }
.pmeta p { color: var(--bone-dim); }
.pmeta a {
  position: relative; color: var(--bone-dim); text-decoration: none;
  padding: 6px 0; min-height: 32px; display: inline-flex; align-items: center;
  transition: color 200ms ease;
}
.pmeta a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px;
  background: var(--walnut); transform: scaleX(0); transform-origin: right;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.pmeta a:hover { color: var(--bone); }
/* An email address is one unbreakable token and the margin is three bays. The
   <wbr> in the markup gives it a sane break point at the @ rather than mid-word. */
.pmeta a { overflow-wrap: break-word; }
.pmeta a:hover::after { transform: scaleX(1); transform-origin: left; }

.essay { max-width: 66ch; }
.essay h2 {
  font-size: 38px; line-height: 1.2;
  border-top: 0; padding-top: 0; margin-top: var(--s3);
}
.essay > p:first-of-type { font-size: 23.75px; line-height: 34px; color: var(--bone); }
.essay .stamp { color: var(--bone-dim); }
/* the author note and the provenance line, set apart from the argument */
.essay .endnote {
  margin-top: var(--s3); padding-top: var(--s2); border-top: 1px solid var(--ink-2);
  color: var(--bone-dim); font-style: italic;
}
/* A footnote, not a label. At 16px mono small-caps the provenance line shouted
   over the author note it sits under. */
.essay .provenance {
  margin-top: var(--s1); color: var(--bone-dim); font-style: normal;
  font-family: 'Switzer', system-ui, sans-serif; font-size: 15px; line-height: 22px;
  letter-spacing: 0; font-variant-caps: normal;
}
.essay .sources { margin-top: var(--s3); padding-top: var(--s2); border-top: 1px solid var(--ink-2); }
/* Same trap as the Insights rows and the CTA: a scoped element selector
   (.essay h2) outranks the component class (.t-label) it is carrying. */
.essay .sources h2 {
  font-family: 'Switzer', system-ui, sans-serif; font-weight: 500; font-size: 14px; line-height: 20px;
  letter-spacing: 0.14em; font-variant-caps: all-small-caps;
  color: var(--bone-dim); margin-top: 0;
}
.essay .sources li { color: var(--bone-dim); font-size: 17px; line-height: 25.5px; }

/* The index is the homepage component, in the sub-page container. Its own
   border-top comes off: the phead's granite rule already closes the band, and
   two hairlines 76px apart with nothing between them read as a mistake. */
.iwrap .index { margin-top: 0; border-top: 0; }
/* the homepage teaser needs a way through to the hub, after the list rather
   than wedged between the eyebrow and the first row */
.insights-all { margin-top: var(--s2); text-align: right; }

@media (max-width: 1100px) {
  .pmeta { position: static; flex-direction: row; flex-wrap: wrap; gap: var(--s1) var(--s2); align-items: baseline; }
  .pmeta .foot-lab { margin-bottom: 0; }
}
@media (max-width: 820px) {
  .essay h2 { font-size: 27px; margin-top: var(--s2); }
  .essay > p:first-of-type { font-size: 19px; line-height: 28px; }
}

/* ---------- /contact/: the three doors, then one form ----------
   Not a generic contact form. The doors ARE the IA argument from
   BRIEF-homepage-rebuild.md, so they lead and the form serves them: choosing a
   door selects the matching intent below rather than opening a second flow.
   No filled panels here either, same as the homepage: a hairline, a number and
   the type. */
.doorset {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3);
  padding-bottom: var(--s4);
}
.doorcard { position: relative; padding-top: var(--s2); display: flex; flex-direction: column; }
.doorcard .rule {
  position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--ink-2); transform: none;
}
/* the middle heading wraps to two lines, which pushed its body 46px out of step
   with the other two. Same trick the homepage doors already use at .door .t-body. */
.doorcard .t-h3 { margin-top: var(--s1); min-height: calc(2 * 1.2em); }
.doorcard .t-body { margin-top: var(--s1); color: var(--bone-dim); max-width: none; }
/* the three bodies are different lengths; the actions still line up */
.doorcard .cta { margin-top: auto; align-self: flex-start; }
.doorcard .t-body { margin-bottom: var(--s2); }
/* 04 and 05 run as a two-up band under the three (judge-v4 items 7 and 8): the
   investor door keeps the ladder's heading size and carries its lighter rank in
   the tone of its eyebrow and body only. */
.doorset-two { grid-template-columns: repeat(3, 1fr); padding-top: 0; }
.doorset:has(+ .doorset-two) { padding-bottom: var(--s2); }
.doorset-two .doorcard .t-body { max-width: 52ch; }
.doorcard-light .eyebrow { opacity: 0.7; }
.doorcard-light .t-body { color: oklch(0.68 0.012 70); }

.joinblock { border-top: 1px solid var(--ink-2); padding-top: var(--s3); scroll-margin-top: calc(93px + var(--s2)); }
/* the "on this page" rail: every target parks its heading clear of the bar */
.psect[id], main[id] { scroll-margin-top: calc(93px + var(--s2)); }
.joincol { position: relative; padding-top: var(--s2); }
.joincol .rule {
  position: absolute; top: 0; left: 0; width: calc(var(--bay) * 2); height: 1px;
  background: var(--bone-dim); transform: none;
}
.joincol .t-h3 { margin-top: 0; }
.joincol > .t-body { margin-top: var(--s1); color: var(--bone-dim); }
.joincol .joinform { margin-top: var(--s2); }
/* the form is the homepage's, unpinned from the scene layer */
.joincol .joinform .fields { max-width: calc(var(--bay) * 9); }
/* the base .joinform is capped at 7 bays for the homepage; here the five chips
   need the whole column to sit on one row */
.joincol .joinform { max-width: none; }
.joincol .wants { width: auto; max-width: none; }
.joincol .want span { padding: 11px 12px; gap: 8px; }
.joincol .want-set { gap: 8px; }
@media (max-width: 1366px) and (min-width: 821px) {
  /* five will not fit one row here; 3 + 2 on a grid reads as one choice, 4 + 1 does not */
  .joincol .want-set { display: grid; grid-template-columns: repeat(3, max-content); justify-items: start; }
}
/* the human line under the button: one line, muted, not a banner */
.form-note { margin-top: var(--s1); color: var(--bone-dim); font-size: 16px; }

@media (max-width: 1100px) {
  .doorset { grid-template-columns: 1fr; gap: var(--s2); padding-bottom: var(--s3); }
  .joinblock { scroll-margin-top: calc(68px + var(--s1)); }
  .psect[id], main[id] { scroll-margin-top: calc(68px + var(--s1)); }
}

/* Insights index with photographs (hers, sent 2026-08-24). The row number gives
   up its column: at 114px a number is a label, and an image in the same slot
   turns a list into an index you actually read. */
.iwrap .irow { grid-template-columns: calc(var(--bay) * 4) 1fr auto; align-items: start; }
.irow-thumb { display: block; }
.irow-thumb img {
  width: 100%; height: auto; display: block;
  border: 1px solid oklch(0.92 0.010 75 / 0.18);
  filter: saturate(0.88) brightness(0.94);
  transition: filter 320ms ease, border-color 320ms ease;
}
.irow:hover .irow-thumb img { filter: none; border-color: oklch(0.92 0.010 75 / 0.4); }
@media (max-width: 1100px) {
  .iwrap .irow { grid-template-columns: 1fr; gap: var(--s1); }
  .irow-thumb { margin-bottom: 8px; }
}

