/* ==========================================================================
   Meridian Development — styles.css
   Single stylesheet. Mobile-first. CSS custom properties.
   Design language: quiet institutional sheet — paper ground, hairline cards,
   one signature device (the 2px gold meridian line), nothing else decorated.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   Generic names (--primary/--secondary/--accent/--bg/--surface) carry the
   palette so Option B swaps in by replacing ONE block. Semantic names map on.
   -------------------------------------------------------------------------- */
:root {
  /* Option A — "Gold Standard" (active) */
  --primary: #0B2545;
  --primary-700: #13315C;
  --secondary: #2E5E8C;
  --accent: #D9A404;
  --accent-600: #B58903;
  --bg: #F7F4EE;
  --surface: #FFFFFF;
  --ink-900: #171717;
  --ink-500: #4A4A4A;
  --line-200: #E3DDD1;

  /* Border ramp. Depth comes from layered surfaces, not from louder lines:
     100 is the card edge, 200 the structural rule, 300 a deliberate emphasis. */
  --line-100: #EDE8DE;
  --line-300: #D4CBB9;
  /* Third ink step, for metadata that must recede without failing AA. */
  --ink-300: #6B665E;
  /* A card tone between paper and white, for stacking two surfaces on paper. */
  --surface-2: #FCFAF6;

  /* Accent alphas. Gold fills stay faint, borders mid, full strength only for
     marks and text — a large gold area cheapens it. */
  --gold-a06: color-mix(in srgb, var(--accent) 6%, transparent);
  --gold-a10: color-mix(in srgb, var(--accent) 10%, transparent);
  --gold-a14: color-mix(in srgb, var(--accent) 14%, transparent);
  --gold-a28: color-mix(in srgb, var(--accent) 28%, transparent);

  /* Ambient ground: a fixed engineering-paper dot field and a noise film, so
     the page reads as stock rather than as a flat fill. Both are CSS/data-URI
     — no extra requests. */
  --grain-dot: color-mix(in srgb, var(--secondary) 17%, transparent);
  /* Same tooth for the dark surfaces, struck in paper instead of slate, so a
     navy band reads as the same stock rather than a flat fill. */
  --grain-dot-dark: color-mix(in srgb, var(--paper-050) 9%, transparent);
  --grain-dot-gold: color-mix(in srgb, var(--navy-900) 15%, transparent);
  --grain-dots-dark: radial-gradient(circle 1.5px at center, var(--grain-dot-dark) 1.4px, transparent 1.4px);
  /* Film grain for the dark surfaces. The paper already has one at 0.055; navy
     needs more because noise reads weaker against a dark ground. */
  --grain-film-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.09'/%3E%3C/svg%3E");
  --grain-film: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.055'/%3E%3C/svg%3E");

  /* Technical metadata face — sheet numbers, date ranges, revision marks.
     System stack, so it costs no font request. */
  /* Both boundary tabs run to one width so the pair always reads as one
     object. Sized to the longest string in the set, MANHATTAN, NY. */
  --tab-w: 156px;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Spacing scale. Meridian ran 38 distinct ad-hoc values; everything routes
     through these eleven now. */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;  --s-7: 40px;  --s-8: 56px;
  --s-9: 72px;  --s-10: 96px; --s-11: 128px;
}
/* Option B — "Civic Slate". To switch, comment out the block above and
   uncomment this one. Nothing else changes.
:root {
  --primary: #1E2A32;
  --primary-700: #2B3B47;
  --secondary: #5F7480;
  --accent: #9A3B2E;
  --accent-600: #7E3025;
  --bg: #EDE8DF;
  --surface: #FFFFFF;
  --ink-900: #171717;
  --ink-500: #4A4A4A;
  --line-200: #DDD5C6;
}
*/
:root {
  /* Semantic aliases used throughout the sheet */
  --navy-900: var(--primary);
  --navy-700: var(--primary-700);
  --slate-500: var(--secondary);
  --gold-500: var(--accent);
  --gold-600: var(--accent-600);
  /* Gold deep enough to carry 11px text on the translucent label plate. The
     plate is not white: it is white at 94% over navy at the top and paper at
     84% at the foot, which composites to #F0F2F5 down to #D2D4D6 — so the
     bottom of the plate is what sets the floor. gold-500 gives 2.17:1 there,
     gold-600 3.08:1, #8A6803 3.50:1; this measures 4.96:1 against the darkest
     part of the face while keeping the plate 16% see-through. */
  --gold-700: #6E5202;
  --paper-050: var(--bg);
  --paper-000: var(--surface);

  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Public Sans", system-ui, sans-serif;

  /* Type scale (rem): 3.25 / 2.25 / 1.5 / 1.25 / 1.0625 / 0.9375 */
  --fs-1: 3.25rem;
  --fs-2: 2.25rem;
  --fs-3: 1.5rem;
  --fs-4: 1.25rem;
  --fs-5: 1.0625rem;
  --fs-6: 0.9375rem;
}

/* --------------------------------------------------------------------------
   2. BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-5);
  font-weight: 400;
  line-height: 1.6;
  color: var(--ink-900);
  background-color: var(--paper-050);
  background-image:
    radial-gradient(ellipse 92% 46% at 50% 0%, var(--gold-a10) 0%, transparent 64%),
    radial-gradient(circle 1.5px at center, var(--grain-dot) 1.4px, transparent 1.4px),
    var(--grain-film);
  background-size: 100% 900px, 18px 18px, 200px 200px;
  background-repeat: no-repeat, repeat, repeat;
  /* Scrolls with the sheet, not pinned to the viewport: the tooth belongs to
     the paper. Also avoids the repaint cost of a fixed background. */
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { margin: 0 0 0.5em; }
h1, h2 {
  /* The same face as the navigation, set bold. A grotesque needs a touch more
     negative tracking than the serif did at display sizes, or the counters open
     up and the line looks loose. */
  font-family: var(--font-body);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.024em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.15rem, 4.5vw, var(--fs-1)); }
h2 { font-size: clamp(1.7rem, 3.2vw, var(--fs-2)); }
h3, h4 { text-wrap: balance; }
p { text-wrap: pretty; }
h3 { font-family: var(--font-body); font-weight: 600; font-size: var(--fs-4); line-height: 1.3; }
h4 { font-family: var(--font-body); font-weight: 600; font-size: var(--fs-5); }
p, ul, ol, dl { margin: 0 0 1em; }
p, li, dd { max-width: 68ch; }
a { color: var(--slate-500); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--navy-900); }
img, svg { max-width: 100%; height: auto; }
:focus-visible { outline: 3px solid var(--slate-500); outline-offset: 2px; }
::selection { background: var(--gold-500); color: var(--navy-900); }

.skip-link {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  background: var(--navy-900); color: var(--paper-000);
  padding: 12px 20px; border-radius: 2px; font-weight: 600;
}
.skip-link:focus { left: 8px; }

.container { max-width: 1160px; margin-inline: auto; padding-inline: 24px; }
.section { padding-block: 64px; }
@media (min-width: 821px) { .section { padding-block: 112px; } }
.section--white { background: var(--paper-000); }
.section--navy { background: var(--navy-900); color: var(--paper-050); }

/* Section header: eyebrow + H2 */
.section-head { margin-bottom: 48px; }
.eyebrow {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 16px;
  font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--slate-500);
}
.on-dark .eyebrow, .section--navy .eyebrow { color: color-mix(in srgb, var(--paper-050) 75%, var(--slate-500)); }
/* THE MERIDIAN LINE — eyebrow marker. Drawn downward on section entry by a
   scroll-driven animation (see §17); no observer, no script. */
.eyebrow::before {
  content: ""; flex: none; width: 2px; height: 24px;
  background: var(--gold-500);
  transform-origin: top center;
}

/* --------------------------------------------------------------------------
   3. COMPONENTS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 16px 32px; border-radius: 2px;
  font-family: var(--font-body); font-size: var(--fs-6); font-weight: 600;
  letter-spacing: 0.02em; text-decoration: none; cursor: pointer;
  border: 1.5px solid transparent;
  position: relative;
  /* The isometric plates shade a face with a darker tone of the SAME material,
     never with black. A button is that solid seen slightly from above: a lit
     top edge, a flat front face, and 2px of its own shade standing under it.
     The faint ambient stops it reading as a cartoon block. */
  box-shadow:
    inset 0 1px 0 var(--btn-lit, transparent),
    0 2px 0 0 var(--btn-side, transparent),
    0 3px 7px var(--btn-cast, transparent);
  transition: background-color 200ms cubic-bezier(.2,.7,.3,1), border-color 200ms,
              color 200ms, box-shadow 130ms ease, transform 130ms ease;
}
.btn:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 var(--btn-lit, transparent),
    0 3px 0 0 var(--btn-side, transparent),
    0 6px 14px var(--btn-cast, transparent);
}
/* Pressed: the face travels the full height of its own side face and lands on
   it, so the solid closes up rather than just dimming. */
.btn:active {
  transform: translateY(2px);
  box-shadow:
    inset 0 1px 0 var(--btn-lit, transparent),
    0 0 0 0 var(--btn-side, transparent),
    0 1px 2px var(--btn-cast, transparent);
}
.btn:focus-visible { outline: 3px solid var(--slate-500); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .btn, .btn:hover, .btn:active,
  .nav-toggle:hover, .nav-toggle:active, .modal-close:hover, .modal-close:active,
  .persona-tile:hover, .persona-tile:active,
  a.card.sheet-card:hover, a.card.sheet-card:active,
  a.trig-card:hover, a.trig-card:active { transform: none; }
}

.btn-primary {
  background: var(--gold-500); color: var(--navy-900);
  border-color: color-mix(in srgb, var(--gold-600) 72%, var(--navy-900));
  --btn-lit: rgb(255 255 255 / .34);
  --btn-side: var(--gold-600);
  --btn-cast: color-mix(in srgb, var(--navy-900) 22%, transparent);
}
.btn-primary:hover { background: color-mix(in srgb, var(--gold-500) 88%, #fff); color: var(--navy-900); }

.btn-secondary {
  background: transparent; border-color: var(--navy-900); color: var(--navy-900);
  --btn-lit: rgb(255 255 255 / .5);
  --btn-side: color-mix(in srgb, var(--navy-900) 26%, transparent);
  --btn-cast: color-mix(in srgb, var(--navy-900) 13%, transparent);
}
.btn-secondary:hover {
  background: var(--navy-900); color: var(--paper-000);
  --btn-side: color-mix(in srgb, var(--navy-900) 62%, #000);
}
.btn-secondary--light {
  border-color: var(--paper-000); color: var(--paper-000);
  --btn-lit: rgb(255 255 255 / .22);
  --btn-side: color-mix(in srgb, var(--paper-050) 30%, transparent);
  --btn-cast: rgb(0 0 0 / .26);
}
.btn-secondary--light:hover {
  background: var(--paper-000); color: var(--navy-900);
  --btn-side: color-mix(in srgb, var(--paper-050) 62%, var(--navy-900));
}

.card {
  background: var(--paper-000);
  border: 1px solid var(--line-100);
  border-radius: 4px;
  /* Depth from layered surface + shadow rather than a louder edge: the card
     sits off the paper, the hairline only finishes it. */
  box-shadow: 0 1px 2px rgb(11 37 69 / .07), 0 6px 22px rgb(11 37 69 / .07),
              0 0 0 1px rgb(255 255 255 / .7) inset;
  padding: 32px;
}
.card h3 { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   DRAFTING HAIRLINE
   A rule that fades out at both ends, the way a drawn line lifts off the page
   at the start and end of a stroke. Used where a hard border would read as a
   box edge: section seams, panel heads, the footer's legal strip.
   -------------------------------------------------------------------------- */
.cta-band, .site-footer .footer-legal, .detail h3 {
  --hair: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--gold-500) 20%, transparent) 14%,
    color-mix(in srgb, var(--gold-500) 42%, transparent) 50%,
    color-mix(in srgb, var(--gold-500) 20%, transparent) 86%,
    transparent 100%);
}
/* The navy CTA band is the one section seam that earns the accent. Full-bleed
   without 100vw — the section is already edge-to-edge, so inset 0 suffices and
   no scrollbar-width overflow can occur. */
.cta-band { position: relative; }
.cta-band::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--hair);
}
/* The footer's wash, inverted. Same geometry — 140% wide by 60% tall, centred
   on the top edge — but dark, so the band sinks where the footer beneath it
   lifts. Read together, the closing band recedes and the footer comes forward,
   which is the order they should be in. Under the content on z-index 0. */
.cta-band::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(140% 60% at 50% 0%,
                    rgb(0 0 0 / .26) 0%,
                    rgb(0 0 0 / .11) 38%,
                    transparent 72%);
}
.cta-band > .container { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   THE WASH — one light entering every surface from its own top edge
   The footer established it and the closing band inverted it. Extended here to
   the CTA rails and to the paper sections, so a reader scrolling the sheet sees
   light fall consistently rather than on two blocks out of twenty.

   Geometry is identical everywhere — 140% of the width by 60% of the height,
   centred on the top edge, out by 72% — and only the ink changes with the
   ground it lands on. On paper it is white and does real work: the dot grain
   is painted on <body>, underneath, so the wash fades the tooth in toward the
   top of each section and the paper reads as lit rather than flat. On navy and
   on gold it is a far weaker sheen, because those grounds already carry their
   own grain and a strong wash would grey them.

   Always ::after, always z-index 0, always pointer-events:none, with the
   container lifted to 1 — the wash sits on the surface, never over the type.
   -------------------------------------------------------------------------- */
.cta-rail::after,
.section:not(.section--white):not(.section--navy)::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(140% 60% at 50% 0%,
                    var(--wash-a, rgb(255 255 255 / .50)) 0%,
                    var(--wash-b, rgb(255 255 255 / .20)) 38%,
                    transparent 72%);
}
.section:not(.section--white):not(.section--navy) { position: relative; }
.section:not(.section--white):not(.section--navy) > .container { position: relative; z-index: 1; }
.cta-rail > .container { position: relative; z-index: 1; }
/* Gold and navy already carry grain of their own, so the sheen is a tenth of
   the strength it takes on bare paper. */
/* Amber is already a light ground, so a sheen calibrated for navy vanished on
   it entirely. Roughly twice the strength, which lifts the top of the band
   toward a paler gold without bleaching it. */
.cta-rail--gold { --wash-a: rgb(255 255 255 / .30); --wash-b: rgb(255 255 255 / .12); }
.cta-rail--dark { --wash-a: rgb(255 255 255 / .085); --wash-b: rgb(255 255 255 / .035); }
/* The boxed sheet paints its own white ground and its own engraved pattern; a
   wash on top of that is invisible at best and muddies the plate at worst. */
.section--sheet::after { content: none; }

/* Programme and project type are labels, so they are drawn as the site's
   label: white plate, hairline edge, lit top, its own shade beneath, gold tick.
   Same object as the trade names on work packages and the keys on the spec
   rows — a pill was the one component still speaking a different language.
   Works on the navy map tooltip too, where the plate reads as a chip on dark. */
.badge {
  position: relative; display: inline-block;
  padding: 5px 11px 5px 14px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900); white-space: nowrap;
}
.badge::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
.badge-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }

.text-link {
  font-weight: 600; text-decoration: none;
  background-image: linear-gradient(90deg, currentColor, currentColor);
  background-size: 0% 1px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size 260ms cubic-bezier(.2,.7,.3,1);
  padding-bottom: 2px;
}
.text-link:hover, a:hover > .text-link { background-size: 100% 1px; }

.grid-2, .grid-3, .grid-4, .grid-5 { display: grid; gap: 24px; }
@media (min-width: 641px) { .grid-2, .grid-3, .grid-4, .grid-5 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 981px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
  .grid-5 { grid-template-columns: repeat(5, 1fr); gap: 16px; }
}

/* --------------------------------------------------------------------------
   4. HEADER / NAV
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper-050) 88%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line-200);
}
.nav {
  max-width: 1160px; margin-inline: auto; padding-inline: 24px;
  height: 72px; display: flex; align-items: center; justify-content: space-between; gap: 24px;
}
.logo { display: inline-flex; color: var(--ink-900); text-decoration: none; }
.logo svg { width: 186px; height: 36px; display: block; }
.nav-links { display: flex; align-items: center; gap: 28px; list-style: none; margin: 0; padding: 0; }
.nav-links a:not(.btn) {
  font-size: var(--fs-6); font-weight: 600; color: var(--ink-900);
  text-decoration: none; padding-block: 6px;
}
.nav-links a:not(.btn):hover { color: var(--slate-500); }
.nav-links a:not(.btn)[aria-current="page"] {
  text-decoration: underline;
  text-decoration-color: var(--gold-500);
  text-decoration-thickness: 2px;
  text-underline-offset: 8px;
}
.nav-cta { padding: 11px 20px; }

/* --------------------------------------------------------------------------
   THE SOLID — a side face means you can press it
   Interactive things are solids: a lit hairline on top, the flat face, and 2px
   of the material's OWN darker tone standing under it. Hover lifts 1px; press
   travels the full height of the side face and lands on it. Non-interactive
   containers are sheets: hairline, layered surface, soft cast, no side face.
   Inputs are the inverse — pressed into the sheet, shade on the inside top.
   -------------------------------------------------------------------------- */
.nav-toggle {
  display: none; width: 44px; height: 44px;
  cursor: pointer; padding: 10px; border-radius: 3px;
  background: var(--paper-000); border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff,
              0 2px 0 0 color-mix(in srgb, var(--navy-900) 20%, transparent),
              0 3px 7px color-mix(in srgb, var(--navy-900) 12%, transparent);
  transition: transform 130ms ease, box-shadow 130ms ease, border-color 150ms;
}
.nav-toggle:hover { transform: translateY(-1px); border-color: var(--slate-500);
  box-shadow: inset 0 1px 0 #fff,
              0 3px 0 0 color-mix(in srgb, var(--navy-900) 20%, transparent),
              0 5px 12px color-mix(in srgb, var(--navy-900) 16%, transparent); }
.nav-toggle:active { transform: translateY(2px);
  box-shadow: inset 0 1px 0 #fff, 0 0 0 0 transparent,
              0 1px 2px color-mix(in srgb, var(--navy-900) 12%, transparent); }
.nav-toggle .bars, .nav-toggle .bars::before, .nav-toggle .bars::after {
  display: block; content: ""; width: 24px; height: 2px;
  background: var(--ink-900); position: relative; transition: transform 200ms, opacity 200ms;
}
.nav-toggle .bars::before { position: absolute; top: -7px; }
.nav-toggle .bars::after { position: absolute; top: 7px; }

@media (max-width: 820px) {
  .nav-toggle { display: block; }
  .nav-links {
    display: none;
    position: fixed; inset: 0; z-index: 60;
    background: var(--navy-900);
    flex-direction: column; justify-content: center; gap: 8px;
    padding: 96px 32px;
  }
  .nav-open .nav-links { display: flex; }
  .nav-open .nav-toggle { position: relative; z-index: 70; }
  .nav-open .nav-toggle .bars { background: transparent; }
  .nav-open .nav-toggle .bars::before { transform: rotate(45deg); top: 0; background: var(--paper-000); }
  .nav-open .nav-toggle .bars::after { transform: rotate(-45deg); top: 0; background: var(--paper-000); }
  .nav-links a:not(.btn) { color: var(--paper-000); font-size: var(--fs-3); font-family: var(--font-display); font-weight: 500; }
  .nav-links a:not(.btn):hover { color: var(--gold-500); }
  .nav-cta-item { margin-top: 24px; }
  .nav-open { overflow: hidden; }
}

/* --------------------------------------------------------------------------
   5. HEROES
   -------------------------------------------------------------------------- */
.hero { position: relative; overflow: hidden; }
/* Transparent, not paper-filled: an opaque fill here would hide the body's
   ambient ground exactly where it matters most — the top of every page. */
.hero--paper { background: transparent; border-bottom: 1px solid var(--line-200); }
.hero--paper .hero-inner { padding-block: 88px 80px; }
.hero--navy { background: var(--navy-900); color: var(--paper-050); }
.hero--navy .hero-inner { position: relative; z-index: 3; padding-block: 128px 116px; }
.hero--navy h1 { color: var(--paper-000); }
.hero-sub { font-size: var(--fs-4); line-height: 1.55; max-width: 56ch; color: var(--ink-500); }
.hero--navy .hero-sub { color: color-mix(in srgb, var(--paper-050) 88%, var(--navy-900)); }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 40px; }

/* --------------------------------------------------------------------------
   6. STAT STRIP
   -------------------------------------------------------------------------- */
/* Deliberate: white rather than paper behind the strip — gold-600 numerals hit
   3.2:1 on white (AA large-text) but only ~2.9:1 on paper-050. */
.stat-strip { border-bottom: 1px solid var(--line-200); background: var(--paper-000); padding-block: 48px; }
.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px 28px; align-items: start; }
/* Each figure hangs off a scale mark: a gold tick over a hairline stem. The
   stem is a FIXED height tied to the figure block, not the cell — a cell-height
   rule stretches with the tallest sibling and reads as a table border. */
.stat { position: relative; padding-left: 16px; }
.stat::before {
  content: ""; position: absolute; left: 0; top: 2px;
  width: 1px; height: 62px; background: var(--line-200);
}
.stat::after {
  content: ""; position: absolute; left: 0; top: 2px;
  width: 1px; height: 15px; background: var(--gold-500);
}
/* Metadata under a figure — the date span behind the duration. Set in the UI
   face at label size so it never competes with the magnitude above it. */
.stat-sub {
  display: block; margin-top: 5px;
  font-family: var(--font-mono);
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--slate-500);
  font-variant-numeric: tabular-nums;
}
/* Data-block header, in the drawing-sheet idiom: tiny caps over a fading rule. */
.qty-head {
  display: flex; justify-content: flex-start; align-items: baseline; gap: 16px;
  margin-bottom: 32px; padding-bottom: 11px; position: relative;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-500);
}
.qty-head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--gold-500) 42%, transparent) 0%,
    color-mix(in srgb, var(--line-200) 90%, transparent) 22%,
    var(--line-200) 100%);
}
.qty-head span:last-child { font-family: var(--font-mono); letter-spacing: 0.06em; color: var(--slate-500); }
@media (min-width: 821px) { .stats { grid-template-columns: repeat(5, 1fr); } }
/* The cover sheet, not a detail sheet: same devices as the project quantity
   band — one continuous datum, a gold tick per figure — struck on paper rather
   than navy. Figures are navy, not gold: a standalone display figure carries no
   accent, the tick beside it does, and gold-600 only reached 3.2:1 here. */
.stat-num {
  display: block;
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.9rem, 3vw, 2.5rem); line-height: 1.1;
  color: var(--navy-900);
  /* proportional, not tabular: tabular widths make 2 sit loose beside 3,500+ */
  font-variant-numeric: proportional-nums lining;
  padding-right: 26px;
}
.stat-label {
  position: relative; display: block;
  margin-top: 16px; padding: 15px 26px 0 0;
  border-top: 1px solid var(--line-300);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-500);
}
.stat-label::before {
  content: ""; position: absolute; left: 0; top: -1px;
  width: 2px; height: 15px; background: var(--gold-500);
}
/* Cells sit flush so the label borders meet into one unbroken datum. */
.stat-strip .stats { gap: 0; }
.stat-strip .stat { padding: 0; }
.stat-strip .stat::before, .stat-strip .stat::after { content: none; }
@media (max-width: 820px) {
  .stat-strip .stats { row-gap: 30px; }
  .stat-strip .stat-num, .stat-strip .stat-label { padding-right: 20px; }
}
.stat-footnote { margin: 36px 0 0; padding-left: 16px; max-width: 62ch; font-size: 0.8125rem; color: var(--ink-500); }
/* At 320px the widest figure ($988MM+) plus its scale tick is a hair wider
   than a half-column, so both step down together below 480px. */
@media (max-width: 480px) {
  .stat { padding-left: 16px; }
  .stat::before { height: 56px; }
  .stat-num { font-size: clamp(1.6rem, 6.5vw, 2.5rem); }
  .stat-footnote { padding-left: 16px; }
}

