/* Billaphong portfolio (Meridian) — component CSS. CANONICAL.
 *
 * This file is the only authored component and layout stylesheet. Generated token CSS
 * (dist/design-system/bp-tokens.css, from src/tokens.json) loads before it and supplies the
 * --bp-* custom properties these rules read.
 *
 * src/build.mjs copies this file to dist/design-system/bp-components.css. The static route
 * surfaces link that generated copy; dist/render.html embeds these exact bytes inline in
 * <head> instead, so it travels as one file. Same authored source either way, so there is
 * nothing to reconcile: component geometry lives here and nowhere else.
 *
 * Requires bp-tokens.css to be loaded first (provides --bp-* variables).
 *
 * Rules:
 *   - Bind to these classes. Do not re-implement control geometry or type steps.
 *   - Never override min-height or vertical padding on .bp-btn at a call site. Every
 *     control-height defect starts there. Need another size? Add one here.
 *   - Section rhythm alternates paper / sunken. Two sunken bands in a row is a defect,
 *     not a variant.
 *   - No LAYOUT BREAKPOINT media queries. Columns collapse on content width via flex-wrap
 *     plus flex-basis, not at guessed device widths. The stylesheet does carry one
 *     prefers-reduced-motion query. The ONE JS breakpoint (--bp-breakpoint-narrow, 820px)
 *     swaps the nav between inline links and a disclosure, and the section rail between a
 *     static list and a disclosure.
 */

/* ── Reset and page ─────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
/* [hidden] must beat the display on .bp-menu-btn and .bp-nav. Without this the
 * disclosure button shows at desktop width. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bp-surface-paper);
  color: var(--bp-ink-body);
  font: var(--bp-type-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img { max-width: 100%; }
a { color: var(--bp-accent-base); text-decoration: none; }
a:hover { color: var(--bp-ink-base); }

/* ── Layout ─────────────────────────────────────────────────────────── */
.bp-container {
  width: 100%;
  max-width: var(--bp-container-max);
  margin: 0 auto;
  padding: 0 var(--bp-container-pad);
}
.bp-section { padding: var(--bp-section-pad) 0; }
.bp-section--tint {
  background: var(--bp-surface-sunken);
  border-top: 1px solid var(--bp-line-hairline);
  border-bottom: 1px solid var(--bp-line-hairline);
}
.bp-section--navy { background: var(--bp-surface-navy); padding: clamp(34px,4.4vw,58px) 0; }
.bp-section--flush-top { padding-top: 0; }
/* Home hero is looser than the other openers. */
.bp-section--hero { padding: clamp(40px,6vw,84px) 0 clamp(30px,4vw,48px); }
/* Work opener. */
.bp-section--hero-work { padding: clamp(34px,4.6vw,62px) 0 clamp(24px,3vw,40px); }
/* How I Work opener. */
.bp-section--hero-how { padding: clamp(34px,4.6vw,62px) 0 clamp(24px,3vw,36px); }
.bp-section--tail { padding-bottom: clamp(44px,6vw,80px); }
/* Utility pages (About, Resume, Contact) open on this rhythm. */
.bp-section--page { padding: clamp(38px,5.5vw,76px) 0 clamp(44px,6vw,84px); }
/* Product-page masthead: tinted band, no top border because the header supplies it. */
.bp-section--masthead { padding: clamp(24px,3vw,40px) 0 clamp(24px,3vw,38px); border-top: 0; }
/* The flagship screenshot band sits flush under the masthead. */
.bp-section--flagship { padding: clamp(24px,3vw,40px) 0 0; }

/* Two-column split. --bp-split-lead sets the media column's flex-grow so the image
 * outweighs the copy; drop it for an even split. To put the media first, add
 * .bp-split--reverse below rather than setting `order` inline: the modifier also handles
 * the wrap direction, which inline `order` alone gets wrong on narrow screens.
 *
 * --bp-split-lead is a per-instance PARAMETER, not a token: it is set on individual rows and
 * is deliberately absent from bp-tokens.css. Both rules that read it carry a fallback, so a
 * row that never sets it still lays out. */
.bp-split { display: flex; flex-wrap: wrap; gap: var(--bp-split-gap); align-items: center; }
/* align-items: center is load-bearing, not a preference. Under wrap-reverse the cross axis
 * inverts, so flex-start would resolve to the BOTTOM of the line; center is center either
 * way. See .bp-split--reverse below. */
.bp-split__text { flex: 1 1 300px; min-width: 0; }
.bp-split__media { flex: var(--bp-split-lead, 1.7) 1 440px; min-width: 0; }
/* Media leads. `order` swaps the two columns while the copy stays first in the DOM, for
 * screen readers and for print. `wrap-reverse` is what keeps that true once the row
 * collapses to one column: without it the reversed order survives the wrap and the image
 * ends up above its own heading, which no other row on the page does. Both properties are
 * required; neither works alone. */
.bp-split--reverse { flex-wrap: wrap-reverse; }
.bp-split--reverse .bp-split__text { order: 2; }
.bp-split--reverse .bp-split__media { order: 1; }

/* Narrative block: heading rail left, prose right. */
/* Gap is component-local; see tokens.json $meta.componentLocal. */
.bp-narrative { display: flex; flex-wrap: wrap; gap: clamp(18px, 3.4vw, 52px); }
.bp-narrative__rail { flex: 0 1 250px; min-width: 0; }
.bp-narrative__body { flex: 999 1 400px; min-width: 0; display: flex; flex-direction: column; gap: 16px; max-width: var(--bp-measure-body); }
.bp-narrative__rule { width: 34px; height: 2px; background: var(--bp-accent-base); margin: 0 0 15px; }
.bp-narrative__sub { margin: 0; font: 400 14.5px/1.62 var(--bp-font-sans); color: var(--bp-ink-muted); max-width: 32ch; text-wrap: pretty; }
/* Home's About teaser: eyebrow alone in a narrow left rail, content right. Only the
 * About teaser uses this; the How I Work teaser above it is a plain stack. Not the same
 * as .bp-narrative, which has a rule and a heading in its rail. */
.bp-teaser-split { display: flex; flex-wrap: wrap; gap: clamp(24px, 4vw, 64px); }
.bp-teaser-split__rail { flex: 0 1 200px; min-width: 0; }
.bp-teaser-split__body { flex: 999 1 440px; min-width: 0; }
.bp-teaser-split__body .bp-h2 { font: 600 clamp(21px,2.4vw,29px)/1.28 var(--bp-font-sans); letter-spacing: -.022em; max-width: 36ch; }

/* Home contact panel: a navy card, heading left and actions right, bottom-aligned.
 * 14px radius is specific to this panel; every other card is --bp-radius-xxl. */
