/* == fonts: generated by `npm run fonts` (sites/tools/fonts.mjs), do not edit by hand == */
/* Self-hosted from Google Fonts. To change the faces, edit the URL below and
   re-run `npm run fonts`; it reads the URL back from here, and the preload
   list too (PAGE.md, "Preload what paints first").
   source: https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400;500;600;700&family=Rosarivo&display=swap
   preload: Libre Franklin:400,Rosarivo:400
*/
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Libre Franklin';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/libre-franklin-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Rosarivo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/rosarivo-400-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Rosarivo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/rosarivo-400-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* == end fonts == */

/* ===========================================================================
   BSM Build
   Built to concept 1 (The Builder's Portfolio) as redrawn page by page in the
   assets chat "LF - bsm-build (Assets)", 20 Oct 2026, with Concept 3's
   project carousel on Home (the client's ask on the 17 Oct call).

   Every value below traces to a measurement in design/SPEC.md. If a number
   here is not in SPEC.md, it is a bug - not a preference.

   UNITS. The Home mockup (design/mockup/home-724.png) is 724px native, so
   sizes are written in MOCKUP pixels as `N * var(--u)`, and --u renders them
   at 1200/724 = 1.657 at 1200 and above and shrinks with the window below
   it. 1200 MAX, NO BLEED (sites/.claude/rules/BUILD.md, "The defaults"): the
   header, page and footer are one 1200 column, bands and photographs
   included. Legibility floors use max() on the same line.
   =========================================================================== */

:root {
  --u: min(calc(1200px / 724), calc(100vw / 724));

  /* --- Colour (sampled off home-724.png, SPEC.md "Palette") -------------- */
  --paper: #f3f1ed;        /* the cream band, 300-400,410-425 */
  --paper-2: #eceae5;      /* the alternate band behind "A new chapter" */
  --outside: #d6d1c6;      /* the window either side of the 1200 column: a deeper stone, so cream bands keep an edge (Jacob, 21 Oct) */
  --bar: #eeede8;          /* header */
  --ink: #1c1b18;          /* headings (2px type samples #000; set to the dark band's family) */
  --body: #464344;         /* body copy, darkest of a body line */
  --muted: #686459;        /* eyebrows and card locations */
  --olive: #7d765e;        /* buttons, header button 570-690,15-28 */
  --olive-hover: #6c6650;  /* the olive one step darker, hover only */
  --olive-ink: #fff;
  --dark: #27271e;         /* the dark band */
  --foot: #1e1e1b;         /* footer */
  --line: rgba(28, 27, 24, 0.14);
  --on-dark: #f3f1ed;
  --on-dark-2: rgba(243, 241, 237, 0.8);

  /* --- Type ---------------------------------------------------------------
     Rosarivo (headlines) and Libre Franklin (everything else), identified
     with font-match on lines from two pages (SPEC.md "Typefaces"). */
  --serif: "Rosarivo", Georgia, "Times New Roman", serif;
  --sans: "Libre Franklin", "Helvetica Neue", Arial, sans-serif;

  /* Sizes SOLVED from measured line widths in Rosarivo / Libre Franklin
     (SPEC.md "Type"): width on the mockup / width at 100px x 100. */
  --f-h1: max(34px, calc(30.5 * var(--u)));    /* "Building Los Angeles," 313 / 10.25 */
  --lh-h1: 1.1;                                /* baselines 216, 249.5: 33.5 / 30.5 */
  --f-h2: max(28px, calc(27.5 * var(--u)));    /* "A new chapter" 189 / 6.79; CTA "Have a project" 181 / 6.67 = 27.1 */
  --lh-h2: 1.18;                               /* chapter baselines 828, 861: 33 / 27.8 */
  --f-h2-row: max(28px, calc(25 * var(--u)));  /* "Recent Projects" 180 / 7.25 = 24.8 */
  --f-h2-sm: max(26px, calc(21 * var(--u)));   /* "Hands-on. Detail-driven." 248 / 11.57 = 21.4; "A straightforward" 169 / 8.32 = 20.3 */
  --lh-h2-sm: 1.18;                            /* process head baselines 1388, 1413, 1438: 25 / 21 */
  --f-h3: max(19px, calc(11 * var(--u)));      /* "New Home Construction" 130 / 11.85 */
  --lh-h3: 1.25;                               /* "Commercial Tenant / Improvement" pitch 14 / 11 */
  --f-step: max(20px, calc(12 * var(--u)));    /* "Coordinate" */
  --f-lede: max(17px, calc(10.5 * var(--u)));  /* hero sub "Custom homes, thoughtful renovations and" 210 / 20.05 */
  --f-body: max(16px, calc(9.2 * var(--u)));   /* "BSM is a Los Angeles construction company..." 289 / 31.3 */
  --lh-body: 1.6;                              /* chapter copy pitch 15 / 9.4; hero sub 14.5 / 9 */
  --f-small: max(15px, calc(8.2 * var(--u)));  /* step "Understand your goals," 87 / 10.65 */
  --lh-small: 1.45;                            /* step copy pitch 11.25 / 8.2 = 1.37, floor font needs the air */
  --f-nav: max(14px, calc(7.5 * var(--u)));    /* nav, cap 5.4 */
  --f-label: max(11px, calc(6 * var(--u)));    /* eyebrows, buttons: caps 4.4 */
  --f-micro: max(11px, calc(5.5 * var(--u)));  /* card location */
  --track: 0.16em;                             /* eyebrow tracking, measured 0.15-0.18 */

  /* --- Layout ------------------------------------------------------------- */
  --gut: max(20px, calc(42 * var(--u)));       /* hero copy x 43, cards x 42 */
  --header-h: max(64px, calc(46 * var(--u)));  /* 0-45 */

  /* --- Rhythm (all measured boundary -> text box) ------------------------- */
  --sec-pt: max(40px, calc(24 * var(--u)));    /* Recent 406 -> eyebrow 427 (21); process 1320 -> 1356 (36): 24 between */
  --sec-pb: max(56px, calc(34 * var(--u)));    /* Recent: last arrow 720 -> 755 */
  --gap-eye: calc(10 * var(--u));              /* eyebrow -> heading cap, 430 -> 447 less the cap */
  --gap-head: max(20px, calc(14 * var(--u)));  /* heading baseline -> body: 468 -> 482 */
  --gap-cta: max(24px, calc(18 * var(--u)));   /* body -> buttons: 309 -> 331 less descender */
  --gap-cards: max(14px, calc(7 * var(--u)));  /* 207 -> 214 */
  --btn-h: max(46px, calc(29 * var(--u)));     /* 331 -> 360 */
  --btn-px: max(16px, calc(17 * var(--u)));    /* "VIEW OUR WORK" 43-160, text 60-142 */
}