/* --------------------------------------------------------------------------
   7. HOME SECTIONS
   -------------------------------------------------------------------------- */
.anatomy-card { padding: 24px; }
/* Sheets: containers that sit on the paper rather than lift off it. */
.map-wrap, .comp-table, .checklist fieldset, .sched-wrap {
  box-shadow: 0 1px 2px rgb(11 37 69 / .06), 0 6px 20px rgb(11 37 69 / .06);
}
.anatomy-card h3 { font-size: var(--fs-6); letter-spacing: 0.01em; }
.anatomy-card p { font-size: 0.875rem; color: var(--ink-500); }

/* A link, so it is a solid — paper standing on darker paper. */
.persona-tile, a.card.sheet-card {
  text-decoration: none; color: inherit;
  box-shadow: inset 0 1px 0 #fff,
              0 2px 0 0 var(--line-300),
              0 4px 12px color-mix(in srgb, var(--navy-900) 10%, transparent);
  transition: transform 130ms ease, box-shadow 130ms ease, border-color 150ms;
}
.persona-tile { display: flex; flex-direction: column; }
.persona-tile:hover, a.card.sheet-card:hover {
  color: inherit; border-color: var(--slate-500); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 #fff,
              0 3px 0 0 var(--line-300),
              0 8px 20px color-mix(in srgb, var(--navy-900) 14%, transparent);
}
.persona-tile:active, a.card.sheet-card:active {
  transform: translateY(2px);
  box-shadow: inset 0 1px 0 #fff, 0 0 0 0 transparent,
              0 1px 3px color-mix(in srgb, var(--navy-900) 12%, transparent);
}
.persona-tile h3 { font-size: var(--fs-5); }
.persona-tile p { color: var(--ink-500); font-size: var(--fs-6); flex: 1; }
.persona-tile .text-link { color: var(--slate-500); font-size: var(--fs-6); }

/* --------------------------------------------------------------------------
   8. CTA BAND & FOOTER
   -------------------------------------------------------------------------- */
.cta-band { background: var(--navy-900); color: var(--paper-050); padding-block: 88px; }
.cta-band h2 { color: var(--paper-000); max-width: 24ch; }
/* `p` here, not `> p`: the proof strip is a <p> too, and at (0,1,1) this rule
   was beating .cta-proof's own 0.6875rem — the strip has been rendering at
   the lead's size ever since it was set in the mono face. Scoped to the
   lead so the strip keeps the label size every other label on the site uses. */
.cta-band p:not(.cta-proof) { font-size: var(--fs-4); color: color-mix(in srgb, var(--paper-050) 82%, var(--navy-900)); margin-bottom: 28px; }
/* Proof line under the CTA: hairline-separated facts, set small and quiet —
   the figures do the work, not the styling. */
.cta-proof {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  margin: 36px 0 0; padding-top: 24px; max-width: none;
  border-top: 1px solid color-mix(in srgb, var(--paper-000) 15%, transparent);
  /* The site sets every label in the mono face at 0.6875rem / 0.1em — the
     quantity-band eyebrows, the sheet tabs, the column heads, the tile sheet
     numbers. */
  font-family: var(--font-mono);
  font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-050) 68%, var(--navy-900));
}
.cta-proof span { display: inline-flex; align-items: center; }
@media (min-width: 981px) {
  .cta-proof { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px 0; }
  .cta-proof span + span {
    margin-left: 22px; padding-left: 22px;
    border-left: 2px solid color-mix(in srgb, var(--gold-500) 82%, transparent);
  }
}

/* ---- The footer as the foot of a drawing sheet -------------------------
   Ground, not a flat fill: the same navy, dot grain and film the CTA band is
   drawn on, closed at the top by the meridian laid flat — the gold rule that
   already runs across the head of every project card. */
.site-footer {
  position: relative;
  background-color: var(--navy-900);
  background-image: var(--grain-film-dark), var(--grain-dots-dark);
  background-size: 200px 200px, 18px 18px;
  color: var(--paper-050);
  padding-block: 64px 36px;
}
.site-footer::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--gold-500),
              color-mix(in srgb, var(--gold-500) 12%, transparent) 62%, transparent);
}
/* A wash of light entering from the top edge, so the footer reads as a lit
   surface rather than a flat block of navy. Wide and shallow — 140% of the
   width by 60% of the height, centred on the top edge — and it stops well
   before the title block, which carries its own frosted light. Kept under the
   content on z-index 0 so it never veils the type, and painted with the grain
   showing through rather than over it. */
.site-footer::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(140% 60% at 50% 0%,
                    rgb(255 255 255 / .085) 0%,
                    rgb(255 255 255 / .035) 38%,
                    transparent 72%);
}
.site-footer > .container { position: relative; z-index: 1; }
.footer-brand .logo { display: inline-block; }
.footer-tag { margin-top: 18px; max-width: 34ch; }
/* Column heads take the site's label voice — mono, caps, gold tick — instead of
   reading as small headings. */
.footer-col h4 {
  position: relative; margin: 0 0 14px; padding-left: 12px;
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-050) 74%, transparent);
}
.footer-col h4::before {
  content: ""; position: absolute; left: 0; top: 1px; bottom: 1px;
  width: 3px; border-radius: 2px; background: var(--gold-500);
}
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin: 0 0 9px; }

/* The title block. Cells in a ruled frame, mono keys over the value — drawn the
   way the quantity band's panels are, one hairline shared between neighbours so
   it reads as a single ruled table rather than four boxes. */
/* --- The title block, under drafting film -------------------------------------
   A drawing set is read through a clear overlay — drafting film laid over the
   sheet — so the glass here is that film rather than an imported fashion. Four
   pieces, in the order that matters: a real fill (a fully transparent panel has
   nothing to frost), the blur itself, a specular top edge, and a raking sheen.
   The single most load-bearing line is the inset white hairline on the top
   edge: without it a blurred panel reads as a flat tint, not as a sheet of
   something. The footer's own film and dot grain sit behind the block, so the
   blur has real texture to diffuse — which is exactly what film does to what
   is under it.
   ---------------------------------------------------------------------------- */
.titleblock {
  position: relative; overflow: hidden;
  display: grid; margin: 56px 0 0; padding: 0;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  border: 1px solid color-mix(in srgb, var(--paper-050) 24%, transparent);
  border-radius: 3px;
  /* the fill: opacity 0 would make the frost vanish entirely */
  background: color-mix(in srgb, var(--paper-050) 7%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  backdrop-filter: blur(10px) saturate(1.15);
  box-shadow: inset 0 1.5px 0 rgb(255 255 255 / .42),
              inset 0 -1px 0 rgb(0 0 0 / .30),
              0 10px 26px rgb(0 0 0 / .26);
}
/* the sheen: light raking across the face from the top left, stopping short of
   halfway so it reads as a reflection and never as a gradient fill */
.titleblock::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image: linear-gradient(104deg,
    rgb(255 255 255 / .15) 0%, rgb(255 255 255 / .05) 26%, transparent 48%);
}
/* Where the blur is unavailable the panel would be a barely-there 7% wash, so
   the fill is raised to carry the borders on its own. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .titleblock { background: color-mix(in srgb, var(--paper-050) 11%, transparent); }
}
/* Transparency is a stated accessibility preference on macOS, iOS and Windows;
   honouring it means an opaque panel and no blur at all. */
@media (prefers-reduced-transparency: reduce) {
  .titleblock {
    -webkit-backdrop-filter: none; backdrop-filter: none;
    background: color-mix(in srgb, var(--paper-050) 10%, var(--navy-900));
  }
  .titleblock::before { content: none; }
}
.tb-cell {
  position: relative; z-index: 1;
  padding: 14px 18px;
  border-left: 1px solid color-mix(in srgb, var(--paper-050) 14%, transparent);
}
.tb-cell:first-child { border-left: 0; }
.titleblock dt {
  font-family: var(--font-mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  /* The film lifts the ground from navy to rgb(28,51,81), which cost the label
     contrast it used to have against the bare footer — 62% measured 5.48:1.
     Raised to hold the site's own bar rather than only the AA minimum. */
  color: color-mix(in srgb, var(--paper-050) 80%, transparent);
}
.titleblock dd {
  margin: 5px 0 0; font-size: var(--fs-6); font-weight: 600;
  color: var(--paper-000);
}
@media (max-width: 640px) {
  /* stacked, so the shared vertical rule becomes a horizontal one */
  .tb-cell { border-left: 0; border-top: 1px solid color-mix(in srgb, var(--paper-050) 14%, transparent); }
  .tb-cell:first-child { border-top: 0; }
}
.site-footer .logo { color: var(--paper-000); }
.footer-grid { display: grid; gap: 40px; }
@media (min-width: 821px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer-tag { margin-top: 20px; max-width: 34ch; color: color-mix(in srgb, var(--paper-050) 80%, var(--navy-900)); font-size: var(--fs-6); }
.footer-col h4 { color: var(--paper-000); font-size: 0.8125rem; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 16px; }
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin-bottom: 10px; }
.site-footer a { color: color-mix(in srgb, var(--paper-050) 85%, var(--navy-900)); text-decoration: none; font-size: var(--fs-6); }
.site-footer a:hover { color: var(--gold-500); }
.footer-legal { margin-top: 48px; padding-top: 24px; position: relative; }
/* Legal, 404 and resource pages carry a lone lockup above the legal strip. */
.footer-logo { display: inline-flex; margin-bottom: 32px; }
.footer-legal--lone { margin-top: 0; padding-top: 24px; }
.footer-legal::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--hair);
}
.footer-legal .copyright { font-size: var(--fs-6); color: color-mix(in srgb, var(--paper-050) 80%, var(--navy-900)); margin-bottom: 12px; }
.footer-disclaimer { font-size: 0.8125rem; color: var(--paper-050); opacity: 0.7; max-width: 90ch; margin: 0; }

/* --------------------------------------------------------------------------
   9. SERVICES — alternating full-width bands (accordion-free by spec)
   -------------------------------------------------------------------------- */
.svc-band { padding-block: 56px; border-bottom: 1px solid var(--line-200); }
.svc-band:nth-of-type(even) { background: var(--paper-000); }
@media (min-width: 821px) { .svc-band { padding-block: 72px; } }
/* Copy left, the sheet's own plate right — and mirrored on alternate bands, so
   the eye crosses the page rather than running down one gutter. The art column
   is `auto`, sized to the plate, so its outer edge lands on the page grid
   whatever that plate's footprint is. */
.svc-inner { display: grid; gap: 32px; }
@media (min-width: 981px) {
  .svc-inner { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 56px; }
  .svc-band:nth-of-type(even) .svc-copy { order: 2; }
  .svc-band:nth-of-type(even) .svc-band-art { order: 1; }
}
.svc-copy { min-width: 0; }
.svc-no {
  margin: 0 0 10px;
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--slate-500);
}
.svc-lead { margin: 0; font-size: var(--fs-5); color: var(--ink-500); max-width: 54ch; }
.svc-band .svc-more { margin: 22px 0 0; }
.svc-band-art { margin: 0; }
.svc-band-art img { display: block; height: 260px; width: auto; max-width: 100%; }
@media (max-width: 1099px) { .svc-band-art img { height: 220px; } }
@media (max-width: 980px)  { .svc-band-art img { height: 180px; } }
.svc-band h2 { font-size: clamp(1.5rem, 2.6vw, 1.9rem); }
.svc-call { font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--slate-500); margin: 0 0 12px; }
.svc-when { margin: 0 0 0; padding-left: 20px; }
.svc-when li { margin-bottom: 8px; color: var(--ink-500); }
/* The "gold outcome line": gold is carried by the meridian rule, not the glyphs —
   gold body text on paper would fail AA (§ accessibility rules win). */
.svc-outcome { margin-top: 20px; font-weight: 600; color: var(--ink-900); border-left: 2px solid var(--gold-500); padding-left: 16px; }
.svc-outcome .svc-outcome-label { display: block; font-size: 0.8125rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-500); }

/* FAQ — stacked pairs, hairline-separated (deliberately not an accordion) */
/* The questions run the full width of the sheet and set as a two-column
   schedule — question in the left column, answer in the right — which is the
   same arrangement the spec rows and the portfolio schedule already use. Each
   row is ticked in gold at its head, the way every rule on this site is. */
.faq { max-width: none; margin: 0; }
.faq > div {
  position: relative; display: grid; gap: 8px 44px; align-items: start;
  border-top: 1px solid var(--line-200); padding: 24px 0 26px;
}
.faq > div:last-child { border-bottom: 1px solid var(--line-200); }
.faq > div::before {
  content: ""; position: absolute; left: 0; top: -1px;
  width: 56px; height: 2px; background: var(--gold-500);
}
@media (min-width: 860px) { .faq > div { grid-template-columns: minmax(0, 30ch) minmax(0, 1fr); } }
.faq dt { font-family: var(--font-display); font-weight: 500; font-size: var(--fs-4); line-height: 1.3; color: var(--navy-900); }
.faq dd { margin: 0; color: var(--ink-500); max-width: 66ch; }

/* --------------------------------------------------------------------------
   10. APPROACH — the phase stepper (signature vertical gold spine)
   -------------------------------------------------------------------------- */
.stepper { list-style: none; margin: 0; padding: 0 0 0 40px; position: relative; counter-reset: step; }
/* One continuous meridian line — the spine of the engagement — but drawn as a
   station line off a drawing rather than as a UI timeline rule. Three things
   the site already does elsewhere, applied here:
     - it is LIT. A white hairline down the right edge, the same lit-edge
       convention .tl-year and the isometric plates use, so the rule sits on the
       sheet instead of being a bar floating over it.
     - it TERMINATES. A drafted line ends on a tick; it does not just stop. The
       ::after carries a 14px terminator at each end, centred on the spine.
     - the stations are NUMBERED, which finally uses the `counter-reset: step`
       that has sat on .stepper unread since the stepper was written.
   The line runs PAST the first and last station, 12px clear at the top, so both
   terminators are visible: at top: 6px the upper tick rendered underneath
   station 01's disc (which spans y 3–27) and only the bottom one showed.
   Geometry, measured: disc 0–24, spine 11–13 (centre 12), copy at 40. */
.stepper::before {
  content: ""; position: absolute; left: 11px; top: -12px; bottom: 6px;
  width: 2px; background: var(--gold-500);
  box-shadow: 1px 0 0 rgb(255 255 255 / .85);
}
.stepper::after {
  content: ""; position: absolute; left: 5px; top: -12px; bottom: 6px; width: 14px;
  background-image: linear-gradient(var(--gold-600) 0 0), linear-gradient(var(--gold-600) 0 0);
  background-size: 14px 2px; background-repeat: no-repeat;
  background-position: left top, left bottom;
}
.step { position: relative; padding-bottom: 56px; }
.step:last-child { padding-bottom: 0; }
/* The station: a numbered disc built from .tl-year's parts — the same
   white-to-paper face, the same lit top edge and settling shadow — rounded, and
   carrying the phase number in mono the way .notes numbers its own. */
.step::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  position: absolute; left: -40px; top: 3px;
  width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 2px solid var(--gold-500);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .12);
  font-family: var(--font-mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.01em; color: var(--navy-900);
  font-variant-numeric: tabular-nums; line-height: 1;
}
/* The last phase is the one you are left holding once we leave, so it takes the
   accent the others do not — the move .tl-item:last-child already makes on the
   schedule. Navy on gold, not gold on paper, so the numeral still carries. */
.step:last-child::before {
  background-image: linear-gradient(180deg, var(--gold-500) 0%, var(--gold-600) 100%);
  border-color: var(--gold-600);
}
.step h2, .step h3 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 16px; }
.step h3 { font-size: var(--fs-4); margin-bottom: 8px; }
.step-when { font-family: var(--font-body); font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--slate-500); }
/* Gold carried by the rule, not the glyphs — body-size gold on paper fails AA. */
.step-deliverable { font-weight: 600; border-left: 2px solid var(--gold-500); padding-left: 16px; }
.step-deliverable .gold { color: var(--ink-500); }

/* --------------------------------------------------------------------------
   11. PROJECTS — drawing-sheet cards and project pages
   Each card is styled as a numbered sheet from a drawing set: title-block
   header strip (hatched, like a mylar border), mini building-count elevation,
   dotted spec leaders, and a plan-stamp footer. Whole card is a link.
   -------------------------------------------------------------------------- */
.project-grid { display: grid; gap: 24px; }
@media (min-width: 821px) { .project-grid { grid-template-columns: repeat(2, 1fr); } }

.sheet-card {
  display: flex; flex-direction: column;
  padding: 0; overflow: hidden;
  text-decoration: none; color: inherit;
  border-left: 3px solid transparent;
  transition: border-color 150ms;
}
.sheet-card { position: relative; transition: border-color 200ms, box-shadow 200ms, transform 200ms; }
/* Accent rule along the sheet's top edge, fading right — the drawing-set
   equivalent of a header band, and the meridian laid flat. */
.sheet-card::after {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--gold-500), color-mix(in srgb, var(--gold-500) 12%, transparent) 62%, transparent);
  opacity: 0; transition: opacity 200ms;
}
.sheet-card:hover, .sheet-card.is-lit {
  border-color: var(--slate-500); border-left-color: var(--gold-500); color: inherit;
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgb(11 37 69 / .07), 0 0 0 1px rgb(255 255 255 / .6) inset;
}
.sheet-card:hover::after, .sheet-card.is-lit::after { opacity: 1; }
.sheet-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 24px;
  border-bottom: 1px solid var(--line-200);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Gold, and the same gold as the sticky project bar on the entry pages —
     colour, grain and the navy hairline under it. A card is the cover of the
     sheet it links to, so it should wear the sheet's own header. */
  color: var(--navy-900);
  background-color: var(--gold-500);
  background-image: radial-gradient(circle 1.5px at center, var(--grain-dot-gold) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
  border-bottom: 1px solid color-mix(in srgb, var(--navy-900) 34%, transparent);
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .10);
}
.sheet-body { display: flex; flex-direction: column; gap: 16px; padding: 24px 24px; }
/* Key view: each card carries the same isometric plate that heads its own
   project sheet, so the listing and the page show the same building.

   Drawn as a background on .sheet-body::before rather than an <img> in the card
   template — the cards are built in JS and the JS budget has 157 bytes left.
   The cards already carry data-project-id, so CSS keys off it for nothing.

   Sized by HEIGHT, not `contain`. All four plates are cropped tight to the
   building (measured: alpha bbox is the full image bar a 1px margin), so a
   common rendered height puts every building at the same scale standing on one
   ground line. `contain` would instead fit each to the box and make the wide
   plate read smaller than the tall one. */
.sheet-body::before {
  content: ""; display: block; flex: none;
  margin: -24px -24px 0; height: 152px;
  background-color: var(--paper-050);
  /* Three layers: the building on top, a white veil, the hero's engraved ground
     underneath. The veil is how a single background gets two strengths — CSS
     cannot set per-layer opacity, so knocking the ground back to the hero's
     .14 means compositing 86% white over it rather than fading the layer. The
     tile stays pinned at 1268px, the same size it renders at on the entry page,
     so the pattern is one scale across the whole site. */
  /* The veil is graded, not flat. A dense row of the engraved pattern lands near
     the foot of the tile and was reading as a hard band along the bottom edge of
     the card — worst on the compact cards, where the band is shorter. Fading the
     veil to near-opaque at the base clears a ground for the building to stand on
     and matches the hero, which fades its ground too (there, left to right). */
  background-image: var(--card-iso),
                    linear-gradient(rgb(255 255 255 / .80) 0%,
                                    rgb(255 255 255 / .84) 52%,
                                    rgb(255 255 255 / 1) 86%),
                    var(--card-ground);
  background-repeat: no-repeat, no-repeat, repeat;
  background-position: center calc(100% - 16px), 0 0, center top;
  background-size: auto 112px, auto, 1268px auto;
  border-bottom: 1px solid var(--line-200);
}
[data-project-id="A"] .sheet-body::before { --card-iso: url("../assets/img/iso-a.webp"); --card-ground: url("../assets/img/hero-ground-a.webp"); }
[data-project-id="B"] .sheet-body::before { --card-iso: url("../assets/img/iso-b.webp"); --card-ground: url("../assets/img/hero-ground-b.webp"); }
[data-project-id="C"] .sheet-body::before { --card-iso: url("../assets/img/iso-c.webp"); --card-ground: url("../assets/img/hero-ground-c.webp"); }
[data-project-id="D"] .sheet-body::before { --card-iso: url("../assets/img/iso-d.webp"); --card-ground: url("../assets/img/hero-ground-d.webp"); }
/* The six service sheets on the home: deliverable plate over that sheet's own ground. */
[data-sheet="pact-rad"] .sheet-body::before { --card-iso: url("../assets/img/dl-pact-rad.webp"); --card-ground: url("../assets/img/hero-ground-pact-rad.webp"); }
[data-sheet="operations"] .sheet-body::before { --card-iso: url("../assets/img/dl-operations.webp"); --card-ground: url("../assets/img/hero-ground-operations.webp"); }
[data-sheet="rehab-scope"] .sheet-body::before { --card-iso: url("../assets/img/dl-rehab-scope.webp"); --card-ground: url("../assets/img/hero-ground-rehab-scope.webp"); }
[data-sheet="compliance"] .sheet-body::before { --card-iso: url("../assets/img/dl-compliance.webp"); --card-ground: url("../assets/img/hero-ground-compliance.webp"); }
[data-sheet="lease-up"] .sheet-body::before { --card-iso: url("../assets/img/dl-lease-up.webp"); --card-ground: url("../assets/img/hero-ground-lease-up.webp"); }
[data-sheet="due-diligence"] .sheet-body::before { --card-iso: url("../assets/img/dl-due-diligence.webp"); --card-ground: url("../assets/img/hero-ground-due-diligence.webp"); }
/* the ground line the buildings stand on */
.sheet-body::before { box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--ink-500) 22%, transparent); }
/* compact cards (homepage preview) carry the same view, shorter */
/* All THREE positions must be restated. A single background-position applies to
   every layer, which shifted the veil up by 12px and left the bottom 12px of the
   engraved ground showing at full strength — a hard dark band along the foot of
   every compact card. */
.sheet-card--compact .sheet-body::before {
  height: 116px;
  background-size: auto 84px, auto, 1268px auto;
  background-position: center calc(100% - 12px), 0 0, center top;
}
.sheet-body .badge-row { margin-bottom: 0; }
.sheet-body .pc-title { margin: 0; }
.sheet-sub { margin: 0; color: var(--ink-500); font-size: var(--fs-6); }
/* The elevation is a fourth row, built like the three under it: buildings mark,
   label plate, and the drawing itself where a value would sit. Source order is
   svg-then-caption, so flex `order` puts the label back in front rather than
   asking the JS — which has 157 bytes left — to emit them the other way round. */
.sheet-elev-wrap {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--line-100);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
.sheet-elev-wrap::before {
  content: ""; order: -2; flex: none; display: block; width: 24px; height: 24px;
  background: url("../assets/img/ic-buildings.webp") center / contain no-repeat;
  filter: drop-shadow(1px 2px 2px rgb(0 0 0 / .30));
}
/* the bars as solids, lit from the same corner as every other object here */
.sheet-elev {
  display: block; margin-left: auto; min-width: 0;
  max-width: 100%; height: 22px; width: auto;
  filter: drop-shadow(1px 2px 1.5px rgb(11 37 69 / .32));
}
.sheet-elev g { fill: var(--navy-900); opacity: 1; }
.sheet-elev > rect { fill: var(--gold-500); opacity: 1; }   /* the ground line */
.sheet-elev-cap {
  order: -1; position: relative; flex: none;
  padding: 5px 10px 5px 13px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900); white-space: nowrap;
}
.sheet-elev-cap::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
.spec-table { display: flex; flex-direction: column; gap: 6px; }
/* Six rows across a full-width plate would stretch the leader dots into a
   desert. Split into columns so each row keeps a readable measure. */