.bp-panel-navy { background: var(--bp-surface-navy); border-radius: 14px; padding: clamp(26px,4vw,52px); display: flex; flex-wrap: wrap; gap: 24px; align-items: flex-end; justify-content: space-between; }
.bp-panel-navy__body { flex: 1 1 380px; min-width: 0; }
.bp-panel-navy__actions { flex: 0 1 auto; display: flex; flex-wrap: wrap; gap: 10px; }
.bp-panel-navy .bp-eyebrow { color: var(--bp-ink-faint); }
.bp-panel-navy__h { margin: 0; font: 600 clamp(23px,3vw,36px)/1.18 var(--bp-font-sans); letter-spacing: -.026em; color: var(--bp-ink-on-navy); max-width: 28ch; text-wrap: pretty; }
/* On navy, the primary action inverts to white and the secondary is an outline. */
.bp-btn--on-navy { background: var(--bp-surface-paper); color: var(--bp-ink-base); border-color: var(--bp-surface-paper); padding: var(--bp-control-pad-primary); }
.bp-btn--on-navy:hover { background: #D6E4F7; color: var(--bp-ink-base); }
.bp-btn--ghost-navy { background: none; color: var(--bp-ink-on-navy); border-color: #33507A; padding: 16px 20px; gap: 9px; }
.bp-btn--ghost-navy:hover { border-color: var(--bp-ink-faint); background: #16305A; color: var(--bp-ink-on-navy); }
/* About opening narrative: a sunken block, so the first three paragraphs read as one
 * lede rather than as body copy. Its lead paragraph steps up in size and weight. */
.bp-inset { margin: 0 0 30px; background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xl); padding: clamp(22px,2.6vw,28px); max-width: 64ch; }
.bp-inset__lead { margin: 0 0 16px; font: 500 clamp(17px,1.7vw,21px)/1.55 var(--bp-font-sans); letter-spacing: -.012em; color: var(--bp-ink-base); text-wrap: pretty; }
.bp-inset p:not(.bp-inset__lead) { margin: 0 0 14px; font: 400 16.5px/1.72 var(--bp-font-sans); color: var(--bp-ink-body); text-wrap: pretty; }
.bp-inset p:last-child { margin-bottom: 0; }

/* A rail action stack: full-width rows, label left and glyph right. */
.bp-rail-actions { display: flex; flex-direction: column; gap: 8px; }
.bp-rail-actions .bp-btn { justify-content: space-between; width: 100%; padding: 15px 18px; font-size: 14.5px; }

/* Boxed facts in a narrow rail: tighter rows, label above value. */
.bp-facts--rail { background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xl); overflow: hidden; }
.bp-facts--rail .bp-facts__row { padding: 14px 16px; }
.bp-facts--rail .bp-facts__row:last-child { border-bottom: 0; }
.bp-facts--rail .bp-facts__term { margin: 0 0 5px; font-size: 11.5px; }
.bp-facts--rail .bp-facts__desc { font: 400 15px/1.6 var(--bp-font-sans); color: var(--bp-ink-base); }
.bp-facts__links { display: flex; flex-direction: column; gap: 6px; }
.bp-facts__links a { display: flex; align-items: center; gap: 7px; min-height: 44px; }

.bp-stack { display: flex; flex-direction: column; gap: 16px; }

/* Product-page shell: section rail beside the article. */
.bp-page { display: flex; flex-wrap: wrap; gap: clamp(20px, 3vw, 48px); }
.bp-page__article { flex: 999 1 480px; min-width: 0; }
.bp-page__section { margin: 0 0 clamp(34px, 4.5vw, 60px); }
.bp-page__section:last-child { margin-bottom: 0; }
/* A row in the /work gallery: one product, image-weighted, alternating side. */
.bp-gallery-row { --bp-split-lead: 1.7; margin: 0 0 clamp(34px, 4.6vw, 64px); }
.bp-gallery-row:last-child { margin-bottom: 0; }
/* Copy column measure inside a gallery row, and the small product card used for a
 * product that has no screenshot yet. */
.bp-gallery-row__copy { max-width: 48ch; margin-bottom: 22px; }
.bp-teaser { flex: 1 1 320px; min-width: 0; }
.bp-teaser__tags { margin: 18px 0 12px; }
.bp-teaser__name { margin: 0 0 10px; font: 600 clamp(21px,2.3vw,27px)/1.14 var(--bp-font-sans); letter-spacing: -.024em; color: var(--bp-ink-base); }
.bp-teaser__copy { margin: 0 0 18px; font: 400 15.5px/1.68 var(--bp-font-sans); color: var(--bp-ink-body); max-width: 46ch; text-wrap: pretty; }
/* Home product row copy measure. */
.bp-product__copy { margin: 0 0 22px; max-width: 46ch; }
/* Utility-page split: the opener (eyebrow, heading, lede) sits in the left column so the
 * right rail begins level with the heading rather than below it. */
/* Gap is component-local; see tokens.json $meta.componentLocal. */
.bp-page-split { display: flex; flex-wrap: wrap; gap: clamp(24px, 3.6vw, 56px); align-items: flex-start; }
.bp-page-split__main { flex: 999 1 420px; min-width: 0; }

/* A narrow supporting column beside a wide body: a facts rail, a link index, a
 * section intro. 260px is the width at which it stops competing with the body. */
.bp-col-rail { flex: 0 1 260px; min-width: 0; }


/* One numbered moment inside a product page: evidence beside copy. */
/* Moment rows are tighter and narrower than product rows: gap and column bases differ, and
 * they centre like every other split, which is required rather than preferred: wrap-reverse
 * inverts the cross axis, so top alignment on a reversed row would resolve to the bottom.
 * The gap is component-local by design (see tokens.json $meta.componentLocal): only
 * --bp-split-gap is shared, because / and /work product rows both use it. */
.bp-moment { --bp-split-lead: 1.4; gap: clamp(18px, 2.6vw, 36px); margin: 0 0 clamp(28px, 3.6vw, 48px); }
.bp-moment > .bp-split__text { flex: 1 1 260px; }
.bp-moment > .bp-split__media { flex: var(--bp-split-lead, 1.4) 1 360px; }
.bp-moment__n { margin: 0 0 10px; font: var(--bp-type-label); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-accent-base); }
/* Copy column of a moment. Tighter than .bp-stack because the paragraphs are short. */
.bp-moment__body { display: flex; flex-direction: column; gap: 14px; }
/* Sophie shows its three supporting surfaces as columns, not as stacked moments:
 * evidence above, then heading and copy. Wraps to one column under ~300px basis. */