/* A minimal reset. Everything else belongs to the design. */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--outside);
  color: var(--body);
  font-family: var(--sans);
  font-size: var(--f-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}
img, picture, video { display: block; max-width: 100%; height: auto; }
h1, h2, h3, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }

/* No bleed: one 1200 column, bands and photographs included. */
.site-header, main, .site-footer { max-width: 1200px; margin-inline: auto; }
main { background: var(--paper); }

:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }
.band--dark :focus-visible, .split--dark :focus-visible, .hero :focus-visible,
.cta :focus-visible, .site-footer :focus-visible { outline-color: var(--on-dark); }

/* The skip link (partials/header.html). */
.skip-link {
  position: absolute; left: -9999px; top: 16px; z-index: 1000;
  padding: 12px 20px; background: var(--ink); color: var(--on-dark);
  text-decoration: none; font-weight: 600;
}
.skip-link:focus { left: 16px; outline: 3px solid var(--on-dark); outline-offset: 2px; }

/* ---------------------------------------------------------------- type --- */
.eyebrow {
  font-family: var(--sans); font-size: var(--f-label); font-weight: 500;
  letter-spacing: var(--track); text-transform: uppercase; line-height: 1.4;
  color: var(--muted);
}
.h1, .h2, .h3 { font-family: var(--serif); font-weight: 400; color: var(--ink); letter-spacing: -0.01em; }
.h1 { font-size: var(--f-h1); line-height: var(--lh-h1); }
.h2 { font-size: var(--f-h2); line-height: var(--lh-h2); }
.h3 { font-size: var(--f-h3); line-height: var(--lh-h3); }
.head-row .h2 { font-size: var(--f-h2-row); }
.h2--sm, .process__head .h2 { font-size: var(--f-h2-sm); line-height: var(--lh-h2-sm); }
.eyebrow + .h1, .eyebrow + .h2 { margin-top: var(--gap-eye); }
.lede { font-size: var(--f-body); line-height: var(--lh-body); }
.hero__copy .lede { font-size: var(--f-lede); }
.h1 + .lede, .h2 + .lede, .h2 + .prose { margin-top: var(--gap-head); }
.prose p + p { margin-top: 0.9em; }
.small { font-size: var(--f-small); line-height: var(--lh-small); }