@media (min-width: 720px) { .spec-table--split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 24px; } }
@media (min-width: 981px) { .spec-table--split { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Built exactly like .wp-row and .cs-row on the project sheets: a light row on
   the paper ground, hairline edge, rounded — carrying a white label plate with
   the gold tick. The band's isometric mark sits outside the plate, so the tick
   keeps the ::before it needs and the icon reads as a mark on the row rather
   than decoration inside the label. */
.spec-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 6px;
  font-size: var(--fs-6); color: var(--navy-900);
  background: var(--surface-2);
  border: 1px solid var(--line-100);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
/* Keyed by row position because specRow() emits no identifier and the JS budget
   has 157 bytes left; the three rows are a fixed set written in one place in
   main.js (Units, Total dev. cost, Construction). */
.spec-row::before {
  content: ""; flex: none; display: block; width: 24px; height: 24px;
  background-repeat: no-repeat; background-position: center; background-size: contain;
  filter: drop-shadow(1px 2px 2px rgb(0 0 0 / .30));
}
/* Those three marks are keyed by row position for the project sheets, where the
   set is fixed at Units / Total dev. cost / Construction. Any other spec table
   would inherit them by accident and reserve a 24px gap for rows four onward,
   so a plate that is not a project sheet opts out. */
/* .detail.plate is drawn for the project sheets, where it deliberately bleeds
   past the container on each side to meet the sheet's own edge — 25px normally
   and 24px below 1200, where it also drops its side borders to run edge to
   edge. This plate stands on the open page under a row of cards, so it squares
   up with the container instead and keeps its box: 1112 at 1440, the same
   measure the cards above it sit on.

   Two classes, not one. `.detail.plate` is (0,2,0) and a bare `.spec-plate`
   at (0,1,0) lost to it silently — the plate stayed 50px wider than the grid.
   At (0,3,0) this also beats the max-width rule, since a media query adds no
   specificity of its own. */
.detail.plate.spec-plate {
  margin-inline: 0;
  border-left: 1px solid var(--line-200);
  border-right: 1px solid var(--line-200);
  border-radius: 8px;
}
.spec-plate .spec-row::before { content: none; }
.spec-row:nth-child(1)::before { background-image: url("../assets/img/ic-units.webp"); }
.spec-row:nth-child(2)::before { background-image: url("../assets/img/ic-total.webp"); }
.spec-row:nth-child(3)::before { background-image: url("../assets/img/ic-on.webp"); }
.spec-k {
  position: relative; flex: none;
  padding: 5px 10px 5px 13px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900); white-space: nowrap;
}
.spec-k::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
.spec-leader { flex: 1; border-bottom: 1px dotted var(--line-300); transform: translateY(-3px); min-width: 12px; }
.spec-v { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; color: var(--navy-900); }
.sheet-foot {
  margin-top: auto;
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 24px;
  /* Navy, drawn like .sheet-tab — the tab is the entry page's own "you are on
     sheet MD-10n" marker, so the card's call to action is the same object seen
     from the other side. Gold arrow, white label. */
  background-image: linear-gradient(180deg, var(--navy-900) 0%,
                    color-mix(in srgb, var(--navy-900) 84%, #000) 100%);
  border-top: 1px solid rgb(0 0 0 / .34);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .07);
  font-size: var(--fs-6); font-weight: 600; color: var(--paper-000);
}
/* The arrow is a control, so it is drawn as one: the same solid as
   .btn-primary — lit top edge, flat gold face, 2px of its own shade standing
   under it. It presses with the card on hover. */
.sheet-foot span:last-child {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 38px; height: 28px; border-radius: 3px;
  background: var(--gold-500); color: var(--navy-900);
  border: 1.5px solid color-mix(in srgb, var(--gold-600) 72%, var(--navy-900));
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .34),
              0 2px 0 0 var(--gold-600),
              0 3px 7px rgb(0 0 0 / .30);
  transition: box-shadow 130ms ease, transform 130ms ease;
}
.sheet-card:hover .sheet-foot span:last-child,
.sheet-card:focus-visible .sheet-foot span:last-child {
  transform: translateY(1px);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .34), 0 1px 0 0 var(--gold-600), 0 2px 4px rgb(0 0 0 / .28);
}
.sheet-card:hover .sheet-foot { color: var(--navy-900); }
.pc-title { font-family: var(--font-display); font-weight: 500; font-size: var(--fs-3); }
.pc-figures { margin: 0; }
.pc-figures .pc-num { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-4); color: var(--gold-600); }
.pc-detail { color: var(--ink-500); font-size: var(--fs-6); margin: 0; }
.pc-meta dt { font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-500); margin-top: 16px; }
.pc-meta dd { margin: 2px 0 0; }
.pc-scope { padding-left: 20px; }
.pc-scope li { margin-bottom: 6px; font-size: var(--fs-6); }
.pc-note { font-style: italic; color: var(--ink-500); border-left: 2px solid var(--gold-500); padding-left: 16px; margin-top: 32px; }
.grid-footnote { margin-top: 24px; font-size: 0.8125rem; color: var(--ink-500); }

/* --------------------------------------------------------------------------
   DRAWING MD-002 — PORTFOLIO SCHEDULE
   The comparative-data companion to the MD-001 map: same drafting-table mat,
   same sheet numbering, a printed schedule instead of a plan. Marks are
   deliberately monochrome — row position and label carry identity, so colour
   is free to mean one thing only: gold = the project you are looking at.
   -------------------------------------------------------------------------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* The portfolio schedule is the cover sheet for the four project sheets, so it
   is built as one: a plate with a base course, and the same gold header the
   cards wear. */
.sched-wrap {
  background: var(--paper-000);
  overflow: hidden;
  border: 1px solid var(--line-200);
  border-bottom: 3px solid var(--line-300);
  border-radius: 8px;
  box-shadow: inset 0 1px 0 #fff, 0 14px 34px rgb(11 37 69 / .10);
}
.sched-head {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 11px 20px;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--navy-900);
  background-color: var(--gold-500);
  background-image: radial-gradient(circle 1.5px at center, var(--grain-dot-gold) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
  border-bottom: 1px solid color-mix(in srgb, var(--navy-900) 34%, transparent);
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .10);
}
/* Eight columns of real data: scroll the sheet, never the page. */
.sched-scroll { overflow-x: auto; }
.sched { width: 100%; border-collapse: collapse; font-size: var(--fs-6); }
.sched th, .sched td { text-align: left; padding: 16px 20px; white-space: nowrap; }
/* Column heads read as one banded course rather than eight loose labels.
   No isometric marks in this row: the column set changes with
   CONFIG.FIGURES_MODE (banded drops Residents and Cost / unit), so icons keyed
   by position would silently sit against the wrong columns the day the client
   switches figure modes. */
.sched thead th {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900);
  background: var(--paper-050);
  border-bottom: 2px solid color-mix(in srgb, var(--navy-900) 26%, transparent);
  box-shadow: inset 0 1px 0 #fff;
  vertical-align: bottom;
}
.sched tbody tr + tr th, .sched tbody tr + tr td { border-top: 1px solid var(--line-200); }
/* Each mark is a link to its sheet, so it is drawn as the site's label plate —
   the same object as a badge or a spec key, and it reads as a control. */
.sched tbody th a {
  position: relative; display: inline-block;
  padding: 5px 11px 5px 14px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900);
  text-decoration: none; font-variant-numeric: tabular-nums; white-space: nowrap;
  transition: box-shadow 130ms ease, transform 130ms ease;
}
.sched tbody th a::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
.sched tbody th a:hover, .sched tbody th a:focus-visible {
  transform: translateY(1px);
  box-shadow: inset 0 1px 0 #fff, 0 1px 1px rgb(11 37 69 / .10);
}
.sched td { color: var(--ink-500); }
.sc-v {
  display: block; margin-bottom: 7px;
  font-weight: 700; color: var(--navy-900);
  font-variant-numeric: tabular-nums lining; /* figures align down the column */
}
.sched tbody tr:hover td, .sched tbody tr:hover th { background: var(--paper-050); }

/* Micro-bars: thin marks, hairline track, 3px rounded data-end. The track is a
   scored groove and the mark a solid standing in it, lit from the same corner
   as every other object on the page. */
.mb { display: block; overflow: visible; filter: drop-shadow(1px 1.5px 1px rgb(11 37 69 / .30)); }
.mb-track { fill: var(--line-300); opacity: .7; }
.mb-fill { fill: var(--navy-900); }
/* Colour does exactly one job here: which project you are on. */
.sched tbody tr.is-focus td, .sched tbody tr.is-focus th { background: color-mix(in srgb, var(--gold-500) 7%, transparent); }
.sched tbody tr.is-focus .mb-fill { fill: var(--gold-600); }
.sched tbody tr.is-focus .sc-v { color: var(--navy-900); }
.sched-note {
  margin: 0; padding: 16px 20px; max-width: none;
  border-top: 1px solid var(--line-200);
  font-size: 0.75rem; color: var(--ink-500);
}
@media (max-width: 640px) {
  .sched th, .sched td { padding: 12px 16px; }
  .sched-head, .sched-note { padding-inline: 16px; }
}

/* Project pages */
.hero-static-art { position: absolute; inset: 0; z-index: 1; }
.hero-static-art svg { width: 100%; height: 100%; display: block; }

/* Hero photograph. The duotone, the defocus behind the text column and the
   scrim are all baked into the file by the export script. Deliberately no
   mix-blend-mode, no backdrop-filter and no isolation: that stack composites
   against different backdrops in different browsers, and when it goes wrong
   the picture paints over the type. Nothing below can do that. The plate is
   delivered wider than the slot so the browser downsamples, never upscales.
   --focal biases the cover crop per plate. */
.hero-photo { position: absolute; inset: 0; z-index: 1; }
.hero-photo img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: var(--focal, 50% 50%);
}
/* A headline running the full container width forces the scrim across the whole
   frame, which leaves no photograph. Two shorter lines free the right half. */
.hero--photo h1 { max-width: 19ch; }
.hero--photo .hero-sub { max-width: 44ch; }

/* The sheet opens white. The plate behind it is a pale watermark baked into
   the file — palest blue-grey at its darkest, faded further toward white
   behind the text column — so no scrim is needed and no blend mode is used.
   Content sits to the top, not centred. The floors keep all four sheets the
   same height; each is the measured tallest of the four at that width. */
.hero--photo {
  background: var(--paper-000);
  color: var(--ink-900);
  border-bottom: 1px solid var(--line-200);
  /* grid-template-columns is load-bearing: .container carries margin-inline:auto,
     and auto inline margins cancel grid stretch, so an implicit auto column
     sizes the block to fit-content and centres it. An explicit 1fr column keeps
     the container full width and the copy hard against the left margin. */
  display: grid; grid-template-columns: minmax(0, 1fr); align-content: start;
  min-height: 640px;
}
/* width:100% is load-bearing. .container carries margin-inline:auto, and auto
   inline margins make a grid item fit-content and centre it — which shrank the
   copy block to 643px and floated it into the middle of the page. An explicit
   width fills the column; max-width then caps it and the auto margins centre
   the 1160px container itself, so the copy sits on the real left margin. */
.hero--photo .hero-inner {
  position: relative; z-index: 3; width: 100%;
  padding-block: 88px 80px;
}
/* Two columns from 900px: the massing on the left, the copy on the right.
   Bottom-aligned, because the plates are drawn at one common scale and all
   four buildings stand on the same ground line. */
/* One box, identical on all four sheets, so the plates carry the same visual
   weight page to page. Each drawing is fitted inside it and stood on the box
   floor, so every building shares a ground line. */
.hero-iso {
  position: relative;
  width: calc(460px * var(--iso-k, 1));
  height: calc(322px * var(--iso-k, 1));
}
.hero-iso img {
  width: 100%; height: 100%; display: block;
  object-fit: contain; object-position: bottom center;
}
/* The plates carry no baked shadow — that would have fouled the alpha key —
   so the weight is drawn here and can be tuned without regenerating. */
.hero-iso::after {
  content: ""; position: absolute; left: 4%; right: 10%; bottom: -10px; height: 18px;
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
              color-mix(in srgb, var(--navy-900) 15%, transparent) 0%, transparent 70%);
  pointer-events: none;
}
@media (min-width: 900px) {
  .hero--photo .hero-inner {
    display: grid; grid-template-columns: minmax(0, 460px) minmax(0, 1fr);
    gap: 56px; align-items: center;
  }
}
@media (max-width: 899px) {
  .hero-iso { margin-bottom: 36px; --iso-k: 0.76; }
}
/* Below 640 the plate is decorative weight the layout cannot afford. */
@media (max-width: 639px) { .hero-iso { display: none; } }
.hero--photo h1 { color: var(--navy-900); }
/* One face across the hero: the sub follows the headline, so the whole block is
   Public Sans rather than two faces stacked. */
.hero--photo .hero-sub {
  /* Blue, a step lighter than the headline, so the pair reads as one voice at
     two weights rather than a navy heading over grey body copy. */
  color: var(--slate-500);
  font-family: var(--font-body); font-weight: 400;
  font-size: 1.1875rem; line-height: 1.55;
}

/* --- Location tab -----------------------------------------------------------
   The pair that straddles the hero/band boundary. SHEET MD-101 hangs into the
   band from its top-right corner, square where it meets the edge and rounded
   below. This is the same tab turned through 180 degrees, sitting on the hero's
   bottom-right corner: rounded above, square where it meets the boundary. Read
   together they are one object cut by the edge between the two sections.
   The offset reproduces .container's own maths — max-width 1160 with 24px of
   inline padding — so the tab lands on the page's content edge, the same 1276
   at 1440 that the headline, the schedule bars and the footer all end on.
   It must be a direct child of .hero--photo, NOT of .hero-inner: .hero-inner is
   itself position:relative, and anchoring to it put the tab at the bottom of the
   copy block rather than on the section's edge, leaving a gap above the join. */
.loc-tab {
  position: absolute; bottom: 0; right: max(24px, calc(50% - 556px)); z-index: 3;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tab-w); height: 32px; padding: 0 15px;
  border-radius: 7px 7px 0 0;
  background-image: linear-gradient(0deg, var(--gold-500) 0%,
                    color-mix(in srgb, var(--gold-500) 88%, #fff) 100%);
  border: 1px solid color-mix(in srgb, var(--gold-600) 70%, var(--navy-900));
  border-bottom: 0;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .34), 0 -2px 6px rgb(11 37 69 / .12);
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--navy-900);
  white-space: nowrap;
}
@media (min-width: 360px)  { .hero--photo { min-height: 560px; } }
@media (min-width: 480px)  { .hero--photo { min-height: 520px; } }
/* From 640 the plate appears and stacks above the copy, so the floor jumps.
   From 900 the two columns sit side by side and it drops back. */
@media (min-width: 640px)  { .hero--photo { min-height: 732px; } }
@media (min-width: 820px)  { .hero--photo { min-height: 740px; } }
@media (min-width: 900px)  { .hero--photo { min-height: 560px; } }
@media (min-width: 1024px) { .hero--photo { min-height: 560px; } }

/* Sheet bar. Replaces the breadcrumb: the way back and the way sideways, on
   one rail between the header and the sheet. */
/* The projects rail runs gold, carrying the same 18px dot field as the rest of
   the site struck in navy, so the pattern reads as one system on every ground. */
/* Sticky: the rail carries the way back and the way sideways, and on a page
   this long it is worth keeping within reach. `top` is the site header's own
   measured height — 73px at every breakpoint — so nothing shows between them.
   z-index sits under the header's 50 so the header always wins. */
.sheet-bar {
  position: sticky; top: 73px; z-index: 15;
  background-color: var(--gold-500);
  background-image: radial-gradient(circle 1.5px at center, var(--grain-dot-gold) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
  border-bottom: 1px solid color-mix(in srgb, var(--navy-900) 34%, transparent);
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .10);
}
.sheet-bar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; padding-block: 12px;
}
.sheet-back {
  font-size: var(--fs-6); font-weight: 600; color: var(--navy-900);
  text-decoration: none; white-space: nowrap;
}
.sheet-back:hover { color: var(--navy-700); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 820px) {
}

.hero-back { display: inline-block; margin-bottom: 20px; font-size: var(--fs-6); font-weight: 600; color: color-mix(in srgb, var(--paper-050) 85%, var(--navy-900)); text-decoration: none; }
.hero-back:hover { color: var(--gold-500); }
.stats--auto { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 821px) { .stats.stats--auto { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } }

/* --------------------------------------------------------------------------
   PROJECT QUANTITY BAND
   A schedule of quantities, not a KPI dashboard. Figures hang off one
   continuous datum the way dimensions hang off a drawing: the datum is the
   label's own top border, so it stays welded to the figure/label boundary at
   every width instead of being positioned by hand, and with zero column gap
   the segments meet into an unbroken rule. Gold appears only as the tick — a
   mark, never as text colour. Columns are sized to their content, so 1,922
   takes the room it needs and 2 does not pretend to need the same.
   -------------------------------------------------------------------------- */
.stat-strip--band {
  --band-pad: 56px;
  position: relative;
  background: var(--navy-700); border-bottom: 0;
  color: var(--paper-000); padding-block: var(--band-pad) 0;
}
/* The band takes the light while its own cells take the dark. Read together
   that is what gives the row depth: the ground lifts toward its top edge, the
   boxes standing on it sink at theirs, and the lit label strip at each box's
   foot comes furthest forward. Same geometry as every other wash on the site.
   The note strip is a static sibling of the container, so it has to be lifted
   too or the wash would lie across it. */
.stat-strip--band::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(140% 60% at 50% 0%,
                    rgb(255 255 255 / .085) 0%,
                    rgb(255 255 255 / .035) 38%,
                    transparent 72%);
}
.stat-strip--band > .container,
.stat-strip--band .stat-note { position: relative; z-index: 1; }
/* The sheet number hangs off the top edge of the band as a file tab: square
   where it meets the edge, rounded where it hangs free. The offset is tied to
   --band-pad, so the tab and the padding cannot drift apart. */
.stat-strip--band > .container { position: relative; }
.sheet-tab {
  /* right:24px, not 0 — the container's own inline padding. right:0 sits on the
     container's padding box (1300 at 1440) while every other element on the
     page sits on its content box (1276). The pair matched each other but not
     the grid. */
  position: absolute; top: calc(-1 * var(--band-pad)); right: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tab-w); height: 32px; padding: 0 15px;
  border-radius: 0 0 7px 7px;
  background-image: linear-gradient(180deg, var(--navy-900) 0%,
                                    color-mix(in srgb, var(--navy-900) 84%, #000) 100%);
  border: 1px solid rgb(0 0 0 / .34); border-top: 0;
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .35), 0 3px 8px rgb(0 0 0 / .22);
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold-500);
  white-space: nowrap;
}
/* "Schedule of quantities" is a tab too, hung from the same top edge at the
   opposite corner — white, so it reads as the hero sheet above carrying down
   into the band, where the navy sheet-tab reads as the band's own hardware. */
.stat-strip--band .qty-head { min-height: 18px; }
/* The head now holds a single span, so it matches :first-child AND :last-child.
   The band's span:last-child rule sits later at equal specificity and would win
   on colour, so both pseudo-classes are named here. */
.stat-strip--band .qty-head span:first-child,
.stat-strip--band .qty-head span:last-child {
  position: absolute; top: calc(-1 * var(--band-pad)); left: 0;
  display: inline-flex; align-items: center; height: 32px; padding: 0 15px;
  border-radius: 0 0 7px 7px;
  background-image: linear-gradient(180deg, var(--paper-000) 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300); border-top: 0;
  box-shadow: inset 0 -1px 0 rgb(11 37 69 / .10), 0 3px 8px rgb(0 0 0 / .20);
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--navy-900);
  white-space: nowrap;
}
/* Below 560 the two tabs would collide, so the schedule label drops back
   in-flow and only the sheet number stays a tab. */
@media (max-width: 560px) {
  .stat-strip--band .qty-head { min-height: 0; }
  .stat-strip--band .qty-head span:first-child,
  .stat-strip--band .qty-head span:last-child {
    position: static; height: auto; padding: 0; border: 0; border-radius: 0;
    background-image: none; box-shadow: none;
    font-family: var(--font-body); font-size: 0.6875rem; letter-spacing: 0.14em;
    color: color-mix(in srgb, var(--paper-050) 64%, transparent);
  }
}
.stat-strip--band .qty-head { color: color-mix(in srgb, var(--paper-050) 64%, transparent); }
/* removed: the sheet number left .qty-head when it became a tab, so this rule
   only reached the schedule label and overrode its tab colour. */
/* No rule under the schedule label. The cornice on the panels below already
   draws that horizontal edge, so the gold hairline was a second line saying
   the same thing 26px higher — and it was the only warm mark in the band
   that did not belong to a figure. The division the eye actually wants here
   is vertical, between the quantities; that is drawn inside the panels. */