.bp-surfaces { display: flex; flex-wrap: wrap; gap: clamp(16px,2.4vw,28px); margin: 0 0 clamp(28px,3.6vw,44px); }
.bp-surface { flex: 1 1 300px; min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.bp-surface__h { margin: 0 0 10px; font: 600 17.5px/1.24 var(--bp-font-sans); letter-spacing: -.018em; color: var(--bp-ink-base); }
.bp-surface__p { margin: 0; font: 400 15.5px/1.7 var(--bp-font-sans); color: var(--bp-ink-body); max-width: 46ch; text-wrap: pretty; }
.bp-surface__p + .bp-surface__p { margin-top: 12px; }

.bp-row { display: flex; flex-wrap: wrap; gap: var(--bp-grid-gap); }
/* A row of buttons. 10px rather than the 12px grid gap, and centre-aligned so an
 * inactive span sits level with a real button. */
.bp-row--controls { gap: 10px; align-items: center; }
/* The action row that sits under a flagship figure. */
.bp-row--under-figure { margin-top: clamp(18px, 2.4vw, 26px); }

/* Route-level layout that was inline in the partials. Each one is a named piece of the
 * page, not a utility: the partials stay semantic and this file keeps the geometry. */

/* Section heading beside a trailing link ("Selected work" + "All work"). */
.bp-section-head { display: flex; flex-wrap: wrap; gap: 12px; align-items: baseline; justify-content: space-between; margin: 0 0 clamp(24px, 3vw, 38px); }

/* Home: the three full-width product rows, then the two-up row beneath them. */
.bp-products { gap: clamp(30px, 4vw, 56px); }
.bp-two-up { gap: clamp(20px, 3vw, 40px); align-items: flex-start; }

/* How I Work: hero paragraph stack, the asymmetric evidence grid, and the tools row. */
.bp-hero-stack { max-width: 64ch; gap: 17px; }
.bp-evidence-col { flex: 1 1 300px; min-width: 0; align-content: stretch; }
.bp-tools { --bp-split-lead: 999; align-items: flex-start; }
.bp-tools__note { max-width: 38ch; }
.bp-tools__chips { flex: 999 1 320px; min-width: 0; align-content: flex-start; }
/* Narrative sections run on one rhythm; they had three near-identical paddings inline. */
.bp-section--narrative { padding: clamp(30px, 4vw, 52px) 0; }
/* The lifecycle figure sits flush under the hero; the tools row closes the page. */
.bp-section--figure { padding: 0 0 clamp(30px, 4vw, 50px); }
.bp-section--tools { padding-top: clamp(30px, 4vw, 54px); }

/* About: the rail is a column, and the portrait is the only bare <img> on the site. */
.bp-col-rail--stack { flex-basis: 250px; display: flex; flex-direction: column; gap: 16px; }
.bp-portrait { display: block; width: 100%; height: auto; border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xxl); background: var(--bp-surface-sunken); }

/* Contact: intro column carries the facts, form column carries the panel. */
.bp-contact { display: flex; flex-wrap: wrap; gap: clamp(24px, 3.6vw, 56px); }
.bp-contact__intro { flex: 1.2 1 400px; min-width: 0; }
.bp-contact__form { flex: 1 1 320px; min-width: 0; }
.bp-contact__lede { margin: 0 0 30px; font: 400 clamp(16.5px, 1.5vw, 19px)/1.72 var(--bp-font-sans); max-width: 58ch; }

/* The About teaser's supporting line runs a touch tighter than the body default. */
.bp-teaser-split__body .bp-body { max-width: 58ch; }

/* ── Type ───────────────────────────────────────────────────────────── */
.bp-eyebrow {
  margin: 0 0 10px;
  font: var(--bp-type-eyebrow);
  letter-spacing: var(--bp-track-eyebrow);
  text-transform: uppercase;
  color: var(--bp-accent-base);
}
/* Display steps. The reference uses a different step per route, deliberately: the home
 * hero is the largest thing on the site, the utility pages are the smallest. Do not
 * collapse these into one step. */
.bp-display { margin: 0 0 clamp(18px,2.4vw,26px); font: var(--bp-type-display); letter-spacing: var(--bp-type-display-track); color: var(--bp-ink-base); max-width: 23ch; text-wrap: pretty; }
.bp-display--work { margin-bottom: 16px; font: 600 clamp(28px,4.4vw,52px)/1.08 var(--bp-font-sans); letter-spacing: -.03em; max-width: 22ch; }
.bp-display--how { margin-bottom: 24px; font: 600 clamp(28px,4.4vw,50px)/1.08 var(--bp-font-sans); letter-spacing: -.032em; max-width: 22ch; }
.bp-display--about { margin-bottom: 24px; font: 600 clamp(26px,4vw,46px)/1.1 var(--bp-font-sans); letter-spacing: -.03em; max-width: 25ch; }
.bp-display--resume { margin-bottom: 20px; font: 600 clamp(26px,4vw,46px)/1.08 var(--bp-font-sans); letter-spacing: -.03em; max-width: 22ch; }
.bp-display--contact { margin-bottom: 20px; font: 600 clamp(28px,4.2vw,48px)/1.06 var(--bp-font-sans); letter-spacing: -.032em; max-width: none; }
/* Product masthead. --lg is for the two products whose names are short enough to carry
 * the larger step (Homebrew, Sophie). */
.bp-h1 { margin: 0 0 16px; font: var(--bp-type-h1-product); letter-spacing: -.032em; color: var(--bp-ink-base); }
.bp-h1--lg { font: var(--bp-type-h1-product-lg); letter-spacing: -.034em; }
.bp-h2 { margin: 0 0 14px; font: var(--bp-type-h2-section); letter-spacing: var(--bp-track-h2); color: var(--bp-ink-base); text-wrap: pretty; }
.bp-h2--page { font: var(--bp-type-h2-page); letter-spacing: var(--bp-track-tight); }
.bp-h2--narrative { font: var(--bp-type-h2-narrative); max-width: 30ch; }
.bp-h3 { margin: 0 0 12px; font: var(--bp-type-h3-product); letter-spacing: var(--bp-track-tight); color: var(--bp-ink-base); }
.bp-h3--sub { font: var(--bp-type-h3-sub); letter-spacing: var(--bp-track-h3); }

.bp-lede { margin: 0; font: var(--bp-type-lede); color: var(--bp-ink-body); max-width: var(--bp-measure-lede); text-wrap: pretty; }
.bp-body { margin: 0; font: var(--bp-type-body); color: var(--bp-ink-body); max-width: var(--bp-measure-body); text-wrap: pretty; }
.bp-body--sm { font: var(--bp-type-body-sm); }
/* About runs a looser leading through its career narrative. */
.bp-body--loose { line-height: 1.78; }
/* About career narrative stack, slightly looser than the default 16px. */
.bp-stack--career { gap: 19px; }
/* Two measures beyond the 66ch default, both of which repeat across routes. */
.bp-body--narrow { max-width: 56ch; }
.bp-body--wide { max-width: 68ch; }
/* Two escapes from the default measure. --full is for copy already constrained by its
   container; --wide on a caption is the How I Work lifecycle note. */
