/* ==========================================================================
   Hollis & Grey — site.css
   Everything that is not a token. Every value below resolves through
   var(--token) from tokens.css. If you need a value that is not a token,
   add it to tokens.css and tokens.json first.

   Sections
   01 Fonts
   02 Reset and base
   03 Layout: container, sections, rhythm
   04 Type roles
   05 Header, nav, mobile menu
   06 Buttons and links
   07 Hero
   08 Symptom grid
   09 Practice fork
   10 Statement band
   11 Steps, tool cards, case study, testimonial, FAQ
   12 Contact page and forms
   13 Footer
   14 Media, figures
   15 Motion and reduced motion
   16 Breakpoints
   ========================================================================== */

/* 01 Fonts ---------------------------------------------------------------- */
/* Variable latin subsets, self-hosted. One file per family covers every
   shipped weight, so nothing loads twice. */
@font-face {
  font-family: 'Jost';
  src: url('../fonts/jost-variable-latin.woff2') format('woff2-variations'),
       url('../fonts/jost-variable-latin.woff2') format('woff2');
  font-weight: 300 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-display-variable-latin.woff2') format('woff2-variations'),
       url('../fonts/playfair-display-variable-latin.woff2') format('woff2');
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122, U+2191, U+2193, U+2212, U+FEFF, U+FFFD;
}

/* 02 Reset and base ------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-surface-primary);
  color: var(--color-text-body);
  font-family: var(--font-sans);
  font-size: var(--type-body);
  font-weight: var(--weight-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; }
ul { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }

a {
  color: var(--color-text-accent);
  text-decoration: none;
}
a:hover { color: var(--color-text-primary); }

:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus-ring);
  outline-offset: var(--focus-ring-offset);
}

.skip-link {
  position: absolute;
  left: var(--space-3);
  top: var(--space-3);
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse);
  font-size: var(--type-button);
  letter-spacing: var(--tracking-button);
  text-transform: uppercase;
  transform: translateY(-200%);
}
.skip-link:focus {
  transform: translateY(0);
  color: var(--color-text-inverse);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* 03 Layout --------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: var(--container-narrow); }

.section {
  padding-block: var(--section-padding-block);
}
.section--cream { background: var(--color-surface-primary); }
.section--tint { background: var(--color-surface-alt); }
.section--inverse {
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse-body);
}
.section--center { text-align: center; }

/* Sentences that land need air around them. */
.stack { display: flex; flex-direction: column; }
.stack--tight { gap: var(--space-3); }
.stack--copy { gap: var(--space-3); }
.stack--block { gap: var(--space-4); }
.stack--section { gap: var(--space-6); }

.section--center .stack { align-items: center; }
.section--center .measure { margin-inline: auto; }

.measure { max-width: var(--measure-body); }
.measure--lead { max-width: var(--measure-lead); }
.measure--quote { max-width: var(--measure-quote); }
.measure--heading { max-width: 24ch; }

/* 04 Type roles ----------------------------------------------------------- */
.display,
.h1, .h2, .h3 {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  color: var(--color-text-primary);
  text-wrap: pretty;
}
.display {
  font-size: var(--type-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  max-width: 20ch;
}
.h1 { font-size: var(--type-h1); line-height: var(--leading-heading); letter-spacing: var(--tracking-h1); max-width: 18ch; }
.h2 { font-size: var(--type-h2); line-height: var(--leading-heading); letter-spacing: var(--tracking-heading); }
.h3 { font-size: var(--type-h3); line-height: var(--leading-h3); letter-spacing: var(--tracking-h3); }

.section--inverse .display,
.section--inverse .h1,
.section--inverse .h2,
.section--inverse .h3 { color: var(--color-text-inverse); }

.eyebrow {
  font-size: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-accent);
}
.section--inverse .eyebrow { color: var(--color-text-inverse-accent); }

.label {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.lead {
  font-size: var(--type-lead);
  font-weight: var(--weight-lead);
  line-height: var(--leading-lead);
  letter-spacing: var(--tracking-lead);
  color: var(--color-text-body);
  max-width: var(--measure-lead);
  text-wrap: pretty;
}
.section--inverse .lead { color: var(--color-text-inverse-body); }

.body { max-width: var(--measure-body); text-wrap: pretty; }
.section--inverse .body,
.section--inverse p { color: var(--color-text-inverse-body); }

.proof {
  font-size: var(--type-lead);
  font-style: italic;
  font-weight: var(--weight-lead);
  line-height: var(--leading-lead);
  color: var(--color-text-accent);
  max-width: var(--measure-quote);
  text-wrap: pretty;
}

.punch {
  font-size: var(--type-body);
  font-weight: var(--weight-emphasis);
  color: var(--color-text-primary);
  margin-top: var(--space-4);
}

.aside {
  font-size: var(--type-caption);
  font-style: italic;
  line-height: 1.7;
  color: var(--color-text-muted);
  max-width: var(--measure-quote);
}

strong { font-weight: var(--weight-emphasis); color: var(--color-text-primary); }
.section--inverse strong { color: var(--color-text-inverse); }

/* 05 Header, nav, mobile menu --------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--color-surface-inverse);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-3);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target-min);
  flex: none;                             /* the wordmark never gives way to the nav */
}
.brand__keyline {
  width: 34px;
  height: 34px;
  border: 1px solid var(--color-border-inverse);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 9.6px;
  flex: none;
}
.brand__mark { width: 25.4px; height: auto; }
.brand__wordmark { height: 12px; width: auto; }

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-5);
}
.nav__link {
  display: inline-flex;
  white-space: nowrap;
  align-items: center;
  min-height: var(--tap-target-min);
  font-size: var(--type-nav);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
  color: var(--color-text-inverse-muted);
  transition: color var(--duration-state) var(--ease-state);
}
.nav__link:hover { color: var(--color-text-inverse); }
.nav__link[aria-current='page'] {
  color: var(--color-text-inverse);
  border-bottom: 1px solid var(--color-accent-rule);
}
.site-header :focus-visible { outline-color: var(--color-focus-ring-inverse); }

.menu-toggle {
  display: none;
  width: var(--tap-target-min);
  height: var(--tap-target-min);
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text-inverse);
  cursor: pointer;
}
.menu-toggle__bars {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: center;
}
.menu-toggle__bars span {
  display: block;
  width: 20px;
  height: 1px;
  background: currentColor;
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--color-surface-inverse);
  padding: var(--space-4);
  display: none;
  flex-direction: column;
}
.mobile-menu[data-open='true'] { display: flex; }
.mobile-menu__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-6);
}
.mobile-menu__close {
  width: var(--tap-target-min);
  height: var(--tap-target-min);
  border: 0;
  background: none;
  color: var(--color-text-inverse);
  font-size: var(--type-h3);
  line-height: 1;
  cursor: pointer;
}
.mobile-menu__list { display: flex; flex-direction: column; }
.mobile-menu__link {
  display: flex;
  align-items: center;
  min-height: 56px;
  font-family: var(--font-serif);
  font-size: var(--type-h2);
  color: var(--color-text-inverse);
  border-bottom: 1px solid var(--color-border-inverse);
}
.mobile-menu__cta { margin-top: var(--space-5); }
.mobile-menu :focus-visible { outline-color: var(--color-focus-ring-inverse); }

/* 06 Buttons and links ---------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-target-min);
  padding: var(--space-2) var(--space-5);
  border: 1px solid transparent;
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse);
  font-family: var(--font-sans);
  font-size: var(--type-button);
  font-weight: var(--weight-body);
  letter-spacing: var(--tracking-button);
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--duration-state) var(--ease-state);
}
.btn:hover { background: var(--color-surface-inverse-2); color: var(--color-text-inverse); }
.btn:active { background: var(--color-navy-700); }
.btn[disabled] {
  background: var(--color-cream-400);
  color: var(--color-text-muted);
  cursor: not-allowed;
}
.btn--inverse { background: var(--color-surface-primary); color: var(--color-text-primary); }
.btn--inverse:hover { background: var(--color-cream-50); color: var(--color-text-primary); }
.btn--inverse:active { background: var(--color-cream-300); }

.link-rule {
  display: inline-flex;
  align-items: flex-end;
  min-height: var(--tap-target-min);
  padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--color-accent-rule);
  color: var(--color-text-primary);
  font-size: var(--type-button);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: border-color var(--duration-state) var(--ease-state);
}
.link-rule:hover { border-color: var(--color-text-primary); color: var(--color-text-primary); }
.section--inverse .link-rule { color: var(--color-text-inverse); }
.section--inverse .link-rule:hover { border-color: var(--color-text-inverse); color: var(--color-text-inverse); }

/* Navy islands — the primary fork door and the primary contact path — sit
   inside cream sections, so `.section--inverse .link-rule` never matches them.
   Without this the link is navy ink on a navy ground: 1.0 contrast, invisible.
   Everything else inside those blocks already has its own inverse override;
   the link was the one that was missed. */
.door--primary .link-rule,
.path--primary .link-rule { color: var(--color-text-inverse); }
.door--primary .link-rule:hover,
.path--primary .link-rule:hover {
  color: var(--color-text-inverse);
  border-color: var(--color-text-inverse);
}

/* 07 Hero ----------------------------------------------------------------- */
.hero {
  padding-block: var(--space-8) var(--space-7);
  text-align: center;
}
.hero .stack { align-items: center; gap: var(--space-4); }
.hero__cta { margin-top: var(--space-2); }

/* 08 Symptom grid --------------------------------------------------------- */
.symptoms {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5) var(--space-6);
}
.symptom {
  font-size: var(--type-body-small);
  line-height: 1.75;
  text-wrap: pretty;
}

/* 09 Practice fork -------------------------------------------------------- */
.fork {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: var(--space-5);
  align-items: start;
}
.door {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
}
.door--primary {
  background: var(--color-surface-inverse);
  padding: var(--space-6) var(--space-5);
}
.door--primary .door__name { font-size: var(--type-h2); color: var(--color-text-inverse); }
.door--primary .door__copy { color: var(--color-text-inverse-body); font-size: var(--type-body); }
.door--primary .label { color: var(--color-text-inverse-accent); }
.door--secondary {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-5) var(--space-4);
}
.door--secondary .door__name { font-size: var(--type-h3); }
.door--secondary .door__copy { font-size: var(--type-body-small); line-height: 1.75; }
.door__name {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  color: var(--color-text-primary);
  line-height: var(--leading-h3);
}
.door .link-rule { align-self: flex-start; margin-top: auto; }

/* 10 Statement band ------------------------------------------------------- */
.statement { text-align: center; }
.statement .stack { align-items: center; gap: var(--space-4); }

/* 11 Steps, tool cards, case study, testimonial, FAQ ----------------------- */
.steps { display: flex; flex-direction: column; gap: var(--space-6); }
.step__number {
  font-family: var(--font-serif);
  font-size: var(--type-body-small);
  letter-spacing: 0.16em;
  color: var(--color-text-accent);
}
.step__name {
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  color: var(--color-text-primary);
  line-height: var(--leading-h3);
}

.cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}
.card {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.card__name {
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  color: var(--color-text-primary);
}
.card__copy { font-size: var(--type-body-small); line-height: 1.75; }

.case {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-6);
}
.case__stats {
  font-size: var(--type-body-small);
  letter-spacing: 0.14em;
  color: var(--color-text-accent);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border-hairline);
}
.case__block { margin-top: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2); }

.quote {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.quote__text {
  font-size: var(--type-quote);
  font-style: italic;
  font-weight: var(--weight-lead);
  line-height: 1.7;
}
.quote__who { font-size: var(--type-body-small); font-weight: var(--weight-emphasis); color: var(--color-text-primary); }
.quote__role { font-size: var(--type-caption); color: var(--color-text-muted); }

.faq { border-top: 1px solid var(--color-border-hairline); padding-block: var(--space-4); }
.faq:last-of-type { border-bottom: 1px solid var(--color-border-hairline); }
.faq__q {
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  color: var(--color-text-primary);
  line-height: var(--leading-h3);
  margin-bottom: var(--space-2);
}

/* 12 Contact page and forms ----------------------------------------------- */
.paths {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: var(--space-5);
  align-items: start;
}
.path {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
}
.path--primary { background: var(--color-surface-inverse); padding: var(--space-6) var(--space-5); }
.path--primary .path__name { color: var(--color-text-inverse); }
.path--primary .path__copy { color: var(--color-text-inverse-body); }
.path--primary .label { color: var(--color-text-inverse-accent); }
.path--secondary {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-5) var(--space-4);
}
.path__name {
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  color: var(--color-text-primary);
  line-height: var(--leading-h3);
}
.path__copy { font-size: var(--type-body-small); line-height: 1.75; }
.path .btn, .path .link-rule { align-self: flex-start; margin-top: auto; }

.form { display: flex; flex-direction: column; gap: var(--space-4); max-width: var(--measure-body); }
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field__label { font-size: var(--type-caption); color: var(--color-text-primary); }
.field__help { font-size: var(--type-caption); color: var(--color-text-muted); }
.field input,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--space-3);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-strong);
  color: var(--color-text-primary);
  font-family: var(--font-sans);
  font-size: var(--type-body);
  line-height: 1.7;
}
.field textarea { padding-block: var(--space-2); resize: vertical; min-height: 140px; }
.field input::placeholder,
.field textarea::placeholder { color: var(--color-text-muted); }
.field input:hover,
.field textarea:hover { border-color: var(--color-text-primary); }
.field input:user-invalid,
.field textarea:user-invalid {
  border-color: var(--color-feedback-error);
  background: var(--color-feedback-error-surface);
}
.field input:user-invalid + .field__help,
.field textarea:user-invalid + .field__help { color: var(--color-feedback-error); }
.form__submit { align-self: flex-start; }
.form__note { font-size: var(--type-caption); color: var(--color-text-muted); max-width: var(--measure-quote); }
.hidden { position: absolute; left: -9999px; }

.notice {
  border: 1px solid var(--color-feedback-success);
  background: var(--color-feedback-success-surface);
  padding: var(--space-3);
  font-size: var(--type-body-small);
  color: var(--color-text-primary);
}

/* 13 Footer ---------------------------------------------------------------
   Two rows. The brand (lockup over its descriptor) and the six links — two
   columns of three, right-aligned — share one band; the base line carries the
   copyright and the one social. It was two stacked columns
   over a base row and stood ~400px tall for four links; this is about half. */
.site-footer {
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse-muted);
  padding-block: var(--space-5) var(--space-4);
}
.site-footer a { color: var(--color-text-inverse-muted); }
.site-footer a:hover { color: var(--color-text-inverse); }

.site-footer__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4) var(--space-6);
  flex-wrap: wrap;
}
.site-footer__brand { display: flex; flex-direction: column; gap: var(--space-2); }
/* Two headed columns beside the brand, aligned to the right edge: "Company
   Links" (the pages plus "Contact Us", which goes to the booking page) and
   "Resources" (the two assessments). The heads are the same size as the
   links, in the light blue accent, so they read as column titles rather than
   as more links. */
.footer-nav {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  gap: var(--space-6);
}
.footer-nav__col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.footer-nav__head {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  margin: 0;
  font-size: var(--type-body-small);
  font-weight: var(--weight-emphasis);
  color: var(--color-text-inverse-accent);
  white-space: nowrap;
}
.footer-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--type-body-small);
  white-space: nowrap;
}