.stat-strip--band .qty-head::after { content: none; }
.stat-strip--band .stats { gap: 0; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 821px) {
  .stat-strip--band .stats.stats--auto { grid-template-columns: repeat(5, 1fr); }
  /* Two panels, not one framed and one bare. The five quantities are two kinds
     of fact — units, buildings and residents describe what stands on the site;
     cost and duration describe what it took to get there — so each gets its own
     wall fragment with a 44px gutter between them.

     Each panel is built like a piece of facade: a projecting CORNICE across the
     top (the heavier top border, with an inset shadow beneath it so the ledge
     reads as casting onto the wall below), the wall itself, and a BASE COURSE
     at the foot. That is the "3D shelf" — it is what a cornice actually is.
     Drawn with the two .stats pseudo-elements, so no markup or JS changes. */
  .stat-strip--band .stats { position: relative; }
  .stat-strip--band .stats--auto::before,
  .stat-strip--band .stats--auto::after,
  .stat-strip--band .stats--one::before {
    content: ""; position: absolute; z-index: 0; pointer-events: none;
    /* -14px, not -22: at 1440 the container box is 140-1300 and the content
       box 164-1276, so -22 put the frame at 142 and 1298 — two pixels off the
       container edge, which read as spilling out. -14 keeps both frames inside
       the container with 10px to spare and still clears the figures. */
    /* bottom:0 — the label plate is the last thing in every cell and now runs
       flush to the panel's foot, so any inset here would show as a gap under
       the bar. */
    top: -26px; bottom: 0; border-radius: 2px;
    background-color: color-mix(in srgb, var(--paper-050) 4%, transparent);
    border: 1px solid color-mix(in srgb, var(--paper-050) 16%, transparent);
    /* cornice above, base course below */
    border-top: 4px solid color-mix(in srgb, var(--paper-050) 34%, transparent);
    border-bottom: 3px solid color-mix(in srgb, var(--navy-900) 70%, #000);
    box-shadow:
      inset 0 7px 9px -7px rgb(0 0 0 / .6),
      inset 0 -6px 8px -6px rgb(0 0 0 / .35),
      0 8px 20px rgb(0 0 0 / .2);
  }
  /* what stands on the site */
  /* Frames align to the container's content box; the CONTENT is inset instead.
     Previously the frame overhung 14px on the left and, worse, panel 2 began
     22px to the RIGHT of where its first cell's content started — so $113MM and
     its label sat outside their own box. Measured at 1440: content box 164-1276,
     cell 4 starts 831, panel 2 border was at 853. */
  /* Column separators: a scored groove between each pair of quantities,
     running the full height of the panel from cornice to base course. They are
     background layers on the panel itself, so they cost no markup and sit
     under the figures and the label plates.

     The stops look arbitrary and are not. Each panel is positioned off the
     .stats box (width S) but a gradient's percentages resolve against the
     PANEL's own width, so a plain 1/3 would miss the column edge by the
     panel's inset. Panel 1 spans 0 -> .6S - 14, and the boundaries to hit are
     at .2S and .4S; solving for the panel's own width W gives W/3 + 14/3 and
     2W/3 + 28/3 — hence the pixel terms. Panel 2 spans .6S + 14 -> S with one
     boundary at .8S, which lands at W/2 - 7. Exact at every viewport, so the
     grooves line up with the gaps between the label plates below them. */
  .stat-strip--band .stats--auto::before,
  .stat-strip--band .stats--auto::after,
  .stat-strip--band .stats--one::before { --sep-d: rgb(3 14 30 / .55); --sep-l: rgb(255 255 255 / .09); }
  .stat-strip--band .stats--auto::before {
    left: 0; right: calc(40% + 14px);
    background-image:
      linear-gradient(90deg, transparent calc(100%/3 + 3.667px), var(--sep-d) 0 calc(100%/3 + 4.667px), var(--sep-l) 0 calc(100%/3 + 5.667px), transparent 0),
      linear-gradient(90deg, transparent calc(200%/3 + 8.333px), var(--sep-d) 0 calc(200%/3 + 9.333px), var(--sep-l) 0 calc(200%/3 + 10.333px), transparent 0);
  }
  /* what it took to get there — recessed a step, so it reads as secondary */
  .stat-strip--band .stats--auto::after {
    left: calc(60% + 14px); right: 0;
    background-color: color-mix(in srgb, var(--navy-900) 26%, transparent);
    background-image:
      linear-gradient(90deg, transparent calc(50% - 8px), var(--sep-d) 0 calc(50% - 7px), var(--sep-l) 0 calc(50% - 6px), transparent 0);
    border-top-color: color-mix(in srgb, var(--paper-050) 22%, transparent);
  }
  /* 22px of clearance between every frame and the content inside it. */
  .stat-strip--band .stat::before,
  .stat-strip--band .stat-num,
  .stat-strip--band .stat-label,
  .stat-strip--band .stat-sub { margin-left: 22px; }
  /* The fourth cell opens the second panel, so it clears that frame by the same
     22px; the third cell's datum stops short so the rule breaks at the gutter
     instead of running across it. */
  .stat-strip--band .stats--auto .stat:nth-child(4)::before,
  .stat-strip--band .stats--auto .stat:nth-child(4) .stat-num,
  .stat-strip--band .stats--auto .stat:nth-child(4) .stat-sub { margin-left: 36px; }
  /* The label is a filled bar, so its inset must be padding: a margin would
     leave gaps and break the bar between cells. Cell 4 starts 14px in, clearing
     the second panel's border, then pads 22px to line its text up with the
     figure above. Cell 3 stops 14px short so the bar breaks at the gutter. */
  .stat-strip--band .stat-label { margin-left: 0; }
  /* Rounded like every other label — but only at the ends of each panel's bar.
     Rounding every cell would scallop the joins between them. */
  .stat-strip--band .stats--auto .stat:nth-child(1) .stat-label { border-radius: 6px 0 0 6px; }
  .stat-strip--band .stats--auto .stat:nth-child(3) .stat-label { border-radius: 0 6px 6px 0; }
  .stat-strip--band .stats--auto .stat:nth-child(4) .stat-label { border-radius: 6px 0 0 6px; }
  .stat-strip--band .stats--auto .stat:nth-child(5) .stat-label { border-radius: 0 6px 6px 0; }
  .stat-strip--band .stats--auto .stat:nth-child(4) .stat-label { margin-left: 14px; }
  .stat-strip--band .stats--auto .stat:nth-child(3) .stat-label { margin-right: 14px; }
  /* display:contents dissolves the cell so the icon, the figure and the label
     become grid items in their own right. Three explicit rows then give every
     label ONE shared track height — a min-height could not, because it is a
     floor: a wrapping label grew past it and the bars ran 58/58/58/78/64 with
     only the tallest reaching the panel foot. Column flow keeps each project's
     three parts stacked in its own column. */
  .stat-strip--band .stats.stats--auto {
    /* minmax(0,1fr), not 1fr: a 1fr track has an automatic minimum, so the long
       TOTAL DEVELOPMENT COST label pushed its column wider than the others and
       the bars stopped lining up with the panel edges — 12px out at 900.
       align-items:stretch because the base .stats rule sets `start`, which
       overrides grid's default and left the bars at their content height. */
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: auto auto 1fr;
    grid-auto-flow: column;
    align-items: stretch;
  }
  .stat-strip--band .stat { display: contents; }
  .stat-strip--band .stat::before,
  .stat-strip--band .stat-num,
  .stat-strip--band .stat-label { position: relative; z-index: 1; }
  /* The label track is 1fr and grid items stretch by default, so every bar
     fills it and they all end level with the panel's foot regardless of how
     many lines the text takes. No explicit height: 100% plus the 16px top
     margin overflowed the track by exactly that margin. */
  .stat-strip--band .stat-label { min-height: 0; }

  /* --- single-panel bars ---------------------------------------------------
     Seventeen of the twenty-two bars carry one kind of fact, so they take one
     frame rather than the schedule's two. The frame spans the .stats box
     exactly, and that is what makes the separators simple: the +3.667/+8.333px
     corrections above exist only because those two panels are INSET from the
     box the columns are measured against. Here the gradient percentages and the
     column edges resolve against the same width, so plain fractions land true.
     -------------------------------------------------------------------------- */
  .stat-strip--band .stats--one {
    grid-template-columns: repeat(var(--n), minmax(0, 1fr));
    grid-template-rows: auto auto 1fr;
    grid-auto-flow: column;
    align-items: stretch;
  }
  .stat-strip--band .stats--n3 { --n: 3; }
  /* The schedule's figures are numerals — 3,078, 40, $854MM — and 40px suits
     them. These bars carry words too, and measured at 1440 the column gives
     232px: "New York City" needs 269px at 40, "Before budget lock" 354, and
     "Pre-award > stabilization" 490. Sizing for the worst of them would put
     every bar at 19px and throw away what makes the band read. So the figure
     steps down to 30 — which fits the common case on one line — and the few
     long phrases wrap to two, at a space. overflow-wrap is left alone on
     purpose: it is what broke "MD-201-206" and the email address mid-token. */
  .stat-strip--band .stats--one .stat-num {
    font-size: clamp(1.5rem, 2.2vw, 1.875rem); line-height: 1.14;
  }
  .stat-strip--band .stats--n4 { --n: 4; }
  .stat-strip--band .stats--one::before { left: 0; right: 0; }
  .stat-strip--band .stats--n4::before { background-image:
      linear-gradient(90deg, transparent 25%, var(--sep-d) 0 calc(25% + 1px), var(--sep-l) 0 calc(25% + 2px), transparent 0),
      linear-gradient(90deg, transparent 50%, var(--sep-d) 0 calc(50% + 1px), var(--sep-l) 0 calc(50% + 2px), transparent 0),
      linear-gradient(90deg, transparent 75%, var(--sep-d) 0 calc(75% + 1px), var(--sep-l) 0 calc(75% + 2px), transparent 0); }
  .stat-strip--band .stats--n3::before { background-image:
      linear-gradient(90deg, transparent 33.3333%, var(--sep-d) 0 calc(33.3333% + 1px), var(--sep-l) 0 calc(33.3333% + 2px), transparent 0),
      linear-gradient(90deg, transparent 66.6667%, var(--sep-d) 0 calc(66.6667% + 1px), var(--sep-l) 0 calc(66.6667% + 2px), transparent 0); }
  /* Column flow fills three explicit rows before moving on, and a cell supplies
     three items only when it carries a mark: the ::before, the figure, the
     label. MD-003's figures have no mark, so its six items wrapped mid-column
     and its three labels landed on three different rows. A zero-height stand-in
     keeps every column three items tall; the row collapses to nothing because
     no cell in that bar has a mark. position/background are reset off the base
     .stat::before, which is the old absolutely-positioned scale-mark stem. */
  .stat-strip--band .stats--one .stat:not([data-i])::before {
    content: ""; position: static; display: block;
    width: auto; height: 0; margin: 0; background: none;
  }
  /* rounded only at the ends of the one bar, as on each panel of the schedule */
  .stat-strip--band .stats--one .stat:first-child .stat-label { border-radius: 6px 0 0 6px; }
  .stat-strip--band .stats--one .stat:last-child  .stat-label { border-radius: 0 6px 6px 0; }
}
/* Cells sit flush so the label borders meet into one rule; the breathing room
   is padding INSIDE each label, which the border-box still spans. Content-sized
   columns broke the datum into five separate underlines — the thing that makes
   this read as a schedule is that the rule is continuous. */
.stat-strip--band .stat { padding: 0; }
/* the per-cell scale mark is retired — the datum below does that job now */
/* the per-cell scale mark is retired; the datum below does that job */
.stat-strip--band .stat::after { content: none; }

/* Isometric marks for the schedule. Generated from PL-300..305, keyed off a
   pure-white ground at tolerance 5, trapped ground removed with a global
   near-white cut, then normalised into one 104px box and bottom-aligned so all
   five stand on a shared ground line. */
/* Keyed on [data-i], which main.js writes. If the script is stale or disabled
   the attribute is absent, no box is reserved, and the schedule simply has no
   marks — rather than a column of empty holes where the icons should be.
   position and background-color are reset explicitly: the base .stat::before is
   the old absolutely-positioned scale-mark stem, and without these the mark
   inherits its box and lands on top of the figure. */
.stat-strip--band .stat[data-i]::before {
  content: ""; position: static; background-color: transparent;
  display: block; width: 52px; height: 52px; margin-bottom: 14px;
  /* drop-shadow traces the icon's own alpha, so the cast follows the isometric
     silhouette instead of boxing it. Offset down and right, matching the light
     the plates and tabs are drawn under. */
  filter: drop-shadow(2px 3px 3px rgb(0 0 0 / .34));
  background-repeat: no-repeat; background-position: left center; background-size: contain;
}
.stat-strip--band .stat[data-i="units"]::before { background-image: url("../assets/img/ic-units.webp"); }
.stat-strip--band .stat[data-i="buildings"]::before { background-image: url("../assets/img/ic-buildings.webp"); }
.stat-strip--band .stat[data-i="residents"]::before { background-image: url("../assets/img/ic-residents.webp"); }
.stat-strip--band .stat[data-i="total"]::before { background-image: url("../assets/img/ic-total.webp"); }
.stat-strip--band .stat[data-i="on"]::before { background-image: url("../assets/img/ic-on.webp"); }
.stat-strip--band .stat[data-i="sheet"]::before { background-image: url("../assets/img/ic-sheet.webp"); }
.stat-strip--band .stat[data-i="stage"]::before { background-image: url("../assets/img/ic-stage.webp"); }
.stat-strip--band .stat[data-i="triggers"]::before { background-image: url("../assets/img/ic-triggers.webp"); }
.stat-strip--band .stat[data-i="market"]::before { background-image: url("../assets/img/ic-market.webp"); }
@media (max-width: 820px) {
  .stat-strip--band .stat[data-i]::before { width: 42px; height: 42px; margin-bottom: 10px; }
}

.stat-strip--band .stat-num a { color: inherit; text-decoration: none; }
.stat-strip--band .stat-num a:hover,
.stat-strip--band .stat-num a:focus-visible {
  color: var(--gold-500); text-decoration: underline; text-underline-offset: 4px;
}
/* MD-003's three figures are an email address, a telephone number and a profile
   name. At the schedule's 40px the address is far wider than its column and
   breaks mid-word, so this bar sets its figures smaller — same face, same
   frame, same datum. */
[data-page="contact"] .stat-strip--band .stats--one .stat-num {
  /* Measured: the address needs 349px at 22px in a 325px column, so it fits at
     20 and under. overflow-wrap stays as a safety valve for narrow viewports —
     an address that cannot fit is better broken than overflowing the bar. */
  font-size: clamp(1rem, 1.3vw, 1.1875rem); line-height: 1.32;
  overflow-wrap: anywhere;
}
.stat-strip--band .stat-num {
  color: var(--paper-000); padding-right: 24px;
  /* standalone display figures: tabular widths make 2 and 1,922 sit loose */
  font-variant-numeric: proportional-nums lining;
}
/* The label keeps carrying the datum — it is the label's own top border and the
   rule must stay continuous across the cell — so the plate is the inner <b>
   rather than the label itself. */
.stat-strip--band .stat-label {
  /* Flex with a floor, not a block: TOTAL DEVELOPMENT COST wraps to two lines
     and every other label is one, so without a common height the plate steps
     between cells instead of running level. 58px clears two lines at 11px. */
  /* flex-wrap, so at 320 the date drops to a second line inside the same plate
     rather than forcing the row wider than the viewport — that was +7px of
     overflow on all four project pages. */
  position: relative; display: flex; align-items: center; gap: 4px 14px;
  flex-wrap: wrap; min-height: 58px; box-sizing: border-box;
  margin: 16px 0 0; padding: 11px 22px;
  /* One wide plate running the width of each panel, in the same solid language
     as the tabs: lit top edge, flat face, its own shade standing beneath. The
     face is translucent, so the dot field and the plan ground read faintly
     through it. No backdrop-filter — that composites differently across
     browsers and this must be dependable. */
  background-image: linear-gradient(180deg,
    color-mix(in srgb, #fff 94%, transparent) 0%,
    color-mix(in srgb, var(--paper-050) 84%, transparent) 100%);
  border-top: 1px solid color-mix(in srgb, #fff 70%, transparent);
  border-bottom: 2px solid color-mix(in srgb, var(--navy-900) 46%, transparent);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .9),
    inset 0 -1px 0 rgb(11 37 69 / .12),
    0 3px 7px rgb(0 0 0 / .24);
  color: var(--navy-900);
}
.stat-strip--band .stat-label b {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--navy-900);
}
/* The date range rides in the same plate as its label, pushed to the right by
   auto margin, so ON SITE and its span read as one box rather than a label with
   something loose underneath. */
.stat-strip--band .stat-label i {
  margin-left: auto; font-style: normal; white-space: nowrap;
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.04em; color: var(--slate-500);
  font-variant-numeric: tabular-nums;
}
/* The column tick sits on the plate now, so it reads as a marker cut into the
   bar rather than a tick hanging off a rule. */
.stat-strip--band .stat-label::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; background: var(--gold-500);
}
.stat-strip--band .stat-sub { padding-right: 24px; color: color-mix(in srgb, var(--paper-050) 58%, transparent); }
/* Title block. A machined bar rather than a flat strip: a light catch along
   the top edge, a shade along the bottom, a shallow gradient down the face and
   a soft drop under it, so it reads as a physical rail carrying the sheet's
   provenance line and its navigation. */
.stat-strip--band .stat-note {
  margin-top: 48px; padding-block: 16px;
  background-color: var(--navy-900);
  border-top: 1px solid rgb(0 0 0 / .38);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--paper-050) 15%, transparent),
    inset 0 -1px 0 rgb(0 0 0 / .45),
    0 -10px 22px rgb(0 0 0 / .16);
}
.stat-note-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
}
.stat-strip--band .stat-footnote { text-shadow: 0 1px 2px rgb(0 0 0 / .38); }

/* Sheet navigation. Navy hardware on the gold rail — the reference's bevel
   inverted: light catch on top, black shade beneath, hairline edge. */
.sheet-nav { display: flex; gap: 8px; flex-shrink: 0; }
.sheet-btn {
  display: inline-flex; flex-direction: column; gap: 1px;
  padding: 7px 14px; border-radius: 3px; text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--navy-900) 62%, #000);
  background-image: linear-gradient(175deg, var(--navy-700) 0%, var(--navy-900) 100%);
  /* Same solid: lit top edge, 2px of its own shade beneath, faint cast. */
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--paper-050) 17%, transparent),
    0 2px 0 0 color-mix(in srgb, var(--navy-900) 55%, #000),
    0 3px 7px rgb(0 0 0 / .26);
  transition: transform 130ms ease, box-shadow 130ms ease;
}
.sheet-btn:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--paper-050) 24%, transparent),
    0 3px 0 0 color-mix(in srgb, var(--navy-900) 55%, #000),
    0 6px 14px rgb(0 0 0 / .3);
}
.sheet-btn:active {
  transform: translateY(2px);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--paper-050) 17%, transparent),
    0 0 0 0 color-mix(in srgb, var(--navy-900) 55%, #000),
    0 1px 2px rgb(0 0 0 / .26);
}
@media (prefers-reduced-motion: reduce) {
  .sheet-btn, .sheet-btn:hover, .sheet-btn:active { transform: none; }
}
.sheet-btn:focus-visible { outline: 3px solid var(--navy-900); outline-offset: 2px; }
.sheet-btn-dir {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-050) 84%, transparent);
}
.sheet-btn-id {
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.06em; color: var(--gold-500);
}
.sheet-btn--prev .sheet-btn-dir::before { content: "\2190\00a0"; }
.sheet-btn--next .sheet-btn-dir::after  { content: "\00a0\2192"; }
.sheet-btn--next { text-align: right; }
@media (max-width: 700px) {
  .stat-note-inner { gap: 20px; }
  .sheet-nav { width: 100%; }
  .sheet-btn { flex: 1 1 0; }
}
.stat-strip--band .stat-footnote {
  margin: 0; padding-left: 0; max-width: 78ch;
  color: color-mix(in srgb, var(--paper-050) 60%, transparent);
}
@media (max-width: 820px) {
  .stat-strip--band .stats { row-gap: 32px; }
  .stat-strip--band .stat-num,
  .stat-strip--band .stat-label { padding-right: 20px; }
}
.proj-nav { display: flex; justify-content: space-between; gap: 16px; border-top: 1px solid var(--line-200); padding-top: 28px; }
.proj-nav a { font-weight: 600; text-decoration: none; }
.proj-nav .proj-nav-dir { display: block; font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-500); margin-bottom: 4px; }
.proj-nav a:hover .proj-nav-dir { color: var(--slate-500); }

/* On a project sheet the MD-001 map recedes except for this project's pylon. */
.map-sheet[data-focus] .pylon-link, .map-sheet[data-focus] .md-callout { opacity: .3; }
.map-sheet[data-focus="A"] .pylon-link[data-pylon="A"], .map-sheet[data-focus="A"] .md-callout--A,
.map-sheet[data-focus="B"] .pylon-link[data-pylon="B"], .map-sheet[data-focus="B"] .md-callout--B,
.map-sheet[data-focus="C"] .pylon-link[data-pylon="C"], .map-sheet[data-focus="C"] .md-callout--C,
.map-sheet[data-focus="D"] .pylon-link[data-pylon="D"], .map-sheet[data-focus="D"] .md-callout--D { opacity: 1; }
.map-sheet[data-focus="A"] .pylon-link[data-pylon="A"] .pyl-halo,
.map-sheet[data-focus="B"] .pylon-link[data-pylon="B"] .pyl-halo,
.map-sheet[data-focus="C"] .pylon-link[data-pylon="C"] .pyl-halo,
.map-sheet[data-focus="D"] .pylon-link[data-pylon="D"] .pyl-halo { stroke-opacity: 1; }
/* --------------------------------------------------------------------------
   SHEET DETAILS (project entry pages)
   Rewritten as schedules, not cards. The earlier version stacked five levels of
   containment and encoded the same fact three ways; this version removes the
   card chrome, the badges, the tint blocks and the legends, and lets alignment
   and type carry the structure. One rule per group, one encoding per fact.
   -------------------------------------------------------------------------- */
.detail { padding: 0; margin-top: 72px; background: none; border: 0; box-shadow: none; }
.detail:first-of-type { margin-top: 0; }
.detail h3 {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  font-family: var(--font-display); font-weight: 500; font-size: var(--fs-3);
  margin: 0 0 6px; padding-bottom: 0; border: 0;
}
.detail h3::after { content: none; }
.detail-no {
  font-family: var(--font-body); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.12em; color: var(--slate-500); flex: none;
}
.detail-lead { margin: 0 0 24px; font-size: var(--fs-6); color: var(--ink-500); max-width: 68ch; }
.detail-lead strong { color: var(--ink-900); }

/* ---- Plates: work packages (.1) and capital structure (.2) ----------------
   These two carry the substance of a project sheet — what was actually done,
   and what paid for it — and they were reading as loose lists in open space.
   Each now sits in its own plate, built the way the rest of the drawing set is
   built: a title block across the top with the gold tick, a body on paper, a
   lit top edge and a base course under it so the plate has thickness. Same
   engraved ground as the hero, pinned to the same 1268px tile, fading out
   downward so it is depth rather than pattern. */
.detail.plate {
  position: relative; overflow: hidden;
  margin-top: 40px; padding: 0;
  /* The plate matches the width of .section--sheet above it, not the text
     column. Measured at 1440: the sheet spans 139-1301 (1162 wide) while the
     container's content box is 164-1276, so the plate has to clear the
     container's 24px inline padding AND its own 1px border on each side.
     .container's padding is a flat 24px at every width, so one value serves. */
  margin-inline: -25px;
  background: var(--paper-000);
  border: 1px solid var(--line-200);
  border-bottom: 3px solid var(--line-300);
  border-radius: 8px;
  box-shadow: inset 0 1px 0 #fff, 0 1px 0 var(--line-100),
              0 14px 34px rgb(11 37 69 / .10);
}
.detail.plate:first-of-type { margin-top: 0; }
/* ...unless a card grid precedes it, in which case it is not the first thing
   in the section and needs the same 40px it would get after another plate. */
.mode-grid + .detail.plate:first-of-type { margin-top: 40px; }
/* 72px is the gap between two plain details; a plate above one was 64 for no
   reason a reader can see. One value now separates a borderless detail from
   whatever sits above it. */
.section--details .detail.plate + .detail:not(.plate) { margin-top: 72px; }
.detail.plate::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-size: 1268px auto; background-repeat: repeat; background-position: center top;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, transparent 62%);
  mask-image: linear-gradient(to bottom, #000 0, transparent 62%);
  opacity: .07;
}
[data-project-page="A"] .detail.plate::before { background-image: url("../assets/img/hero-ground-a.webp"); }
[data-project-page="B"] .detail.plate::before { background-image: url("../assets/img/hero-ground-b.webp"); }
[data-project-page="C"] .detail.plate::before { background-image: url("../assets/img/hero-ground-c.webp"); }
[data-project-page="D"] .detail.plate::before { background-image: url("../assets/img/hero-ground-d.webp"); }

/* The title block is drawn as the sheet tab — SHEET MD-104 on the blue band —
   rather than as a pale bar: navy face shaded to its own darker tone, gold mono
   set in caps. No gold tick here; on the tab the whole label is the accent. */
.detail.plate > h3 {
  position: relative; z-index: 1;
  /* 24px, matching .container's own inline padding: the plate's content box now
     starts where the container's padding box starts, so a 24px gutter lands the
     label on the same 164 the rest of the page is set to at 1440. */
  align-items: center; margin: 0; padding: 13px 24px;
  font-family: var(--font-mono); font-weight: 600; font-size: 0.8125rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold-500);
  background-image: linear-gradient(180deg, var(--navy-900) 0%,
                    color-mix(in srgb, var(--navy-900) 84%, #000) 100%);
  border-bottom: 1px solid rgb(0 0 0 / .34);
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .35), inset 0 1px 0 rgb(255 255 255 / .08);
}
.detail.plate > h3 .detail-no {
  font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.06em;
  color: color-mix(in srgb, var(--paper-050) 62%, transparent);
  font-variant-numeric: tabular-nums;
}
/* body: everything after the title block shares the plate's inner gutter, and
   it is the same 24px, so the lead and the rows start on the page grid too */
.detail.plate > *:not(h3) { position: relative; z-index: 1; padding-inline: 24px; }
.detail.plate > h3 + * { padding-top: 22px; }
.detail.plate > *:last-child { padding-bottom: 24px; }
/* rows sit on the plate, so they need a touch more separation than on bare paper */
.detail.plate .wp-row, .detail.plate .cs-row { background: var(--paper-050); }
.detail.plate .tier-note { margin-top: 14px; }

/* Framing prose — projects.html, approach.html. Set at reading size, not
   label size: these paragraphs are argument, not caption. */
.proj-intro p { font-size: var(--fs-5); color: var(--ink-500); max-width: 66ch; }
.proj-intro p strong { color: var(--ink-900); }
.need-list { list-style: none; margin: 0 0 20px; padding: 0; max-width: 66ch; }
.need-list li { padding: 16px 0; border-top: 1px solid var(--line-200); color: var(--ink-500); max-width: none; }
.need-list li:last-child { border-bottom: 1px solid var(--line-200); }
.need-list strong { color: var(--ink-900); display: block; margin-bottom: 2px; }
.need-note { font-size: var(--fs-6); color: var(--ink-500); max-width: 60ch; }