.bp-body--full, .bp-lede--full { max-width: none; }
.bp-caption--wide { max-width: 70ch; }
/* Teaser copy on Home and How I Work: heading measure, then body measure. */
.bp-teaser__h { max-width: 26ch; }
.bp-teaser__p { max-width: 62ch; }
/* Resume opening statement. */
.bp-note--lede { max-width: 64ch; }
/* The product-count line under the /work lede, and the Resume PDF note. */
.bp-count { margin: 0; font: 500 14.5px/1.7 var(--bp-font-sans); color: var(--bp-ink-muted); }
.bp-note { margin: 0; font: 400 15.5px/1.7 var(--bp-font-sans); color: var(--bp-ink-body); max-width: 62ch; text-wrap: pretty; }
.bp-meta { margin: 0; font: var(--bp-type-meta); color: var(--bp-ink-muted); text-wrap: pretty; }
.bp-caption { margin: 0; font: var(--bp-type-caption); color: var(--bp-ink-muted); text-wrap: pretty; }
.bp-label { margin: 0; font: var(--bp-type-label); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-label--accent { color: var(--bp-accent-base); }
/* Accent run inside a heading: the last phrase of the home headline. */
.bp-accent { color: var(--bp-accent-base); }

/* ── Buttons ─────────────────────────────────────────────────────────
 * <a class="bp-btn bp-btn--primary" href="/work">View my work</a>
 * <span class="bp-btn bp-btn--inactive">Watch walkthrough <span class="bp-btn__note">Not yet recorded</span></span>
 *
 * An unavailable destination is a SPAN, never a disabled anchor. It is not focusable
 * and carries a note saying why. Do not ship an <a href="#"> placeholder.
 */
/* --bp-control-h is the MINIMUM box height, not the rendered height: padding and the
 * 15px/1 label decide the actual box, and 46px is the floor that keeps every control a
 * legal touch target. A secondary button renders 15+15+15 = 45px of content and padding,
 * so the floor is what puts it at 46. Primary renders 16+15+16 = 47 and exceeds it.
 * Do not restate 46px as an exact height. */
.bp-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--bp-control-h);
  padding: var(--bp-control-pad);
  border-radius: var(--bp-radius-lg);
  border: 1px solid transparent;
  font: var(--bp-type-control);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--bp-motion-slow), border-color var(--bp-motion-slow), color var(--bp-motion-slow), box-shadow var(--bp-motion-slow);
}
.bp-btn--primary {
  padding: var(--bp-control-pad-primary);
  background: var(--bp-ink-base);
  color: var(--bp-ink-on-navy);
  border-color: var(--bp-ink-base);
  box-shadow: var(--bp-elev-cta);
}
.bp-btn--primary:hover { background: var(--bp-accent-base); border-color: var(--bp-accent-base); color: var(--bp-ink-on-navy); box-shadow: var(--bp-elev-cta-hover); }
.bp-btn--secondary { background: var(--bp-surface-paper); color: var(--bp-ink-base); border-color: var(--bp-line-strong); }
.bp-btn--secondary:hover { background: var(--bp-surface-sunken); border-color: var(--bp-accent-base); color: var(--bp-ink-base); }
.bp-btn--inactive { background: var(--bp-surface-paper); color: var(--bp-ink-muted); border-color: var(--bp-line-hairline); opacity: .7; cursor: default; gap: 9px; }
.bp-btn__note { font: var(--bp-type-label); font-weight: 500; letter-spacing: .06em; text-transform: uppercase; color: var(--bp-ink-faint); }
/* An unavailable control that still needs primary weight in the layout (the Resume
 * download). Navy at reduced opacity, so it reads as the main action, not yet live. */
.bp-btn--inactive-primary { background: var(--bp-ink-base); border-color: var(--bp-ink-base); color: var(--bp-ink-on-navy); opacity: .62; cursor: default; gap: 11px; padding: var(--bp-control-pad-primary); box-shadow: var(--bp-elev-cta); }
/* Its note is sentence case, not the uppercase used on secondary inactive controls. */
.bp-btn--inactive-primary .bp-btn__note { font: 500 11.5px/1 var(--bp-font-sans); letter-spacing: .06em; text-transform: none; color: var(--bp-ink-faint); }

.bp-textlink { display: inline-flex; align-items: center; gap: 7px; min-height: 24px; font: 600 13.5px/1 var(--bp-font-sans); color: var(--bp-accent-base); }
.bp-textlink:hover { color: var(--bp-ink-base); }
.bp-backlink { display: inline-flex; align-items: center; gap: 8px; font: 500 13.5px/1 var(--bp-font-sans); color: var(--bp-ink-muted); }
.bp-backlink:hover { color: var(--bp-ink-base); }

/* ── Tags and chips ─────────────────────────────────────────────────── */
.bp-tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.bp-tags--masthead { margin: 8px 0 16px; }
.bp-tags--flush { margin: 0; }
.bp-tag {
  font: var(--bp-type-tag);
  letter-spacing: var(--bp-track-tag);
  text-transform: uppercase;
  background: var(--bp-accent-tint);
  color: var(--bp-accent-on-tint);
  padding: 7px 11px;
  border-radius: var(--bp-radius-sm);
}
.bp-tag--quiet { background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); color: var(--bp-ink-body); padding: 6px 11px; }
.bp-chips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
.bp-chip { font: 400 13.5px/1 var(--bp-font-sans); background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); color: var(--bp-ink-body); padding: 10px 13px; border-radius: var(--bp-radius-sm); }

/* Availability pill (home hero) and the solid status chip on a product page. These are
 * the only two places a dot indicator is used; do not reuse either as a bullet. */
.bp-status { display: inline-flex; align-items: center; gap: 9px; background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-pill); padding: 7px 14px 7px 11px; font: 500 12.5px/1 var(--bp-font-sans); color: var(--bp-ink-body); }
.bp-status__dot { width: 7px; height: 7px; border-radius: var(--bp-radius-pill); background: var(--bp-state-live); display: block; flex: none; }
/* The availability line above the Home hero heading. */
.bp-status--hero { margin: 0 0 18px; }
.bp-status--solid { background: var(--bp-ink-base); border-color: var(--bp-ink-base); border-radius: var(--bp-radius-md); padding: 11px 14px; font: 600 13px/1 var(--bp-font-sans); color: var(--bp-ink-on-navy); gap: 8px; }
.bp-status--solid .bp-status__dot { background: var(--bp-state-live-on-navy); }
/* The block a solid status chip sits in, beside its one-paragraph explanation. */
.bp-statusbar { background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xl); padding: 20px; display: flex; flex-wrap: wrap; gap: 14px 22px; align-items: center; margin: 0 0 22px; }
.bp-statusbar__note { flex: 1 1 280px; margin: 0; font: 400 15.5px/1.68 var(--bp-font-sans); color: var(--bp-ink-body); text-wrap: pretty; }