/* ------------------------------------------------------------- buttons --- */
.btn {
  /* inline-block, not flex: the arrow rides the last line of the label, so a
     long label wraps to two lines rather than three (check:buttons). */
  display: inline-block; vertical-align: middle; line-height: 1.45;
  min-height: var(--btn-h); padding: calc((var(--btn-h) - 1.45em) / 2) var(--btn-px);
  background: var(--olive); color: var(--olive-ink); border: 1px solid var(--olive);
  font-family: var(--sans); font-size: var(--f-label); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; text-decoration: none;
  text-align: center; cursor: pointer; /* labels may wrap: "Learn more about our work in Altadena" is wider than a 320 phone */
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}
.btn > span[aria-hidden] { margin-left: 0.6em; }
.btn:hover { background: var(--olive-hover); border-color: var(--olive-hover); }
.btn--ghost { background: transparent; border-color: rgba(243, 241, 237, 0.85); color: var(--on-dark); }
.btn--ghost:hover { background: rgba(243, 241, 237, 0.12); border-color: var(--on-dark); }
.btn-row { display: flex; flex-wrap: wrap; gap: max(12px, calc(16 * var(--u))); }
* + .btn-row, * + .btn-wrap { margin-top: var(--gap-cta); }

.link-arrow {
  display: inline-flex; align-items: center; gap: 0.6em; min-height: 44px;
  font-size: var(--f-label); font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; text-decoration: none; color: var(--ink);
}
.link-arrow:hover { color: var(--olive); }

/* -------------------------------------------------------------- header --- */
.site-header {
  position: relative; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  min-height: var(--header-h); padding-inline: var(--gut);
  background: var(--bar);
}
/* The client's own logo, taken from bsmbuild.com (assets/MANIFEST.md), in the
   slot the mockups give the wordmark. Its navy letters vanish on the footer's
   near-black, so the footer sets it on a panel of the header colour. */
.brand { display: inline-flex; align-items: center; text-decoration: none; line-height: 0; }
.brand__logo { display: block; width: auto; height: max(30px, calc(22 * var(--u))); }

.nav { display: flex; align-items: center; gap: calc(21 * var(--u)); }
.nav a {
  position: relative; display: inline-flex; align-items: center; min-height: 44px;
  font-size: var(--f-nav); color: var(--ink); text-decoration: none;
}
.nav a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 10px; height: 1px; background: currentColor;
}
.nav a:hover { color: var(--olive); }
.nav__toggle { display: none; }
.header-actions { display: flex; align-items: center; gap: 12px; }

/* ---------------------------------------------------------------- hero --- */
.hero { position: relative; display: grid; color: var(--on-dark); background: var(--dark); isolation: isolate; }
.hero__media { grid-area: 1 / 1; position: relative; min-height: calc(310 * var(--u)); } /* mockup 360; ChatGPT desktop review (20 Oct): shorter by ~15% */
.hero__media img, .hero__media .ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The wash under the copy: darkest on the copy's side, gone by the middle. */
.hero__media::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  /* Two washes: the copy side, and the bottom right under the location tag.
     Re-derived 20 Oct for contrast (check:derived): the 768 layout put the
     About and Residential headings over open sky. */
  background:
    linear-gradient(0deg, rgba(20, 20, 16, 0.78) 0%, rgba(20, 20, 16, 0) 40%),
    linear-gradient(90deg, rgba(20, 20, 16, 0.8) 0%, rgba(20, 20, 16, 0.62) 45%, rgba(20, 20, 16, 0) 80%);
}
.hero__copy {
  /* Sits low: eyebrow 125 below the photo's top, buttons end 45 above its
     bottom (hero 46-405). */
  grid-area: 1 / 1; position: relative; z-index: 2; align-self: center;
  padding: calc(36 * var(--u)) var(--gut);
  max-width: calc(380 * var(--u) + 2 * var(--gut)); /* longest drawn hero line, "clarity and craftsmanship.", ends x 293 at the residential mockup scale; Rosarivo sets it wider */
}
.hero__copy .eyebrow { color: rgba(243, 241, 237, 0.9); }
.hero__copy .h1 { color: var(--on-dark); }
.hero__copy .lede { color: var(--on-dark-2); max-width: max(30ch, calc(215 * var(--u))); margin-top: calc(var(--gap-head) + 8px); }
.h1--narrow { max-width: 16ch; } /* "Real work. / Lasting results." on two lines, as drawn */
.hero__tag {
  grid-area: 1 / 1; z-index: 2; align-self: end; justify-self: end;
  margin: 0 calc(var(--gut) + 12px) calc(30 * var(--u)); color: var(--on-dark);
  font-size: var(--f-micro); letter-spacing: var(--track); text-transform: uppercase; font-weight: 600;
}
.hero--short .hero__media { min-height: calc(240 * var(--u)); } /* Projects ~400 at 1200 (review) */
/* A project page opens on the client's own 3:2 photograph: a taller frame so
   most of it survives (check:layout). */