/* .0 — the claim each sheet supports, stated before the evidence. */
.proves-head {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.2rem, 2.2vw, 1.5rem); line-height: 1.32;
  color: var(--ink-900); margin: 0 0 12px; max-width: 40ch;
}
.proves-body { font-size: var(--fs-6); color: var(--ink-500); max-width: 64ch; margin: 0; }

/* .1 — trade in the left column, its lines in the right. */
/* Work packages and capital structure are the two list components on the
   sheet, and they were the last things still drawn as bare rules. They now take
   the page's own language: a light surface on the paper ground, a hairline
   edge, rounded like the schedule bars, and the trade name carried on a small
   plate with the gold tick that marks a column everywhere else. */
.wp-row, .cs-row {
  display: grid; gap: 10px 28px; padding: 16px 18px; margin-bottom: 8px;
  background: var(--surface-2);
  border: 1px solid var(--line-100); border-radius: 6px;
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
@media (min-width: 641px) { .wp-row, .cs-row { grid-template-columns: 186px 1fr; gap: 0 28px; align-items: start; } }
.wp-row h4 { display: flex; align-items: center; gap: 10px; margin: 0; }
/* the trade name as a plate, in the same solid language as the schedule labels */
.wp-row h4 { 
  position: relative; align-self: start;
  padding: 6px 10px 6px 14px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900);
}
.wp-row h4::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
.wp-row h4 span {
  margin-left: auto; padding-left: 8px;
  font-family: var(--font-mono); font-size: 0.75rem; color: var(--slate-500);
  font-variant-numeric: tabular-nums;
}
.wp-row ul { list-style: none; margin: 0; padding: 0; }
.wp-row li { margin: 0 0 7px; max-width: 62ch; color: var(--ink-900); }
.wp-row li:last-child { margin-bottom: 0; }
/* The one marked state on the page: work that enters an occupied apartment.
   The lead says two lines are marked, so they are marked — a gold wash across
   the whole line, not just a dot beside it. Occupied rehab is the subject of
   this practice; on a scope list it is the distinction worth seeing first. */
.wp-row li.u {
  position: relative; margin-left: -10px; padding: 2px 0 2px 26px;
  border-radius: 3px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--gold-500) 15%, transparent) 0%,
    color-mix(in srgb, var(--gold-500) 4%, transparent) 62%,
    transparent 100%);
}
.wp-row li.u::before {
  content: ""; position: absolute; left: 10px; top: 12px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--gold-500);
}

/* .2 — label and source on one baseline, priority order top to bottom. */
/* Priority reads down the page, so the tier plate lightens as it goes: senior
   carries the gold tick, the tier under it slate, everything below that the
   hairline. Written as a default plus two overrides rather than a fixed ladder
   — project A runs four tiers (a state energy grant sits between sub and
   equity) and a three-step nth-child list left its last tick unpainted. */
.cs-lab::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px;
  background: var(--line-300);
}
.cs-row:first-child .cs-lab::before { background: var(--gold-500); }
.cs-row:nth-child(2) .cs-lab::before { background: var(--slate-500); }
.cs-lab {
  position: relative; align-self: start; justify-self: start;
  padding: 6px 12px 6px 14px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900);
}
.cs-src { color: var(--ink-900); max-width: 62ch; }
.wp-row:last-child, .cs-row:last-child { border-bottom: 1px solid var(--line-200); }
.tier-note { margin: 16px 0 0; font-size: 0.75rem; color: var(--ink-500); }

/* .3 — one line, two directly-labelled marks; the gap between them is the point. */
.chrono { display: block; width: 100%; height: auto; margin-top: 4px; }
.ch-axis { stroke: var(--line-200); }
.ch-era { fill: var(--slate-500); opacity: .5; }
.ch-work { fill: var(--gold-500); }
.ch-lbl {
  font-family: var(--font-body); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; fill: var(--ink-500);
}
.ch-lbl--w { fill: var(--gold-600); }
.chrono .ta-e { text-anchor: end; }

/* .4 — the only first-person voice on the site. */
.op-note {
  margin: 0; padding: 4px 0 0 24px; position: relative;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(1.15rem, 2.1vw, 1.45rem); line-height: 1.42;
  color: var(--ink-900); max-width: 46ch;
}
.op-note::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 2px; background: var(--gold-500); }
.op-note cite {
  display: block; margin-top: 16px;
  font-family: var(--font-body); font-style: normal;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--slate-500);
}
.op-terms { margin: 24px 0 0; padding-top: 16px; position: relative; font-size: 0.8125rem; color: var(--ink-500); max-width: 74ch; }
.op-terms::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: var(--line-200); }

/* --------------------------------------------------------------------------
   12. PORTFOLIO MAP — "Drawing No. MD-001"
   Drafting-sheet aesthetic; fully legible with JS disabled. The sheet lies
   flat on an engineer's graph-paper mat that echoes its own 24/96 graticule;
   the borough plates give a paper-thickness shadow, nothing is angled.
   -------------------------------------------------------------------------- */
.map-wrap {
  position: relative; margin-bottom: 56px;
  padding: 32px;
  border: 1px solid var(--line-200);
  border-radius: 4px;
  background-color: var(--paper-000);
  background-image:
    linear-gradient(var(--line-200) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-200) 1px, transparent 1px),
    linear-gradient(color-mix(in srgb, var(--line-200) 45%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--line-200) 45%, transparent) 1px, transparent 1px);
  background-size: 96px 96px, 96px 96px, 24px 24px, 24px 24px;
}
@media (max-width: 640px) { .map-wrap { padding: 10px; } }
.map-sheet {
  position: relative;
  background: var(--navy-900);
  border-radius: 2px;
  min-height: 300px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .06);
}
@media (min-width: 821px) { .map-sheet { min-height: 420px; } }
/* Double drawing border */
.map-sheet::before {
  content: ""; position: absolute; inset: 8px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--paper-000) 25%, transparent);
}
.map-sheet::after {
  content: ""; position: absolute; inset: 14px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--paper-000) 12%, transparent);
}
.map-svg { display: block; width: 100%; height: auto; }
.map-svg text { font-family: var(--font-body); }

.md-gridline { stroke: var(--paper-000); stroke-opacity: 0.06; }
.md-gridline-major { stroke: var(--paper-000); stroke-opacity: 0.1; }
.md-meridian { stroke: var(--gold-500); stroke-width: 2; }
.md-hatchline { stroke: var(--paper-000); stroke-opacity: 0.05; }
.md-plate { fill: var(--navy-700); }
.md-fill { fill: var(--paper-000); fill-opacity: 0.04; }
/* Open space. Drawn under the borough strokes so the coastline stays the
   dominant line; unlabelled, so it reads as terrain, not as a landmark. */
.md-park { fill: var(--paper-000); fill-opacity: 0.055; stroke: none; }
/* Regional context — the mainland and Long Island beyond the city line. */
.md-land {
  fill: var(--paper-000); fill-opacity: 0.022;
  stroke: var(--paper-000); stroke-opacity: 0.13; stroke-width: 1;
  fill-rule: nonzero; /* neighbouring counties overlap; fill once, not twice */
  stroke-linejoin: round;
}
/* Water lining — three concentric bands off the coast, widest and faintest
   outermost, so the land edge reads as shelving into deeper water. */
/* Round joins are not decoration here. At stroke-width 15 a miter join on a
   sharp coastal point extends to miter-limit x half-width — every inlet and
   pier head fired a starburst out into the water. Round caps the join at the
   stroke radius, which is also what a real chart's shore lining does. */
.md-shore use { fill: none; stroke: var(--paper-000); stroke-linejoin: round; stroke-linecap: round; }
.md-shore .sh-3 use { stroke-width: 15; stroke-opacity: 0.016; }
.md-shore .sh-2 use { stroke-width: 9;  stroke-opacity: 0.030; }
.md-shore .sh-1 use { stroke-width: 4;  stroke-opacity: 0.052; }
.md-hatchfill { fill: url(#md-hatch); }
.md-line { fill: none; stroke: var(--paper-000); stroke-opacity: 0.85; stroke-width: 1.25px; stroke-linejoin: round; }
.md-ctx { fill: none; stroke: var(--paper-000); stroke-opacity: 0.35; stroke-width: 1px; stroke-linejoin: round; }
.md-label { fill: var(--paper-000); fill-opacity: 0.4; font-size: 9px; font-weight: 600; letter-spacing: 2px; }
.md-tick { stroke: var(--paper-000); stroke-opacity: 0.15; }
.md-furniture text { fill: var(--paper-000); font-weight: 600; letter-spacing: 1.2px; }
.md-north circle { fill: none; stroke: var(--paper-000); stroke-opacity: 0.4; }
.md-north line { stroke: var(--gold-500); stroke-width: 1.5; }
.md-north text { font-size: 10px; }
.tb-frame { fill: var(--navy-900); stroke: var(--paper-000); stroke-opacity: 0.35; }
.map-svg .md-water { fill: var(--paper-000); fill-opacity: 0.35; font-size: 8px; letter-spacing: 3px; font-style: italic; font-weight: 400; }
.map-svg .md-ref { fill: var(--paper-000); fill-opacity: 0.3; font-size: 8px; text-anchor: middle; }
.map-svg .md-legend-title { fill: var(--paper-000); fill-opacity: 0.7; font-size: 9px; letter-spacing: 2px; }
.map-svg .md-legend-text { fill: var(--paper-000); fill-opacity: 0.55; font-size: 8px; letter-spacing: 1px; }
.md-ctx-sample { stroke: var(--paper-000); stroke-opacity: 0.35; }
.md-anim .md-legend { opacity: 0; }
.md-drawn .md-legend { opacity: 1; transition: opacity 300ms ease 1750ms; }
.tb-line { stroke: var(--paper-000); stroke-opacity: 0.2; }
.tb-text { font-size: 10px; }
.tb-text--dim { fill-opacity: 0.75; }

.md-leader { stroke: var(--gold-500); stroke-width: 1; stroke-opacity: 0.8; }
.md-chiprect { fill: var(--navy-900); stroke: var(--paper-000); stroke-opacity: 0.3; }
.md-chiptext { font-size: 10px; letter-spacing: 1px; fill: var(--paper-000); text-anchor: middle; font-weight: 600; }
@media (max-width: 640px) { .md-callouts { display: none; } } /* info moves to tap → card */

.pylon-link { cursor: pointer; }
.pylon-link:focus { outline: none; }
.pylon-link:focus-visible .pyl-halo { stroke: var(--slate-500); stroke-width: 3; stroke-opacity: 1; }
.pyl-hit { fill: transparent; }
/* Keep tap targets ≥44px CSS even when the 1000-unit viewBox scales down. */
@media (max-width: 820px) {
  .pyl-hit { transform: scale(2.4); transform-box: fill-box; transform-origin: center; }
}
.pyl-shadow { fill: var(--navy-700); opacity: 0.4; }
.pyl-front { fill: var(--gold-500); }
.pyl-side { fill: var(--gold-600); }
.pyl-cap { fill: var(--gold-500); }
.pyl-halo { fill: none; stroke: var(--paper-000); stroke-width: 1.5px; stroke-opacity: 0.6; transform-box: fill-box; transform-origin: center; }
@media (prefers-reduced-motion: no-preference) {
  .pyl-halo { animation: md-pulse 4s ease-in-out infinite; }
}
@keyframes md-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.15); }
}
.pyl-body { transform-box: fill-box; transform-origin: center bottom; transition: transform 200ms ease; }
.pylon-link.is-lit .pyl-body, .pylon-link:hover .pyl-body, .pylon-link:focus-visible .pyl-body { transform: translateY(-3px); }
.pylon-link.is-lit .pyl-halo, .pylon-link:hover .pyl-halo { stroke-opacity: 1; }
.md-zone-label { font-size: 8px; letter-spacing: 1.5px; fill: var(--paper-000); fill-opacity: 0.7; font-weight: 600; }

/* Entrance sequence (≤2s). Default CSS shows the finished drawing; JS adds
   .md-anim (prep) then .md-drawn on intersection — motion-safe only. */
.md-anim .md-grid-rects { opacity: 0; }
.md-anim .md-line, .md-anim .md-ctx { stroke-dasharray: 1; stroke-dashoffset: 1; }
.md-anim .md-plate, .md-anim .md-hatchfill, .md-anim .md-fill { opacity: 0; }
.md-anim .pyl-body { transform: scaleY(0); }
.md-anim .md-furniture, .md-anim .md-callouts, .md-anim .pyl-halo, .md-anim .md-label, .md-anim .pyl-shadow { opacity: 0; }
.md-drawn .md-grid-rects { opacity: 1; transition: opacity 300ms ease; }
.md-drawn .md-line, .md-drawn .md-ctx { stroke-dashoffset: 0; transition: stroke-dashoffset 900ms ease 300ms; }
.md-drawn .md-line:nth-of-type(2), .md-drawn .md-ctx:nth-of-type(2) { transition-delay: 420ms; }
.md-drawn .md-line:nth-of-type(3), .md-drawn .md-ctx:nth-of-type(3) { transition-delay: 540ms; }
.md-drawn .md-plate, .md-drawn .md-hatchfill, .md-drawn .md-fill { opacity: 1; transition: opacity 250ms ease 1150ms; }
.md-drawn .pyl-body { transform: scaleY(1); transition: transform 500ms cubic-bezier(.2,.7,.3,1) 1400ms; }
.md-drawn .pylon-link:nth-of-type(2) .pyl-body { transition-delay: 1500ms; }
.md-drawn .pylon-link:nth-of-type(3) .pyl-body { transition-delay: 1600ms; }
.md-drawn .pylon-link:nth-of-type(4) .pyl-body { transition-delay: 1700ms; }
.md-drawn .md-furniture, .md-drawn .md-callouts, .md-drawn .pyl-halo, .md-drawn .md-label, .md-drawn .pyl-shadow { opacity: 1; transition: opacity 300ms ease 1750ms; }

.map-tip {
  position: absolute; z-index: 10; width: 240px;
  background: var(--navy-900); color: var(--paper-050);
  border: 1px solid color-mix(in srgb, var(--paper-000) 30%, transparent);
  border-radius: 2px; padding: 16px 16px;
  font-size: 0.8125rem; pointer-events: none;
  transform: translateY(-100%);
}
.map-tip .badge { background: var(--navy-700); border-color: var(--navy-700); color: var(--paper-050); }
.map-tip p { margin: 8px 0 0; max-width: none; }

/* --------------------------------------------------------------------------
   13. COMPLIANCE — comparison table, modals, resources, insights
   -------------------------------------------------------------------------- */
.comp-table { width: 100%; border-collapse: collapse; background: var(--paper-000); border: 1px solid var(--line-200); }
.comp-table caption { text-align: left; padding-bottom: 12px; font-size: var(--fs-6); color: var(--ink-500); }
.comp-table th, .comp-table td { text-align: left; vertical-align: top; padding: 16px 20px; border-bottom: 1px solid var(--line-200); }
.comp-table thead th { font-size: 0.8125rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--slate-500); background: var(--paper-050); }
.comp-table tbody th { font-weight: 600; width: 20%; }
.comp-table td { color: var(--ink-500); }
.table-note { margin-top: 24px; font-size: 0.8125rem; color: var(--ink-500); }
@media (max-width: 640px) {
  /* Stack into cards: each row becomes a labeled block. */
  .comp-table thead { position: absolute; left: -9999px; }
  .comp-table, .comp-table tbody, .comp-table tr, .comp-table th, .comp-table td { display: block; width: 100%; }
  .comp-table tr { border-bottom: 2px solid var(--line-200); padding: 8px 0; }
  .comp-table tbody th { background: var(--paper-050); }
  .comp-table td { border-bottom: 0; padding-top: 4px; }
  .comp-table td::before { content: attr(data-col); display: block; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--slate-500); margin-bottom: 2px; }
}

.resource-card { display: flex; flex-direction: column; gap: 12px; }
.resource-card .btn { align-self: flex-start; margin-top: auto; }
.resource-art { border: 1px solid var(--line-200); border-radius: 2px; background: var(--paper-050); }

.insight-card { position: relative; }
.soon-badge {
  position: absolute; top: 20px; right: 20px;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-500); border: 1px solid var(--line-200); border-radius: 999px; padding: 3px 10px;
  background: var(--paper-050);
}
.insight-card h3 { padding-right: 96px; }

/* Native <dialog>: the platform supplies the top layer, backdrop, focus trap,
   ESC handling and background inerting. We only supply the look. */
dialog.modal {
  max-width: 440px; width: calc(100% - 48px);
  padding: 0; border: 0; background: transparent; color: inherit;
  margin: auto; /* centres in the top layer */
}
dialog.modal::backdrop { background: color-mix(in srgb, var(--navy-900) 62%, transparent); }
.modal-inner { position: relative; }
/* :has() is Baseline — no class toggling needed to lock the page behind a modal. */
body:has(dialog[open]) { overflow: hidden; }
.modal-close {
  position: absolute; top: 12px; right: 12px; z-index: 1; width: 40px; height: 40px;
  cursor: pointer; font-size: 1.4rem; line-height: 1;
  color: var(--ink-900); border-radius: 3px;
  background: var(--paper-000); border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff,
              0 2px 0 0 color-mix(in srgb, var(--navy-900) 20%, transparent),
              0 3px 7px color-mix(in srgb, var(--navy-900) 12%, transparent);
  transition: transform 130ms ease, box-shadow 130ms ease, border-color 150ms;
}
.modal-close:hover { transform: translateY(-1px); border-color: var(--slate-500);
  box-shadow: inset 0 1px 0 #fff,
              0 3px 0 0 color-mix(in srgb, var(--navy-900) 20%, transparent),
              0 5px 12px color-mix(in srgb, var(--navy-900) 16%, transparent); }
.modal-close:active { transform: translateY(2px);
  box-shadow: inset 0 1px 0 #fff, 0 0 0 0 transparent, 0 1px 2px rgb(11 37 69 / .12); }
.modal-close:hover { color: var(--ink-900); }

/* --------------------------------------------------------------------------
   14. ABOUT — bio, timeline, values
   -------------------------------------------------------------------------- */
.bio-grid { display: grid; gap: 40px; }
@media (min-width: 821px) { .bio-grid { grid-template-columns: 320px 1fr; align-items: start; } }
.portrait-slot {
  position: relative; border: 1px solid var(--line-200); border-radius: 4px;
  background: var(--paper-000); overflow: hidden;
}
.portrait-label {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 10px; text-align: center;
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-500); background: color-mix(in srgb, var(--paper-050) 92%, transparent);
  border-top: 1px solid var(--line-200);
}

/* The old horizontal rail lived here: seven equal columns above 981px, with a
   spine and gold ticks. Seven roles across a 1112px column gave each 158px,
   which the label plates cannot fit and the role text cannot survive. Replaced
   by the schedule rows at the foot of this file. */

.values-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; }
@media (min-width: 821px) { .values-list { grid-template-columns: repeat(2, 1fr); } }
.values-list li { border-left: 2px solid var(--gold-500); padding: 4px 0 4px 20px; font-family: var(--font-display); font-weight: 500; font-size: var(--fs-4); }

/* --------------------------------------------------------------------------
   15. CONTACT
   -------------------------------------------------------------------------- */
.contact-grid { display: grid; gap: 48px; }
/* Grid items default to min-width:auto, so the select's longest option
   ("Violations or inspection issue") would widen the column past 320px. */
.contact-grid > * { min-width: 0; }
@media (min-width: 981px) { .contact-grid { grid-template-columns: 1.3fr 1fr; align-items: start; } }
.field { margin-bottom: 24px; }
.field label { display: block; font-weight: 600; font-size: var(--fs-6); margin-bottom: 8px; }
/* The inverse solid: pressed into the sheet, so the shade sits on the inside
   top edge and the lit hairline at the bottom. Nothing here lifts. */
.field input, .field select, .field textarea {
  width: 100%; min-width: 0; max-width: 100%; padding: 12px 16px;
  font-family: var(--font-body); font-size: var(--fs-5); color: var(--ink-900);
  background: var(--paper-000); border: 1px solid var(--line-300); border-radius: 2px;
  box-shadow: inset 0 2px 0 rgb(11 37 69 / .07), inset 0 -1px 0 #fff;
  transition: border-color 150ms, box-shadow 150ms;
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--slate-500); }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: 3px solid var(--slate-500); outline-offset: 2px; border-color: var(--slate-500);
}
.field textarea { min-height: 140px; resize: vertical; }
.field [aria-invalid="true"] { border-color: var(--gold-600); }
/* Error text stays ink (AA); gold marks the field via the rule and border only. */
.field-error { margin: 8px 0 0; font-size: var(--fs-6); font-weight: 600; color: var(--ink-900); border-left: 2px solid var(--gold-600); padding-left: 10px; }
.form-success { margin-top: 20px; font-weight: 600; color: var(--navy-900); border-left: 2px solid var(--gold-500); padding-left: 16px; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; } /* honeypot */
.form-assurance { margin: 16px 0 0; font-size: 0.8125rem; color: var(--ink-500); max-width: 46ch; }

/* "What happens next" — a real sequence, so it is numbered. */
.next-card { border-top: 3px solid var(--gold-500); }
.next-title { font-size: var(--fs-4); font-family: var(--font-display); font-weight: 500; margin-bottom: 20px; }
.next-steps { list-style: none; margin: 0; padding: 0; counter-reset: nx; }
.next-steps li {
  counter-increment: nx;
  position: relative; padding: 0 0 20px 40px;
  font-size: var(--fs-6); color: var(--ink-500); max-width: none;
}
.next-steps li:last-child { padding-bottom: 0; }
.next-steps li::before {
  content: counter(nx, decimal-leading-zero);
  position: absolute; left: 0; top: 1px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.8125rem;
  color: var(--gold-600); font-variant-numeric: tabular-nums;
}
.next-when { display: block; font-weight: 600; color: var(--ink-900); margin-bottom: 2px; }

.contact-aside .card { margin-bottom: 24px; }
.contact-aside dt { font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--slate-500); margin-top: 16px; }
.contact-aside dt:first-child { margin-top: 0; }
.contact-aside dd { margin: 4px 0 0; }

/* --------------------------------------------------------------------------
   16. RESOURCES (checklists) & PROSE PAGES & 404
   -------------------------------------------------------------------------- */
.checklist fieldset { border: 1px solid var(--line-200); border-radius: 4px; background: var(--paper-000); padding: 24px 28px; margin: 0 0 24px; }
.checklist legend { font-weight: 600; font-size: var(--fs-4); padding: 0 8px; font-family: var(--font-display); }
.check-item { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 12px; }
.check-item input { width: 20px; height: 20px; margin-top: 3px; accent-color: var(--navy-900); flex: none; }
.resource-note { font-size: 0.8125rem; color: var(--ink-500); border-top: 1px solid var(--line-200); padding-top: 20px; margin-top: 32px; }

.prose h2 { font-size: var(--fs-3); margin-top: 2em; }
.prose { max-width: 72ch; }

.page-404 .hero-inner { text-align: center; padding-block: 120px; }
.page-404 .hero-actions { justify-content: center; }

/* --------------------------------------------------------------------------
   17. NAVIGATION, MOTION & PRINT
   -------------------------------------------------------------------------- */
/* Cross-document view transitions — continuous navigation between pages with
   no router and no JavaScript. Chrome 126+ / Safari 18.2+; anywhere else this
   at-rule is ignored and navigation is an ordinary page load. */
@view-transition { navigation: auto; }
/* Hold the furniture still so only the page body crosses over. */
.site-header { view-transition-name: site-header; }
.site-footer { view-transition-name: site-footer; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* Scroll-driven meridian reveal. Replaces an IntersectionObserver: the tick
   draws itself as its section scrolls in, with no script and no observer.
   Chromium + Safari; Firefox has not shipped scroll-driven animations, so the
   @supports guard leaves it with the finished line — the same thing the
   reduced-motion path renders. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .eyebrow::before {
      animation: meridian-draw linear both;
      animation-timeline: view();
      animation-range: entry 8% entry 46%;
    }
    @keyframes meridian-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .sheet-card:hover, .sheet-card.is-lit { transform: none; }
  .text-link { background-size: 100% 1px; }
}