/* ── Figure — the primary evidence container ─────────────────────────
 * <figure class="bp-figure">
 *   <img class="bp-figure__img" [src] [alt] [width] [height]>
 *   <figcaption class="bp-figure__cap">…</figcaption>
 * </figure>
 *
 * Screenshots are the largest object on a product page by design. The image is
 * click-to-enlarge (see bp-lightbox) and MUST carry width/height so the row does
 * not reflow while it loads.
 */
.bp-figure {
  margin: 0;
  background: var(--bp-surface-paper);
  border: 1px solid var(--bp-line-hairline);
  border-radius: var(--bp-radius-xxl);
  overflow: hidden;
  box-shadow: var(--bp-elev-card);
}
/* The enlarge trigger is a real <button>, so it is keyboard reachable and announces
 * itself. The image inside it is decorative-by-wrapper: the button carries the label.
 *   <button class="bp-figure__zoom" type="button" aria-label="Enlarge: …">
 *     <img class="bp-figure__img" [src] [alt] [width] [height]>
 *   </button>
 */
.bp-figure__zoom { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.bp-figure__img { display: block; width: 100%; height: auto; }
.bp-figure__cap { border-top: 1px solid var(--bp-line-hairline); padding: 13px 18px; font: var(--bp-type-caption); color: var(--bp-ink-muted); }
.bp-figure__bar {
  padding: 13px 18px;
  border-bottom: 1px solid var(--bp-line-hairline);
  background: var(--bp-surface-sunken);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  align-items: center;
  font: 500 12px/1 var(--bp-font-sans);
  color: var(--bp-ink-body);
}
.bp-figure__bar-title { font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--bp-ink-base); }

/* Window chrome for a placeholder that stands in for a real screen. */
.bp-window__bar { display: flex; align-items: center; gap: 11px; padding: 11px 14px; background: var(--bp-surface-sunken); border-bottom: 1px solid var(--bp-line-hairline); }
.bp-window__lights { display: flex; gap: 5px; }
.bp-window__lights span { width: 8px; height: 8px; border-radius: var(--bp-radius-pill); background: var(--bp-line-strong); display: block; }
.bp-window__name { font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: .06em; text-transform: uppercase; color: var(--bp-ink-muted); }

/* A slot awaiting a real asset. It must READ as unfinished: dashed, labelled, and
 * saying what belongs there. Never fill one with a stand-in graphic. */