.site-footer__base {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-inverse);
}
.copyright { font-size: var(--type-body-small); letter-spacing: 0.1em; color: var(--color-text-inverse-accent); }
.copyright a { color: var(--color-text-inverse-accent); }
.site-footer__tag { color: var(--color-text-inverse-accent); }
.site-footer__base .copyright { margin-right: auto; }
.socials { display: flex; gap: var(--space-2); }
.socials a {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border-inverse);
  font-size: var(--type-caption);
  transition: border-color var(--duration-state) var(--ease-state);
}
.socials a:hover { border-color: var(--color-text-inverse); }
.site-footer :focus-visible { outline-color: var(--color-focus-ring-inverse); }

@media (max-width: 759px) {
  .site-footer__row { flex-direction: column; align-items: flex-start; gap: var(--space-3); }
  .footer-nav { flex-direction: column; justify-content: flex-start; gap: var(--space-4); }
}

/* 14 Media, figures ------------------------------------------------------- */
.figure { margin: 0; }
.figure img { width: 100%; height: auto; }
.figure__caption { margin-top: var(--space-2); font-size: var(--type-caption); color: var(--color-text-muted); }

/* 15 Motion --------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* 16 Breakpoints ---------------------------------------------------------- */
@media (max-width: 1023px) {
  .symptoms { grid-template-columns: repeat(2, 1fr); }
}

/* The desktop nav needs the 1024 breakpoint, not 760: four items, a long
   dropdown label and a CTA button do not fit on one line below that, and the
   labels were wrapping. The full-screen menu takes over earlier instead. */
@media (max-width: 1023px) {
  .nav { display: none; }
  .menu-toggle { display: inline-flex; align-items: center; justify-content: center; }
}

@media (max-width: 759px) {
  .symptoms { grid-template-columns: 1fr; }
  .fork,
  .paths { grid-template-columns: 1fr; }
  .cards { grid-template-columns: 1fr; }
  .hero { padding-block: var(--space-6) var(--space-6); }
  .door--primary { padding: var(--space-5) var(--space-4); }
  .case { padding: var(--space-4); }
  .btn { width: 100%; }
  .path .btn { width: auto; }
}

/* 17 Calendly embed container -------------------------------------------- */
.embed {
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-6) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 620px;
  align-items: flex-start;
  justify-content: center;
}
.embed[data-placeholder] {
  border-style: dashed;
  border-color: var(--color-border-strong);
}
.embed__url {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--type-caption);
  color: var(--color-text-muted);
  word-break: break-all;
}

/* 18 Round 2: bio, testimonial grid, section heading, portrait ------------ */
.section-heading {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h2);
  line-height: var(--leading-heading);
  color: var(--color-text-primary);
  text-align: center;
  max-width: 28ch;
  margin-inline: auto;
  text-wrap: balance;
}

.bio {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: var(--space-6);
  align-items: start;
}
.bio__portrait { margin: 0; }
.bio__portrait img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; }
.bio__body { display: flex; flex-direction: column; gap: var(--space-3); }
.bio__name { font-family: var(--font-serif); font-size: var(--type-h2); color: var(--color-text-primary); line-height: var(--leading-heading); }
.bio__line { font-family: var(--font-serif); font-size: var(--type-h3); color: var(--color-text-primary); line-height: var(--leading-h3); }

.quotes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  align-items: start;
}
.quote--single { max-width: var(--measure-body); }

.narrow-block { max-width: var(--container-narrow); margin-inline: auto; }

@media (max-width: 1023px) {
  .quotes { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 759px) {
  .quotes { grid-template-columns: 1fr; }
  .bio { grid-template-columns: 1fr; gap: var(--space-4); }
  .bio__portrait { max-width: 260px; }
}

/* 19 Round 3: booking embed, legal long-form, utility pages --------------- */
/* Full container width: Calendly only shows its two-column desktop layout
   (event details beside the calendar, then the times beside that) when it
   has about 1,000px to work with. Narrower than that it falls back to a
   stacked layout that scrolls inside the frame, which reads as broken. The
   widget carries data-resize="true", so Calendly sets the iframe height to
   the page it is showing; min-height is only the floor while it loads.
   No box of our own around it: Calendly draws its own white card, and the
   embed's background_color is set to this band's cream (surface-alt) so the
   card sits straight on the page, the way it does on calendly.com. */
.booking { width: 100%; }
.booking__widget { min-height: 520px; }
/* Google Calendar's schedule page in an iframe. Google gives no resize
   signal, so the frame is a fixed window. Measured September 2026 at the
   frame's 1044px desktop width: Google's page ends at 616px with the
   description collapsed (the default) and 765px expanded, with the five
   slot rows it always draws. 800px fits both; a day with more than about
   six slots scrolls inside the frame. Tablet measured 785px expanded at
   720px wide. A phone gets Google's stacked page, which is taller than any
   sensible frame, so it scrolls. */
.booking__frame { display: block; width: 100%; height: 800px; border: 0; background: var(--color-surface-raised); }
@media (max-width: 1023px) { .booking__frame { height: 840px; } }
@media (max-width: 759px) { .booking__frame { height: 900px; } }
.booking__note { max-width: none; }
.booking__fallback {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-5);
}

.legal { max-width: var(--measure-legal); }
.legal h2 {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  color: var(--color-text-primary);
  margin-top: var(--space-6);
  margin-bottom: var(--space-3);
}
.legal h2:first-child { margin-top: 0; }
.legal h3 {
  font-family: var(--font-sans);
  font-weight: var(--weight-emphasis);
  font-size: var(--type-body);
  line-height: var(--leading-tight);
  color: var(--color-text-primary);
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
}
.legal p { margin-bottom: var(--space-3); }
.legal p:last-child { margin-bottom: 0; }
.legal ul {
  list-style: none;
  margin-bottom: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.legal li { padding-left: var(--space-3); border-left: 1px solid var(--color-accent-rule-soft); }
.legal__meta {
  font-size: var(--type-caption);
  color: var(--color-text-muted);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border-hairline);
}

/* Staging only. Delete this block and the .draft-banner element to launch. */
.draft-banner {
  background: var(--internal-gap-surface);
  border: 1px dashed var(--internal-gap-border);
  color: var(--internal-gap-text);
  padding: var(--space-3) var(--space-4);
  font-size: var(--type-body-small);
  line-height: var(--leading-tight);
  max-width: var(--container-narrow);
  margin-inline: auto;
  margin-bottom: var(--space-6);
}

.error-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: center;
  margin-top: var(--space-3);
}

/* ==========================================================================
   20 Round 4 — the visual system
   Graphics complement the approved copy. They sit beside it or beneath it.
   They never reorganise it, and they never add text to the page.
   ========================================================================== */

/* 20.1 Split hero: navy, copy left, image slot right ---------------------- */
.hero--split {
  padding-block: var(--space-7);
  text-align: left;
}
.hero--split .hero__inner {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: var(--space-7);
  align-items: center;
}
.hero--split .stack { align-items: flex-start; gap: var(--space-4); }
.hero--split .display { max-width: 15ch; }
.hero--split .lead,
.hero--split .proof { margin-inline: 0; }
.hero--split .proof { color: var(--color-text-inverse-accent); }
.hero--split .hero__cta { margin-top: var(--space-2); }

/* Half-height band for pages whose job is the form or the embed. */
.hero--band { padding-block: var(--space-6); text-align: left; }
.hero--band .stack { align-items: flex-start; }
.hero--band .proof { color: var(--color-text-inverse-accent); }

.section--inverse .btn {
  background: var(--color-surface-primary);
  color: var(--color-text-primary);
}
.section--inverse .btn:hover { background: var(--color-cream-50); color: var(--color-text-primary); }
.section--inverse .btn:active { background: var(--color-cream-300); }
.section--inverse .aside { color: var(--color-text-inverse-muted); }
.section--inverse :focus-visible { outline-color: var(--color-focus-ring-inverse); }

/* 20.2 Image slot ---------------------------------------------------------
   Holds photography when it exists. Until then a quiet abstract placeholder,
   drawn from planes and hairlines, with no subject and nothing to read. */
.hero__slot {
  position: relative;
  width: 100%;
  max-width: 520px;
  justify-self: end;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--slot-border);
  background: var(--slot-ground);
  overflow: hidden;
}
.hero__slot > svg,
.hero__slot > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.slot-tag {
  position: absolute;
  left: var(--space-3);
  bottom: var(--space-3);
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--slot-tag);
}
.plane-1 { fill: var(--plane-1); }
.plane-2 { fill: var(--plane-2); }
.hair-1 { stroke: var(--hairline-1); stroke-width: 1; fill: none; }
.hair-2 { stroke: var(--hairline-2); stroke-width: 1; fill: none; }

/* 20.3 Glyphs -------------------------------------------------------------
   One fixed icon column, so six icons in a grid share a left edge and a top
   line regardless of how long each sentence runs. */
.icon {
  width: var(--icon-size);
  height: var(--icon-size);
  flex: none;
  fill: none;
  stroke: var(--color-text-primary);
  stroke-width: var(--stroke-glyph);
  stroke-linecap: square;
  stroke-linejoin: miter;
}
.icon .accent { stroke: var(--color-accent-rule); }
.section--inverse .icon { stroke: var(--color-text-inverse); }
.section--inverse .icon .accent { stroke: var(--color-text-inverse-accent); }

.symptom {
  display: grid;
  grid-template-columns: var(--icon-col) 1fr;
  gap: 0 var(--space-3);
  align-items: start;
}
.symptom .icon { margin-top: 3px; }

.card__icon { margin-bottom: var(--space-1); }
.door__icon { margin-bottom: var(--space-2); }
.door--primary .door__icon { stroke: var(--color-text-inverse); }
.door--primary .door__icon .accent { stroke: var(--color-text-inverse-accent); }

/* 20.4 Instruments --------------------------------------------------------
   Small data drawings that sit beneath the sentence they illustrate and
   never replace it. Labelled only with figures already in the copy. */
.instrument {
  margin-top: var(--space-4);
  width: 100%;
  max-width: 460px;
}
.instrument--wide { max-width: 560px; }
.section--center .instrument { margin-inline: auto; }
.instrument svg { width: 100%; height: auto; }
.ins-axis { stroke: var(--color-text-primary); stroke-width: 1.2; }
.ins-tick { stroke: var(--color-text-primary); stroke-width: 1; }
.ins-num  { font-family: var(--font-sans); font-size: var(--type-graphic-label); fill: var(--color-text-muted); }
.ins-cap  { font-family: var(--font-sans); font-size: var(--type-graphic-caption); letter-spacing: var(--tracking-eyebrow); fill: var(--color-text-accent); }
.ins-zone { fill: var(--color-text-primary); fill-opacity: 0.09; }
.ins-band { fill: var(--color-accent-rule); fill-opacity: 0.2; }
.ins-pt   { fill: var(--color-text-primary); }
.ins-pt2  { fill: var(--color-text-accent); }
.ins-dim  { stroke: var(--color-accent-rule); stroke-width: 1; }
.ins-fill { fill: var(--color-text-primary); fill-opacity: 0.1; }
.ins-edge { stroke: var(--color-accent-rule); stroke-width: 1; fill: none; }

.section--inverse .ins-axis,
.section--inverse .ins-tick { stroke: var(--color-text-inverse); }
.section--inverse .ins-num { fill: var(--color-text-inverse-muted); }
.section--inverse .ins-cap { fill: var(--color-text-inverse-accent); }
.section--inverse .ins-zone { fill: var(--color-text-inverse); fill-opacity: 0.12; }
.section--inverse .ins-fill { fill: var(--color-text-inverse); fill-opacity: 0.14; }

/* Case study scope, set as a labelled row instead of a run of text.
   Same five items, same order, same words. */
.counts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border-hairline);
}
.count { display: flex; flex-direction: column; gap: 2px; }
.count__n {
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  line-height: 1;
  color: var(--color-text-primary);
}
.count__l {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* 20.5 Section rule -------------------------------------------------------
   Where two sections on the same ground meet. Nothing sits on the rule. */
.section--ruled { border-top: 1px solid var(--color-border-hairline); }

/* Staging furniture. Deleting every [data-staging] element and this rule
   leaves the page with nothing on it that is not approved copy. */
[data-staging] { }

/* 20.6 Breakpoints for the above ------------------------------------------ */
@media (max-width: 1023px) {
  .hero--split .hero__inner { gap: var(--space-5); }
  .hero__slot { max-width: 400px; }
}
@media (max-width: 759px) {
  .hero--split { padding-block: var(--space-6); }
  .hero--split .hero__inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .hero__slot { justify-self: stretch; max-width: none; aspect-ratio: 16 / 10; }
  .hero--split .hero__cta { width: 100%; }
  .symptom { grid-template-columns: var(--icon-col) 1fr; }
  .counts { gap: var(--space-3) var(--space-5); }
}
.icon .dot { fill: var(--color-accent-rule); stroke: none; }
.section--inverse .icon .dot { fill: var(--color-text-inverse-accent); }

/* 21 Nav dropdown -------------------------------------------------------------
   "How We Help You" opens a panel with the two practice pages. Desktop only;
   below 760px the mobile menu (section 05) shows the same links as a group.
   Progressive enhancement: with JS off, .nav__panel stays in the flow and the
   two links are reachable directly. */
.nav__item--menu { position: relative; }

.nav__disclosure {
  border: 0;
  background: none;
  cursor: pointer;
  gap: var(--space-1);
  text-transform: uppercase;
  font-family: var(--font-sans);
}
.nav__disclosure[data-current] {
  color: var(--color-text-inverse);
  border-bottom: 1px solid var(--color-accent-rule);
}
.nav__caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid currentColor;
  transition: transform var(--duration-state) var(--ease-state);
}
.nav__disclosure[aria-expanded='true'] { color: var(--color-text-inverse); }
.nav__disclosure[aria-expanded='true'] .nav__caret { transform: rotate(180deg); }

.nav__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  min-width: 320px;
  display: flex;
  flex-direction: column;
  background: var(--color-surface-inverse);
  border: 1px solid var(--color-border-inverse);
  padding: var(--space-2);
}
.nav__panel[hidden] { display: none; }

/* The panel sits below the button with a visible gap. That gap is dead space,
   and crossing it fired mouseleave and shut the menu before the pointer
   arrived. This transparent bridge makes the button and the panel one
   continuous hover target. JS adds a short close delay on top, which covers
   diagonal movement past the panel's edge. */
.nav__panel::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--space-2) * -1);
  height: var(--space-2);
}
/* Widen the target the pointer is aiming for. */
.nav__item--menu::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 100%;
  height: var(--space-2);
}


.nav__panel-link {
  display: flex;
  align-items: center;
  min-height: var(--tap-target-min);
  padding: var(--space-2) var(--space-3);
  font-size: var(--type-nav);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
  color: var(--color-text-inverse-muted);
  transition: color var(--duration-state) var(--ease-state),
              background var(--duration-state) var(--ease-state);
}
.nav__panel-link:hover,
.nav__panel-link[aria-current='page'] { color: var(--color-text-inverse); }
.nav__panel-link:hover { background: var(--color-surface-inverse-2); }

.nav__link--cta {
  border: 1px solid var(--color-accent-rule);
  padding-inline: var(--space-4);
  color: var(--color-text-inverse);
}
.nav__link--cta:hover {
  background: var(--color-accent-rule);
  color: var(--color-text-inverse);
}

/* Mobile menu: the "How We Help You" group (section 05 covers the rest) */
.mobile-menu__group {
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--color-border-inverse);
}
.mobile-menu__label {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-inverse-accent);
  margin-bottom: var(--space-2);
}
.mobile-menu__sublink {
  display: flex;
  align-items: center;
  min-height: 52px;
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  color: var(--color-text-inverse);
}