.hero--project .hero__media { min-height: max(420px, calc(400 * var(--u))); }

/* --------------------------------------------------------------- bands --- */
.band { padding: var(--sec-pt) var(--gut) var(--sec-pb); background: var(--paper); }
.band--alt { background: var(--paper-2); }
.band--dark { background: var(--dark); color: var(--on-dark-2); }
.band--dark .h2, .band--dark .h3 { color: var(--on-dark); }
.band--dark .eyebrow { color: rgba(243, 241, 237, 0.7); }
.band--tight-top { padding-top: max(28px, calc(14 * var(--u))); }

/* A band's heading row: eyebrow + heading on the left, intro + link right.
   Recent Projects: heading x 43, intro x 408-660 (SPEC.md). */
.head-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, calc(260 * var(--u)));
  gap: 20px calc(40 * var(--u)); align-items: end;
}
.head-row__aside { font-size: var(--f-small); line-height: var(--lh-small); }
.head-row__links { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 8px; }
.head-row + * { margin-top: max(24px, calc(18 * var(--u))); } /* 24 measured; tightened per the desktop review */

/* --------------------------------------------------------------- cards --- */
.cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap-cards); }
.cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.card { display: flex; flex-direction: column; text-decoration: none; color: inherit; }
/* Every card in a row ends on one line, arrows included (review). */
.cards > li, .slider__track > li { display: flex; }
.cards > li > .card, .slider__track > li > .card { flex: 1; }
/* Service rows: the capability images are drawn ~1.42:1 (commercial-724 x 27-173). */
.cards--wide .card__img { aspect-ratio: 3 / 2; }
.card__img { aspect-ratio: 152 / 139; width: 100%; height: auto; object-fit: cover; background: var(--paper-2); }
.cards--3 .card__img { aspect-ratio: 3 / 2; }
.card__loc { margin-top: max(10px, calc(9 * var(--u))); font-size: var(--f-micro); font-weight: 600; letter-spacing: var(--track); text-transform: uppercase; color: var(--muted); }
.card .h3 { margin-top: max(6px, calc(4 * var(--u))); }
.card__desc { margin-top: max(8px, calc(6 * var(--u))); font-size: var(--f-small); line-height: var(--lh-small); }
.card__go { margin-top: auto; padding-top: max(12px, calc(10 * var(--u))); font-size: max(16px, calc(9 * var(--u))); color: var(--ink); }
a.card:hover .h3 { color: var(--olive); }
.band--dark .card__loc { color: rgba(243, 241, 237, 0.65); }
.band--dark .card__go { color: var(--on-dark); }

/* The carousel (Concept 3's "Current Projects", renamed Recent Projects on
   the 17 Oct call). Four across at the Home mockup's measure; the rest
   scroll in by the arrows or a swipe. */
.slider__track {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: calc((100% - 3 * var(--gap-cards)) / 4);
  gap: var(--gap-cards); overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; overscroll-behavior-x: contain;
}
.slider__track::-webkit-scrollbar { display: none; }
.slider__track > * { scroll-snap-align: start; }
.slider__nav { display: flex; gap: 10px; }
.slider__btn {
  display: inline-grid; place-items: center; width: 44px; height: 44px;
  border: 1px solid var(--line); border-radius: 50%; background: transparent;
  color: var(--ink); font-size: 18px; line-height: 1; cursor: pointer;
}
.slider__btn:hover { border-color: var(--olive); color: var(--olive); }
.slider__btn[disabled] { opacity: .35; cursor: default; }

/* --------------------------------------------------------------- split --- */
/* Text on one side, a photograph to the column edge on the other.
   "A new chapter": text x 40-280, photo x 302-724, band 757-1029. */