.bp-placeholder {
  border: 1px dashed var(--bp-line-strong);
  border-radius: var(--bp-radius-xl);
  background: var(--bp-surface-paper);
  padding: 18px;
  min-height: 158px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.bp-placeholder--figure { margin: 14px; min-height: 300px; align-items: center; justify-content: center; text-align: center; padding: 26px; }
.bp-placeholder__kicker { margin: 0; font: var(--bp-type-label); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-placeholder__title { margin: 0; font: 600 15.5px/1.34 var(--bp-font-sans); letter-spacing: -.018em; color: var(--bp-ink-base); }
.bp-placeholder__desc { margin: 0; font: 400 14px/1.6 var(--bp-font-sans); color: var(--bp-ink-body); text-wrap: pretty; }
/* One slot per page may lead: larger box and type, same dashed treatment. */
.bp-placeholder--flagship { flex: 1 1 460px; min-height: 250px; padding: 20px; }
.bp-placeholder--flagship .bp-placeholder__title { font-size: 18px; }
.bp-placeholder--flagship .bp-placeholder__desc { font-size: 15px; }
/* Grid-cell default, so a set of slots wraps evenly. */
.bp-placeholder--cell { flex: 1 1 240px; min-width: 0; min-height: 154px; padding: 17px; }
/* Content-section slot on an unfinished product page: a wider measure and looser
 * leading, because the text here is a brief to the author, not UI copy. */
.bp-placeholder--brief .bp-placeholder__desc { font-size: 15px; line-height: 1.68; max-width: 62ch; }
/* Two placeholder figure heights: --compact for a card slot, --flagship-figure for the
 * one that leads a product page. */
.bp-placeholder--compact { min-height: 200px; }
.bp-placeholder--lead { min-height: 330px; }
.bp-placeholder__desc--narrow { max-width: 40ch; }
.bp-placeholder__desc--mid { max-width: 46ch; }
.bp-placeholder--figure .bp-placeholder__desc { font-size: 14.5px; line-height: 1.6; max-width: 44ch; }

/* ── Cards ──────────────────────────────────────────────────────────── */
.bp-card { background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xxl); box-shadow: var(--bp-elev-card); overflow: hidden; }
.bp-card--pad { padding: 20px; }
.bp-card--flex { flex: 1 1 250px; min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.bp-card__heading { margin: 0; padding: 14px 16px; border-bottom: 1px solid var(--bp-line-hairline); font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-card__title { margin: 0; font: 600 18px/1.26 var(--bp-font-sans); letter-spacing: -.02em; color: var(--bp-ink-base); }
.bp-card__desc { margin: 0; font: 400 14.5px/1.62 var(--bp-font-sans); color: var(--bp-ink-muted); flex: 1 1 auto; text-wrap: pretty; }

/* Definition rows inside a card: term left, explanation right, hairline between.
 *
 * <dl class="bp-card bp-defs">
 *   <div class="bp-defs__row">
 *     <dt class="bp-defs__term">...</dt>
 *     <dd class="bp-defs__desc">...</dd>
 *   </div>
 * </dl>
 *
 * A real <dl>, so the term/description pairing is in the document rather than only in the
 * class names. The row wrapper is required: dt and dd cannot be flex children of the dl and
 * keep their pairing. margin resets below neutralise the UA defaults on dl and dd.
 *
 * This is the ONLY pattern for "decisions that matter" style content. Do not substitute a
 * bulleted list. */
.bp-defs { margin: 0; }
.bp-defs .bp-defs__desc { margin-inline-start: 0; }
.bp-defs__row { padding: 20px; border-bottom: 1px solid var(--bp-line-hairline); display: flex; flex-wrap: wrap; gap: 12px 26px; }
.bp-defs__row:last-child { border-bottom: 0; }
.bp-defs__term { flex: 0 1 230px; margin: 0; font: 600 16px/1.36 var(--bp-font-sans); letter-spacing: -.018em; color: var(--bp-ink-base); }
.bp-defs__desc { flex: 999 1 280px; margin: 0; font: 400 15.5px/1.7 var(--bp-font-sans); color: var(--bp-ink-body); text-wrap: pretty; }

/* ── Facts list ──────────────────────────────────────────────────────
 * <dl class="bp-facts">
 *   <div class="bp-facts__row">
 *     <dt class="bp-facts__term">Email</dt>
 *     <dd class="bp-facts__desc"><a href="mailto:...">...</a></dd>
 *   </div>
 * </dl>
 *
 * Short label-over-value pairs in a rail or column. Distinct from .bp-defs, which is
 * term-beside-explanation inside a card. The row wrapper is required: dt and dd cannot
 * be flex children of the dl and keep their pairing.
 */
.bp-facts { margin: 0; }
.bp-facts__row { padding: 14px 0; border-bottom: 1px solid var(--bp-line-hairline); }
.bp-facts__term { margin: 0 0 6px; font: var(--bp-type-label); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-facts__desc { margin: 0; font: 400 14.5px/1.6 var(--bp-font-sans); color: var(--bp-ink-body); }
/* Boxed variant: one card, hairline-separated rows, term beside value. This is the
 * Contact and About treatment; the stacked default is for a narrow rail. */
.bp-facts--boxed { background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xl); overflow: hidden; }
.bp-facts--boxed .bp-facts__row { display: flex; flex-wrap: wrap; gap: 8px 24px; align-items: baseline; padding: 16px 18px; }
.bp-facts--boxed .bp-facts__row:last-child { border-bottom: 0; }
.bp-facts--boxed .bp-facts__term { flex: 0 1 100px; margin: 0; font-size: 11.5px; }
.bp-facts--boxed .bp-facts__desc { flex: 999 1 200px; font: 400 16px/1.5 var(--bp-font-sans); color: var(--bp-ink-base); }
.bp-facts--boxed .bp-facts__desc a { display: inline-flex; align-items: center; gap: 8px; }

/* ── Index rows ──────────────────────────────────────────────────────
 * <div class="bp-card">
 *   <a class="bp-index__row" href="/work/homebrew">
 *     <span class="bp-index__label">Homebrew</span><span class="bp-index__n">01</span></a>
 * </div>
 *
 * A numbered list of destinations inside a card (the Resume rail). The last row drops
 * its border; --accent marks a row that is not part of the numbered set.
 */
.bp-index__row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--bp-line-hairline); color: var(--bp-ink-base); min-height: 46px; transition: background var(--bp-motion-base); }
.bp-index__row:last-child { border-bottom: 0; }
.bp-index__row:hover { background: var(--bp-surface-sunken); }
.bp-index__row--accent { color: var(--bp-accent-base); }
.bp-index__label { font: 600 15px/1.35 var(--bp-font-sans); letter-spacing: -.018em; }
.bp-index__n { font: var(--bp-type-label); letter-spacing: .06em; color: var(--bp-ink-muted); }

/* ── Diagram ─────────────────────────────────────────────────────────
 * <div class="bp-diagram">
 *   <span class="bp-diagram__node">Agent</span>
 *   <span class="bp-diagram__link" aria-hidden="true"></span>
 *   <span class="bp-diagram__node bp-diagram__node--navy">Sophie</span>
 * </div>
 *
 * Flat left-to-right relationship chain inside a .bp-figure. Node tone marks role:
 * default is external, --navy is the surface the user works in, --tint is the product
 * being explained. The connector is decorative and aria-hidden.
 */
.bp-diagram { padding: 20px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.bp-diagram__node { flex: 1 1 120px; text-align: center; border-radius: var(--bp-radius-lg); padding: 16px 12px; font: 600 14px/1.3 var(--bp-font-sans); letter-spacing: -.012em; background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); color: var(--bp-ink-base); }
.bp-diagram__node--navy { background: var(--bp-surface-navy); border-color: var(--bp-surface-navy); color: var(--bp-ink-on-navy); }
.bp-diagram__node--tint { background: var(--bp-accent-tint); border-color: var(--bp-accent-tint-border); }
.bp-diagram__link { flex: none; width: 16px; height: 1px; background: var(--bp-line-strong); }

/* ── Lifecycle / stage tiles ─────────────────────────────────────────
 * A stage is neutral by default and tinted when AI is involved. The navy dot marks
 * a stage that waits for a person. Both are semantic, not decorative: do not tint a
 * stage for visual rhythm. */
.bp-tiles { padding: 18px; display: flex; flex-wrap: wrap; gap: 8px; align-items: stretch; }
.bp-tile { flex: 1 1 130px; min-width: 0; background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-lg); padding: 14px; position: relative; }
.bp-tile--ai { background: var(--bp-accent-tint); border-color: var(--bp-accent-tint-border); }
.bp-tile__n { margin: 0 0 9px; font: 600 11px/1 var(--bp-font-sans); letter-spacing: .06em; color: var(--bp-ink-muted); }
.bp-tile--ai .bp-tile__n, .bp-tile--ai .bp-tile__note { color: var(--bp-accent-on-tint); }
.bp-tile__name { margin: 0 0 5px; font: 600 14.5px/1.28 var(--bp-font-sans); color: var(--bp-ink-base); }
.bp-tile__note { margin: 0; font: 400 11.5px/1.35 var(--bp-font-sans); color: var(--bp-ink-muted); }
.bp-tile__flag { position: absolute; top: 13px; right: 13px; width: 9px; height: 9px; border-radius: 3px; background: var(--bp-ink-base); display: block; }
/* How I Work uses five wider stages; Homebrew uses six narrower ones. Same component,
 * one basis/padding modifier, so neither page has to restate tile geometry. */
.bp-tile--wide { flex-basis: 150px; padding: 15px; }
.bp-tile--wide .bp-tile__flag { top: 14px; right: 14px; }
.bp-legend { display: inline-flex; align-items: center; gap: 7px; }
.bp-legend__swatch { width: 9px; height: 9px; border-radius: 3px; display: block; }
/* Semantic, matching .bp-tile--ai and .bp-tile__flag. */
.bp-legend__swatch--ai { background: var(--bp-accent-base); }
.bp-legend__swatch--human { background: var(--bp-ink-base); }

/* ── Form ───────────────────────────────────────────────────────────
 * <form class="bp-form">
 *   <div class="bp-field">
 *     <label class="bp-field__label" for="name">Name</label>
 *     <input class="bp-input" id="name" name="name" type="text" required>
 *   </div>
 *   <button class="bp-btn bp-btn--primary bp-form__submit" type="submit">Send message</button>
 *   <p class="bp-form__result" data-tone="ok" role="status" hidden></p>
 *   <p class="bp-form__result" data-tone="error" role="alert" hidden></p>
 *   <p class="bp-form__note">Delivery runs through a form service…</p>
 * </form>
 *
 * Inputs carry no `required` attribute: validation is one path in js/contact.js, so the
 * browser never shows its own bubble. Inputs share the 46px control height with .bp-btn.
 * The two result blocks are the only place a message appears: no alert(), no toast.
 */