/* 22 FAQ accordion ------------------------------------------------------------
   Native <details>/<summary>. No JavaScript: it opens, closes and takes keyboard
   focus with the script blocked, which the rest of this site also assumes.
   Scoped to details.faq so the plain article.faq blocks on /sfo are unaffected.

   Motion follows the kit: a state transition on the row and the chevron, and one
   entrance on the answer. prefers-reduced-motion zeroes both through the tokens
   and the global rule in section 15. */

details.faq { padding-block: 0; }

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--tap-target-min);
  padding-block: var(--space-4);
  cursor: pointer;
  list-style: none;                       /* Firefox marker */
  transition: color var(--duration-state) var(--ease-state);
}
.faq__q::-webkit-details-marker { display: none; }   /* Safari marker */
.faq__q:hover .faq__qt { color: var(--color-text-accent); }
.faq__q:hover .faq__icon { stroke: var(--color-text-accent); }

/* The row is the control, so the focus ring belongs on the row. */
.faq__q:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus-ring);
  outline-offset: var(--focus-ring-offset);
}

.faq__qt {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  color: var(--color-text-primary);
  transition: color var(--duration-state) var(--ease-state);
  text-wrap: pretty;
}

.faq__icon {
  flex: none;
  width: var(--icon-size);
  height: var(--icon-size);
  fill: none;
  stroke: var(--color-accent-rule);
  stroke-width: var(--stroke-glyph);
  stroke-linecap: square;
  stroke-linejoin: miter;
  transition: transform var(--duration-state) var(--ease-state),
              stroke var(--duration-state) var(--ease-state);
}
details.faq[open] .faq__icon { transform: rotate(180deg); }

.faq__a { padding-bottom: var(--space-4); }
.faq__a .body { max-width: var(--measure-body); }

/* Answer slides and fades in. Runs on open only — closing stays instant, which
   is what every browser does natively without a height animation. */
details.faq[open] .faq__a {
  animation: faq-open var(--duration-enter) var(--ease-entrance);
}
@keyframes faq-open {
  from { opacity: 0; transform: translateY(calc(var(--space-1) * -1)); }
  to   { opacity: 1; transform: none; }
}

/* Progressive enhancement: browsers that can interpolate to auto animate the
   real height, so the rows below move smoothly instead of jumping. Everything
   else keeps the fade above and is none the worse. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
  details.faq::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size var(--duration-enter) var(--ease-entrance),
                content-visibility var(--duration-enter) allow-discrete;
  }
  details.faq[open]::details-content { block-size: auto; }
  details.faq[open] .faq__a { animation: none; }   /* height alone is enough */
}

/* 23 Symptom cards ------------------------------------------------------------
   Card treatment for the home page's "Sound familiar?" grid.

   Structure is the Stripe / Apple feature grid: one container per item, icon on
   top, a large term over a smaller sentence. The surface language is this
   brand's, not theirs — flat, square, hairline-bordered, no shadow and no
   radius, matching the .card blocks already on /sfo and /founders.

   No copy moved. The term is already <strong> in the approved markup; it is
   promoted to its own line, which changes no words and no reading order.

   Modifier-scoped. The plain .symptoms grids on /sfo and /founders are
   untouched; add symptoms--cards to those lists to match. */

.symptoms--cards { gap: var(--grid-gap); }

.symptoms--cards .symptom {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-hairline);
  padding: var(--space-5);
}

/* Icon sits on top of the card rather than in a column beside the text. */
.symptoms--cards .symptom .icon { margin-top: 0; }

.symptoms--cards .symptom strong {
  display: block;
  margin-bottom: var(--space-2);
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  letter-spacing: var(--tracking-h3);
  color: var(--color-text-primary);
}

.symptoms--cards .symptom p {
  font-size: var(--type-body-small);
  line-height: 1.75;
  color: var(--color-text-body);
}

/* 24 Instrument band ----------------------------------------------------------
   The home page's "Sound familiar?" band. Sits between two cream sections and
   needs to separate from both, so it takes the navy ground.

   It uses navy-800, not the navy-900 of the hero and the statement band. That
   half-step keeps the "Fee-for-service. No AUM." band lower down as the darkest
   thing on the page, which is the contrast the brief asks it to carry.

   The cards become lit panels on that ground: navy-700, hairline edge, accent
   glyph. Reads as an instrument panel rather than a marketing grid. */

.section--instrument { background: var(--color-surface-inverse-deep); }

.section--instrument .symptoms--cards .symptom {
  background: var(--color-surface-panel);
  border-color: var(--color-border-inverse);
  transition: border-color var(--duration-state) var(--ease-state),
              background-color var(--duration-state) var(--ease-state);
}
/* Hover lifts the edge only. The border uses the navy-side accent (#7FA8C4,
   4.7 on this ground) — the kit forbids the cream-side #4A7FA5 on navy, and it
   measures 2.76 here, under the 3:1 floor for non-text. The earlier navy-600
   ground change is gone: it dropped body text to 4.52, too close to the line
   for a hover state. */
.section--instrument .symptoms--cards .symptom:hover {
  border-color: var(--color-text-inverse-accent);
}

/* Text has to flip: the base card rules set navy ink for the cream ground. */
.section--instrument .symptoms--cards .symptom strong { color: var(--color-text-inverse); }
.section--instrument .symptoms--cards .symptom p { color: var(--color-text-inverse-body); }

/* Scroll-linked entrance. CSS only — no observer, no script. Each card crosses
   the viewport at a slightly different point, so the stagger is free.
   Guarded twice: browsers without scroll-driven animation show the cards
   normally, and anyone who asks for less motion gets none. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .section--instrument .symptoms--cards .symptom {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 30%;
    }
    @keyframes instrument-rise {
      from { opacity: 0; transform: translateY(var(--space-4)); }
      to   { opacity: 1; transform: none; }
    }
  }
}

/* 25 Practice fork, interactive -----------------------------------------------
   The doors were static blocks with one small link at the bottom. The whole
   door is now the target — the Stripe / Apple feature-card pattern — using a
   stretched link so the accessible name and the keyboard stop stay on the one
   real <a>. Nothing is added to the tab order.

   The asymmetry is untouched: the primary door keeps 1.25fr, the navy ground
   and the "primary practice" label. That lopsidedness is load-bearing. */

.door { position: relative; }
.door .link-rule::after {
  content: "";
  position: absolute;
  inset: 0;
}

.door {
  transition: transform var(--duration-state) var(--ease-state),
              background-color var(--duration-state) var(--ease-state),
              border-color var(--duration-state) var(--ease-state);
}
.door:hover { transform: translateY(calc(var(--space-1) * -0.5)); }
.door--primary:hover { background: var(--color-surface-inverse-2); }
.door--secondary:hover { border-color: var(--color-accent-rule); }

/* The underline is already there; hover just brings it up to full strength. */
.door .link-rule { transition: border-color var(--duration-state) var(--ease-state); }
.door--primary:hover .link-rule { border-color: var(--color-text-inverse-accent); }
.door--secondary:hover .link-rule { border-color: var(--color-text-primary); }

/* Focus lands on the link but the whole door is the hit area, so show the ring
   on the door rather than on a one-line <a> at the bottom of it.

   The whole block is behind @supports. Suppressing the link's own ring is only
   safe once the door's ring is guaranteed — without the guard, a browser
   lacking :has() would render no focus indicator at all. */
@supports selector(:has(*)) {
  .door:has(.link-rule:focus-visible) {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: var(--focus-ring-offset);
  }
  .door--primary:has(.link-rule:focus-visible) { outline-color: var(--color-focus-ring-inverse); }
  .door .link-rule:focus-visible { outline: none; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .fork .door {
      animation: door-rise linear both;
      animation-timeline: view();
    }
    .fork .door--primary   { animation-range: entry 5%  cover 26%; }
    .fork .door--secondary { animation-range: entry 12% cover 34%; }
    @keyframes door-rise {
      from { opacity: 0; transform: translateY(var(--space-4)); }
      to   { opacity: 1; transform: none; }
    }
  }
}

/* 26 Closing CTA --------------------------------------------------------------
   The last thing on the page and the one action that matters. It gets a low
   pool of accent light behind it, and the button carries a specular sweep —
   a glass highlight crossing the face, resting between passes.

   Both are decorative light on a navy face, using --color-glow and
   --color-shine. Neither sets, outlines or overlaps text, so no contrast
   pairing changes: the button is still cream on navy-900 at 16.31.

   The sweep is the one thing on this site that moves without being asked. It is
   here because it is the close, and it stops entirely under
   prefers-reduced-motion. */

.section--cta { position: relative; overflow: hidden; }
.section--cta .container { position: relative; z-index: 1; }
/* The pool is anchored to the button itself (see §32 .cta__btn::before), so
   it follows the button wherever the layout puts it. */

.btn--lg {
  min-height: calc(var(--tap-target-min) + var(--space-3));
  padding-inline: var(--space-7);
}

.btn--shine { position: relative; overflow: hidden; isolation: isolate; }
.btn--shine::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: -60%;
  width: 40%;
  background: linear-gradient(100deg, transparent, var(--color-shine), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}
.btn--shine:hover { background: var(--color-surface-inverse); }

@media (prefers-reduced-motion: no-preference) {
  .btn--shine::after { animation: btn-sweep var(--duration-ambient) var(--ease-state) infinite; }
  /* Hovering restarts the pass immediately instead of waiting out the rest. */
  .btn--shine:hover::after { animation: btn-sweep var(--duration-sweep) var(--ease-state); }
  @keyframes btn-sweep {
    0%   { inset-inline-start: -60%; }
    18%  { inset-inline-start: 130%; }
    100% { inset-inline-start: 130%; }
  }
}

/* 27 Glass hero ---------------------------------------------------------------
   Home only. The artwork is a background, not a graphic: it carries no meaning,
   sits behind the type, and is aria-hidden at the source. Copy left, brand
   right, and the image mass sits between and behind them.

   assets/img/hero-glass.svg is drawn in the palette — navy-700 and navy-600
   panes, accent-rule mullions, cream catch-lights. It is not the cyan of the
   reference images; it is those images' architecture in this brand's colours. */

.hero--glass {
  position: relative;
  overflow: hidden;
  text-align: left;                     /* base .hero centres; this one does not */
  padding-block: var(--space-9) var(--space-8);
  background-color: var(--color-surface-inverse);
  background-image: url('../img/hero-glass.svg?v=20260911');
  background-repeat: no-repeat;
  background-position: right center;
  background-size: cover;
}
.hero--glass .container { position: relative; z-index: 1; }

.hero--glass .hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) auto;
  align-items: center;
  gap: var(--space-7);
}

.hero--glass .hero__copy { gap: var(--space-4); align-items: flex-start; }
.hero--glass .display {
  max-width: 15ch;
  /* `pretty` only tunes the last line, which left "middle of" stranded short in
     the middle. `balance` evens every line instead. */
  text-wrap: balance;
}
.hero--glass .lead,
.hero--glass .proof { margin-inline: 0; }

/* The lead runs as one continuous line where it fits (the forced break at its
   comma was removed at the client's request). */
@media (min-width: 760px) {
  .hero--glass .lead { max-width: none; }
}
.hero--glass .hero__cta { margin-top: var(--space-2); }

/* --- the brand block ------------------------------------------------------
   The boxed HG mark over the descriptor. The lockup width is set by the
   descriptor, not the mark: at the eyebrow size the two words very nearly fill
   300px on their own, so justification has almost no slack to distribute and
   the spacing reads as drawn rather than stretched. The mark centres over it. */
.hero__mark {
  justify-self: end;
  /* The descriptor sets the width. It is nowrap, so the lockup is exactly as
     wide as the words need with an ordinary space between them — no
     justification, so nothing stretches as the viewport grows. The mark then
     takes 100% of that, which is what puts the two on the same edges. */
  width: max-content;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.hero__mark-badge {
  /* Squared off to the lockup width, so the mark and the descriptor share both
     edges. Held well back so it reads as glass — the artwork behind shows
     through the cream field rather than sitting under an opaque tile.

     width:0 + min-width:100% keeps the SVG's 1688px intrinsic size out of the
     parent's max-content calculation. Without it the lockup sizes to the image
     instead of to the text. */
  width: 0;
  min-width: 100%;
  aspect-ratio: 1;
  height: auto;
  opacity: 0.62;
}
.hero__mark-descriptor {
  white-space: nowrap;
  word-spacing: 0.14em;
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-inverse);
  font-size: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  /* `.section--inverse p` outranks a single class, so this is declared at the
     same weight to stay honest about what renders. */
  color: var(--color-text-inverse-body);
}

/* --- tablet: same side-by-side layout as desktop, one size down ----------- */
@media (max-width: 1023px) {
  /* The lockup stays on the right beside the copy. Only the descriptor drops a
     type step, and because it sets the lockup width the mark shrinks with it —
     267px to 238px — so the two keep their matching edges. */
  .hero__mark-descriptor { font-size: var(--type-label); letter-spacing: var(--tracking-label); }
  .hero--glass .hero__inner { gap: var(--space-6); }
  .hero--glass { background-position: 72% center; }
}

/* --- mobile: the brand block stacks above the copy ------------------------ */
@media (max-width: 759px) {
  .hero--glass .hero__inner {
    grid-template-columns: 1fr;
    gap: var(--space-3);
    justify-items: start;
  }
  .hero__mark { justify-self: start; order: -1; }
  /* Stacked above the headline the mark does not need to carry the full lockup
     width the way it does beside the copy. */
  .hero__mark-badge { min-width: 68%; }
  .hero--glass { padding-block: var(--space-4) var(--space-6); }
  .hero--glass .hero__mark-descriptor { margin-top: var(--space-3); padding-top: var(--space-2); }
  .hero--glass .hero__cta { width: 100%; }
  /* Crop to the busy side so the artwork still reads as glass, not a smudge. */
  .hero--glass { background-position: 78% center; background-size: auto 118%; }
}

/* The mark is square, so its height tracks the lockup width. Where the viewport
   is short it is the one element that can give, so it steps back further and the
   button stays above the fold. These sit below the tablet rule, so they override
   its 68%. */
@media (max-width: 759px) and (max-height: 760px) {
  .hero__mark-badge { min-width: 72%; }
}
@media (max-width: 759px) and (max-height: 700px) {
  .hero__mark-badge { min-width: 60%; }
}
@media (max-width: 759px) and (max-height: 620px) {
  .hero__mark-badge { min-width: 44%; }
  .hero--glass { padding-block: var(--space-3) var(--space-5); }
}

/* 28 Hero motion --------------------------------------------------------------
   Three layers, all CSS, no script.

   1 ENTRANCE   The copy rises and fades in, staggered, once on load.
   2 AMBIENT    A slow light crosses the glass, the way sun tracks a facade.
   3 ORBIT      A rim light travels the edge of the button, continuously.

   Every one is wrapped in prefers-reduced-motion: no-preference, so motion is
   opt-in rather than something to switch off. With it disabled — or with the
   stylesheet half-loaded — nothing is hidden and nothing is mid-transform.

   The kit's position is that this firm does not animate. That still holds for
   the rest of the site; this is the one screen where the client asked for it. */