/* Client reviews (Home): heading left, the quote card right, after the
   assets chat's mockup of 21 Oct (design/chatgpt-reviews-mockup-home.jpg).
   The stars are its star icon as a mask, like .points__icon. */
.reviews { display: grid; grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr); gap: calc(20 * var(--u)); align-items: center; }
.reviews__list { display: grid; gap: calc(12 * var(--u)); }
.review {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: calc(12 * var(--u));
  padding: max(24px, calc(18 * var(--u))) max(24px, calc(20 * var(--u)));
  background: var(--paper); border-radius: 6px;
}
.review__mark { font-family: var(--serif); font-size: max(56px, calc(40 * var(--u))); line-height: 0.8; color: var(--olive); opacity: 0.55; }
.review__stars { display: flex; gap: 4px; }
.review__stars span {
  width: round(max(18px, calc(11 * var(--u))), 1px); aspect-ratio: 1; flex: none; background: var(--olive);
  -webkit-mask: url(/assets/icons/star.png) center / contain no-repeat; mask: url(/assets/icons/star.png) center / contain no-repeat;
}
.review__text { margin-top: max(10px, calc(6 * var(--u))); font-size: var(--f-small); line-height: var(--lh-small); color: var(--body); }
.review__who { display: grid; gap: 2px; margin-top: max(14px, calc(9 * var(--u))); padding-top: max(10px, calc(7 * var(--u))); border-top: 1px solid var(--line); font-size: var(--f-label); color: var(--muted); }
.review__who span:first-child { color: var(--ink); font-weight: 500; }

.split { display: grid; grid-template-columns: minmax(0, 0.84fr) minmax(0, 1.16fr); background: var(--paper-2); }
.split--media-left { grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr); }
.split--media-left .split__media { order: -1; }
.split--paper { background: var(--paper); }
.split--dark { background: var(--dark); color: var(--on-dark-2); }
.split--dark .h2 { color: var(--on-dark); }
.split--dark .eyebrow { color: rgba(243, 241, 237, 0.7); }
.split__copy { align-self: center; padding: max(48px, calc(30 * var(--u))) calc(22 * var(--u)) max(48px, calc(30 * var(--u))) var(--gut); } /* copy x 40-280 against a photo at 302: 22 to the seam */
.split--media-left .split__copy { padding-left: calc(24 * var(--u)); padding-right: var(--gut); } /* photo to 378, copy from 402 */
.split__copy .prose, .split__copy .lede { max-width: 52ch; }
.split__media { position: relative; min-height: calc(272 * var(--u)); }
.split__media img, .split__media .ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* ------------------------------------------------------------- process --- */
/* Heading x 43-235, rule at 255, four steps 280-710 (SPEC.md). */
.process { display: grid; grid-template-columns: minmax(0, calc(190 * var(--u))) minmax(0, 1fr); gap: calc(26 * var(--u)); } /* heading 43-235 in the mockup; narrowed so four steps keep ~23 characters a line at the 15px floor (check:layout) */
.process__head { padding-right: calc(10 * var(--u)); border-right: 1px solid var(--line); }
.steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: calc(10 * var(--u)); }
.step__num {
  display: grid; place-items: center;
  width: max(48px, calc(32 * var(--u))); height: max(48px, calc(32 * var(--u)));
  border: 1px solid var(--olive); border-radius: 50%;
  font-size: max(14px, calc(7 * var(--u))); color: var(--ink);
}
.step__title { margin-top: max(14px, calc(10 * var(--u))); font-family: var(--serif); font-weight: 400; font-size: var(--f-step); color: var(--ink); line-height: 1.2; }
.step p { margin-top: max(8px, calc(6 * var(--u))); font-size: var(--f-small); line-height: var(--lh-small); }

/* -------------------------------------------------------- points ------- */
/* Four short points with a rule between (the dark band on Commercial, the
   values on About). Icons wait on Bundle 06 (assets/MANIFEST.md). */