.bp-form { display: flex; flex-direction: column; gap: 15px; }
.bp-field { display: flex; flex-direction: column; gap: 7px; }
.bp-field__label { margin: 0; font: var(--bp-type-label); letter-spacing: .06em; text-transform: uppercase; color: var(--bp-ink-base); }
.bp-input, .bp-textarea {
  font: 400 15.5px/1.6 var(--bp-font-sans);
  color: var(--bp-ink-base);
  background: var(--bp-surface-paper);
  border: 1px solid var(--bp-line-strong);
  border-radius: var(--bp-radius-lg);
  padding: 13px 14px;
}
.bp-input { line-height: 1; padding: 0 14px; min-height: var(--bp-control-h); }
.bp-textarea { resize: vertical; }
.bp-input:focus, .bp-textarea:focus { border-color: var(--bp-accent-base); }
.bp-form__submit { align-self: flex-start; }
/* The form sits in its own card. Fields use the sunken surface so they read as inputs
 * against the white panel. Labels are sentence case, 13.5px, above the field. */
.bp-form--panel { background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-xxl); padding: clamp(22px,3vw,28px); gap: 18px; box-shadow: var(--bp-elev-card); }
.bp-form__heading { margin: 0; font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-form--panel .bp-field__label { font: 500 13.5px/1 var(--bp-font-sans); letter-spacing: 0; text-transform: none; color: var(--bp-ink-body); }
.bp-form--panel .bp-input, .bp-form--panel .bp-textarea { background: var(--bp-surface-sunken); padding: 14px; }
.bp-form--panel .bp-input { font: 400 15px/1.4 var(--bp-font-sans); }
.bp-form--panel .bp-textarea { font: 400 15px/1.55 var(--bp-font-sans); }
.bp-form__submit { border: 0; box-shadow: var(--bp-elev-cta); }
.bp-form__submit[disabled] { opacity: .7; cursor: default; }
/* The delivery note lives inside the panel, under the result blocks. */
.bp-form__note { margin: 0; font: 400 12.5px/1.6 var(--bp-font-sans); color: var(--bp-ink-muted); }
/* Result messages are contained blocks, not a bare coloured line. Two exist because
 * their semantics differ: success is role="status", failure is role="alert". */
.bp-form__result { margin: 0; font: 400 14.5px/1.62 var(--bp-font-sans); border-radius: var(--bp-radius-lg); padding: 14px; }
.bp-form__result[data-tone="ok"] { color: var(--bp-state-ok-text); background: var(--bp-state-ok-bg); border: 1px solid var(--bp-state-ok-border); }
.bp-form__result[data-tone="error"] { color: var(--bp-state-error-text); background: var(--bp-state-error-bg); border: 1px solid var(--bp-state-error-border); }

/* ── Statement band ─────────────────────────────────────────────────── */
.bp-quote { margin: 0 auto; max-width: 26ch; font: 600 clamp(23px,3.2vw,40px)/1.2 var(--bp-font-sans); letter-spacing: -.03em; color: var(--bp-ink-on-navy); text-align: center; text-wrap: pretty; }
.bp-quote__sub { margin: 18px auto 0; max-width: 56ch; font: var(--bp-type-meta); color: var(--bp-ink-on-navy-soft); text-align: center; text-wrap: pretty; }

/* ── Shell: header ──────────────────────────────────────────────────── */
.bp-header { position: sticky; top: 0; z-index: var(--bp-z-header); background: var(--bp-surface-header); -webkit-backdrop-filter: blur(var(--bp-blur-header)); backdrop-filter: blur(var(--bp-blur-header)); border-bottom: 1px solid var(--bp-line-hairline); }
.bp-header__inner { display: flex; align-items: center; justify-content: space-between; gap: 20px; height: var(--bp-header-h); }
.bp-wordmark { display: flex; align-items: center; gap: 11px; flex: none; }
/* The shell owns the wordmark's structure and path geometry; its colour is token-driven. */
.bp-wordmark__plate { fill: var(--bp-ink-base); }
.bp-wordmark__glyph { fill: none; stroke: var(--bp-ink-on-navy); }
.bp-wordmark__name { font: 600 15px/1 var(--bp-font-sans); letter-spacing: -.012em; color: var(--bp-ink-base); display: block; }
.bp-wordmark__role { font: 500 11px/1 var(--bp-font-sans); letter-spacing: .04em; color: var(--bp-ink-muted); display: block; }
.bp-nav { display: flex; align-items: center; gap: clamp(10px,1.6vw,22px); }
.bp-nav__link { position: relative; font: 500 14.5px/1 var(--bp-font-sans); color: var(--bp-ink-body); padding: 9px 2px; white-space: nowrap; transition: color var(--bp-motion-base); }
.bp-nav__link:hover { color: var(--bp-ink-base); }
/* Current-page marker. Also set aria-current="page" — the underline alone is not
 * available to assistive tech. */
.bp-nav__link[aria-current="page"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--bp-accent-base); border-radius: 2px; }
.bp-nav__cta { background: var(--bp-ink-base); color: var(--bp-ink-on-navy); border-radius: var(--bp-radius-md); padding: 12px 18px; margin-left: 6px; display: inline-flex; align-items: center; font: 600 14.5px/1 var(--bp-font-sans); transition: background var(--bp-motion-slow); }
.bp-nav__cta:hover { background: var(--bp-accent-base); color: var(--bp-ink-on-navy); }
.bp-menu-btn { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 44px; height: 44px; flex: none; background: var(--bp-surface-paper); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-lg); cursor: pointer; }
.bp-menu-btn span { width: 16px; height: 1.5px; background: var(--bp-ink-base); border-radius: 2px; display: block; }
/* Disclosure panel. Padding matches the container gutter so the links line up with the
 * wordmark above them. */
.bp-menu { display: flex; flex-direction: column; gap: 2px; border-top: 1px solid var(--bp-line-hairline); padding: 8px var(--bp-container-pad) 18px; }
.bp-menu a { display: flex; align-items: center; padding: 14px 12px; border-radius: var(--bp-radius-lg); font: 500 16.5px/1 var(--bp-font-sans); color: var(--bp-ink-base); min-height: var(--bp-control-h); }
.bp-menu a:hover { background: var(--bp-surface-sunken); }
.bp-menu a[aria-current="page"] { background: var(--bp-accent-tint); color: var(--bp-accent-on-tint); }

/* ── Shell: on-this-page rail ───────────────────────────────────────── */
.bp-toc { flex: 0 1 200px; min-width: 0; align-self: flex-start; position: sticky; top: calc(var(--bp-header-h) + 4px); z-index: var(--bp-z-rail); background: var(--bp-surface-paper); padding: clamp(14px,3.2vw,40px) 0 0; }
/* Wide: a static heading and an always-open list. Narrow: a disclosure button and a list
 * that collapses. Both live here so the four product pages cannot drift apart; js/nav.js
 * owns only the state, never the geometry. */