@property --hg-orbit {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* --- 3 Orbit: the button's rim light -------------------------------------- */
/* A conic gradient rotated behind the button and masked to a ring, so only the
   sliver outside the face shows. On the navy ground it reads as a light
   travelling the edge rather than a border that blinks. */
.btn--orbit { position: relative; }

/* The halo. Sits behind the button's own background, so only the part that
   spills past the edge is visible — that is what makes it read as light rather
   than as a second border. z-index works here because .btn--orbit is
   position:relative with z-index:auto, so it is not a stacking context. */
.btn--orbit::after {
  content: "";
  position: absolute;
  inset: -10px;
  z-index: -1;
  background: conic-gradient(from var(--hg-orbit),
    transparent 0deg 236deg,
    var(--color-text-inverse-accent) 300deg,
    transparent 360deg);
  filter: blur(9px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-state) var(--ease-state);
}

.btn--orbit::before {
  content: "";
  position: absolute;
  inset: -2px;
  padding: 2px;
  background: conic-gradient(from var(--hg-orbit),
    transparent 0deg 232deg,
    var(--color-text-inverse-accent) 296deg,
    var(--color-text-inverse) 320deg,
    var(--color-text-inverse-accent) 344deg,
    transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-state) var(--ease-state);
}

@media (prefers-reduced-motion: no-preference) {
  .btn--orbit::before,
  .btn--orbit::after {
    animation: hg-orbit 5.5s linear infinite;
  }
  .btn--orbit::before { opacity: 0.9; }
  .btn--orbit::after  { opacity: 0.55; }
  /* Hovering speeds the pass and brings the halo up. */
  .btn--orbit:hover::before { opacity: 1; animation-duration: 2.4s; }
  .btn--orbit:hover::after  { opacity: 0.8; animation-duration: 2.4s; }
  @keyframes hg-orbit { to { --hg-orbit: 360deg; } }

  /* (The ambient light that used to cross the glass every 17s was removed at
     the client's request; the hero now has the entrance and the orbit only.) */

  /* --- 1 Entrance: the copy arrives --------------------------------------- */
  .hero--glass .display,
  .hero--glass .lead,
  .hero--glass .hero__cta,
  .hero--glass .hero__mark {
    animation: hg-arrive 700ms var(--ease-entrance) both;
  }
  .hero--glass .display   { animation-delay: 60ms; }
  .hero--glass .lead      { animation-delay: 160ms; }
  .hero--glass .hero__cta { animation-delay: 260ms; }
  .hero--glass .hero__mark{ animation-delay: 200ms; }
  @keyframes hg-arrive {
    from { opacity: 0; transform: translateY(var(--space-3)); }
    to   { opacity: 1; transform: none; }
  }
}

/* 29 Diagnosis band -----------------------------------------------------------
   "You built something that works…" and "Sound familiar?" were two sections —
   a centred cream narrative followed by a navy card grid. They are one section
   now, because they are one argument: here is what is wrong, here is the
   evidence, here is the promise.

   Merging them fixed a copy problem as much as a layout one. "That's what we
   fix." used to close the cream section, and then "Sound familiar?" reopened
   the wound directly underneath it. The resolution landed before the evidence.
   No words changed; that line simply moved to the end, where it now resolves
   the whole argument and hands off to "Where does this apply?".

   The layout follows the home hero, which is the house standard: an asymmetric
   two-column head, left aligned, no centring. Head left is the claim, head
   right is the story that supports it. Everything below runs full width.

   Ground: this is the first light plate after the navy hero. It sits on the
   tint (cream-200) so it stays a step apart from the cream "Where does this
   apply?" directly below — the site's own cream / tint alternation. */

.diagnosis__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}

/* --- head: the claim, and the story under it ------------------------------ */
.diagnosis__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--space-6) var(--space-7);
  align-items: start;
}
/* 17ch keeps the heading to four short lines beside the story rather than two
   long ones over it. The centred version ran to 24ch and needed the whole
   width to itself. */
.diagnosis__head .h2 { max-width: 17ch; }

.diagnosis__story {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 52ch;
}
.diagnosis__story p {
  font-size: var(--type-body);
  line-height: var(--leading-body);
}

/* "The context changes. The problem doesn't." was a body paragraph. It is the
   turn in the argument, so it is set a step up and in full navy. Same words,
   same position in the reading order. */
.diagnosis__story .diagnosis__pull {
  margin-top: var(--space-2);
  font-size: var(--type-lead);
  font-weight: var(--weight-emphasis);
  line-height: var(--leading-lead);
  color: var(--color-text-accent);
}

/* --- evidence: the hinge, then the grid ----------------------------------- */
.diagnosis__evidence {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* The eyebrow sits on its own rule instead of floating above the grid, which
   is what made the old section feel like it started twice. */
.diagnosis__hinge {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.diagnosis__hinge::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border-strong);
}

/* --- close: the caveat and the promise on one line ------------------------ */
.diagnosis__close {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3) var(--space-7);
  align-items: baseline;
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border-strong);
}
/* Not an aside. It is the conclusion the six rows add up to, so it speaks in
   the same voice as the story above: body size, body colour, upright. Small
   italic grey read as fine print, which is the opposite of the point. */
.diagnosis__close .aside {
  max-width: 52ch;
  font-size: var(--type-body);
  font-style: normal;
  line-height: var(--leading-body);
  color: var(--color-text-body);
}
.diagnosis__close .punch {
  margin-top: 0;
  font-size: var(--type-lead);
  color: var(--color-text-primary);
  white-space: nowrap;
}
/* The promise and the action sit together at the row's right edge: the line
   first, the booking button under it, both ranged right so the button ends on
   the same edge the drawings above end on. The row is baseline-aligned, so
   the caveat and the promise still share a baseline. */
.diagnosis__act {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}

@media (max-width: 1023px) {
  .diagnosis__inner { gap: var(--space-6); }
  .diagnosis__head { grid-template-columns: 1fr; gap: var(--space-5); }
  .diagnosis__head .h2 { max-width: 22ch; }
}

@media (max-width: 759px) {
  .diagnosis__close { grid-template-columns: 1fr; gap: var(--space-4); }
  .diagnosis__close .punch { white-space: normal; }
  .diagnosis__act { align-items: flex-start; }
}
@media (max-width: 479px) {
  .diagnosis__act .btn { width: 100%; }
}

/* 30 The findings list ----------------------------------------------------------
   Six symptoms as a numbered list, each row carrying its own small drawing:
   number · drawing · title and one sentence, on hairline rules. No cards, no
   panels, no paper texture. The drawing sits on the section ground like an
   annotation in a specification sheet.

   Three earlier passes are recorded so nobody repeats them. Six white cards
   with a drawing on top read as a feature grid. One composite blueprint with
   six callouts read as a collage. One sticky sheet showing a single drawing
   at a time was clean on desktop and gave mobile nothing at all. This pass
   keeps the list (which is what stopped it reading as features) and gives
   every row its own visual on every device.

   The drawings share one language — navy line, one accent, square caps, a
   240×120 viewBox — and each shows its symptom as a state, not a symbol.
   The resting state is the failure state, so the message reads with no
   interaction. On a pointer device, hovering the row replays the failure
   from a clean start; on touch the same replay is scroll-driven. Linework
   draws itself in on scroll entry. All CSS; keyframes run FROM the clean
   state TO the values the rest rules already set, so nothing ever jumps. */

.diagnosis__label {
  font-size: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  font-weight: var(--weight-emphasis);
  color: var(--color-text-accent);
}

.findings { list-style: none; margin: 0; padding: 0; }

/* One row: the text runs left, the drawing sits at the right edge, so the row
   fills its width. The number lives on the title's own line — same line box,
   so the two baselines agree — and the sentence indents to the title. */
.finding {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  grid-template-areas: "text fig";
  column-gap: var(--space-6);
  align-items: center;
  padding-block: 20px;
  border-top: 1px solid var(--color-border-hairline);
  transition: border-color var(--duration-state) var(--ease-state);
}
.finding:last-child { border-bottom: 1px solid var(--color-border-hairline); }

.finding__text {
  grid-area: text;
  position: relative;
  padding-left: 52px;
  max-width: calc(52px + 56ch);
  font-size: var(--type-body-small);
  line-height: 1.65;
  color: var(--color-text-body);
  text-wrap: pretty;
}
.finding__text strong {
  display: block;
  margin-bottom: 2px;
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-lead);
  line-height: var(--leading-h3);
  letter-spacing: var(--tracking-h3);
  color: var(--color-text-primary);
}
.finding__n {
  position: absolute;
  left: 0;
  top: 0;
  /* the title's line box in px — a unitless inherit would rescale to 15px */
  line-height: calc(var(--type-lead) * var(--leading-h3));
  padding-top: 1px;
  font-family: var(--font-sans);
  font-size: var(--type-eyebrow);
  font-weight: var(--weight-emphasis);
  letter-spacing: var(--tracking-label);
  color: var(--color-text-accent);
  transition: color var(--duration-state) var(--ease-state);
}
.finding__fig { grid-area: fig; margin: 0; width: 100%; }
.finding__fig .dg { display: block; width: 100%; height: auto; }

/* The row under the pointer (or the finger, on touch) takes a step down the
   cream ramp. The tint bleeds to the section's gutter so the row reads as a
   band, not a box; the text does not move. */
.finding {
  margin-inline: calc(-1 * var(--space-3));
  padding-inline: var(--space-3);
  transition: border-color var(--duration-state) var(--ease-state),
              background-color var(--duration-state) var(--ease-state);
}
.finding:hover,
.finding:active {
  background-color: var(--color-cream-300);
}
.finding:hover .finding__n,
.finding:active .finding__n { color: var(--color-text-primary); }
@media (hover: hover) {
  .finding:hover { border-top-color: var(--color-text-primary); }
}
@media (hover: none) {
  .finding { -webkit-tap-highlight-color: transparent; }
  .finding:active { transition-duration: 0ms; }
}

@media (min-width: 600px) and (max-width: 1023px) {
  .finding { grid-template-columns: minmax(0, 1fr) 220px; column-gap: var(--space-5); }
}
/* below 600 the drawing stacks under the sentence, indented to the title */
@media (max-width: 599px) {
  .finding {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "text" "fig";
    row-gap: var(--space-3);
  }
  .finding__text { padding-left: 44px; }
  .finding__fig { max-width: 232px; margin-left: 44px; }
}

/* --- the highlight: a marker wash sweeps under a phrase as it enters --------
   Used twice in the section, on purpose: under "You built something that
   works." at the top and under "That's what we fix." at the bottom. The same
   mark opens the argument and closes it. */
.hl {
  background-image: linear-gradient(var(--color-highlight), var(--color-highlight));
  background-repeat: no-repeat;
  background-size: 100% 0.42em;
  background-position: 0 0.86em;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hl {
      animation: hl-sweep linear both;
      animation-timeline: view();
      animation-range: entry 35% entry 85%;
    }
    @keyframes hl-sweep { from { background-size: 0% 0.42em; } to { background-size: 100% 0.42em; } }
  }
}

/* --- the drawing language -------------------------------------------------- */
.dg {
  fill: none;
  stroke: var(--color-text-primary);
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
  overflow: visible;
}
.dg .a    { stroke: var(--color-accent-rule); }
.dg .f    { stroke-opacity: 0.3; }
.dg .node { fill: var(--color-surface-alt); }

/* rest states: the failure, already happened */
.hub__flow { opacity: 0; stroke-dasharray: 3 9; }
.hub__ring { transform-box: fill-box; transform-origin: center; }
.clock__mark { fill: var(--color-text-primary); stroke: none; transform-box: fill-box; transform-origin: center bottom; }
.gantt__base { fill: var(--color-text-primary); fill-opacity: 0.18; stroke: none; }
.gantt__over { fill: var(--color-accent-rule); fill-opacity: 0.6; stroke: none; transform-box: fill-box; transform-origin: left center; }
.gantt__lost { fill-opacity: 0.06; stroke: var(--color-text-primary); stroke-opacity: 0.3; stroke-dasharray: 2 2; }
.net__link   { opacity: 0.4; }
.net__leaver { transform: translateX(36px); opacity: 0.3; transform-box: view-box; }
.rail__right { transform: rotate(9deg) translateY(3px); transform-origin: 173px 52px; transform-box: view-box; }
.beam__post  { opacity: 0; transform-box: view-box; }
.beam__beam  { transform: rotate(7deg); transform-origin: 48px 41px; transform-box: view-box; }

/* keyframes: clean → failure */
@keyframes dg-draw   { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes dg-flow   { 0% { opacity: 0; stroke-dashoffset: 0; } 12% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; stroke-dashoffset: -60; } }
@keyframes dg-ring   { 0% { transform: scale(0.6); opacity: 0; } 60% { transform: scale(1.12); opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
@keyframes dg-pop    { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes dg-grow   { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes dg-lose   { from { fill-opacity: 0.18; stroke-opacity: 0; } to { fill-opacity: 0.06; stroke-opacity: 0.3; } }
@keyframes dg-leave  { 0% { transform: none; opacity: 1; } 30% { transform: none; opacity: 1; } 100% { transform: translateX(36px); opacity: 0.3; } }
@keyframes dg-sever  { from { opacity: 0.9; } to { opacity: 0.4; } }
@keyframes dg-accrue { from { opacity: 0; } to { opacity: 1; } }
@keyframes dg-break  { from { transform: none; } to { transform: rotate(9deg) translateY(3px); } }
@keyframes dg-fault  { from { opacity: 0; } to { opacity: 1; } }
@keyframes dg-post   { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(8px); } }
@keyframes dg-tilt   { from { transform: none; } to { transform: rotate(7deg); } }

/* --- pointer: hover the row, the failure replays ----------------------------- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .finding:hover .hub__flow { animation: dg-flow 1.4s linear both; }
  .finding:hover .hub__ring { animation: dg-ring 1.4s var(--ease-state) both; }
  .finding:hover .clock__mark { animation: dg-pop 260ms var(--ease-entrance) both; animation-delay: calc(var(--i) * 80ms); }
  .finding:hover .gantt__over { animation: dg-grow 700ms var(--ease-state) both; animation-delay: calc(200ms + var(--i) * 120ms); }
  .finding:hover .gantt__lost { animation: dg-lose 900ms var(--ease-state) 300ms both; }
  .finding:hover .net__leaver { animation: dg-leave 1.1s var(--ease-state) 200ms both; }
  .finding:hover .net__link   { animation: dg-sever 1.1s var(--ease-state) 300ms both; }
  .finding:hover .rail__tick  { animation: dg-accrue 200ms linear both; animation-delay: calc(var(--i) * 90ms); }
  .finding:hover .rail__right { animation: dg-break 400ms cubic-bezier(0.5, 0, 1, 1) 850ms both; }
  .finding:hover .rail__fault { animation: dg-fault 300ms linear 850ms both; }
  .finding:hover .beam__post  { animation: dg-post 500ms var(--ease-state) 200ms both; }
  .finding:hover .beam__beam  { animation: dg-tilt 600ms cubic-bezier(0.5, 0, 1, 1) 600ms both; }
}

/* --- scroll: rows rise, lines draw; touch gets the replays on scroll -------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .finding {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 25%;
    }
    .dg .d {
      stroke-dasharray: 1;
      animation: dg-draw linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 40%;
    }
  }
  @media (hover: none) and (prefers-reduced-motion: no-preference) {
    .hub__flow, .hub__ring, .clock__mark, .gantt__over, .gantt__lost,
    .net__leaver, .net__link, .rail__tick, .rail__right, .rail__fault,
    .beam__post, .beam__beam {
      animation-timeline: view();
      animation-range: cover 25% cover 60%;
      animation-fill-mode: both;
      animation-timing-function: linear;
    }
    .hub__flow    { animation-name: dg-flow; }
    .hub__ring    { animation-name: dg-ring; }
    .clock__mark  { animation-name: dg-pop;   animation-range: cover calc(25% + var(--i) * 2.5%) cover calc(38% + var(--i) * 2.5%); }
    .gantt__over  { animation-name: dg-grow;  animation-range: cover calc(30% + var(--i) * 4%)   cover calc(45% + var(--i) * 4%); }
    .gantt__lost  { animation-name: dg-lose; }
    .net__leaver  { animation-name: dg-leave; }
    .net__link    { animation-name: dg-sever; }
    .rail__tick   { animation-name: dg-accrue; animation-range: cover calc(25% + var(--i) * 2.5%) cover calc(30% + var(--i) * 2.5%); }
    .rail__right  { animation-name: dg-break; animation-range: cover 50% cover 60%; }
    .rail__fault  { animation-name: dg-fault; animation-range: cover 50% cover 60%; }
    .beam__post   { animation-name: dg-post;  animation-range: cover 25% cover 40%; }
    .beam__beam   { animation-name: dg-tilt;  animation-range: cover 42% cover 58%; }
  }
}

/* 31 The practice band ----------------------------------------------------------
   "Where does this apply?" (a cream fork with two doors) and "Fee-for-service.
   No AUM. No referral fees." (a navy statement band) were two sections. They
   are one: who this is for, then how we work — the two practices and the one
   way of working, in that order, because the reader coming off the diagnosis
   asks "for whom?" before "on what terms?".

   Three layouts were tried. Statement-as-headline with the doors stacked
   beside it demoted the primary practice to a sidebar; statement-as-headline
   with the paragraphs as a footer orphaned the terms. This one keeps the
   sequence: a head that mirrors the home hero, the doors, then the terms on
   a rule with the statement still set as a line.

   Ground is navy-900 — the second and last navy-900 plate on the page — with
   its own artwork behind it: assets/img/practice-scale.svg, glass volumes at
   different scales rising from one ground line, in the hero's material and
   palette. The mass sits right and low; the top-left stays quiet for the
   heading. The subject is the point — different businesses, different sizes,
   one operation underneath. The doors become lit panels: primary on navy-700, secondary
   translucent, both hairline-edged. The icons are two small drawings in the
   site's instrument language: a held centre (nested squares) and a company
   that grew (rising steps). */

.practice { position: relative; overflow: hidden; }
.practice::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url('../img/practice-scale.svg?v=20260911');
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: cover;
  opacity: 0.85;
  pointer-events: none;
}
.practice > .container { position: relative; }

.practice__inner { display: flex; flex-direction: column; gap: var(--space-7); }

/* --- head: mirrors the hero ------------------------------------------------ */
.practice__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--space-5) var(--space-7);
  align-items: end;
}
.practice__head .h2 { max-width: none; }
.practice__head .lead { max-width: 44ch; }