@media print {
  /* Resource checklists print black on white, no chrome. */
  .site-header, .site-footer, .cta-band, .no-print, .skip-link { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .checklist fieldset { border-color: #000; box-shadow: none; break-inside: avoid; }
  /* No solids on paper: every side face and cast is screen-only, or a printed
     control comes out as a grey smudge. */
  .btn, .sheet-btn, .nav-toggle, .modal-close, .persona-tile,
  .card, a.card.sheet-card, .map-wrap, .comp-table, .sched-wrap, .sheet-tab,
  .field input, .field select, .field textarea, .stat-note {
    box-shadow: none !important; transform: none !important;
  }
  .sheet-bar, .stat-strip--band, .section--navy { background: #fff !important; color: #000 !important; }
  .eyebrow { color: #000; }
  .eyebrow::before { background: #000; }
  a { color: #000; text-decoration: none; }
}

/* Dark surfaces carry the grain too — the ambient ground is a property of the
   whole sheet, not just the paper half. Struck in paper at 9% over navy, on the
   same 18px grid as the light side so the two register with each other. */
.section--navy, .cta-band, .site-footer, .stat-strip--band {
  background-image: var(--grain-film-dark), var(--grain-dots-dark);
  background-size: 200px 200px, 18px 18px;
}
.stat-strip--band .stat-note {
  background-image:
    linear-gradient(180deg, color-mix(in srgb, var(--paper-050) 6%, transparent) 0%,
                            transparent 45%, rgb(0 0 0 / .18) 100%),
    radial-gradient(circle 1.5px at center, var(--grain-dot-dark) 1.4px, transparent 1.4px);
  background-size: 100% 100%, 18px 18px;
}
/* Placed last on purpose: several of these surfaces set `background:` shorthand
   in their own rules, which resets background-image to none. Source order, not
   specificity, is what decides here. */

/* Engraved ground, one per project. Each is generated from that project's own
   isometric plate (PL-451..454): the same building re-cut as a bank-note
   intaglio motif and repeated across a plan grid. Repeated small rather than
   enlarged, because a single large vignette put a ghost behind the headline at
   a 10.6x density imbalance; a tiled motif has no focal point, so it cannot
   compete, and coverage is even by construction. Measured 21.9-26.9% ink with
   column spread 1.08-1.40x. Slate on white with no alpha: the hero ground is
   white and alpha cost 5x the bytes. Left 30-52% faded so the isometric plate
   stands on clean ground. */
.hero--photo { position: relative; }
.hero--photo::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  /* A fixed 1440px tile, not `cover`: cover scaled the pattern with the
     viewport and with each box's height, so the hero and the sheet rendered it
     at different sizes. Pinning the tile makes the ground one size everywhere.
     The fade is a CSS mask now rather than baked into the file, so one asset
     serves the hero fading left and the sheet fading right. */
  background-size: 1268px auto; background-repeat: repeat;
  background-position: center top;
  -webkit-mask-image: linear-gradient(to right, transparent 0 30%, #000 52%);
  mask-image: linear-gradient(to right, transparent 0 30%, #000 52%);
  opacity: .14;
}
/* [data-page="home"] .hero--photo::before { background-image: url("../assets/img/hero-ground-home.webp"); }  — uncomment when GR-560 lands */
/* [data-page="approach"] .hero--photo::before { background-image: url("../assets/img/hero-ground-approach.webp"); }  — GR-570 */
/* [data-page="readiness"] .hero--photo::before { background-image: url("../assets/img/hero-ground-readiness.webp"); }  — GR-580 */
[data-page="services"] .hero--photo::before { background-image: url("../assets/img/hero-ground-services.webp"); }
[data-page="pact-rad"] .hero--photo::before { background-image: url("../assets/img/hero-ground-pact-rad.webp"); }
[data-page="operations"] .hero--photo::before { background-image: url("../assets/img/hero-ground-operations.webp"); }
[data-page="rehab-scope"] .hero--photo::before { background-image: url("../assets/img/hero-ground-rehab-scope.webp"); }
[data-page="compliance"] .hero--photo::before { background-image: url("../assets/img/hero-ground-compliance.webp"); }
[data-page="lease-up"] .hero--photo::before { background-image: url("../assets/img/hero-ground-lease-up.webp"); }
[data-page="due-diligence"] .hero--photo::before { background-image: url("../assets/img/hero-ground-due-diligence.webp"); }
[data-page="projects"] .hero--photo::before { background-image: url("../assets/img/hero-ground-projects.webp"); }
[data-page="contact"] .hero--photo::before { background-image: url("../assets/img/hero-ground-contact.webp"); }
[data-project-page="A"] .hero--photo::before { background-image: url("../assets/img/hero-ground-a.webp"); }
[data-project-page="B"] .hero--photo::before { background-image: url("../assets/img/hero-ground-b.webp"); }
[data-project-page="C"] .hero--photo::before { background-image: url("../assets/img/hero-ground-c.webp"); }
[data-project-page="D"] .hero--photo::before { background-image: url("../assets/img/hero-ground-d.webp"); }
/* Only the container needs lifting above the ground; a blanket `> *` also hit
   .loc-tab, and at equal specificity later in the file it overrode that tab's
   position:absolute with relative, dropping it back into flow. */
.hero--photo > .container { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   DRAWING SECTIONS — roof plans as the ground
   MD-001 and MD-002 are the two drawing sheets on a project page, so the plan
   of that same project belongs behind them: the map and the schedule read as
   overlays on the estate they describe. Generated from PL-460..464 — a plan
   base with no reference image, then four arrangements off it.
   Slate lines with an alpha channel rather than baked on, because MD-001 sits
   on paper and MD-002 on white; one asset serves both. Alpha is compressed
   (alpha_quality 25-60) — WebP stores it losslessly by default, which cost
   3x the bytes. Coverage evened by mirroring alternate tiles; C tiles at 0.65
   because its blank bay left a 1.79x column spread at full size.
   -------------------------------------------------------------------------- */
.section--drawing { position: relative; overflow: hidden; }
.section--drawing::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-repeat: repeat; background-position: center top;
  background-size: 1200px auto;
  opacity: .13;
}
.section--drawing > .container { position: relative; z-index: 1; }
[data-project-page="A"] .section--drawing::before { background-image: url("../assets/img/drawing-plan-a.webp"); }
[data-project-page="B"] .section--drawing::before { background-image: url("../assets/img/drawing-plan-b.webp"); }
[data-project-page="C"] .section--drawing::before { background-image: url("../assets/img/drawing-plan-c.webp"); }
[data-project-page="D"] .section--drawing::before { background-image: url("../assets/img/drawing-plan-d.webp"); }

/* --------------------------------------------------------------------------
   THE SHEET — "What was built" as a large tab
   SHEET MD-104 hangs into the band from its top edge: square where it meets
   the edge, rounded where it hangs free. This is the same object at section
   scale. It butts straight onto the band's title-block strip with no gap, runs
   to the container's width so the paper ground and its grain show either side,
   and rounds off at the foot. The top border is removed for the same reason the
   tab's is: the edge it meets is not its own.
   -------------------------------------------------------------------------- */
.section--sheet {
  background: var(--paper-000);
  position: relative; overflow: hidden;
  /* 1160 + the two 1px borders: the border sits outside the content box, so a
     flat 1160 pushed everything inside 1px right of every other section on the
     page — measured 165 against 164 at every width. */
  max-width: calc(1160px + 2px);
  margin: 0 auto;
  padding-block: 64px 0;
  border: 1px solid var(--line-200);
  border-top: 0;
  border-radius: 0 0 10px 10px;
  box-shadow:
    inset 0 1px 0 #fff,
    0 12px 34px rgb(11 37 69 / .10);
}
@media (min-width: 821px) { .section--sheet { padding-block: 80px 0; } }
/* Below 1200 the box would run to the viewport edge, where a border and a
   radius read as an accident rather than a tab. It becomes a plain band. */
@media (max-width: 1199px) {
  .section--sheet { border-left: 0; border-right: 0; border-radius: 0; max-width: none; }
  /* the sheet goes edge to edge below 1200, so the plates follow it there too */
  .detail.plate { margin-inline: -24px; border-left: 0; border-right: 0; border-radius: 0; }
}
/* The sheet closes on its own rail: a line of plain speech and the one action
   the page is for. Navy, so it reads as the foot of the sheet rather than
   another card, and it carries the box's bottom radius so the two are one
   object. Full-bleed inside the box, flush to its edges. */
.sheet-cta {
  position: relative; z-index: 1;
  margin-top: 56px;
  background-color: var(--navy-900);
  background-image: var(--grain-film-dark), var(--grain-dots-dark);
  background-size: 200px 200px, 18px 18px;
  border-radius: 0 0 9px 9px;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--paper-050) 14%, transparent);
}
@media (max-width: 1199px) { .sheet-cta { border-radius: 0; } }
.sheet-cta-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; padding-block: 24px;
}
.sheet-cta p {
  margin: 0; max-width: 52ch;
  font-size: var(--fs-5); color: color-mix(in srgb, var(--paper-050) 86%, transparent);
}
.sheet-cta .btn { flex: none; }

/* The rest of the sheet is a secondary section on the page's own paper ground:
   no box, no engraved ground, so the boxed opening reads as the sheet's face
   and the work packages as the body behind it. */
.section--details { padding-block: 56px 72px; }
@media (min-width: 821px) { .section--details { padding-block: 64px 96px; } }

/* The hero's own engraved ground, mirrored. The hero fades out across its LEFT
   so the isometric plate stands clear; here the drawing sits on the right, so
   the same asset is flipped with scaleX(-1) and the fade lands on the right
   instead. One asset, two orientations — no second export. */
.section--sheet::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  /* The same 1440px tile as the hero, so the ground is one size on both. The
     mask is the hero's, mirrored — no scaleX(-1) needed once the fade is CSS
     rather than baked into the file. */
  background-size: 1268px auto; background-repeat: repeat;
  background-position: center top;
  -webkit-mask-image: linear-gradient(to left, transparent 0 30%, #000 52%);
  mask-image: linear-gradient(to left, transparent 0 30%, #000 52%);
  opacity: .14;
}
[data-page="contact"] .section--sheet::before { background-image: url("../assets/img/hero-ground-contact.webp"); }
[data-project-page="A"] .section--sheet::before { background-image: url("../assets/img/hero-ground-a.webp"); }
[data-project-page="B"] .section--sheet::before { background-image: url("../assets/img/hero-ground-b.webp"); }
[data-project-page="C"] .section--sheet::before { background-image: url("../assets/img/hero-ground-c.webp"); }
[data-project-page="D"] .section--sheet::before { background-image: url("../assets/img/hero-ground-d.webp"); }
.section--sheet > .container { position: relative; z-index: 1; }

/* The head runs two columns from 900px: the statement left, the drawing right.
   Below that the drawing sits under the heading at its own scale. */
.section--sheet .section-head { position: relative; }
/* The grid wraps the WHOLE left column — heading and opening statement — not
   just the heading. With only the heading in it, the row height was set by the
   360px drawing and a one-line heading was centred in it, leaving ~250px of
   dead white above and below. align-items:start so both columns hang from the
   same top edge. */
@media (min-width: 900px) {
  .sheet-grid {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
    gap: 56px; align-items: start;
  }
}
.sheet-col { min-width: 0; }
.section--sheet .section-head { margin-bottom: 40px; }
.sheet-head-text { min-width: 0; }
/* The sheet takes the hero's colouring: navy heading, the same lighter blue
   under it. */
.section--sheet .proves-head { color: var(--navy-900); }
.section--sheet .proves-body,
.section--sheet .detail-lead { color: var(--slate-500); }
.sheet-figure { margin: 0; }
/* No drop-shadow here: these plates came back with pure-white roof faces, so
   they cannot be keyed and ship opaque on the sheet's own white ground — a
   shadow would trace the rectangle rather than the drawing. */
/* Every figure ships in the same 360x360 box, contain-fitted and centred, so
   the four sit at identical size whatever their proportions — D is a tall
   narrow stack and A a wide row, and without a common box they read as four
   different scales. Keyed, so the engraved ground shows through around them. */
.sheet-figure { display: flex; justify-content: center; }
.sheet-figure img { display: block; width: 100%; max-width: 360px; height: auto; }
@media (max-width: 899px) {
  .sheet-figure { margin-top: 32px; max-width: 320px; }
}

/* --------------------------------------------------------------------------
   STICKY CTA RAIL
   One action, always in reach on a long page. It is NOT always on: it appears
   once the hero has scrolled away and hides again while the page's own CTA
   band is on screen, so the same offer never shows twice at once. Rendered
   hidden and revealed by script, so with JavaScript off it never appears at
   all rather than sitting permanently over the content.
   -------------------------------------------------------------------------- */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background-color: var(--navy-900);
  background-image: var(--grain-film-dark), var(--grain-dots-dark);
  background-size: 200px 200px, 18px 18px;
  border-top: 1px solid color-mix(in srgb, var(--gold-500) 55%, transparent);
  box-shadow: 0 -8px 24px rgb(11 37 69 / .22);
  transform: translateY(100%);
  transition: transform 260ms cubic-bezier(.2,.7,.3,1);
}
.sticky-cta.is-on { transform: translateY(0); }
.sticky-cta-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding-block: 14px;
}
.sticky-cta p {
  margin: 0; max-width: 62ch;
  font-size: var(--fs-6); color: color-mix(in srgb, var(--paper-050) 84%, transparent);
}
.sticky-cta .btn { flex: none; padding: 12px 24px; }
@media (max-width: 700px) {
  .sticky-cta p { display: none; }
  .sticky-cta-inner { justify-content: center; }
  .sticky-cta .btn { width: 100%; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) { .sticky-cta { transition: none; } }
@media print { .sticky-cta { display: none !important; } }

/* The hidden attribute has to win. .btn sets display:inline-flex, which is more
   specific than the UA sheet's [hidden]{display:none}, so `back.hidden = true`
   left the Back button on screen on step one. Anything given a display value by
   a component class has the same problem. */
[hidden] { display: none !important; }

/* ===== Intake: the multi-step form on contact.html ======================
   The markup is one complete form. js/form.js hides all but the step in view
   and adds the register, branching and persistence; with scripting off every
   fieldset is simply visible and the form submits as it always did. Styling
   here therefore has to read correctly BOTH as a stack of sections and as a
   single card at a time. */
/* No plate of its own — the enclosure already is one. Gutters match the rails
   above and below so every edge inside the object lines up. */
.istep {
  position: relative; margin: 0; padding: 24px;
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
/* A floor, so the enclosure does not resize under the reader as they move
   between a three-tile step and a three-field one. Only when stepping: with
   scripting off every step is on screen at once and the floor would pad each
   of the seven, adding a screen and a half of dead space to the page. */
@media (min-width: 640px) { .intake.is-stepped .istep { min-height: 344px; } }
/* With scripting off every step is on screen at once, and without their own
   borders they would run together. One hairline between them. */
.intake:not(.is-stepped) .istep + .istep { border-top: 1px solid var(--line-200); }
.istep::after { content: ""; display: table; clear: both; }
/* float:left, not display:block — a legend is laid out on the fieldset's border
   and ignores normal flow, which left the question sitting on the plate's top
   edge with a hole underneath it. Floating takes it out of that special case
   and back into the box; .istep clears it. */
.istep legend, .istep-h {
  /* padding, not margin: the legend is floated, so cleared content below it
     gets clearance rather than margin — a margin-bottom here is absorbed and
     the first field ends up sitting on the question. Padding grows the float
     box itself, which clearance does respect. */
  float: left; width: 100%; padding: 0 0 8px; margin: 0;
  font-family: var(--font-body); font-weight: 700; font-size: var(--fs-4);
  letter-spacing: -0.018em; line-height: 1.2; color: var(--navy-900);
}
.istep-note { clear: both; margin: 6px 0 0; font-size: var(--fs-6); color: var(--ink-500); max-width: 60ch; }
.istep .field:last-child { margin-bottom: 0; }
.istep .field { clear: both; margin-top: 18px; margin-bottom: 0; }
.field .opt { font-weight: 400; color: var(--slate-500); font-size: 0.8125em; }

/* The control panel: a navy strip above the form carrying where you are in the
   set, how far along, and the figure. Built in the band's material — navy face,
   dot grain, lit top edge, its own shade beneath — so the one piece of hardware
   on the page belongs to the same drawing as the quantity bands. */
/* The form is one object: a head rail, a body, a foot rail, inside a single
   enclosure built the same way .sched-wrap and the project plates are. The
   rails are flush to its edges — no gaps, no floating boxes — so the whole
   thing reads as one piece of equipment rather than three stacked cards. */
.intake {
  position: relative; overflow: hidden;
  background: var(--paper-000);
  border: 1px solid var(--line-200);
  border-bottom: 3px solid var(--line-300);
  border-radius: 8px;
  /* no inset lit edge here: the head rail is flush to the top and carries its
     own, and the enclosure's showed as a white hairline above the navy. */
  box-shadow: 0 14px 34px rgb(11 37 69 / .10);
}
.intake-reg {
  display: flex; align-items: center; gap: 16px;
  margin: 0; padding: 13px 24px;
  background-color: var(--navy-700);
  background-image: radial-gradient(circle 1.5px at center, var(--grain-dot-dark) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
  border-bottom: 1px solid rgb(0 0 0 / .42);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .12),
              inset 0 -1px 0 rgb(0 0 0 / .40);
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-050) 72%, transparent);
}
.intake-reg-n { flex: none; }
.intake-reg b { color: var(--paper-000); font-variant-numeric: tabular-nums; }
/* the bar is a groove cut into the panel, with the mark standing in it */
.intake-reg-bar {
  flex: 1; min-width: 60px; height: 8px; border-radius: 4px; overflow: hidden;
  background: rgb(0 0 0 / .34);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / .55), 0 1px 0 rgb(255 255 255 / .08);
}
.intake-reg-bar i {
  display: block; height: 100%; width: 0; border-radius: 4px;
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--gold-500) 88%, #fff) 0%, var(--gold-500) 100%);
  box-shadow: 0 0 6px rgb(242 183 5 / .35);
  transition: width 280ms cubic-bezier(.2,.7,.3,1);
}
.intake-reg-pct {
  flex: none; min-width: 4ch; text-align: right;
  color: var(--gold-500); font-variant-numeric: tabular-nums;
}
@media (max-width: 460px) {
  .intake-reg { flex-wrap: wrap; gap: 8px 12px; }
  .intake-reg-bar { order: 3; flex-basis: 100%; }
}

/* Answer tiles. The visible plate is the <span>, not the <label>, so the
   checked state works through `input:checked + span` — no :has() dependency,
   which matters because losing the selected state to an unsupported selector
   would be an accessibility failure, not a cosmetic one. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 10px; margin-top: 16px; clear: both; }
.tile { position: relative; display: block; cursor: pointer; }
.tile input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.tile span {
  position: relative; display: flex; align-items: center; min-height: 52px;
  padding: 11px 14px 11px 20px; border-radius: 6px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, 0 2px 0 0 var(--line-300), 0 3px 7px rgb(11 37 69 / .08);
  font-size: var(--fs-6); font-weight: 600; color: var(--navy-900);
  transition: box-shadow 130ms ease, transform 130ms ease, border-color 130ms ease;
}
.tile span::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 6px 0 0 6px; background: var(--line-300);
  transition: background-color 130ms ease;
}
.tile:hover span { transform: translateY(1px); box-shadow: inset 0 1px 0 #fff, 0 1px 0 0 var(--line-300), 0 2px 5px rgb(11 37 69 / .12); }
.tile input:checked + span {
  border-color: color-mix(in srgb, var(--gold-600) 72%, var(--navy-900));
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--gold-500) 22%, #fff) 0%,
    color-mix(in srgb, var(--gold-500) 10%, #fff) 100%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .8), 0 2px 0 0 var(--gold-600), 0 3px 8px rgb(11 37 69 / .16);
}
.tile input:checked + span::before { background: var(--gold-500); }
.tile input:focus-visible + span { outline: 2px solid var(--navy-900); outline-offset: 3px; }
.istep.is-bad .tiles { outline: 2px solid var(--err-500, #B4413C); outline-offset: 8px; border-radius: 8px; }

/* Interstitial: the answers so far, played back as a short schedule. */
.recap { clear: both; display: flex; flex-direction: column; gap: 6px; margin: 16px 0 0; }
.recap-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 6px;
  background: var(--surface-2); border: 1px solid var(--line-100);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
.recap-k {
  position: relative; flex: none; padding: 5px 10px 5px 13px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900); white-space: nowrap;
}
.recap-k::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500); }
.recap-v { margin-left: auto; font-weight: 700; color: var(--navy-900); text-align: right; }