.bp-toc__disclosure { display: none; width: 100%; align-items: center; justify-content: space-between; gap: 12px; background: var(--bp-surface-sunken); border: 1px solid var(--bp-line-hairline); border-radius: var(--bp-radius-lg); padding: 13px 15px; min-height: var(--bp-control-h); cursor: pointer; font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-base); }
.bp-toc__disclosure-sign { font: 400 17px/1 var(--bp-font-sans); color: var(--bp-ink-muted); }
.bp-toc__list { display: flex; flex-direction: column; gap: 1px; padding-top: 8px; }
.bp-toc[data-narrow="true"] .bp-toc__heading { display: none; }
.bp-toc[data-narrow="true"] .bp-toc__disclosure { display: flex; }
.bp-toc[data-narrow="true"] .bp-toc__list[hidden] { display: none; }
.bp-toc__btn { width: 100%; text-align: left; border: 0; background: transparent; border-radius: var(--bp-radius-md); padding: 10px 12px; cursor: pointer; display: flex; gap: 10px; align-items: baseline; font: 500 13.5px/1.35 var(--bp-font-sans); color: var(--bp-ink-body); transition: background var(--bp-motion-base), color var(--bp-motion-base); }
.bp-toc__btn:hover { background: var(--bp-surface-sunken); color: var(--bp-ink-base); }
.bp-toc__btn[aria-current="true"] { background: var(--bp-accent-tint); color: var(--bp-ink-base); }
.bp-toc__n { font: 600 10.5px/1.5 var(--bp-font-sans); letter-spacing: .05em; flex: none; color: var(--bp-ink-muted); }
.bp-toc__heading { margin: 0 0 10px; font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }

/* ── Shell: footer ──────────────────────────────────────────────────── */
.bp-footer { padding: clamp(30px, 4vw, 48px) 0 clamp(22px, 3vw, 30px); border-top: 1px solid var(--bp-line-hairline); background: var(--bp-surface-sunken); }
.bp-footer__cols { display: flex; flex-wrap: wrap; gap: 24px 44px; margin: 0 0 26px; }
.bp-footer__nav { display: flex; flex-direction: column; }
.bp-footer__nav a { padding: 7px 0; font: 400 14.5px/1 var(--bp-font-sans); color: var(--bp-ink-body); }
.bp-footer__nav a:hover { color: var(--bp-accent-base); }
.bp-footer__heading { margin: 0 0 10px; font: 600 11.5px/1 var(--bp-font-sans); letter-spacing: var(--bp-track-label); text-transform: uppercase; color: var(--bp-ink-muted); }
.bp-footer__brand { flex: 1 1 250px; min-width: 0; }
.bp-footer__name { margin: 0 0 9px; font: 600 15px/1 var(--bp-font-sans); letter-spacing: -.012em; color: var(--bp-ink-base); }
.bp-footer__note { margin: 0; font: 400 14px/1.65 var(--bp-font-sans); color: var(--bp-ink-muted); max-width: 36ch; }
/* Column widths differ on purpose so the three navs balance against the brand block. */
.bp-footer__nav--pages { flex: 0 1 140px; }
.bp-footer__nav--work { flex: 0 1 200px; }
.bp-footer__nav--links { flex: 0 1 130px; }
.bp-footer__legal { margin: 0; padding-top: 16px; border-top: 1px solid var(--bp-line-hairline); display: flex; flex-wrap: wrap; gap: 8px 22px; justify-content: space-between; font: 400 13px/1.5 var(--bp-font-sans); color: var(--bp-ink-muted); }
.bp-footer__legal p { margin: 0; }

/* ── Lightbox ────────────────────────────────────────────────────────
 * Opens at the screenshot's NATURAL width and scrolls, because these are dense
 * enterprise screens and a fit-to-viewport view is unreadable. "Fit to screen"
 * is the secondary state, not the default.
 *
 * Focus contract, implemented in js/lightbox.js: opening moves focus to the Close
 * control, focus is trapped inside while open, Escape / Close / backdrop all dismiss,
 * and focus returns to the exact .bp-figure__zoom button that opened it.
 */
.bp-lightbox { position: fixed; inset: 0; z-index: 200; background: var(--bp-scrim); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); display: flex; flex-direction: column; }
.bp-lightbox__stage { flex: 1 1 auto; min-height: 0; overflow: auto; padding: clamp(52px,6vw,64px) clamp(8px,1.2vw,20px) clamp(10px,1.4vw,18px); cursor: zoom-out; -webkit-overflow-scrolling: touch; }
.bp-lightbox__img { display: block; margin: 0 auto; max-width: none; height: auto; border: 1px solid var(--bp-line-on-navy-soft); border-radius: 6px; background: var(--bp-surface-paper); cursor: zoom-out; }
.bp-lightbox__img--fit { max-width: 100%; max-height: 100%; width: auto; cursor: zoom-in; }
.bp-lightbox__bar { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px 18px; padding: 12px clamp(10px,2vw,22px) clamp(12px,1.6vw,18px); border-top: 1px solid var(--bp-line-on-navy); }
.bp-lightbox__cap { margin: 0; max-width: 74ch; text-align: center; font: var(--bp-type-caption); color: var(--bp-line-strong); }
.bp-lightbox__tools { position: absolute; top: clamp(10px,1.6vw,18px); right: clamp(10px,1.6vw,18px); display: flex; gap: 8px; }
.bp-lightbox__tool { display: inline-flex; align-items: center; gap: 8px; background: var(--bp-lightbox-tool); border: 1px solid var(--bp-line-lightbox-tool); color: var(--bp-ink-on-navy); font: 600 13.5px/1 var(--bp-font-sans); padding: 11px 15px; border-radius: var(--bp-radius-lg); min-height: 44px; cursor: pointer; }
.bp-lightbox__tool:hover { background: var(--bp-lightbox-tool-hover); }

/* ── Skip link ───────────────────────────────────────────────────────
 * First focusable element in the shell, before the header:
 *   <a class="bp-skip" href="#main">Skip to content</a>
 * Off-screen until focused, then it lands under the sticky header.
 */
.bp-skip { position: absolute; left: 12px; top: -60px; z-index: calc(var(--bp-z-header) + 1); background: var(--bp-ink-base); color: var(--bp-ink-on-navy); font: 600 14px/1 var(--bp-font-sans); padding: 13px 16px; border-radius: var(--bp-radius-lg); transition: top var(--bp-motion-base); }
.bp-skip:focus-visible { top: 12px; color: var(--bp-ink-on-navy); }

/* ── Focus ──────────────────────────────────────────────────────────── */
:where(a, button, [tabindex]):focus-visible { outline: 2px solid var(--bp-accent-base); outline-offset: 2px; border-radius: 4px; }

/* ── Motion preference ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 1ms !important; animation-duration: 1ms !important; }
}