/* --- doors on navy ---------------------------------------------------------- */
.fork--inverse .door {
  border: 1px solid var(--color-border-inverse);
  color: var(--color-text-inverse-body);
}
.fork--inverse .door--primary {
  background: var(--color-surface-panel);
  padding: var(--space-6) var(--space-5);
}
.fork--inverse .door--secondary {
  background: rgba(0, 23, 39, 0.55);
  padding: var(--space-6) var(--space-5);
}
.fork--inverse .door .label     { color: var(--color-text-inverse-accent); }
.fork--inverse .door__name      { color: var(--color-text-inverse); }
.fork--inverse .door__copy      { color: var(--color-text-inverse-body); font-size: var(--type-body); }
.fork--inverse .door .link-rule { color: var(--color-text-inverse); }
.fork--inverse .door:hover { border-color: var(--color-text-inverse-accent); }
.fork--inverse .door--primary:hover { background: var(--color-surface-panel); }
.fork--inverse .door:hover .link-rule { border-color: var(--color-text-inverse-accent); }

.door__glyph {
  width: 48px;
  height: 48px;
  margin-bottom: var(--space-2);
  stroke: var(--color-text-inverse);
}
.door__glyph .a { stroke: var(--color-text-inverse-accent); }
.door--primary   .door__glyph .node { fill: var(--color-surface-panel); }
.door--secondary .door__glyph .node { fill: var(--color-surface-inverse); }

/* --- terms: the statement on a rule ------------------------------------------ */
.practice__terms {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--space-4) var(--space-7);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-border-inverse);
}
.practice__statement {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h2);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
  color: var(--color-text-inverse);
  max-width: 26ch;
}
.practice__terms p {
  font-size: var(--type-body);
  line-height: var(--leading-body);
  color: var(--color-text-inverse-body);
  max-width: 52ch;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .practice__terms {
      animation: door-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 28%;
    }
  }
}

@media (max-width: 1023px) {
  .practice__inner { gap: var(--space-6); }
  .practice__head,
  .practice__terms { grid-template-columns: 1fr; gap: var(--space-4); }
}
@media (max-width: 759px) {
  .fork--inverse .door--primary,
  .fork--inverse .door--secondary { padding: var(--space-5) var(--space-4); }
  /* a tall narrow crop centres on the middle of the cluster; anchor right so
     the tallest volume stays in frame */
  .practice::before { background-position: right bottom; }
}

/* 32 The close ------------------------------------------------------------------
   The last section and the one action on the page. Three layouts were
   compared: a focused centre (clean, but it only asserts the call is easy), an
   invitation panel (boxes the finale into a card; the button wrapped), and
   this — the ask on the left, and on the right the answer to the hesitation
   nobody says out loud: what actually happens if I click.

   Left: eyebrow, the heading, the button, three short facts that remove the
   anxiety (confidential, no obligation, nothing to prepare), and the
   advisor route. Right: the call in three numbered lines — the lead sentence
   from before, split — and who is on the other end. The button is the only
   navy object in the section; nothing else competes with it. It carries the same orbit rim light as the hero button (btn--orbit) and a
   low pool of accent light behind it. Not the specular sweep as well:
   btn--shine and btn--orbit both use ::after and cannot be stacked. The layout mirrors the home hero, so the page closes the way it
   opened. */

.cta {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: var(--space-7) var(--space-8);
  align-items: center;
}

.cta__ask {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}
.cta__ask .h2 { max-width: 18ch; text-wrap: balance; }

/* The button clips its own overflow for the sweep, so the pool lives on a
   wrapper behind it rather than on the button itself. */
.cta__halo { position: relative; display: inline-block; margin-top: var(--space-2); }
.cta__halo::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 180%;
  height: 340%;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center, var(--color-glow), transparent 68%);
  pointer-events: none;
}
.cta__halo .cta__btn { position: relative; }

.cta__assure {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cta__assure li::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 10px;
  background: var(--color-accent-rule);
  vertical-align: middle;
}

.cta__what {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.cta__label {
  font-size: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  font-weight: var(--weight-emphasis);
  color: var(--color-text-accent);
}
.cta__steps { list-style: none; margin: 0; padding: 0; }
.cta__steps li {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: baseline;
  padding-block: var(--space-3);
  border-top: 1px solid var(--color-border-hairline);
  font-size: var(--type-body);
  font-weight: var(--weight-emphasis);
  line-height: var(--leading-body);
  color: var(--color-text-primary);
}
.cta__steps li:last-child { border-bottom: 1px solid var(--color-border-hairline); }
.cta__n {
  font-size: var(--type-eyebrow);
  font-weight: var(--weight-emphasis);
  letter-spacing: var(--tracking-label);
  color: var(--color-text-accent);
}


/* entrance: the steps arrive one after another as the close comes into view */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cta__steps li {
      animation: instrument-rise linear both;
      animation-timeline: view();
    }
    .cta__steps li:nth-child(1) { animation-range: entry 10% entry 100%; }
    .cta__steps li:nth-child(2) { animation-range: entry 20% entry 100%; }
    .cta__steps li:nth-child(3) { animation-range: entry 30% entry 100%; }
  }
}

@media (max-width: 1023px) {
  .cta { grid-template-columns: 1fr; gap: var(--space-6); }
  .cta__ask .h2 { max-width: 22ch; }
}
@media (max-width: 759px) {
  .cta__halo { display: block; }
  /* the label runs to two balanced lines on a phone instead of three ragged ones */
  .cta__halo .cta__btn {
    width: 100%;
    padding-inline: var(--space-3);
    text-align: center;
    text-wrap: balance;
    line-height: 1.4;
  }
}

/* 33 Motion for the practice band and the close ------------------------------
   Everything here is scroll-driven and opt-in: nothing moves unless the
   browser supports view() timelines and the reader has not asked for less
   motion. Without either, every element sits at its final state.

   Practice band — the head arrives in three beats (eyebrow, heading, lead);
   the door glyphs draw their linework as the doors rise; the three sentences
   of the fee statement land one after another, because they are three
   declarations; and the artwork behind it all drifts a few percent against
   the scroll, so the glass volumes sit a plane behind the panels.

   The close — the ask arrives in beats (eyebrow, heading, button, facts,
   advisor line); the rules under the three steps draw from the left as each
   step rises; and the pool of light behind the button breathes slowly, the
   one ambient loop in the section. */

/* rules that draw: the steps' hairlines become pseudo-elements so they can scale */
.cta__steps li { border-top: 0; position: relative; }
.cta__steps li:last-child { border-bottom: 0; }
.cta__steps li::before,
.cta__steps li:last-child::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--color-border-hairline);
  transform-origin: left center;
}
.cta__steps li::before { top: 0; }
.cta__steps li:last-child::after { bottom: 0; }

.practice__statement span { display: inline-block; }

@keyframes rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes practice-drift {
  from { transform: translateY(-4%) scale(1.08); }
  to   { transform: translateY(4%)  scale(1.08); }
}
@keyframes pool-breathe { 50% { opacity: 0.55; } }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* --- practice band --- */
    .practice__head > * {
      animation: instrument-rise linear both;
      animation-timeline: view();
    }
    .practice__head .stack   { animation-range: entry 5% cover 24%; }
    .practice__head .lead    { animation-range: entry 12% cover 30%; }

    .practice__statement span {
      animation: instrument-rise linear both;
      animation-timeline: view();
    }
    .practice__statement span:nth-child(1) { animation-range: entry 5%  cover 26%; }
    .practice__statement span:nth-child(3) { animation-range: entry 14% cover 34%; }
    .practice__statement span:nth-child(4) { animation-range: entry 23% cover 42%; }

    .door__glyph .d {
      stroke-dasharray: 1;
      animation: dg-draw linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 45%;
    }

    .practice::before {
      animation: practice-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }

    /* --- the close --- */
    .cta__ask > * {
      animation: instrument-rise linear both;
      animation-timeline: view();
    }
    /* entry-based: the close sits just above the footer, so 'fully in view'
       has to mean 'fully arrived' even on a tall screen */
    .cta__ask .eyebrow    { animation-range: entry 5%  entry 70%; }
    .cta__ask .h2         { animation-range: entry 15% entry 80%; }
    .cta__ask .cta__halo  { animation-range: entry 25% entry 90%; }
    .cta__ask .cta__assure{ animation-range: entry 35% entry 100%; }
    .cta__ask .aside      { animation-range: entry 45% entry 100%; }

    .cta__steps li::before,
    .cta__steps li:last-child::after {
      animation: rule-draw linear both;
      animation-timeline: view();
    }
    .cta__steps li:nth-child(1)::before { animation-range: entry 0%  entry 100%; }
    .cta__steps li:nth-child(2)::before { animation-range: entry 0%  entry 100%; }
    .cta__steps li:nth-child(3)::before { animation-range: entry 0%  entry 100%; }
    .cta__steps li:last-child::after    { animation-range: entry 0%  entry 100%; }
  }
}
@media (prefers-reduced-motion: no-preference) {
  .cta__halo::before { animation: pool-breathe 7s var(--ease-state) infinite; }
}

/* 34 Motion fallback ------------------------------------------------------------
   Every entrance on the home page is a scroll-driven animation. Safari before
   26 and Firefox do not run those, so in them the page sat still. The kit's
   audience is on Safari and iPhones, so that is not an edge case.

   assets/js/motion.js detects the missing support and flips these rules on:
   html.js-motion marks the page, .m-rise / .m-draw / .m-sweep mark the
   targets, and .is-visible lands as each one enters the viewport. Until the
   script has run nothing is hidden, so a failed load never hides content.
   Stagger comes from --m, the element's index among its siblings.

   The keyframes are declared here at top level on purpose: the scroll-driven
   ones (instrument-rise, door-rise, hl-sweep) live inside @supports blocks
   and do not exist in the browsers this section is for. */

@keyframes m-rise  { from { opacity: 0; transform: translateY(var(--space-4)); } to { opacity: 1; transform: none; } }
@keyframes m-sweep { from { background-size: 0% 0.42em; } to { background-size: 100% 0.42em; } }
@keyframes m-rule  { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes m-draw  { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

.js-motion .m-rise:not(.is-visible) { opacity: 0; }
.js-motion .m-rise.is-visible {
  animation: m-rise 640ms var(--ease-entrance) both;
  animation-delay: min(calc(var(--m, 0) * 80ms), 400ms);
}

.js-motion .m-draw .d { stroke-dasharray: 1; stroke-dashoffset: 1; }
.js-motion .m-draw.is-visible .d { animation: m-draw 800ms var(--ease-state) 120ms both; }

.js-motion .m-sweep { background-size: 0% 0.42em; }
.js-motion .m-sweep.is-visible { animation: m-sweep 700ms var(--ease-state) 240ms both; }

.js-motion .cta__steps li::before,
.js-motion .cta__steps li:last-child::after { transform: scaleX(0); }
.js-motion .cta__steps li.is-visible::before,
.js-motion .cta__steps li.is-visible:last-child::after {
  animation: m-rule 520ms var(--ease-state) both;
  animation-delay: min(calc(var(--m, 0) * 80ms), 400ms);
}

/* touch devices: the findings' failure replays were scroll-driven; here they
   play once as each row arrives, on the same keyframes the hover uses */
@media (hover: none) {
  .js-motion .finding.is-visible .hub__flow   { animation: dg-flow 1.4s linear 500ms both; }
  .js-motion .finding.is-visible .hub__ring   { animation: dg-ring 1.4s var(--ease-state) 500ms both; }
  .js-motion .finding.is-visible .clock__mark { animation: dg-pop 260ms var(--ease-entrance) both; animation-delay: calc(500ms + var(--i) * 80ms); }
  .js-motion .finding.is-visible .gantt__over { animation: dg-grow 700ms var(--ease-state) both; animation-delay: calc(600ms + var(--i) * 120ms); }
  .js-motion .finding.is-visible .gantt__lost { animation: dg-lose 900ms var(--ease-state) 700ms both; }
  .js-motion .finding.is-visible .net__leaver { animation: dg-leave 1.1s var(--ease-state) 600ms both; }
  .js-motion .finding.is-visible .net__link   { animation: dg-sever 1.1s var(--ease-state) 700ms both; }
  .js-motion .finding.is-visible .rail__tick  { animation: dg-accrue 200ms linear both; animation-delay: calc(500ms + var(--i) * 90ms); }
  .js-motion .finding.is-visible .rail__right { animation: dg-break 400ms cubic-bezier(0.5, 0, 1, 1) 1300ms both; }
  .js-motion .finding.is-visible .rail__fault { animation: dg-fault 300ms linear 1300ms both; }
  .js-motion .finding.is-visible .beam__post  { animation: dg-post 500ms var(--ease-state) 600ms both; }
  .js-motion .finding.is-visible .beam__beam  { animation: dg-tilt 600ms cubic-bezier(0.5, 0, 1, 1) 1000ms both; }
}

/* 35 The voices ------------------------------------------------------------------
   Six testimonials before the footer, as a carousel of glass cards: the
   featured card centred, its neighbours showing at either edge, on the
   navy-800 ground with the hero's glass artwork dimmed behind it so the cards
   have something to refract. No heading copy — the cards are the section.

   The track is a scroll-snap strip that swipes on touch and scrolls by hand
   with no script; assets/js/voices.js adds the arrows, the counter, keyboard
   arrows, and a 10s auto-advance (a 45-word quote reads in about eleven) that
   pauses while the reader is on it, stops off screen, and never runs under
   reduced motion, and loops: a clone of the last card sits before the first
   and of the first after the last, and when the strip settles on a clone it
   jumps, unseen, to the real card. Beneath the track, six progress segments
   are the only controls.

   The centred card is fully lit; its neighbours sit back at 45% and a touch
   smaller. Each card that arrives in the centre rises briefly and its marker
   phrase sweeps in — time-based, every browser. */

.voices {
  --voice-w: min(640px, 82vw);
  position: relative;
  overflow: hidden;
  background: var(--color-surface-inverse);
  padding-block: var(--space-7);
}
/* assets/img/voices-light.svg: no objects, only light — two soft pools of
   accent light and three heavily blurred panes on the header and footer's
   navy-900, so the glass cards have depth to refract and nothing to look at.
   Its top and bottom settle to navy-900 exactly, so the band meets the
   close above and the footer below without a seam. It drifts a few percent
   against the scroll, like the practice band. */
.voices::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url('../img/voices-light.svg?v=20260911');
  background-size: cover;
  background-position: center;
  opacity: 0.85;
  pointer-events: none;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .voices::before {
      animation: practice-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}
.voices::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(900px, 90vw);
  height: 56%;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center, var(--color-glow), transparent 68%);
  opacity: 0.7;
  pointer-events: none;
}
.voices__stage { position: relative; z-index: 1; display: flex; flex-direction: column; gap: var(--space-5); }