.intake-foot {
  padding: 16px 24px;
  background: var(--paper-050);
  border-top: 1px solid var(--line-200);
  box-shadow: inset 0 1px 0 #fff;
}
.intake-nav { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Back sits at the left edge and the primary action at the right, so the rail
   reads as a direction of travel rather than a pair of buttons. */
.intake-nav [data-submit] { margin-left: auto; }
.intake-foot .form-assurance { margin: 14px 0 0; max-width: 60ch; }
.intake-foot .form-success, .intake-foot .form-fail { margin-top: 14px; }
.btn-ghost {
  background: transparent; color: var(--navy-900);
  border-color: var(--line-300);
  --btn-lit: rgb(255 255 255 / .7);
  --btn-side: var(--line-300);
}
.btn-ghost:hover { background: var(--paper-050); }
.form-fail { margin: 14px 0 0; font-size: var(--fs-6); color: var(--err-500, #B4413C); }
.linkish {
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
/* Without scripting the register and the nav's Back button are meaningless. */
.intake:not(.is-stepped) .intake-reg, .intake:not(.is-stepped) [data-back] { display: none; }

/* ---- Reach band (contact.html) -----------------------------------------
   The quantity band with routes in place of figures. Each cell carries its own
   panel rather than sharing the two big frames the project band uses: those are
   split 40/60 for exactly five cells, and this row has to survive a phone
   number that may or may not be configured. */
.stat-strip--reach .reach {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
}
.stat-strip--reach .reach-cell {
  display: flex; flex-direction: column; justify-content: space-between;
  min-height: 118px; padding: 18px 0 0; border-radius: 2px;
  background-color: color-mix(in srgb, var(--paper-050) 4%, transparent);
  /* The same wash as everywhere else, dark, entering from the cell's own top
     edge. It sinks the head of the box so the lit label strip at its foot comes
     forward — the box reads as a solid with a face rather than a flat panel.
     Painted as the cell's own background-image, under its children, so the
     value and the label strip are untouched. */
  background-image: radial-gradient(140% 60% at 50% 0%,
                    rgb(0 0 0 / .30) 0%, rgb(0 0 0 / .13) 38%, transparent 72%);
  border: 1px solid color-mix(in srgb, var(--paper-050) 16%, transparent);
  /* cornice above, base course below — the same solid the band's panels are cut from */
  border-top: 4px solid color-mix(in srgb, var(--paper-050) 34%, transparent);
  border-bottom: 3px solid color-mix(in srgb, var(--navy-900) 70%, #000);
  box-shadow: inset 0 7px 9px -7px rgb(0 0 0 / .6),
              inset 0 -6px 8px -6px rgb(0 0 0 / .35),
              0 8px 20px rgb(0 0 0 / .2);
}
.reach-v {
  display: block; padding: 0 18px 16px;
  font-family: var(--font-body); font-weight: 700; font-size: 1.0625rem;
  line-height: 1.35; color: var(--paper-000);
  /* an address is one long token; let it break rather than widen the column */
  overflow-wrap: anywhere;
}
.reach-v a { color: inherit; text-decoration: none; }
.reach-v a:hover, .reach-v a:focus-visible { color: var(--gold-500); text-decoration: underline; text-underline-offset: 4px; }
/* the same wide label plate that closes every cell in the quantity band */
.reach-k {
  display: flex; align-items: center; min-height: 42px;
  margin-top: auto; padding: 10px 18px;
  background-image: linear-gradient(180deg,
    color-mix(in srgb, #fff 94%, transparent) 0%,
    color-mix(in srgb, var(--paper-050) 84%, transparent) 100%);
  border-top: 1px solid color-mix(in srgb, #fff 70%, transparent);
  border-bottom: 2px solid color-mix(in srgb, var(--navy-900) 46%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .9),
              inset 0 -1px 0 rgb(11 37 69 / .12), 0 3px 7px rgb(0 0 0 / .24);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--navy-900);
}

/* Social marks. Navy plates on the footer's navy ground — the same solid as the
   sheet tab, lit on top with its own shade beneath — so they read as hardware
   fixed to the sheet rather than as free-floating brand icons. Each hides
   itself while its CONFIG url is empty, so nothing ships as a dead "#" link. */
.social { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; margin: 22px 0 0; padding: 0; }
.social-link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 4px;
  background-image: linear-gradient(180deg, var(--navy-700) 0%,
                    color-mix(in srgb, var(--navy-900) 88%, #000) 100%);
  border: 1px solid rgb(0 0 0 / .42);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .12),
              0 2px 0 0 rgb(0 0 0 / .38), 0 3px 8px rgb(0 0 0 / .26);
  color: color-mix(in srgb, var(--paper-050) 82%, transparent);
  transition: color 160ms ease, transform 130ms ease, box-shadow 130ms ease;
}
.social-link svg { width: 19px; height: 19px; display: block; }
.social-link:hover, .social-link:focus-visible {
  color: var(--gold-500); transform: translateY(1px);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .12), 0 1px 0 0 rgb(0 0 0 / .38), 0 2px 5px rgb(0 0 0 / .24);
}

/* ---- MD-004 office marker ----------------------------------------------
   A surveyor's staff standing on the sheet with the practice's mark flying as
   its flag — the same object family as the gold pylons on MD-001, which encode
   portfolio size by height. This one encodes nothing; it just says here. */
.md-office .mo-shadow { fill: var(--navy-700); opacity: .55; }
.md-office .mo-staff { stroke: var(--gold-500); stroke-width: 2; stroke-linecap: round; }
.md-office .mo-pin { fill: var(--gold-500); }
.md-office .mo-flag-face {
  fill: var(--navy-900);
  stroke: var(--gold-500); stroke-width: 1.2;
}
.md-office .mo-flag { filter: drop-shadow(1px 2px 3px rgb(0 0 0 / .45)); }
/* it draws itself in with the rest of the sheet rather than appearing on top */
.md-anim .md-office { opacity: 0; }
.md-drawn .md-office { opacity: 1; transition: opacity 260ms ease 1250ms; }

/* ---- CTA band: the plate beside the ask ---------------------------------
   Same object as a hero plate, on the same ground line, but on navy — so it is
   the palette-inverted file, not a CSS filter. invert() would have swung the
   gold accent to cyan; the swap was done on the navy/cream axis only, with the
   warm accent left alone. */

/* Service band titles are links now that each service has its own sheet. */
.svc-title-link { color: inherit; text-decoration: none; }
.svc-title-link:hover, .svc-title-link:focus-visible {
  color: var(--slate-500); text-decoration: underline; text-underline-offset: 5px;
}
.svc-more { margin: 18px 0 0; }
.svc-back { margin: 40px 0 0; }

/* ---- "You'd call us when" -----------------------------------------------
   Not a bulleted list. Each trigger is an index card in a register: its own
   plate, its own number on a gold tab cut into the left edge. The register
   metaphor is the site's own — a plan chest full of tabbed sheets — and it lets
   a reader scan for the one situation that is theirs rather than read a list. */
.trig { display: grid; gap: 22px; }
@media (min-width: 860px) {
  /* the art column only opens when there is art to put in it. 156px, not 232:
     the plate is a mark beside the cards, not a second hero — at 232 it carried
     more weight than the triggers it introduces. */
  .trig:has(.trig-art img) { grid-template-columns: minmax(0, 156px) minmax(0, 1fr); gap: 30px; align-items: start; }
}
.trig-art { margin: 0; }
.trig-art:not(:has(img)) { display: none; }
.trig-art img { display: block; width: 100%; height: auto; }
@media (max-width: 859px) { .trig-art img { width: 132px; } }

.trig-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.trig-card {
  /* the card is a container, not prose. The global `p, li, dd { max-width: 68ch }`
     capped it at 707px inside a 1112px column, so the cards stopped two-thirds
     of the way across. The measure belongs on the paragraph inside it. */
  max-width: none;
  position: relative; display: flex; align-items: flex-start; gap: 16px;
  padding: 16px 18px 16px 20px; border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--line-100);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
/* the tab: gold, cut into the card's left edge, the way an index tab sits in a
   drawer rather than floating beside the card */
.trig-card::before {
  content: ""; position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px; border-radius: 3px 0 0 3px; background: var(--gold-500);
}
.trig-n {
  flex: none; min-width: 2.4ch;
  font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 600;
  letter-spacing: 0.06em; color: var(--gold-700);
  font-variant-numeric: tabular-nums;
  padding-top: 1px;
}
.trig-card p { margin: 0; color: var(--ink-900); max-width: 62ch; }
/* An index card with a title, a line and a lead — the home's failure modes and
   its funnel. As an anchor it lifts like a sheet card; the colour stays the
   card's own so the whole surface reads as one object rather than a link. */
.trig-body { min-width: 0; }
.trig-body h3 { margin: 0 0 4px; font-size: var(--fs-5); }
.trig-body .text-link { display: inline-block; margin-top: 10px; }
a.trig-card { color: inherit; text-decoration: none; transition: transform 120ms ease, box-shadow 120ms ease; }
a.trig-card:hover { transform: translateY(-2px); box-shadow: inset 0 1px 0 #fff, 0 8px 18px rgb(11 37 69 / .10); }
a.trig-card:active { transform: translateY(0); }
.method-grid { display: grid; gap: 40px; align-items: start; }
@media (min-width: 900px) { .method-grid { grid-template-columns: minmax(0, 1fr) 34ch; gap: 72px; } }
.method-aside h3 { margin: 0 0 10px; font-size: var(--fs-4); }
.method-aside p { margin: 0 0 12px; color: var(--ink-500); }
/* Service sheet cards sit three across, 200px narrower than the project cards. */
.sheet-card--svc .pc-title { font-size: var(--fs-4); }


.stat-strip--reach .reach-cell[data-m] .reach-v::before {
  content: ""; flex: none; display: block; width: 38px; height: 38px;
  background-repeat: no-repeat; background-position: center; background-size: contain;
  filter: drop-shadow(2px 3px 3px rgb(0 0 0 / .34));
}
.reach-cell[data-m="sheet"] .reach-v::before    { background-image: url("../assets/img/ic-sheet.webp"); }
.reach-cell[data-m="stage"] .reach-v::before    { background-image: url("../assets/img/ic-stage.webp"); }
.reach-cell[data-m="triggers"] .reach-v::before { background-image: url("../assets/img/ic-triggers.webp"); }
.reach-cell[data-m="market"] .reach-v::before   { background-image: url("../assets/img/ic-market.webp"); }
.reach-cell[data-m="units"] .reach-v::before    { background-image: url("../assets/img/ic-units.webp"); }
.reach-cell[data-m="on"] .reach-v::before       { background-image: url("../assets/img/ic-on.webp"); }
.reach-cell[data-m="total"] .reach-v::before    { background-image: url("../assets/img/ic-total.webp"); }
@media (max-width: 820px) {
  .stat-strip--reach .reach-cell[data-m] .reach-v::before { width: 30px; height: 30px; }
}

/* The line that introduces the triggers. It names what the situations beneath it
   have in common, so the cards read as instances rather than a list. */
.trig-lead {
  margin: 0 0 20px; max-width: 66ch;
  font-size: var(--fs-5); line-height: 1.55; color: var(--ink-500);
}
.trig-lead strong { color: var(--ink-900); }

/* The engagement section: one statement of what the work produces, set at
   reading size and given the room a section gets. It sits between the band and
   the triggers because that is the order the questions arrive in — where does
   this sit, what does it produce, when would I start it. */
/* Text left, deliverable right — mirroring the hero, which puts its plate on
   the left. The column only opens when there is a drawing to put in it, so the
   section reads correctly before the plates land and after. */
.svc-wrap { display: grid; gap: 28px; }
@media (min-width: 900px) {
  /* start, not center: the text is shorter than the plate, and centring it left
     a couple of hundred pixels of air between the heading and its own first
     line. The column reads top-down, so both columns begin at the top. */
  /* `auto`, not a fixed 320: with a fixed column the plate is centred inside it
     by object-fit, so a narrow drawing (the plumb bob renders 176 wide, the
     concertina 320) left a gap on the right and its edge missed the page grid.
     An auto column is exactly as wide as the plate, so every plate's right edge
     lands on 1276 like everything else on the page. */
  .svc-wrap:has(.svc-art img) { grid-template-columns: minmax(0, 1fr) auto; gap: 56px; align-items: start; }
}
.svc-art { margin: 0; }
.svc-art:not(:has(img)) { display: none; }
/* A fixed box with contain, exactly as .hero-iso does — width:100% scaled every
   plate UP to the column width, so the narrow ones (the plumb bob is 365 wide
   against the concertina's 1003) rendered half as tall again as the wide ones
   and blew the row open. Height is the constant; footprint varies. */
/* Height is the constant, width follows the footprint — the same rule the six
   hero plates and the four project plates already obey. */
.svc-art img { display: block; height: 300px; width: auto; max-width: 100%; }
/* Optical top alignment. align-items:start levels the two BOXES, but a line of
   text does not start at its box top — half-leading plus the ascender-to-cap
   gap put the first glyph 6px lower at this size, while the plate's ink begins
   at its very first pixel row. Measured at 6px on all six pages at 1440, 1155
   and 960; expressed against --fs-4 so it tracks the type rather than a
   viewport. */
.svc-art { padding-top: calc(var(--fs-4) * 0.3); }
@media (max-width: 1099px) { .svc-art img { height: 240px; } }
@media (max-width: 899px) { .svc-art img { height: 200px; } }
.svc-intro { max-width: 68ch; }
/* the standing line reads as the claim, the paragraph as the working detail
   under it — two sizes, one voice */
.svc-intro-body {
  margin: 18px 0 0; font-size: var(--fs-5); line-height: 1.62;
  color: var(--ink-500); text-wrap: pretty;
}
.svc-intro-body strong { color: var(--ink-900); }
.svc-intro-p {
  margin: 0; font-size: var(--fs-4); line-height: 1.5;
  color: var(--ink-900); letter-spacing: -0.008em; text-wrap: pretty;
}

/* ---- Projects intro: prose left, drawing right --------------------------
   The same pairing every service sheet uses, and the same rules: the column is
   sized to the drawing rather than fixed, so its right edge lands on the page
   grid whatever the plate's footprint; height is the constant; and the plate is
   nudged down by the type's own cap offset so its top reads level with the
   first line of prose rather than merely sharing a box edge. */
.proj-grid { display: grid; gap: 32px; }
@media (min-width: 1000px) {
  .proj-grid:has(.proj-art img) { grid-template-columns: minmax(0, 1fr) auto; gap: 56px; align-items: start; }
}
.proj-art { margin: 0; padding-top: calc(var(--fs-5) * 0.3); }
.proj-art:not(:has(img)) { display: none; }
.proj-art img { display: block; height: 300px; width: auto; max-width: 100%; }
@media (max-width: 1099px) { .proj-art img { height: 240px; } }
@media (max-width: 999px) { .proj-art img { height: 200px; } }

/* Mini stats on the service listing. Two facts, because two are all that vary
   between the six: the stage of a deal the work belongs to, and how many
   situations start it. Laid out as cells rather than a run-on line — label
   above value, a ruled divider between — so the pair reads as a small schedule
   rather than a caption. The stage cell carries its own mark, keyed off
   data-stage so it can never drift onto the wrong service. */
.svc-stats { display: flex; flex-wrap: wrap; gap: 0; margin: 22px 0 0; }
.svc-stat { display: flex; flex-direction: column; gap: 5px; padding-right: 26px; }
.svc-stat + .svc-stat { padding-left: 26px; border-left: 1px solid var(--line-200); }
.svc-stats dt {
  font-family: var(--font-mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--slate-500);
}
.svc-stats dd {
  /* gap 0: flex gap applies even to a zero-width ::before, which indented every
     stage value by 10px while its mark was still missing. The spacing rides on
     the mark itself instead. */
  display: flex; align-items: center; gap: 0;
  margin: 0; font-size: var(--fs-6); font-weight: 700; color: var(--navy-900);
  font-variant-numeric: tabular-nums; line-height: 1.25;
}
/* Stage marks — IC-320…325, in PROMPTS-stages.html. The box is sized by --mark,
   which only exists on a stage that has its file, so a stage without one renders
   with no reserved gap and no 404. Uncomment a line as its mark lands. */
.svc-stat--stage dd::before {
  content: ""; flex: none; display: block;
  width: var(--mark, 0); height: var(--mark, 0);
  margin-right: var(--mark-gap, 0);
  background-repeat: no-repeat; background-position: center; background-size: contain;
  filter: drop-shadow(1px 2px 2px rgb(0 0 0 / .28));
}
/* .svc-stat--stage[data-stage="pre-award"]   { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="pre-award"] dd::before   { background-image: url("../assets/img/ic-st-pre-award.webp"); } */
/* .svc-stat--stage[data-stage="live-ops"]    { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="live-ops"] dd::before    { background-image: url("../assets/img/ic-st-live-ops.webp"); } */
/* .svc-stat--stage[data-stage="budget-lock"] { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="budget-lock"] dd::before { background-image: url("../assets/img/ic-st-budget-lock.webp"); } */
/* .svc-stat--stage[data-stage="any-stage"]   { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="any-stage"] dd::before   { background-image: url("../assets/img/ic-st-any-stage.webp"); } */
/* .svc-stat--stage[data-stage="conversion"]  { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="conversion"] dd::before  { background-image: url("../assets/img/ic-st-conversion.webp"); } */
/* .svc-stat--stage[data-stage="pre-close"]   { --mark: 30px; --mark-gap: 10px; } .svc-stat--stage[data-stage="pre-close"] dd::before   { background-image: url("../assets/img/ic-st-pre-close.webp"); } */

@media (max-width: 520px) {
  .svc-stat + .svc-stat { padding-left: 0; border-left: 0; border-top: 1px solid var(--line-200); padding-top: 14px; margin-top: 14px; }
  .svc-stats { flex-direction: column; }
}

/* ---- About ---------------------------------------------------------------
   The career list becomes a schedule: the year on a label plate with the gold
   tick, the role beside it, each entry a row on the paper ground — the same
   object as a work-package row or a capital-structure tier. It was a bare
   two-column list, which was the last component on the site still outside the
   drawing-set language. */
.timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.tl-item {
  max-width: none;
  display: grid; gap: 4px 20px; align-items: center;
  padding: 12px 16px; border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--line-100);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgb(11 37 69 / .05);
}
@media (min-width: 560px) { .tl-item { grid-template-columns: 104px minmax(0, 1fr); } }
.tl-year {
  position: relative; justify-self: start;
  padding: 5px 12px 5px 15px; border-radius: 4px;
  background-image: linear-gradient(180deg, #fff 0%, var(--paper-050) 100%);
  border: 1px solid var(--line-300);
  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgb(11 37 69 / .08), 0 1px 2px rgb(11 37 69 / .07);
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.08em; color: var(--navy-900);
  font-variant-numeric: tabular-nums;
}
.tl-year::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: 4px 0 0 4px; background: var(--gold-500);
}
/* the last entry is the firm itself, so it takes the accent the others do not */
.tl-item:last-child { background: color-mix(in srgb, var(--gold-500) 6%, var(--surface-2)); }
.tl-what { color: var(--ink-900); font-weight: 600; }

/* "Why Meridian" prints the mark the paragraph is describing. */
.why-grid { display: grid; gap: 32px; align-items: center; }
@media (min-width: 720px) { .why-grid { grid-template-columns: minmax(0, 1fr) auto; gap: 56px; } }
.why-grid p { margin: 0; max-width: 62ch; font-size: var(--fs-5); color: var(--ink-500); }
.why-mark { margin: 0; }
.why-mark svg { display: block; height: 132px; width: auto; }

/* Portrait: a drawing frame with its own title block, so an image that has not
   arrived reads as a reserved plate rather than a broken one. */
.portrait-slot {
  position: relative; background: var(--paper-000);
  border: 1px solid var(--line-200); border-bottom: 3px solid var(--line-300);
  border-radius: 8px; overflow: hidden;
  box-shadow: inset 0 1px 0 #fff, 0 12px 30px rgb(11 37 69 / .08);
}
.portrait-slot svg { display: block; width: 100%; height: auto; }
.portrait-label {
  display: block; padding: 11px 16px;
  background-image: linear-gradient(180deg, var(--navy-900) 0%, color-mix(in srgb, var(--navy-900) 84%, #000) 100%);
  border-top: 1px solid rgb(0 0 0 / .34);
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold-500);
}

/* --------------------------------------------------------------------------
   COVER SHEET FURNITURE — index.html
   The objects a cover sheet carries that the site had not used yet: detail
   callouts, general notes, the index of drawings. Plus the home's own
   arrangements of components that exist.
   -------------------------------------------------------------------------- */
/* three failure modes across, bodies stretched so the callouts sit on one line */
@media (min-width: 981px) {
  .trig-list--3 { grid-template-columns: repeat(3, 1fr); gap: 16px; }
  .trig-list--3 .trig-card { align-items: stretch; }
  .trig-list--3 .trig-n { align-self: flex-start; }
}
.trig-list--3 .trig-body { display: flex; flex-direction: column; }
/* .trig-card p sets margin:0 at (0,1,1); this needs (0,2,0) to win margin-top:auto */
.trig-card .trig-ref { display: flex; align-items: center; gap: 12px; margin-top: auto; padding-top: 16px; font-size: 0.8125rem; color: var(--ink-500); max-width: none; }
/* the calendar under the failure modes */
.dates { margin-top: 40px; padding-top: 32px; border-top: 1px solid var(--line-200); }
.dates .trig-art img { width: 156px; }
/* "1 Feb 2027" needs 132, not the timeline's 104, or it breaks into two lines */
@media (min-width: 560px) { .dates .tl-item { grid-template-columns: 132px minmax(0, 1fr); } }
.dates .tl-year { white-space: nowrap; }
[data-page="home"] .resource-note { border-top: 0; padding-top: 0; margin-top: 24px; }
.dates .trig-lead { margin-bottom: 14px; }
.dates .tl-what strong { color: var(--ink-900); font-weight: 600; }
.dates-note { margin: 14px 0 0; font-size: 0.8125rem; color: var(--ink-500); }
/* method column + general notes plate */
.method-grid .step { padding-bottom: 36px; }

/* --- Marks on the phases -----------------------------------------------------
   The instrument hangs on the LEFT, in the gutter, but the gutter is already
   occupied: .stepper::before draws the gold spine at x 11–13 and .step::before
   sets the numbered station at x 0–24. So the mark takes a lane of its own between
   the dot and the copy, and the stepper's own left padding grows to open it:

       x:  0 ──── 24   40 ──────── 112   136 ─────────▶
          station + spine  instrument       copy
                            72px

   16px clear of the station, 24px clear of the copy. The mark is positioned
   absolutely rather than laid out in a grid column, which means a step whose
   mark has not been drawn yet still starts its copy on the same 132 as the
   others — the lane is held open by the padding, not by the image.

   Below 641 the mark is dropped and the padding returns to 40. At 320 the copy
   would otherwise be squeezed to 176px, and the site already makes this call
   for the hero plates: decorative weight the layout cannot afford. */
@media (min-width: 641px) {
  .stepper:has(.step-ic img) { padding-left: 136px; }
  /* The station dot is positioned against .step, not against .stepper, so it
     travels with the padding: at 136 it landed on x 96–120, straight through
     the instrument. Pulled back to the spine it belongs on — .step's left edge
     is the padding value, so -136 returns it to x 0–24, exactly where it sits
     when there is no instrument at all. */
  .stepper:has(.step-ic img) .step::before { left: -136px; }
  .stepper:has(.step-ic img) .step-ic {
    position: absolute; left: -96px; top: 0;
    width: 72px; height: 72px; margin: 0;
  }
  .step-ic img { display: block; width: 100%; height: 100%; }
}
@media (max-width: 640px) { .step-ic { display: none; } }
.step-body { min-width: 0; }
/* the lead that introduces the phases, set at section-intro size */
.sec-lead { margin: 0; font-size: var(--fs-5); color: var(--ink-500); max-width: 62ch; }
.sec-lead strong { color: var(--ink-900); }
/* The lead belongs to the heading, not to the content it introduces. Written
   when .sec-lead only ever appeared inside .sec-wrap, where the 32px grid gap
   supplied the spacing; as a direct child of .container it inherited none, so
   the rhythm read h2 -> 48 -> lead -> 0 -> content. Reversed here: the head
   tightens to 20 and the lead carries the section's standard 48px to content.
   .sec-wrap gets the same treatment for the sections that wrap lead + art.
   Both are scoped to direct container children so the in-.sec-wrap use, where
   the grid gap still owns the spacing, is untouched. */
.section-head:has(+ .sec-lead:not(:last-child)),
.section-head:has(+ .sec-wrap:not(:last-child)) { margin-bottom: 20px; }
.section > .container > .sec-lead:not(:last-child),
.section > .container > .sec-wrap:not(:last-child) { margin-bottom: 48px; }
.method-grid .detail.plate { margin: 0; }
.notes { list-style: none; margin: 0; padding: 0; counter-reset: note; }
.notes li { display: grid; grid-template-columns: 2.4ch minmax(0, 1fr); gap: 12px; padding: 10px 0; border-top: 1px solid var(--line-100); font-size: var(--fs-6); color: var(--ink-900); max-width: none; }
.notes li:first-child { border-top: 0; padding-top: 0; }
.notes li::before { counter-increment: note; content: counter(note, decimal-leading-zero); font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; color: var(--gold-700); padding-top: 2px; font-variant-numeric: tabular-nums; }
.notes-foot { margin: 14px 0 0; }
/* index of drawings */
.index-grid { display: grid; gap: 32px 40px; }
@media (min-width: 720px) { .index-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.index-h { margin: 0 0 6px; font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--slate-500); }
.index-h + .index + .index-h { margin-top: 28px; }
.index { list-style: none; margin: 0; padding: 0; }
.index li { display: grid; grid-template-columns: 7ch minmax(0, 1fr); gap: 12px; padding: 9px 0; border-top: 1px solid var(--line-100); font-size: var(--fs-6); max-width: none; }
.index-no { font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em; color: var(--gold-700); font-variant-numeric: tabular-nums; padding-top: 2px; }
.index-dim { color: var(--ink-500); font-weight: 400; }
.index a { color: var(--ink-900); text-decoration: none; background: linear-gradient(var(--gold-500), var(--gold-500)) no-repeat 0 100% / 0 1px; transition: background-size 160ms ease; }
.index a:hover { background-size: 100% 1px; }
/* two-up and lone variants used by the approach, compliance and resource sheets */
@media (min-width: 720px) {
  .trig-list--2 { grid-template-columns: repeat(2, 1fr); gap: 16px; }
  .trig-list--2 .trig-card { align-items: stretch; }
  .trig-list--2 .trig-n { align-self: flex-start; }
}
.trig-list--2 .trig-body { display: flex; flex-direction: column; }
.dates--lone { margin-top: 0; padding-top: 0; border-top: 0; }
.hero-copy .sheet-back { display: inline-block; margin-bottom: 18px; }
/* the 404's index sits under a centred hero, so it centres too */
.page-404 .index-h { text-align: center; }
.page-404 .index { margin-inline: auto; }
/* the 404 centres its index, so the line above it centres too */
.page-404 .index-h { margin-bottom: 12px; }
.page-404 .section > .container > .sec-lead { text-align: center; margin-inline: auto; margin-bottom: 28px; }

/* --------------------------------------------------------------------------
   CTA RAIL — the mid-sheet call
   One component on three grounds: gold where it opens the service set, navy
   after the portfolio, paper as the last quiet exit before the closing band.

   Each rail carries four things, not one: a 21:9 plate, the ask, a
   lower-commitment second path, and a proof strip. Readers reach a given
   scroll depth with different readiness — asking everyone for a call loses the
   ones who are not there yet — so the second path is a micro-conversion
   calibrated to depth (the checklists early, where only the problem has been
   stated; the schedule later, where the proof is being weighed). The strip
   answers the objection where the decision is made, and carries the phone for
   anyone who would rather not use a form at all.

   The rule across the top is drawn as a gradient, not `border-style: dashed`,
   because the UA dash is short and even — a drafted line is heavy and widely
   spaced.
   -------------------------------------------------------------------------- */
.cta-rail { position: relative; background: var(--paper-050); }
.cta-rail::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background-image: repeating-linear-gradient(90deg, var(--navy-900) 0 18px, transparent 18px 30px);
}
.cta-rail-inner {
  display: grid; grid-template-columns: minmax(0, 1fr);
  align-items: center; gap: 18px 32px; padding-block: 26px;
}
@media (min-width: 981px) {
  /* two columns until there is a plate to put in the third. An explicit track
     is not removed when its only item is display:none — the copy was being
     squeezed into the empty 300px art column — so the wide layout opens only
     when the image is actually present, the way every art column here does. */
  .cta-rail-inner { grid-template-columns: minmax(0, 1fr) auto; }
  .cta-rail-inner:has(.rail-art img) { grid-template-columns: 300px minmax(0, 1fr) auto; }
}
/* the plate: 21:9, the same ratio the engraved grounds are drawn at. The slot
   collapses entirely until its art lands, the way every other art column on
   the site does, so no page ships a broken image box. */
.rail-art { margin: 0; }
.rail-art:not(:has(img)) { display: none; }
.rail-art img {
  display: block; width: 100%; height: auto; aspect-ratio: 21 / 9;
  object-fit: contain; object-position: center;
}
.rail-body { min-width: 0; }
.rail-say { margin: 0; max-width: 54ch; font-size: var(--fs-5); color: var(--ink-900); }
/* the second path: present, but a full step quieter than the button */
.rail-alt { display: inline-block; margin-top: 9px; font-size: 0.875rem; color: var(--slate-500); }
.cta-rail .btn { justify-self: start; }
@media (min-width: 981px) { .cta-rail .btn { justify-self: end; } }
/* the proof strip: the mono label the quantity bands and sheet tabs use, each
   item divided by a gold rule. The icon rides as a background on the item
   itself rather than a pseudo-element, because ::before is the divider — and
   it sizes from --mk, which stays 0 until that mark's file lands, so an
   unfinished icon costs no reserved gap. */
/* The proof strip. Mobile first it is a plain stack — the claims are long
   uppercase mono strings and a single one is wider than a 320px viewport, so
   a row cannot be made to fit at any padding. From the wide breakpoint it
   becomes a row, and takes the rail's own columns as a subgrid so its last
   cell IS the button's cell: that is what puts the phone dead centre under the
   button without guessing at a width that changes with the label. */
.rail-proof {
  grid-column: 1 / -1; margin: 0; padding-top: 17px; max-width: none;
  border-top: 1px solid var(--line-200);
  display: flex; flex-direction: column; align-items: flex-start; gap: 9px;
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-500);
}
.rail-claims { display: flex; flex-direction: column; align-items: flex-start; gap: 9px; }
.rail-proof span { display: inline-flex; align-items: center; }
/* The marks ride as CSS masks filled with currentColor, so one rule serves the
   gold rail, the navy rail and the paper rail — the icon takes the same colour
   the label already resolved to, on every ground, with no markup per rail. */
.rail-claims span[data-p]::before, .cta-proof span[data-p]::before {
  content: ""; flex: none; width: 15px; height: 15px; margin-right: 9px;
  background-color: currentColor;
  -webkit-mask: var(--mk) no-repeat center / contain;
  mask: var(--mk) no-repeat center / contain;
}
[data-p="reply"]   { --mk: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M12%203.2a8.8%208.8%200%201%200%200%2017.6%208.8%208.8%200%200%200%200-17.6zM12%207.2v5.2l3.6%202.1'/%3E%3C/svg%3E"); }
[data-p="talk"]    { --mk: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%205h16v11H8l-4%204z'/%3E%3C/svg%3E"); }
[data-p="private"] { --mk: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M7.5%2010.5V8a4.5%204.5%200%200%201%209%200v2.5M5%2010.5h14v10H5z'/%3E%3C/svg%3E"); }
[data-p="units"]   { --mk: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%2021V7l6-3v17M10%2021V11l8-3v13M3%2021h18'/%3E%3C/svg%3E"); }
[data-p="years"]   { --mk: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%206h16v14H4zM4%2010h16M8%203.2v4M16%203.2v4'/%3E%3C/svg%3E"); }
@media (min-width: 981px) {
  .rail-proof { display: grid; grid-template-columns: subgrid; align-items: center; }
  .rail-claims { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 9px 0; grid-column: 1 / -2; }
  .rail-claims span { padding-right: 22px; }
  .rail-claims span + span {
    margin-left: 22px; padding-left: 22px;
    border-left: 2px solid color-mix(in srgb, var(--gold-500) 82%, transparent);
  }
  .rail-phone { grid-column: -2 / -1; justify-self: center; }
}
.rail-phone { padding-right: 0; border-left: 0; }
/* the number is the one thing in the strip a reader may want to act on
   directly, so it steps up out of the 11px label size the rest sits at */
.rail-phone a { font-weight: 700; font-size: 1.0625rem; letter-spacing: 0.03em; }
.rail-phone a { color: var(--navy-900); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--gold-500) 70%, transparent); }
.rail-phone a:hover { border-bottom-color: var(--gold-500); }
@media (max-width: 640px) { .rail-phone { margin-left: 0; } }

/* --- gold: the first call, where the set opens ------------------------------ */
.cta-rail--gold {
  background-color: var(--gold-500);
  background-image: radial-gradient(circle 1.5px at center,
                    color-mix(in srgb, var(--navy-900) 13%, transparent) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
}
.cta-rail--gold::before {
  background-image: repeating-linear-gradient(90deg, var(--navy-900) 0 18px, transparent 18px 30px);
}
.cta-rail--gold .rail-say { color: var(--navy-900); }
.cta-rail--gold .rail-alt { color: color-mix(in srgb, var(--navy-900) 92%, transparent); }
.cta-rail--gold .rail-proof {
  border-top-color: color-mix(in srgb, var(--navy-900) 22%, transparent);
  color: color-mix(in srgb, var(--navy-900) 90%, transparent);
}
.cta-rail--gold .rail-claims span + span { border-left-color: color-mix(in srgb, var(--navy-900) 34%, transparent); }
.cta-rail--gold .rail-claims span[data-p]::before { opacity: .9; }
.cta-rail--gold .rail-phone a { color: var(--navy-900); border-bottom-color: color-mix(in srgb, var(--navy-900) 45%, transparent); }
/* the gold button is invisible on gold, so the ask inverts to navy here */
.cta-rail--gold .btn-primary {
  background: var(--navy-900); color: var(--paper-000);
  border-color: color-mix(in srgb, var(--navy-900) 55%, #000);
  --btn-lit: rgb(255 255 255 / .18);
  --btn-side: color-mix(in srgb, var(--navy-900) 58%, #000);
  /* No ambient cast here. Every other ground this button sits on is dark, where
     a soft shadow reads as depth; on amber it reads as dirt around the box. The
     2px side course still gives it its thickness. */
  --btn-cast: transparent;
}
.cta-rail--gold .btn-primary:hover { background: color-mix(in srgb, var(--navy-900) 86%, #fff); color: var(--paper-000); }

/* --- navy: after the portfolio ---------------------------------------------- */
.cta-rail--dark {
  background-color: var(--navy-900);
  background-image: var(--grain-film-dark), var(--grain-dots-dark);
  background-size: 200px 200px, 18px 18px;
}
.cta-rail--dark::before {
  background-image: repeating-linear-gradient(90deg, var(--gold-500) 0 18px, transparent 18px 30px);
}
.cta-rail--dark .rail-say { color: color-mix(in srgb, var(--paper-050) 92%, transparent); }
.cta-rail--dark .rail-alt { color: color-mix(in srgb, var(--paper-050) 76%, transparent); }
.cta-rail--dark .rail-proof {
  border-top-color: color-mix(in srgb, var(--paper-000) 15%, transparent);
  color: color-mix(in srgb, var(--paper-050) 68%, var(--navy-900));
}
.cta-rail--dark .rail-phone a { color: var(--gold-500); border-bottom-color: color-mix(in srgb, var(--gold-500) 55%, transparent); }
@media print { .cta-rail { display: none; } }

/* --------------------------------------------------------------------------
   FORM FIELD MARKS + NAV ARROWS
   The mark sits on the label, not inside the input: an icon in the box needs
   padding that fights the inset shade, and a label mark stays legible once
   the field is filled.
   -------------------------------------------------------------------------- */
.field label { display: flex; align-items: center; gap: 8px; }
.field-ic { flex: none; width: 16px; height: 16px; color: var(--slate-500); }
.field label .opt { margin-left: auto; }
/* Direction on the two nav buttons. Drawn as pseudo-elements because form.js
   rewrites the submit button's textContent between "Next" and "Send it over."
   on every step, which would wipe anything nested inside it. `.is-next` is a
   class the script already toggles, so the arrow shows only while a next step
   exists — the final send gets none. */
.intake-nav [data-back]::before { content: "\2190"; margin-right: 9px; }
.intake-nav [data-submit].is-next::after { content: "\2192"; margin-left: 9px; }

/* --------------------------------------------------------------------------
   DEPTH ON THE DARK SURFACES
   Type on a dark ground reads flatter than type on paper — there is no grain
   beneath it to catch the light. A one-pixel shadow sets the letters on top of
   the surface rather than in it, and a lit hairline along the top edge gives
   each dark band a lip. Buttons are excluded: a button is already a lit object
   with its own edges, and a shadow on its label only muddies it.
   -------------------------------------------------------------------------- */
.site-footer :is(p, a, h4, dt, dd, li, span),
.cta-band :is(h2, p, span),
/* .qty-head span is navy type on its own near-white plate —
   a dark shadow under dark type on a light ground is grime, not depth. Only the
   elements that actually sit on the navy are listed. */
.stat-strip--band :is(.stat-num, .stat-label, .stat-footnote),
.cta-rail--dark :is(.rail-say, .rail-alt, .rail-proof, .rail-phone a),
.sheet-cta p, .sheet-foot span, .section--navy :is(h2, h3, p, li),
.detail.plate > h3, .sheet-tab { text-shadow: 0 1px 2px rgb(0 0 0 / .38); }
.site-footer .btn, .cta-band .btn, .cta-rail--dark .btn,
.sheet-cta .btn, .section--navy .btn { text-shadow: none; }
/* the lip: one lit pixel where each dark band meets what is above it */
.cta-band, .cta-rail--dark, .stat-strip--band { box-shadow: inset 0 1px 0 rgb(255 255 255 / .07); }

/* --------------------------------------------------------------------------
   MODE CARDS — the three failure modes on the cover sheet
   Built to the project card's anatomy — head strip, body, a foot that is the
   action — with the two key colours swapped: navy at the head where the sheet
   card wears gold, paper at the foot where the sheet card goes navy. A sheet
   card is a cover you open; a mode card is a diagnosis you recognise, so it
   opens dark and closes light. The detail callout moves into the foot, where
   it becomes the mark on the link rather than a footnote under the text.
   -------------------------------------------------------------------------- */
.mode-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; }
/* The global `p, li, dd { max-width: 68ch }` is a prose measure and these are
   containers. On the funnel the <li> wraps the card rather than being it, so
   the full-width card was capped at exactly 707px — 68ch — instead of spanning
   the grid. Same trap .trig-card fell into. */
.mode-grid > li { max-width: none; }
@media (min-width: 641px) { .mode-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 981px) { .mode-grid { grid-template-columns: repeat(3, 1fr); } }
.mode-card {
  display: flex; flex-direction: column; max-width: none;
  background: var(--paper-000);
  border: 1px solid var(--line-200); border-bottom: 3px solid var(--line-300);
  border-radius: 8px; overflow: hidden;
  box-shadow: inset 0 1px 0 #fff, 0 1px 0 var(--line-100), 0 10px 26px rgb(11 37 69 / .08);
}
.mode-head {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 10px 20px;
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold-500);
  background-image: linear-gradient(180deg, var(--navy-900) 0%,
                    color-mix(in srgb, var(--navy-900) 84%, #000) 100%);
  border-bottom: 1px solid rgb(0 0 0 / .34);
  box-shadow: inset 0 -1px 0 rgb(0 0 0 / .35), inset 0 1px 0 rgb(255 255 255 / .08);
  text-shadow: 0 1px 2px rgb(0 0 0 / .38);
}
.mode-head span:last-child { color: color-mix(in srgb, var(--paper-050) 82%, transparent); }
.mode-body { flex: 1 1 auto; padding: 20px 20px 22px; }
/* Two across rather than three, because these four carry a mark and a longer
   body than the cover sheet's three do. */
@media (min-width: 720px) and (max-width: 980px) { .mode-grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 981px) { .mode-grid--2 { grid-template-columns: repeat(2, 1fr); } }
/* The mark sits beside the copy inside the card, not above it: these headings
   run to two lines and a mark on its own row would push the foot out of line
   across the pair. */
/* Reserved across the whole grid, not per card: as soon as ONE card in a row
   has a mark, every card in that row keeps the lane, so three headings sitting
   side by side stay on one measure while a mark is still outstanding. */
.mode-grid:has(.mode-ic img) .mode-body--ic,
.mode-body--ic:has(.mode-ic img) { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 18px; align-items: start; }
/* Mirror of the same arrangement with the mark on the right, and only once
   there is a mark to put there — a stage whose mark has not been drawn keeps
   its copy on the full width instead of reserving an empty column. */
.mode-body--ir:has(.mode-ic img) { display: grid; grid-template-columns: minmax(0, 1fr) 56px; gap: 18px; align-items: start; }
.mode-copy { display: block; min-width: 0; }
/* Five into two columns leaves an orphan, so the last one takes the full width.
   It is the violations card — the one people arrive on in a hurry. */
@media (min-width: 720px) { .mode-grid--last-wide > li:last-child { grid-column: 1 / -1; } }
/* The whole card is the link here, not just its foot: this is the funnel, and a
   56px target inside a card people are meant to click is a needless obstacle.
   Spans rather than divs inside the anchor so the markup stays valid. */
a.mode-card--link { text-decoration: none; color: inherit; transition: transform 130ms ease, box-shadow 130ms ease; }
/* A card with nothing to link to. These three are statements, not routes, so
   they end on the plate's own base course rather than on a gold foot that would
   promise a destination. Everything above the foot is unchanged, which is what
   keeps them reading as the same object.
   This comment was spliced into the middle of the selector list above it, which
   left one rule reading `a.mode-card--link .mode-head, a.mode-card--link
   .mode-body, a.mode-card--link .mode-card--closed .mode-body {padding-bottom:
   24px}`. Two consequences, both measured: the funnel's head strip stood 53px
   tall against 39px on every other mode card, and the rule below never fired at
   all, so a closed card kept the open card's 22px. The link cards need no
   padding of their own -- .mode-card is a flex column, so its span children
   blockify on their own. */
.mode-card--closed .mode-body { padding-bottom: 24px; }

/* A foot that states rather than links. The gold strip is where a mode card
   puts its payoff, and on a phase card the payoff is the thing you are handed —
   so it carries the deliverable instead of a destination. No arrow button,
   because there is nothing to press: on this site the navy arrow is what marks
   a foot as a control, and the label strip at the foot of a band cell is the
   precedent for a foot that is only a label. */
.mode-foot--static { margin: auto 0 0; cursor: default; }
.mode-foot--static:hover { background-color: var(--gold-500); }
.mode-kicker {
  flex: none; font-family: var(--font-mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: color-mix(in srgb, var(--navy-900) 62%, transparent);
}
.mode-foot--static .mode-ref { font-weight: 600; }

.mode-foot { display: flex; }
a.mode-card--link .mode-body { flex-direction: column; }
a.mode-card--link:hover { transform: translateY(-2px); box-shadow: inset 0 1px 0 #fff, 0 1px 0 var(--line-100), 0 14px 30px rgb(11 37 69 / .13); }
a.mode-card--link:hover .mode-foot { background-color: color-mix(in srgb, var(--gold-500) 88%, #fff); }
a.mode-card--link:hover .mode-btn { transform: translateY(1px); box-shadow: inset 0 1px 0 rgb(255 255 255 / .20), 0 1px 0 0 color-mix(in srgb, var(--navy-900) 52%, #000), 0 2px 4px rgb(0 0 0 / .26); }
a.mode-card--link:focus-visible { outline: 3px solid var(--slate-500); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { a.mode-card--link:hover { transform: none; } }
.mode-ic { margin: 0; }
.mode-ic img { display: block; width: 56px; height: 56px; }
@media (max-width: 480px) {
  .mode-body--ic { grid-template-columns: 44px minmax(0, 1fr); gap: 14px; }
  .mode-ic img { width: 44px; height: 44px; }
}
.mode-body h3 { margin: 0 0 9px; font-size: var(--fs-5); line-height: 1.25; }
.mode-body p { margin: 0; max-width: none; font-size: var(--fs-6); color: var(--ink-500); }
/* The foot is the action, so it wears the gold the sheet card wears at its
   head — the two cards trade the colour between top and bottom. Same grain and
   the same navy hairline as .sheet-head, so it is recognisably the same strip
   of gold, just at the other end of the card. */
.mode-foot {
  display: flex; align-items: center; gap: 14px; margin-top: auto;
  padding: 12px 16px 12px 18px; text-decoration: none;
  background-color: var(--gold-500);
  background-image: radial-gradient(circle 1.5px at center, var(--grain-dot-gold) 1.4px, transparent 1.4px);
  background-size: 18px 18px;
  border-top: 1px solid color-mix(in srgb, var(--navy-900) 34%, transparent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .24);
  font-size: 0.8125rem; font-weight: 600; color: var(--navy-900);
  transition: background-color 140ms ease;
}
.mode-foot:hover { background-color: color-mix(in srgb, var(--gold-500) 88%, #fff); }
.mode-foot:focus-visible { outline: 3px solid var(--navy-900); outline-offset: -3px; }
.mode-ref { flex: 1 1 auto; min-width: 0; }
/* The control, built exactly like the sheet card's arrow but inverted: the
   sheet card sets a gold button on navy, this sets a navy button on gold.
   Lit top edge, flat face, two pixels of its own shade standing under it —
   and it presses with the card, the same 1px, on hover and focus. */
.mode-btn {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 38px; height: 28px; border-radius: 3px;
  background: var(--navy-900); color: var(--paper-000);
  border: 1.5px solid color-mix(in srgb, var(--navy-900) 46%, #000);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .20),
              0 2px 0 0 color-mix(in srgb, var(--navy-900) 52%, #000),
              0 3px 7px rgb(0 0 0 / .28);
  transition: box-shadow 130ms ease, transform 130ms ease;
}
.mode-foot:hover .mode-btn, .mode-foot:focus-visible .mode-btn {
  transform: translateY(1px);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .20),
              0 1px 0 0 color-mix(in srgb, var(--navy-900) 52%, #000),
              0 2px 4px rgb(0 0 0 / .26);
}
@media (prefers-reduced-motion: reduce) {
  .mode-foot:hover .mode-btn, .mode-foot:focus-visible .mode-btn { transform: none; }
}

/* --- Footer menu marks -------------------------------------------------------
   Ten links, so the marks stay quiet: paper at just over half strength, warming
   to gold with the label on hover. Gold on all ten at rest would out-shout the
   two column heads, which are the only gold in this part of the sheet.
   ---------------------------------------------------------------------------- */
.footer-col li a { display: inline-flex; align-items: center; gap: 10px; }
.footer-ic {
  flex: none; width: 15px; height: 15px;
  color: color-mix(in srgb, var(--paper-050) 66%, transparent);
  transition: color 140ms ease;
}
.footer-col li a:hover .footer-ic { color: var(--gold-500); }

/* --- The mark, closing the sheet ---------------------------------------------
   A drawing set signs off with the practice's mark beside the title block. Here
   it stands at the right of the closing band at full height — the two outer
   piers dropped back to a watermark so they sit in the navy rather than on it,
   and the meridian itself left in gold, because that stroke is the whole point
   of the name. Decorative, so it is hidden from assistive tech and dropped
   entirely once the band goes to one column.
   ---------------------------------------------------------------------------- */
.cta-inner { display: grid; gap: 40px; align-items: center; }
@media (min-width: 900px) { .cta-inner { grid-template-columns: minmax(0, 1fr) auto; gap: 72px; } }
.cta-copy { min-width: 0; }
.cta-mark { margin: 0; justify-self: end; }
.cta-mark svg { display: block; height: 208px; width: auto; }
/* Front faces, lifted from 17% to 26%: with a shaded side behind it the mark has
   to read as a solid catching light, not as a hole in the navy. */
.cta-mark .cm-a { fill: color-mix(in srgb, var(--paper-050) 26%, transparent); }
.cta-mark .cm-b { fill: color-mix(in srgb, var(--gold-500) 78%, transparent); }
/* The swept side faces. Light on this site comes from the upper left — tops
   cream, right faces darkest — so the solid is extruded down and to the right
   and what shows is its shaded side. Black at 34% rather than a named navy, so
   it stays a shadow of whatever ground it is laid on. */
.cta-mark .cm-side { fill: rgb(0 0 0 / .34); }
.cta-mark .cm-side--b { fill: color-mix(in srgb, var(--gold-600) 46%, rgb(0 0 0 / .62)); }
@media (max-width: 899px) { .cta-mark { display: none; } }

/* --- The mark on the ask -----------------------------------------------------
   Every button that leads to the contact sheet carries the same speech mark the
   footer's Contact link uses, so the action and its destination wear one glyph.
   Keyed on the href, so it reaches all of them — nav, hero, rails, closing band
   and the sticky bar — without a class on any. The form's own submit button has
   no href and is correctly left alone.
   ---------------------------------------------------------------------------- */
.btn-primary[href*="contact"]::before {
  content: ""; flex: none; width: 16px; height: 16px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%205h16v11H8l-4%204z'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23000'%20stroke-width='1.8'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%205h16v11H8l-4%204z'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* --------------------------------------------------------------------------
   SECTION ART
   One art column for the sections that have a plate: the copy or the list on
   the left, the drawing on the right. Built like every other art column here —
   the column only opens when there is an image to put in it, so a section with
   no plate yet lays out exactly as it did before.
   -------------------------------------------------------------------------- */
.sec-wrap { display: grid; gap: 32px; }
@media (min-width: 900px) {
  .sec-wrap:has(.sec-art img) { grid-template-columns: minmax(0, 1fr) 260px; gap: 56px; align-items: center; }
  .sec-wrap--wide:has(.sec-art img) { grid-template-columns: minmax(0, 1fr) 300px; }
}
.sec-art { margin: 0; }
.sec-art:not(:has(img)) { display: none; }
.sec-art img { display: block; width: 100%; height: auto; }
@media (max-width: 899px) { .sec-art img { width: 200px; margin: 0 auto; } }

/* The rail plate. The generator returns a fixed 3:2 frame whatever the prompt
   asks for, so the drawings trim to 1.6–2.2:1 rather than 21:9. A fixed box
   with object-fit: contain holds every rail to one height regardless — the same
   arrangement .hero-iso uses for the four project plates. */
.rail-art img { width: 100%; height: 132px; object-fit: contain; object-position: center; aspect-ratio: auto; }

/* The checklist cards carry a plate now instead of hand-drawn SVG, so the art
   box stops being a bordered banner and becomes a centred drawing. */
img.resource-art { display: block; height: 168px; width: auto; margin: 0 auto 2px; border: 0; background: none; box-shadow: none; }