.points { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.points > li { padding: 0 calc(16 * var(--u)); border-left: 1px solid var(--line); }
.points > li:first-child { padding-left: 0; border-left: 0; }
.points .h3 { font-size: var(--f-step); }
.points p { margin-top: 8px; font-size: var(--f-small); line-height: var(--lh-small); }
.band--dark .points > li { border-color: rgba(243, 241, 237, 0.18); }
/* Icons from the assets chat (Bundle 06), cut from its sheet and keyed off
   white; drawn as a mask so the colour stays a token (BUILD.md, Assets). */
.points__icon {
  display: block; width: max(40px, calc(24 * var(--u))); aspect-ratio: 1;
  margin-bottom: max(12px, calc(8 * var(--u)));
  background: var(--olive); pointer-events: none;
  -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat;
}
.band--dark .points__icon { background: var(--on-dark); }
.head-row + .points { margin-top: max(28px, calc(20 * var(--u))); }

/* ----------------------------------------------------------------- cta --- */
.cta { position: relative; display: grid; color: var(--on-dark); background: var(--dark); isolation: isolate; }
.cta__media { grid-area: 1 / 1; position: relative; min-height: calc(187 * var(--u)); }
.cta__media img, .cta__media .ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cta__media::after { content: ""; position: absolute; inset: 0; z-index: 1; background: linear-gradient(90deg, rgba(20,20,16,.72) 0%, rgba(20,20,16,.45) 40%, rgba(20,20,16,0) 70%); }
.cta__copy { grid-area: 1 / 1; z-index: 2; align-self: center; padding: max(48px, calc(28 * var(--u))) var(--gut); }
.cta__copy .eyebrow { color: rgba(243, 241, 237, 0.9); }
.cta__copy .h2 { color: var(--on-dark); max-width: 17ch; } /* "Planning a tenant / improvement or" breaks at ~17 characters */
.cta__copy .lede { color: var(--on-dark-2); margin-top: max(10px, calc(8 * var(--u))); max-width: 40ch; }

/* ------------------------------------------------------------ ai badge --- */
/* Every ChatGPT stand-in carries this, so BSM can see which photos to replace
   (Jacob, 21 Oct). Top right: the hero's bottom right holds the location tag,
   and a card's image is its first child. Delete the span with the photo. */
.card { position: relative; }
.ai-badge {
  position: absolute; top: max(8px, calc(6 * var(--u))); right: max(8px, calc(6 * var(--u))); z-index: 3;
  padding: 3px 8px; border-radius: 3px; background: rgba(20, 20, 16, 0.62); color: var(--on-dark);
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em; line-height: 1.4; pointer-events: none;
}

/* -------------------------------------------------------------- footer --- */
.site-footer {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16px 32px;
  padding: max(32px, calc(20 * var(--u))) var(--gut) max(20px, calc(12 * var(--u)));
  background: var(--foot); color: var(--on-dark-2);
}
.site-footer .brand { background: var(--bar); padding: max(8px, calc(5 * var(--u))) max(12px, calc(8 * var(--u))); border-radius: 4px; }
.footer-nav { display: flex; justify-content: center; flex-wrap: wrap; gap: 0 calc(16 * var(--u)); }
.footer-nav a { display: inline-flex; align-items: center; min-height: 44px; font-size: var(--f-nav); color: var(--on-dark); text-decoration: none; position: relative; }
.footer-nav a[aria-current="page"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: 10px; height: 1px; background: currentColor; }
.footer-meta { text-align: right; font-size: var(--f-micro); line-height: 1.8; }
.footer-meta a { text-decoration: none; }
.footer-legal { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 24px; padding-top: 12px; border-top: 1px solid rgba(243, 241, 237, 0.12); font-size: var(--f-micro); }
.footer-legal a { color: var(--on-dark-2); }
.footer-legal .credit { text-decoration: none; }

/* ------------------------------------------------------------ projects --- */
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.filters__label { margin-right: 10px; }
.chip {
  min-height: 44px; padding: 0 max(18px, calc(14 * var(--u)));
  border: 1px solid var(--line); background: transparent; color: var(--ink);
  font-size: var(--f-small); cursor: pointer;
}
.chip[aria-pressed="true"] { background: var(--paper-2); border-color: var(--ink); color: var(--ink); } /* quieter than a primary button (review) */
.chip { min-height: 44px; }
.filters + .cards { margin-top: max(24px, calc(16 * var(--u))); }
[data-tags][hidden] { display: none; }
.filters__empty { margin-top: 24px; font-size: var(--f-small); }

/* A project's own page: the facts, then every photograph. */
.facts { display: flex; flex-wrap: wrap; gap: 10px 40px; margin-top: var(--gap-head); }
.facts dt { font-size: var(--f-micro); letter-spacing: var(--track); text-transform: uppercase; color: rgba(243, 241, 237, 0.7); font-weight: 600; }
.facts dd { color: var(--on-dark); }
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap-cards); }
.gallery__item img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 3 / 2; }
.gallery__item--tall img { aspect-ratio: 2 / 3; }