/* --- the track: full-bleed, the first and last card still centre ---------- */
.voices__track {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  overflow-y: hidden;
  padding-inline: calc(50% - var(--voice-w) / 2);
  padding-block: var(--space-2);
  scroll-snap-type: x mandatory;
  scroll-padding-inline: calc(50% - var(--voice-w) / 2);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.voices__track::-webkit-scrollbar { display: none; }

/* --- the card: glass --------------------------------------------------------- */
.voice {
  flex: 0 0 var(--voice-w);
  scroll-snap-align: center;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: 0;
  padding: var(--space-6) var(--space-6) var(--space-5);
  background: rgba(245, 242, 235, 0.045);   /* thin glass; the ground is quiet enough */
  border: 1px solid var(--color-border-inverse);
  -webkit-backdrop-filter: blur(18px);
          backdrop-filter: blur(18px);
  opacity: 0.45;
  transform: scale(0.96);
  transition: opacity var(--duration-enter) var(--ease-state),
              transform var(--duration-enter) var(--ease-state),
              border-color var(--duration-enter) var(--ease-state);
}
.voice::before {           /* the lit top edge that reads as glass */
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-shine), transparent);
  opacity: 0.7;
}
.voice.is-active {
  opacity: 1;
  transform: none;
  border-color: rgba(245, 242, 235, 0.32);
}
/* without the script every card is lit */
.voices:not(.is-js) .voice { opacity: 1; transform: none; }

.voice__tag {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-inverse-accent);
}
/* The quote is set in the sans, light, at lead size. The serif at h3 size
   was the headline voice and overwhelmed a card. */
.voice__text {
  font-family: var(--font-sans);
  font-size: var(--type-lead);
  font-weight: var(--weight-lead);
  line-height: 1.6;
  color: var(--color-text-inverse-body);
  text-wrap: pretty;
  min-height: calc(var(--type-lead) * 1.6 * 5);
}
.voice__text .hl { color: var(--color-text-inverse); }
.voice__by { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--space-3); row-gap: 2px; margin-top: auto; }
.voice__who { font-size: var(--type-body-small); font-weight: var(--weight-emphasis); color: var(--color-text-inverse); }
.voice__role { font-size: var(--type-caption); color: var(--color-text-inverse-muted); }

/* the marker on navy: the highlight token is an accent wash, fine on either ground */
.voices .hl { animation: none; background-size: 100% 0.42em; }
.voices.is-js .voice .hl { background-size: 0% 0.42em; }
.voices.is-js .voice.is-active .hl { animation: m-sweep 700ms var(--ease-state) 420ms both; }
@media (prefers-reduced-motion: no-preference) {
  .voices.is-js .voice.is-active .voice__text,
  .voices.is-js .voice.is-active .voice__by { animation: m-rise 480ms var(--ease-entrance) both; }
  .voices.is-js .voice.is-active .voice__by { animation-delay: 100ms; }
}
@media (prefers-reduced-motion: reduce) {
  .voices.is-js .voice .hl { background-size: 100% 0.42em; animation: none; }
  .voice { transition: none; }
}

/* --- controls: arrow · progress · arrow · counter ----------------------------- */
/* The six segments are the controls: each is a 44px-tall button around a
   2px line. Past segments filled, the current one filling in time with the
   advance. No arrows and no counter — the segments say where you are and
   take you anywhere. */
.voices__controls {
  display: none;
  align-items: center;
  gap: var(--space-2);
  width: min(var(--voice-w), 100% - 2 * var(--gutter));
  margin-inline: auto;
}
.voices.is-js .voices__controls { display: flex; }
.voices__seg {
  flex: 1;
  height: var(--tap-target-min);
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.voices__seg:focus-visible { outline-color: var(--color-focus-ring-inverse); }
.voices__seg span {
  position: relative;
  display: block;
  width: 100%;
  height: 2px;
  background: var(--color-border-inverse);
  overflow: hidden;
  transition: background-color var(--duration-state) var(--ease-state);
}
.voices__seg:hover span { background: var(--color-text-inverse-muted); }
.voices__seg span::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-text-inverse-accent);
  transform: scaleX(0);
  transform-origin: left center;
}
.voices__seg.is-done span::after,
.voices__seg.is-active span::after { transform: scaleX(1); }
@keyframes voices-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.voices.is-playing .voices__seg.is-active span::after { animation: voices-fill 10s linear both; }


/* the whole stage arrives with the section */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .voices__stage {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 55%;
    }
  }
}

@media (max-width: 759px) {
  .voices { --voice-w: 84vw; padding-block: var(--space-6); }
  .voice { padding: var(--space-5) var(--space-4) var(--space-4); }
  .voice__text { font-size: var(--type-lead); min-height: calc(var(--type-lead) * 1.45 * 6); }
}

/* 36 Subpage heroes ------------------------------------------------------------
   /sfo, /founders, /about, /schedule and the utility pages take the home's
   glass hero (§27, §28): navy, the glass artwork, copy left. Where the home
   shows the HG mark on the right, a practice page shows a drawing of its own
   subject on a glass panel — the office around the principal for /sfo, the
   company that grew for /founders — and /about shows the portrait in the
   same frame. The panel arrives with the copy (§28 entrance). */
.hero__panel {
  position: relative;
  justify-self: end;
  width: 420px;
  max-width: 100%;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: var(--space-5);
  background: rgba(245, 242, 235, 0.05);
  border: 1px solid var(--color-border-inverse);
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
}
.hero__panel::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-shine), transparent);
  opacity: 0.7;
}
.hero__panel .dg { width: 100%; height: 100%; stroke: var(--color-text-inverse); stroke-width: 1.5; }
.hero__panel .dg .a { stroke: var(--color-text-inverse-accent); }
.hero__panel .dg .f { stroke-opacity: 0.35; }
.hero__panel .dg .node { fill: var(--color-surface-inverse); }
.hero__panel .dg .hero__core { fill: var(--color-text-inverse-accent); }
/* The portrait panel sits in the grid's auto column, so the <img> must not
   contribute its 640px intrinsic width — same trap as the home's mark badge. */
.hero__panel--portrait { padding: var(--space-3); aspect-ratio: 4 / 5; width: 360px; }
.hero__panel--portrait img { width: 0; min-width: 100%; height: 100%; object-fit: cover; display: block; }

/* the utility heroes (schedule, thank-you, 404) run copy only */
.hero--glass.hero--copy .hero__inner { grid-template-columns: minmax(0, 1fr); }
.hero--glass.hero--copy .display { max-width: 18ch; }
.hero--glass .hero__links { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5); margin-top: var(--space-2); }
.hero--glass .hero__links .link-rule { color: var(--color-text-inverse); }

@media (prefers-reduced-motion: no-preference) {
  .hero--glass .hero__panel { animation: hg-arrive 700ms var(--ease-entrance) 200ms both; }
}
@media (max-width: 759px) {
  .hero__panel { justify-self: start; width: 72%; max-width: 280px; order: -1; }
  .hero__panel--portrait { width: 64%; max-width: 240px; }
}

/* 37 The moves --------------------------------------------------------------------
   "How we work" as a ledger: 01 Diagnose · 02 Install · 03 Sustain, one ruled
   row each — the same language as the findings, and it reads as a sequence,
   which three columns did not. */
.moves { display: flex; flex-direction: column; }
.move {
  position: relative;
  display: grid;
  grid-template-columns: 60px 200px minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
  padding-block: var(--space-4);
  margin: 0;
}
.move::before,
.move:last-child::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 1px;
  background: var(--color-border-hairline);
  transform-origin: left center;
}
.move::before { top: 0; }
.move:last-child::after { bottom: 0; }
.move__n {
  padding-top: 6px;
  font-size: var(--type-eyebrow);
  font-weight: var(--weight-emphasis);
  letter-spacing: var(--tracking-label);
  color: var(--color-text-accent);
}
.move__name {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  color: var(--color-text-primary);
}
.move__body { display: flex; flex-direction: column; gap: var(--space-3); max-width: 58ch; }
.move__body p { font-size: var(--type-body); line-height: var(--leading-body); color: var(--color-text-body); }

/* --- section heads on the light grounds: the hero's two columns ----------- */
.band__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--space-4) var(--space-7);
  align-items: end;
  margin-bottom: var(--space-6);
}
.band__head .h2 { max-width: 16ch; }
.band__head .lead { max-width: 44ch; }
.band__head .eyebrow { color: var(--color-text-accent); }

@media (max-width: 1023px) {
  .band__head { grid-template-columns: 1fr; gap: var(--space-3); }
  .band__head .h2 { max-width: 22ch; }
}
@media (max-width: 759px) {
  .move { grid-template-columns: 44px minmax(0, 1fr); grid-template-areas: "n name" "n body"; gap: var(--space-2) var(--space-3); }
  .move__n { grid-area: n; }
  .move__name { grid-area: name; }
  .move__body { grid-area: body; }
}

/* 38 The tools ------------------------------------------------------------------
   "What we bring" on the practice band's ground (§31): navy-900, the scale
   artwork, lit glass panels in a 2×2. The first panel carries the score
   instrument. On /sfo the fee statement and the asset-range instrument close
   the band on a rule, as they do on the home. */
.tools {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}
.tool {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  background: rgba(0, 23, 39, 0.55);
  border: 1px solid var(--color-border-inverse);
  color: var(--color-text-inverse-body);
  transition: border-color var(--duration-state) var(--ease-state);
}
.tool:hover { border-color: var(--color-text-inverse-accent); }
.tool::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-shine), transparent);
  opacity: 0.6;
}
.tool--lead { background: var(--color-surface-panel); }
.tool__name {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  color: var(--color-text-inverse);
}
.tool__copy { font-size: var(--type-body-small); line-height: 1.7; color: var(--color-text-inverse-body); }
.tool__copy strong { color: var(--color-text-inverse); }
.tool .icon { width: 32px; height: 32px; stroke: var(--color-text-inverse); }
.tool .icon .accent { stroke: var(--color-text-inverse-accent); }
.tool .icon .dot { fill: var(--color-text-inverse-accent); }
.tool .instrument { margin-top: var(--space-2); }
.practice__terms .instrument { margin-top: var(--space-3); grid-column: 1 / -1; max-width: 560px; }

@media (max-width: 759px) {
  .tools { grid-template-columns: 1fr; }
  .tool { padding: var(--space-4); }
}

/* 39 A single voice -------------------------------------------------------------
   /about carries one quote on the voices ground (§35) with no controls. The
   track still centres the one card; the script leaves it alone. */
.voices--single .voices__track { justify-content: center; padding-inline: var(--gutter); }
.voices--single .voice { opacity: 1; transform: none; }
.voices--single .voice__text { min-height: 0; }

/* --- the case study head and the accordion head on the light grounds ------ */
.faq-band { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: var(--space-6) var(--space-8); align-items: start; }
.faq-band .band__head { margin-bottom: 0; display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.faq__a > .stack { gap: var(--space-3); }
@media (max-width: 1023px) { .faq-band { grid-template-columns: 1fr; } }

/* --- motion for the new pieces (scroll-driven; §34 covers the fallback) --- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .move, .tool, .case__block, .counts, .band__head > *, .faq-band .faq {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 60%;
    }
    .move::before, .move:last-child::after {
      animation: rule-draw linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }
  }
}
.js-motion .move::before, .js-motion .move:last-child::after { transform: scaleX(0); }
.js-motion .move.is-visible::before, .js-motion .move.is-visible:last-child::after { animation: m-rule 520ms var(--ease-state) both; }

/* 40 Warm lines and beige labels --------------------------------------------------
   The light plates were flat and their hairlines read grey (navy at 12%).
   Two quiet moves, no artwork: hairlines on the light grounds go warm
   (cream-400), and on the navy plates the eyebrows, labels, voice tags and
   the footer's small type turn beige, so a little cream lives inside the dark.
   Scoped to body.lit so the client can judge it on one page first.

   (A version with light washes and translucent vellum sheets behind the
   content was tried and rejected — too much of a picture.) */

.lit .finding { border-top-color: var(--color-border-warm); }
.lit .finding:last-child { border-bottom-color: var(--color-border-warm); }
.lit .diagnosis__hinge::after { background: var(--color-border-warm); }
.lit .diagnosis__close { border-top-color: var(--color-border-warm); }
.lit .cta__steps li::before,
.lit .cta__steps li:last-child::after { background: var(--color-border-warm); }
.lit .finding__fig .dg .f { stroke: var(--color-cream-400); stroke-opacity: 1; }

.lit .hero--glass .hero__mark-descriptor,
.lit .practice .eyebrow,
.lit .practice .door .label,
.lit .voices .voice__tag,
.lit .site-footer .site-footer__tag,
.lit .site-footer .copyright,
.lit .site-footer .copyright a { color: var(--color-text-inverse-warm); }
.lit .practice .door__glyph .a { stroke: var(--color-text-inverse-warm); }

/* --- more of the cream ramp on the light plates ---------------------------- */
/* The close moves down the ramp to cream-300, the darkest light plate, so the
   page runs navy → cream-200 → navy → cream-300 → navy instead of two near-
   identical light plates. Small accent and muted type re-pair for that ground. */
.lit .section--cta { background: var(--color-surface-deep); }
.lit .section--cta .eyebrow,
.lit .section--cta .cta__label,
.lit .section--cta .cta__n { color: var(--color-text-accent-deep); }
.lit .section--cta .cta__assure,
.lit .section--cta .aside { color: var(--color-text-body); }
.lit .section--cta .cta__assure li::before { background: var(--color-text-accent-deep); }
.lit .section--cta .cta__steps li::before,
.lit .section--cta .cta__steps li:last-child::after { background: var(--color-cream-400); }
.lit .section--cta .btn { background: var(--color-surface-inverse); }

/* the diagnosis stays on cream-200, but its drawings' nodes and the number
   gutter take a step darker */
.lit .finding__fig .dg .node { fill: var(--color-cream-300); }
.lit .diagnosis__close .aside { color: var(--color-text-body); }

/* 41 About: the record ---------------------------------------------------------------
   A ruled ledger in the findings' language, no boxes. It replaces the bio's
   four paragraphs: a figure and a label in the gutter (the case study's
   counts, one to a row) with a line of text beside it. The head — portrait,
   label, heading, two lines — stays put on the left while the rows scroll
   past it. (A three-row "what's covered" ledger in the problem band was
   tried and dropped at the client's request; the band keeps its original
   copy, with one paragraph added.) */
.record-band__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: var(--space-6) var(--space-8);
  align-items: start;
}
.record-band__head {
  position: sticky;
  top: var(--space-6);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}