/* --------------------------------------------------------------- team ---- */
/* Two small portrait cards under the team copy: the About mockup draws one
   card about 150 wide beside the copy, at 1024 (about-1024 x 848-1000). */
.team { display: grid; grid-template-columns: repeat(2, minmax(0, max(140px, calc(100 * var(--u))))); gap: max(20px, calc(14 * var(--u))); margin-top: max(24px, calc(18 * var(--u))); }
.person__photo { aspect-ratio: 4 / 5; width: 100%; }
.person__name { margin-top: 12px; font-family: var(--serif); font-size: var(--f-h3); color: var(--on-dark); line-height: 1.2; }
.person__role { margin-top: 4px; font-size: var(--f-micro); letter-spacing: var(--track); text-transform: uppercase; color: rgba(243, 241, 237, 0.7); }

/* ------------------------------------------------------------- contact --- */
.contact { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: calc(40 * var(--u)); }
.form { display: grid; gap: 14px; }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.field { display: grid; gap: 8px; align-content: start; }
.field label { font-size: var(--f-micro); letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.field input, .field select, .field textarea {
  width: 100%; min-height: 48px; padding: 12px 14px;
  border: 1px solid rgba(28, 27, 24, 0.32); background: #faf9f6; color: var(--ink); border-radius: 0;
  font-size: 16px;
}
.field textarea { min-height: 124px; resize: vertical; }
.field [aria-invalid="true"] { border-color: #a1372b; }
.field__error { font-size: 14px; color: #a1372b; }
.form .btn { width: 100%; }
.form__status { min-height: 1.5em; font-size: var(--f-small); }
.form__status--ok { color: #3c5a2e; }
.form__status--bad { color: #a1372b; }
.honeypot { position: absolute; left: -9999px; }
.info { display: grid; gap: max(20px, calc(14 * var(--u))); margin-top: var(--gap-head); }
.info__value { font-family: var(--serif); font-size: var(--f-h3); color: var(--ink); line-height: 1.25; }
.info__value a { text-decoration: none; }
.info__value a:hover { color: var(--olive); }
.info p + p { margin-top: 4px; }
/* Contact rows lead with an icon in a soft circle (contact-1024 x 588-630). */
.info__item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: max(14px, calc(10 * var(--u))); align-items: start; }
.info__icon { width: max(44px, calc(26 * var(--u))); aspect-ratio: 1; border-radius: 50%; background: var(--paper-2); position: relative; }
.info__icon::after { content: ""; position: absolute; inset: 24%; background: var(--ink); -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat; }
.contact__note { margin-top: max(24px, calc(16 * var(--u))); padding-top: max(16px, calc(12 * var(--u))); border-top: 1px solid var(--line); }
.contact__note .small { margin-top: 6px; }

/* -------------------------------------------------------- placeholders --- */
/* .ph: a box at the measured frame standing in for a photograph that exists
   nowhere yet; names its assets/MANIFEST.md row. .todo: an unconfirmed fact
   (design/BRIEF.md). Both are meant to be seen. */
.ph { display: grid; place-items: center; padding: 12px; background: repeating-linear-gradient(135deg, #dcd8cf 0 12px, #e4e0d8 12px 24px); color: #57534a; font-size: 13px; text-align: center; }
.person__photo.ph { aspect-ratio: 4 / 5; }
.todo { display: inline-block; padding: 1px 8px; background: #f6e7a6; color: #3a3420; font-family: var(--sans); font-size: 13px; letter-spacing: 0; text-transform: none; border-radius: 3px; }

.prose--page h2 { font-family: var(--serif); font-weight: 400; color: var(--ink); font-size: var(--f-step); margin-top: 1.6em; }
.prose--page p, .prose--page ul { margin-top: 0.8em; max-width: 68ch; }
.prose--page ul { list-style: disc; padding-left: 1.2em; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; scroll-behavior: auto !important; }
}

/* ===================================================== structure only ==== */
/* Breakpoints change structure, never a size (PAGE.md). */

@media (max-width: 1100px) {
  /* Splits stack here: below it the copy column is too narrow for the longer
     buttons and the photograph is cropped to a sliver (check:buttons,
     check:layout). */
  .split, .split--media-left { grid-template-columns: minmax(0, 1fr); }
  /* Stacked photos stay short: 16:9, not the 3:2 file (ChatGPT tablet review). */
  .split__media, .split--media-left .split__media { order: 2; min-height: 0; aspect-ratio: 16 / 9; }
  .split--media-left .split__copy, .split__copy { padding-inline: var(--gut); }
  .split--portrait .split__media { aspect-ratio: 4 / 3; }
  .team { grid-template-columns: repeat(2, minmax(0, 240px)); }
}

@media (max-width: 1200px) {
  /* Below the build width the four steps need the full row (check:layout). */
  .process { grid-template-columns: minmax(0, 1fr); row-gap: 32px; }
  .process__head { border-right: 0; padding-right: 0; }
}

@media (max-width: 1000px) {
  .reviews { grid-template-columns: 1fr; align-items: start; }
  .nav { display: none; }
  .nav.is-open {
    display: flex; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 8px var(--gut) 20px; background: var(--bar);
    border-top: 1px solid var(--line);
  }
  .nav.is-open a { border-bottom: 1px solid var(--line); min-height: 52px; }
  .nav.is-open a[aria-current="page"]::after { display: none; }
  .nav.is-open a[aria-current="page"] { color: var(--olive); }
  .nav__toggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0 14px;
    border: 1px solid var(--line); background: transparent; color: var(--ink);
    font-size: 13px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer;
  }
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 32px; }
  /* A three-card row stays three across on a tablet: two plus an orphan
     read as unfinished (tablet review). */
  .cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .header-cta { --btn-h: 44px; padding-inline: 14px; letter-spacing: 0.08em; }
  .slider__track { grid-auto-columns: calc((100% - var(--gap-cards)) / 2); }
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 28px; }
  .points { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 28px; }
  .points > li:nth-child(3) { padding-left: 0; border-left: 0; }
}

@media (max-width: 760px) {
  .header-cta { display: none; }
  .head-row { grid-template-columns: 1fr; }
  /* A phone hero is the words, then the photograph, then the actions
     (MOBILE.md 11): the copy leaves the photo for a band of its own. */
  .hero, .cta { display: flex; flex-direction: column; }
  .hero__copy, .cta__copy { display: contents; }
  .hero__copy > *, .cta__copy > * { order: 1; padding-inline: var(--gut); }
  .hero__copy > :first-child, .cta__copy > :first-child { padding-top: 40px; }
  .hero__copy > .lede, .cta__copy > .lede { padding-bottom: 32px; max-width: none; }
  .hero__copy > .btn-row, .cta__copy > .btn-wrap { order: 3; margin-top: 0; padding-block: 18px 28px; } /* mobile review: a slimmer action band */
  .hero__media, .cta__media { order: 2; min-height: 0; aspect-ratio: 16 / 9; }
  .hero--project .hero__media { min-height: 0; aspect-ratio: 3 / 2; }
  .hero__media::after, .cta__media::after { display: none; }
  .hero__tag { display: none; }
  .contact { grid-template-columns: 1fr; gap: 48px; }
  .site-footer { grid-template-columns: 1fr; }
  .footer-nav { justify-content: flex-start; gap: 0 20px; }
  .footer-meta { text-align: left; }
}

@media (max-width: 560px) {
  .review { grid-template-columns: 1fr; gap: 0; }
  .review__mark { font-size: 48px; height: 0.45em; }
  /* The long labels lose their lead-in, so a button stays one line on a
     phone: "OUR WORK IN ALTADENA" rather than three lines (check:buttons). */
  .btn__more { display: none; }
  .cards, .cards--3 { grid-template-columns: 1fr; }
  .slider__track { grid-auto-columns: 86%; }
  .steps, .points { grid-template-columns: 1fr; }
  .points > li { padding-left: 0; border-left: 0; }
  .form__row { grid-template-columns: 1fr; }
  .gallery { grid-template-columns: 1fr; }
  .gallery__item--tall img { aspect-ratio: 4 / 5; }
  .team { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Phone cards (ChatGPT mobile review): shorter photos, tighter stack. */
  .cards, .cards--3 { row-gap: 32px; }
  .card__img, .cards--3 .card__img, .cards--wide .card__img { aspect-ratio: 4 / 3; }
  .card__go { padding-top: 6px; }
  .points { row-gap: 20px; }
  .footer-nav { display: grid; grid-template-columns: repeat(3, auto); justify-content: start; justify-items: start; gap: 0 24px; }
  .site-footer .brand { justify-self: start; }
}