/* The portrait leads the head: 4:5 in a hairline frame, the panel's own
   padding, narrow enough that the head still fits a laptop viewport while it
   is stuck. On phones it goes a little wider. */
.record-band__portrait {
  margin: 0 0 var(--space-2);
  width: 200px;
  max-width: 100%;
  padding: var(--space-2);
  border: 1px solid var(--color-border-warm);
}
.record-band__portrait img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; display: block; }
.record-band__head .label { color: var(--color-text-accent); }
.record-band__head .h2 { max-width: 22ch; }
.record-band__head .lead { max-width: 40ch; }
.record-band__note { max-width: 44ch; font-size: var(--type-body); line-height: var(--leading-body); color: var(--color-text-body); }
.record { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.record__row {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: var(--space-2) var(--space-5);
  align-items: start;
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-border-warm);
}
.record__row:last-child { border-bottom: 1px solid var(--color-border-warm); }
.record__fig { display: flex; flex-direction: column; gap: 4px; padding-top: 3px; }
.record__n {
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: 1;
  color: var(--color-text-primary);
  white-space: nowrap;
}
.record__l {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.record__body p { max-width: 52ch; font-size: var(--type-body); line-height: var(--leading-body); color: var(--color-text-body); }
.record__close {
  margin-top: var(--space-4);
  font-size: var(--type-lead);
  font-weight: var(--weight-emphasis);
  line-height: var(--leading-lead);
  color: var(--color-text-accent);
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .record__row, .record-band__head > *, .record__close {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 60%;
    }
  }
}

@media (max-width: 1023px) {
  .record-band__inner { grid-template-columns: 1fr; }
  .record-band__head { position: static; }
}
@media (max-width: 759px) {
  .record-band__portrait { width: 240px; }
  .record__row { grid-template-columns: 1fr; gap: var(--space-2); }
  .record__fig { flex-direction: row; align-items: baseline; gap: var(--space-2); }
}

/* --- the about page's problem band: thesis left, story right ---------------
   The home's head is a claim beside its story. Here the left column carries
   the section's reason — label, the line, the purpose in one lead — so the
   two columns weigh the same instead of a lone heading facing a page of
   text. */
.diagnosis__thesis { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.diagnosis__thesis .lead { max-width: 38ch; color: var(--color-text-primary); }
.diagnosis__thesis .diagnosis__label { margin-bottom: var(--space-1); }
.diagnosis__thesis .diagnosis__pull {
  margin-top: 0;
  font-size: var(--type-lead);
  font-weight: var(--weight-emphasis);
  line-height: var(--leading-lead);
  color: var(--color-text-accent);
}
.diagnosis__thesis-btn { margin-top: var(--space-2); }

/* 42 SFO: the transforming office, and the case as a band --------------------------
   HERO. The panel's drawing is no longer a still. It loops, 14s: everything
   wired to the principal (eight seats on a ring, every spoke to the centre)
   → the operating layer draws itself in, the seats travel to a square around
   it, and the only lines that reach the principal are the layer's four. Then
   it lets go and the office slides back, so the loop is seamless. The rest
   state — no animation, reduced motion — is the ordered office. The
   descriptor "Operational infrastructure for single family offices" sits
   under the panel the way the home's sits under the mark.
   CASE. No white box. The band is the cream-200 ground with the result as
   the focal point: the score before and after at display size, the scale
   under it, and a plain reading of what the score means, stuck on the left
   while the situation and the six things built scroll past on the right. */
.hero__visual {
  justify-self: end;
  width: 420px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.hero__visual .hero__panel { width: 100%; justify-self: auto; }
.hero__visual .hero__mark-descriptor { white-space: normal; width: 100%; text-wrap: balance; }

.dg--transform .ring, .dg--transform .spoke { opacity: 0; }
.dg--transform .sat { transform: translate(var(--dx), var(--dy)); }
.dg--transform .layer .draw { stroke-dasharray: 1; stroke-dashoffset: 0; }
.dg--transform .link { stroke-opacity: 0.55; }
@media (prefers-reduced-motion: no-preference) {
  .dg--transform .ring, .dg--transform .spoke { animation: ho-chaos 14s var(--ease-state) infinite; }
  .dg--transform .sat { animation: ho-seat 14s var(--ease-state) infinite; }
  .dg--transform .layer { animation: ho-order 14s var(--ease-state) infinite; }
  .dg--transform .layer .draw { animation: ho-draw 14s var(--ease-state) infinite; }
  /* the seats leave one after another, then the loop resets together */
  .dg--transform .sat { animation-delay: calc(var(--i) * 90ms); }
}
@keyframes ho-chaos { 0%, 30% { opacity: 1; } 44%, 86% { opacity: 0; } 100% { opacity: 1; } }
@keyframes ho-seat  { 0%, 30% { transform: translate(0, 0); } 46%, 86% { transform: translate(var(--dx), var(--dy)); } 100% { transform: translate(0, 0); } }
@keyframes ho-order { 0%, 30% { opacity: 0; } 36%, 88% { opacity: 1; } 100% { opacity: 0; } }
@keyframes ho-draw  { 0%, 31% { stroke-dashoffset: 1; } 48%, 88% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 1; } }

.case-band__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: var(--space-6) var(--space-8);
  align-items: start;
}
.case-band__result {
  position: sticky;
  top: var(--space-6);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-warm);
}
.case-band__result .label { color: var(--color-text-accent); }
.result__fig {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-display);
  line-height: 1;
  color: var(--color-text-primary);
}
.result__from { color: var(--color-text-muted); }
.result__arrow { font-size: 0.6em; color: var(--color-text-accent); }
.result__to { color: var(--color-text-accent); }
.result__l {
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.case-band__result .instrument { width: 100%; max-width: 480px; margin-top: var(--space-2); }
.result__read { max-width: 44ch; font-size: var(--type-body); line-height: var(--leading-body); color: var(--color-text-body); }
.case-band__story { display: flex; flex-direction: column; }
.case-band__story .counts { border-bottom-color: var(--color-border-warm); }
.case-band__story .case__block { margin-top: var(--space-5); }
.case-band__story .case__block .label { color: var(--color-text-accent); }
/* the built list stacks name over line at every width: two columns of
   three-word names and short lines both wrapped in the story column */
.moves--built .move { grid-template-columns: 40px minmax(0, 1fr); grid-template-areas: "n name" "n body"; gap: var(--space-1) var(--space-3); padding-block: var(--space-3); }
.moves--built .move__n { grid-area: n; }
.moves--built .move__name { grid-area: name; margin: 0; font-size: var(--type-lead); }
.moves--built .move__body { grid-area: body; }
.moves--built .move::before, .moves--built .move:last-child::after { background: var(--color-border-warm); }
.moves--built .move__body p { font-size: var(--type-body-small); }
.case-band .band__head .h2 { max-width: 20ch; }
.case-band__story .counts { gap: var(--space-3) var(--space-5); }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .case-band__result > * {
      animation: instrument-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 60%;
    }
  }
}

@media (max-width: 1023px) {
  .case-band__inner { grid-template-columns: 1fr; }
  .case-band__result { position: static; }
}
@media (max-width: 759px) {
  .hero__visual { justify-self: start; width: 72%; max-width: 280px; order: -1; }
  .hero__visual .hero__mark-descriptor { margin-top: var(--space-3); padding-top: var(--space-2); }
}

/* --- /sfo: the diagnosis split in two -------------------------------------
   "You had this handled once" is its own band on cream-100 (the head only:
   claim left, story right). "Sound familiar?" follows on cream-200 with a
   band head, the six findings, and the close with its button. */
.diagnosis--findings .band__head { margin-bottom: var(--space-2); }
.diagnosis--findings .diagnosis__inner { gap: var(--space-5); }

/* --- /sfo round three ---------------------------------------------------------
   The case's story column takes a rule on its left instead of a box. The
   findings band carries "Sound familiar?" under the list, then the "You had
   this handled once" narrative on its own rule, then the close. The tools
   head runs full width, and each tool panel carries a low-opacity drawing
   of its subject that moves slowly: the score marker settling at 3, the
   blueprint being traced, the right person moving into the right seat, the
   cadence ticking along its rail. Rest state (reduced motion) is the settled
   one in each. The FAQ stacks head over questions, one column. */
.case-band__story { padding-left: var(--space-5); border-left: 1px solid var(--color-border-warm); }
.diagnosis__familiar {
  margin: 0;
  padding-top: var(--space-3);
  text-align: right;
  font-family: var(--font-serif);
  font-weight: var(--weight-serif);
  font-size: var(--type-h3);
  line-height: var(--leading-h3);
  color: var(--color-text-primary);
}
.diagnosis__narrative { padding-top: var(--space-6); border-top: 1px solid var(--color-border-warm); }
.practice__head--full { grid-template-columns: minmax(0, 1fr); }
.practice__head--full .h2 { max-width: none; }

.tool__art {
  align-self: flex-end;
  margin-top: auto;
  padding-top: var(--space-2);
  width: 160px;
  height: 96px;
  fill: none;
  stroke: var(--color-text-inverse);
  stroke-width: 1.5;
  stroke-linecap: square;
  opacity: 0.32;
  pointer-events: none;
  transition: opacity var(--duration-state) var(--ease-state);
}
.tool:hover .tool__art { opacity: 0.6; }
.tool__art .a { stroke: var(--color-text-inverse-accent); }
.tool__art .f { stroke-opacity: 0.4; }
.tool__art .fa { fill: var(--color-text-inverse-accent); stroke: none; }
.tool__art .zone { fill: var(--color-text-inverse); fill-opacity: 0.12; stroke: none; }
.tool__art .bp__trace { stroke-dasharray: 0.18 0.82; stroke-width: 2.5; }
@media (prefers-reduced-motion: no-preference) {
  .tool__art--score .score__pt { animation: tool-score 9s var(--ease-state) infinite; }
  .tool__art--plan .bp__trace   { animation: tool-trace 9s linear infinite; }
  .tool__art--seats .seat__one  { animation: tool-seat 10s var(--ease-state) infinite; }
  .tool__art--cadence .cad__block { animation: tool-cadence 12s linear infinite; }
}
@keyframes tool-score   { 0%, 20% { transform: translateX(81.6px); } 60%, 90% { transform: translateX(0); } 100% { transform: translateX(81.6px); } }
@keyframes tool-trace   { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes tool-seat    { 0%, 22% { transform: translate(-36px, -26px); } 45%, 88% { transform: translate(0, 0); } 100% { transform: translate(-36px, -26px); } }
@keyframes tool-cadence { from { transform: translateX(0); } to { transform: translateX(136px); } }

.faq-band--stack { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.faq-band--stack .faqs { max-width: 820px; }
.faq-band--stack .h2 { max-width: none; }

@media (max-width: 759px) {
  .case-band__story { padding-left: 0; border-left: 0; padding-top: var(--space-4); border-top: 1px solid var(--color-border-warm); }
  .diagnosis__familiar { text-align: left; }
  .tool__art { width: 120px; height: 72px; }
}

/* --- /sfo round four ------------------------------------------------------------
   One-column heads (.band__head--stack) for the case, the findings and how
   we work. The result column is narrower so the five counts sit on one line.
   The findings band ends on "Sound familiar?"; "You had this handled once" is
   its own one-column band, closing on two punch lines and the button. The
   tools band drops its scale artwork for plain navy, and each panel's drawing
   fills its ground at low opacity. The interruption drawing is the principal's
   morning cut into pieces. */
.band__head--stack { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); align-items: start; }
.band__head--stack .h2 { max-width: 26ch; }
.band__head--stack .lead { max-width: 56ch; }
@media (min-width: 1024px) { .case-band__inner { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr); } }
.case-band__result .instrument { max-width: 100%; }

.diagnosis--handled .diagnosis__inner { gap: var(--space-6); }
.narrative { display: flex; flex-direction: column; gap: var(--space-4); }
.narrative .h2 { max-width: 20ch; }
.narrative .diagnosis__story { max-width: 62ch; }
.diagnosis__close--stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}
.diagnosis__close--stack .punch { white-space: normal; max-width: 40ch; }
.diagnosis__close--stack .btn { margin-top: var(--space-2); }

/* the morning, cut: a bar of the principal's day in pieces, an arrival mark
   over every cut */
.dg--cut .cut__seg { fill: var(--color-text-primary); fill-opacity: 0.16; stroke: none; }
.dg--cut .cut__mark { stroke: var(--color-text-accent); }
@media (prefers-reduced-motion: no-preference) {
  .finding:hover .dg--cut .cut__mark { animation: cut-drop 420ms var(--ease-entrance) both; animation-delay: calc(var(--i) * 70ms); }
  .finding:hover .dg--cut .cut__seg  { animation: cut-seg 420ms var(--ease-entrance) both; animation-delay: calc(var(--i) * 70ms); }
}
@keyframes cut-drop { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes cut-seg  { from { fill-opacity: 0.4; } to { fill-opacity: 0.16; } }

.practice--plain::before { display: none; }
.practice--plain { background: var(--color-surface-inverse); }
.practice--plain .tool { background: rgba(245, 242, 235, 0.03); min-height: 250px; overflow: hidden; }
.practice--plain .tool__art {
  position: absolute;
  inset: var(--space-4) var(--space-4) var(--space-4) 52%;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  align-self: auto;
  opacity: 0.16;
}
.practice--plain .tool:hover .tool__art { opacity: 0.32; }
.practice--plain .tool__name { position: relative; max-width: 14ch; }
.practice--plain .tool__copy { position: relative; max-width: 27ch; }

/* the drawings: the blueprint draws itself wall by wall; the person takes the
   right seat; the operating system ticks its list and raises its bars */
.tool__art .bp__wall { stroke-dasharray: 1; stroke-dashoffset: 0; }
.tool__art .seat--right { stroke-opacity: 1; }
.tool__art .person--one { transform: translate(0, 0); }
.tool__art .os__box { fill: var(--color-text-inverse-accent); stroke: var(--color-text-inverse); }
.tool__art .os__bar { fill: var(--color-text-inverse); fill-opacity: 0.35; stroke: none; transform-box: fill-box; transform-origin: bottom center; }
.tool__art .os__bar.a { fill: var(--color-text-inverse-accent); fill-opacity: 0.9; }
@media (prefers-reduced-motion: no-preference) {
  .tool__art .bp__wall { animation: bp-wall 10s linear infinite; animation-delay: calc(var(--i) * 0.45s); }
  .tool__art .person--one { animation: person-seat 10s var(--ease-state) infinite; }
  .tool__art .seat--right { animation: seat-light 10s var(--ease-state) infinite; }
  .tool__art .os__box { animation: os-check 8s steps(1, end) infinite; animation-delay: calc(var(--i) * 0.7s); }
  .tool__art .os__bar { animation: os-bar 8s var(--ease-state) infinite; animation-delay: calc(var(--i) * 0.25s); }
  .tool__art .os__cursor { animation: os-blink 1.2s steps(1, end) infinite; }
}
@keyframes bp-wall     { 0% { stroke-dashoffset: 1; } 28% { stroke-dashoffset: 0; } 84% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 1; } }
@keyframes person-seat { 0%, 22% { transform: translate(-70px, 0); } 42%, 86% { transform: translate(0, 0); } 100% { transform: translate(-70px, 0); } }
@keyframes seat-light  { 0%, 36% { stroke-opacity: 0.35; } 46%, 86% { stroke-opacity: 1; } 100% { stroke-opacity: 0.35; } }
@keyframes os-check    { 0%, 12% { fill: transparent; } 24%, 82% { fill: var(--color-text-inverse-accent); } 100% { fill: transparent; } }
@keyframes os-bar      { 0%, 10% { transform: scaleY(0.15); } 40%, 84% { transform: scaleY(1); } 100% { transform: scaleY(0.15); } }
@keyframes os-blink    { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }

@media (max-width: 759px) {
  .practice--plain .tool { min-height: 0; padding-bottom: calc(var(--space-4) + 72px); }
  .practice--plain .tool__art { inset: auto var(--space-3) var(--space-3) auto; width: 120px; height: 72px; }
}

/* --- /sfo round five --------------------------------------------------------------
   The hero has no panel: the transforming office runs large and faint on the
   right as the hero's moving ground, and the glass artwork behind it drifts.
   The statement is the headline. The case headline holds one line on
   desktop. "Sound familiar?" sits left. The "handled once" band runs full
   width. The tool drawings sit at the bottom right with the panel padded so
   the copy never reaches them. The fee statement lives in "How we work" on
   the light ground. */
.hero--sfo .display { max-width: 15ch; }
.hero--sfo .lead { max-width: 52ch; }
@media (max-width: 759px) {
}

@media (min-width: 1024px) {
  .case-band .band__head .h2 { max-width: none; white-space: nowrap; font-size: min(var(--type-h3), 2.15vw); }
}
.diagnosis__familiar { text-align: left; }
.narrative .h2, .narrative .diagnosis__story, .diagnosis__close--stack .punch { max-width: none; }

.practice--plain .tool { min-height: 0; padding-bottom: calc(var(--space-4) + 96px); }
.practice--plain .tool__art { inset: auto var(--space-4) var(--space-4) auto; width: 160px; height: 96px; opacity: 0.4; }
.practice--plain .tool:hover .tool__art { opacity: 0.7; }
.practice--plain .tool__name { max-width: none; }
.practice--plain .tool__copy { max-width: 52ch; }

.terms--light { margin-top: var(--space-5); padding-top: 0; border-top: 0; }
.terms--light .practice__statement { color: var(--color-text-primary); }
.terms--light .practice__statement span:nth-child(3),
.terms--light .practice__statement span:nth-child(4) { color: var(--color-text-muted); }
.terms--light p { color: var(--color-text-body); }

/* --- /sfo hero: the infrastructure schematic as the moving ground ----------
   No cubes. A full-bleed line drawing of an office's operating
   infrastructure: entities and properties above an operating rail, people
   below it, every one wired to the rail, and only the rail reaching the
   principal at the right. Work flows along every line (a dash pattern
   travelling), the nodes light in turn, and the whole drawing drifts a
   little. Masked away on the left so the copy sits on clean navy. */
.hero__infra {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.55;
  -webkit-mask-image: linear-gradient(90deg, transparent 22%, rgba(0,0,0,0.3) 42%, #000 62%);
          mask-image: linear-gradient(90deg, transparent 22%, rgba(0,0,0,0.3) 42%, #000 62%);
}
.hero__infra .infra { width: 100%; height: 100%; display: block; fill: none; }
.infra .grid path { stroke: var(--color-text-inverse); stroke-opacity: 0.06; stroke-width: 1; }
.infra .line { stroke: var(--color-text-inverse); stroke-opacity: 0.45; stroke-width: 1.2; }
.infra .rail { stroke-opacity: 0.7; stroke-width: 2; }
.infra .ticks path { stroke-opacity: 0.35; }
.infra .flow { stroke: var(--color-text-inverse-accent); stroke-width: 2.4; stroke-dasharray: 7 21; stroke-opacity: 0.85; }
.infra .node rect, .infra .node circle, .infra .node path { stroke: var(--color-text-inverse); stroke-width: 1.5; fill: var(--color-surface-inverse); }
.infra .node .inner { fill: none; stroke-opacity: 0.55; }
.infra .core rect { stroke-width: 1.8; }
.infra .core .inner { stroke: var(--color-text-inverse-accent); stroke-opacity: 1; }
.infra .core .heart { fill: var(--color-text-inverse-accent); stroke: none; }
@media (prefers-reduced-motion: no-preference) {
  .hero__infra { animation: infra-drift 40s ease-in-out infinite alternate; }
  .infra .flow { animation: infra-flow 2.8s linear infinite; animation-delay: calc(var(--i, 0) * -0.35s); }
  .infra .flow--rail { animation-duration: 5s; }
  .infra .node--pulse rect, .infra .node--pulse circle, .infra .node--pulse path { animation: infra-pulse 7s var(--ease-state) infinite; animation-delay: calc(var(--i) * 0.6s); }
  .infra .core .heart { animation: infra-heart 3.5s var(--ease-state) infinite; }
}
@keyframes infra-flow  { to { stroke-dashoffset: -28; } }
@keyframes infra-drift { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-2.5%, -1.5%, 0); } }
@keyframes infra-pulse { 0%, 6% { stroke: var(--color-text-inverse-accent); stroke-width: 2.2; } 22%, 100% { stroke: var(--color-text-inverse); stroke-width: 1.5; } }
@keyframes infra-heart { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
@media (max-width: 759px) {
  .hero__infra { opacity: 0.4; -webkit-mask-image: linear-gradient(180deg, transparent 20%, #000 70%); mask-image: linear-gradient(180deg, transparent 20%, #000 70%); }
}

/* --- /founders: the same pieces as /sfo, on its own copy ------------------
   The hero's moving ground is a company schematic (teams above the rail,
   people below, the founder at the right). The tools come first, on the
   light ground so they do not run into the navy hero: the same panels and
   drawings with the light palette. */
.practice--light { background: var(--color-surface-primary); }
.practice--light .eyebrow, .lit .practice--light .eyebrow { color: var(--color-text-accent); }
.practice--light .h2 { color: var(--color-text-primary); }
.practice--light .tool {
  background: rgba(0, 23, 39, 0.025);
  border-color: var(--color-border-warm);
  color: var(--color-text-body);
}
.practice--light .tool::before { background: linear-gradient(90deg, transparent, var(--color-border-strong), transparent); opacity: 0.8; }
.practice--light .tool:hover { border-color: var(--color-text-accent); }
.practice--light .tool__name { color: var(--color-text-primary); }
.practice--light .tool__copy { color: var(--color-text-body); }
.practice--light .tool__copy strong { color: var(--color-text-primary); }
.practice--light .tool__art { stroke: var(--color-text-primary); opacity: 0.32; }
.practice--light .tool:hover .tool__art { opacity: 0.6; }
.practice--light .tool__art .a { stroke: var(--color-text-accent); }
.practice--light .tool__art .fa { fill: var(--color-text-accent); }
.practice--light .tool__art .zone { fill: var(--color-text-primary); }
.practice--light .tool__art .os__box { fill: var(--color-text-accent); stroke: var(--color-text-primary); }
.practice--light .tool__art .os__bar { fill: var(--color-text-primary); }
.practice--light .tool__art .os__bar.a { fill: var(--color-text-accent); }
@media (prefers-reduced-motion: no-preference) {
  .practice--light .tool__art .os__box { animation-name: os-check-light; }
}
@keyframes os-check-light { 0%, 12% { fill: transparent; } 24%, 82% { fill: var(--color-text-accent); } 100% { fill: transparent; } }

/* --- founders round two (shared with /sfo where the classes match) ---------
   The findings head holds one line each on desktop; the close under the
   findings loses its own rule (the last row already draws one) and stacks
   the caveat over "Sound familiar?"; a stacked head's lead runs the full
   column. */
@media (min-width: 1024px) {
  .diagnosis--findings .band__head .h2 { max-width: none; white-space: nowrap; font-size: min(var(--type-h2), 2.6vw); }
}
.band__head--stack .lead { max-width: none; }
.diagnosis--findings .diagnosis__close { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); border-top: 0; padding-top: var(--space-3); }
.diagnosis--findings .diagnosis__close .aside { max-width: none; }
.diagnosis--findings .diagnosis__act { align-items: flex-start; }

/* --- questions centred; the footer on phones ---------------------------------
   The one-column FAQ head and its accordion sit centred on /sfo and
   /founders; the rows keep their text on the left so the chevrons line up.
   On phones the footer's two link columns sit side by side with wrapping
   links and tighter rows, so the footer is about half as tall. */
.faq-band--stack { justify-items: center; }
.faq-band--stack .band__head { text-align: center; align-items: center; }
.faq-band--stack .band__head .h2 { margin-inline: auto; }
.faq-band--stack .faqs { width: 100%; margin-inline: auto; }

@media (max-width: 759px) {
  .site-footer { padding-block: var(--space-4) var(--space-3); }
  .site-footer__row { gap: var(--space-4); }
  .footer-nav { flex-direction: row; gap: var(--space-4); width: 100%; }
  .footer-nav__col { flex: 1 1 0; min-width: 0; }
  .footer-nav__head, .footer-nav__link { min-height: 0; white-space: normal; line-height: 1.35; padding-block: 5px; }
  .site-footer__base { margin-top: var(--space-3); padding-top: var(--space-3); }
}

/* --- /sfo hero: a still, in the home's glass language ------------------------
   hero-sfo.svg (generated by the scratch script gen_hero_sfo.py) is the
   home's glass artwork composed as an office's operating infrastructure:
   entity tiles above a glass operating rail, people tiles below, hairlines
   wiring each to the rail, and the principal as the one larger block the
   rail runs into. The copy edge is settled to navy as on the home. */
.hero--sfo-static {
  background-image: url('../img/hero-sfo.svg?v=20260911');
  background-size: cover;
  background-position: right center;
}
@media (max-width: 759px) {
  /* the copy runs over the artwork on a phone, so a navy wash sits over it */
  .hero--sfo-static { background-image: linear-gradient(180deg, rgba(0, 23, 39, 0.35), rgba(0, 23, 39, 0.05)), url('../img/hero-sfo.svg?v=20260911'); background-position: 90% center; background-size: auto 130%; }
}
.hero--founders-static {
  background-image: url('../img/hero-founders.svg?v=20260911');
  background-size: cover;
  background-position: right center;
}
@media (max-width: 759px) {
  .hero--founders-static { background-image: linear-gradient(180deg, rgba(0, 23, 39, 0.35), rgba(0, 23, 39, 0.05)), url('../img/hero-founders.svg?v=20260911'); background-position: 92% center; background-size: auto 130%; }
}

/* --- full columns ----------------------------------------------------------------
   The close's heading runs the full width of its column at every size. On
   tablets, where the two-column heads have already stacked, the claims,
   stories and leads run the full width instead of stopping at their desktop
   measures. */
.cta__ask .h2 { max-width: none; }
@media (max-width: 1023px) {
  .cta__ask .h2 { max-width: none; }
  .diagnosis__head .h2, .diagnosis__story, .diagnosis__story .diagnosis__pull { max-width: none; }
  .diagnosis__thesis .lead, .diagnosis__thesis .diagnosis__pull { max-width: none; }
  .record-band__head .h2, .record-band__head .lead, .record-band__note { max-width: none; }
  .band__head--stack .h2, .band__head--stack .lead, .band__head .h2, .band__head .lead { max-width: none; }
  .result__read, .case-band__result .instrument { max-width: none; }
  .narrative .h2 { max-width: none; }
  .case-band .band__head .h2 { max-width: none; }
}

/* 43 The assessment's calls -----------------------------------------------------
   The Principal Dependency Assessment, the lead magnet. A beige band inside
   the sticky header on the home, SFO, founders and about pages, so it stays
   put under the nav as the page scrolls on every screen; a button under the
   result on /sfo's case; a button in the Dependency Score panel on /founders.
   (A nav item was tried and removed: it did not fit the header.) */
.pda-banner {
  background: var(--color-surface-deep);
  border-top: 1px solid var(--color-cream-400);
  border-bottom: 1px solid var(--color-cream-400);
}
.pda-banner__inner { padding-block: var(--space-2); }
.pda-banner__text {
  margin: 0;
  text-align: center;
  font-size: var(--type-body-small);
  line-height: 1.45;
  color: var(--color-text-primary);
}
.pda-banner__q { color: var(--color-text-body); }
.pda-banner__link {
  color: var(--color-text-accent);
  font-weight: var(--weight-emphasis);
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  transition: border-color var(--duration-state) var(--ease-state);
}
.pda-banner__link::after { content: "\2192"; margin-left: 0.35em; }
.pda-banner__link:hover { border-bottom-color: currentColor; }
.pda-banner :focus-visible { outline-color: var(--color-focus-ring); }
.result__cta { margin-top: var(--space-2); }
.tool__cta { position: relative; align-self: flex-start; margin-top: var(--space-1); margin-bottom: var(--space-3); }
@media (max-width: 759px) {
  .pda-banner__inner { padding-block: var(--space-1); }
  .pda-banner__text { font-size: var(--type-caption); }
  .pda-banner__link { white-space: normal; }
}

/* --- home: the qualifying line, and the practices as one block ------------------
   The qualifying line sits under the hero's button on the home and about
   pages, on its own hairline, body-small in the warm cream. The practice band
   carries one full-width primary block for the family office and, under it,
   founders as a single line with its link — not a second card. */
.hero__qualify {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-inverse);
  max-width: 52ch;
  font-size: var(--type-body-small);
  line-height: 1.6;
  color: var(--color-text-inverse-warm);
}
.fork--single { grid-template-columns: minmax(0, 1fr); }
.door--wide .door__copy { max-width: 60ch; }
/* founders as the minor card: same family as the primary block, a step
   quieter — a solid navy fill (the artwork showed through and cost the copy
   its legibility), the glyph beside the text instead of above it, the name
   at h3, the copy at body-small, less padding */
.fork--minor { margin-top: var(--space-3); }
.fork--inverse .door--minor {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  padding: var(--space-4) var(--space-5);
  background: rgba(0, 23, 39, 0.72);   /* the secondary card's fill, a shade deeper, so the copy sits on solid ground */
  border-color: var(--color-border-inverse);
}
.door--minor .door__glyph { width: 36px; height: 36px; margin: 0; }
.door--minor .door__text { display: flex; flex-direction: column; gap: var(--space-2); }
.door--minor .door__name { font-size: var(--type-h3); }
.fork--inverse .door--minor .door__copy { font-size: var(--type-body-small); line-height: 1.7; max-width: 60ch; }
.door--minor .link-rule { align-self: flex-start; }
@media (max-width: 759px) {
  .fork--inverse .door--minor { grid-template-columns: 1fr; padding: var(--space-4); }
}
