/* ============================================================================
   architecture.css - canonical section pattern library (Website Factory).
   The canonical copy lives in the factory template (template/src/css/) and is
   synced to every site by sync-admin; sites must NOT hand-edit this file. Fix
   the template copy and re-sync instead. Theme tokens live in the site-owned
   /css/theme.css, which loads AFTER this file: this file only consumes tokens,
   and the type-scale :root block below is the one thing a theme may override
   (a later :root in theme.css wins). No other rule here may depend on load
   order.
   ========================================================================== */

/* Type scale (architecture, not theme) */
:root {
  --text-h1: clamp(2.25rem, 1.5rem + 3.5vw, 4rem);
  --text-h2: clamp(1.6rem, 1.3rem + 1.2vw, 2.25rem);
  --text-h3: 1.25rem;
  --text-sub: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
}

/* ---------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--body);
  color: var(--ink);
  background: var(--bg);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
img, svg, video { display: block; max-width: 100%; }
/* Running-text spacing. The reset above zeroes every margin, which is right for components
   and wrong for prose: two <p> in a row touched with no gap at all. `p + p` is the
   unambiguous global fix. `.flow` is the opt-in container for blocks that mix headings,
   paragraphs and lists - it spaces DIRECT children only, so components that manage their own
   margins (cards, steps, post-cards) are never double-spaced. `.prose-band`'s container gets
   the same treatment for free, since running text is the whole point of it. */
p + p { margin-top: var(--space-3); }
.flow > * + *, .prose-band .container > * + * { margin-top: var(--space-3); }
.flow > * + h2, .flow > * + h3,
.prose-band .container > * + h2, .prose-band .container > * + h3 { margin-top: var(--space-5); }
.flow > .eyebrow, .prose-band .container > .eyebrow { margin-bottom: 0; }
.flow > .eyebrow + *, .prose-band .container > .eyebrow + * { margin-top: var(--space-2); }
input, button, textarea, select { font: inherit; }
a { color: var(--accent-deep); }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  html { scroll-behavior: auto; }
}

h1, h2, h3, h4 {
  font-family: var(--display);
  line-height: 1.12;
  letter-spacing: -0.015em;
  font-weight: 700;
}
h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
p, li { max-width: 65ch; }
/* Defensive wrapping. One 120-character unbroken heading pushed a live page to a 5631px
   document scrollWidth against a 1135px viewport - a working horizontal scrollbar on the
   public home page, overflowing HTML/BODY/MAIN/SECTION/.container from the H1, because
   nothing in this file broke long tokens. The realistic trigger is not 120 A's: it is a
   URL, an email address, a hashtag or a compound word pasted into any CMS-editable heading
   or paragraph. break-word, not anywhere: break-word only splits a token once its line
   genuinely overflows, so the intrinsic sizing of normal text is untouched. overflow-wrap
   inherits, so child inline elements (links inside a paragraph) are covered too. Note for
   any future automated guard: the glyphs escape their boxes while every bounding rect
   stays viewport-sized, so a rect-based check reports zero overflow - measure document
   scrollWidth, not rects. */
h1, h2, h3, h4, p, li { overflow-wrap: break-word; }

/* --------------------------------------------------------------- layout */
.container {
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--space-4);
}
.band { padding-block: var(--space-6); background: var(--bg); }
.band--alt { background: var(--bg-alt); }
.band--dark {
  background: var(--bg-dark);
  color: var(--ink-inverse);
  --muted: color-mix(in srgb, var(--ink-inverse) 78%, transparent);
  --line: color-mix(in srgb, var(--ink-inverse) 18%, transparent);
}
.band--dark a { color: var(--ink-inverse); }
.band__head { max-width: 46rem; margin-bottom: var(--space-5); }
.band__head--center { margin-inline: auto; text-align: center; }

/* ---------------------------------------------------------------- atoms */
.eyebrow {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--accent-2-deep);
  margin-bottom: var(--space-2);
}
.band--dark .eyebrow, .hero--bleed .eyebrow { color: var(--accent-2); }

.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--btn-ink);
  font-weight: 600;
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: var(--radius);
  padding: 0.8rem 1.6rem;
  cursor: pointer;
  transition: background 0.18s, transform 0.18s;
}
.btn:hover { background: var(--accent-deep); }
.btn--sm { padding: 0.5rem 1.05rem; }
.btn--ghost {
  display: inline-block;
  font-weight: 600;
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 2px solid currentColor;
  border-radius: var(--radius);
  padding: 0.8rem 1.6rem;
  transition: background 0.18s;
}
.btn--ghost:hover { background: color-mix(in srgb, currentColor 8%, transparent); }
.btn--ghost-inverse { color: var(--ink-inverse); }
button.btn { border: 2px solid transparent; }

.cta-row { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin-top: var(--space-4); }

.link-arrow {
  font-weight: 600;
  color: var(--accent-deep);
  text-decoration: none;
}
.link-arrow::after { content: " \2192"; display: inline-block; transition: transform 0.18s; }
.link-arrow:hover::after { transform: translateX(3px); }
.band--dark .link-arrow, .link-arrow--inverse { color: var(--ink-inverse); }

.trust-row {
  list-style: none;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-4);
  font-size: 0.9rem;
  color: var(--muted);
}
.trust-row .stars, .stars { color: var(--accent-2); letter-spacing: 0.05em; }
.band--dark .trust-row, .trust-row--inverse { color: color-mix(in srgb, var(--ink-inverse) 80%, transparent); }

.stat-row { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-5); margin-top: var(--space-5); }
.stat-row strong { display: block; font-family: var(--display); font-weight: 800; font-size: 1.75rem; }
.stat-row span { font-size: 0.85rem; color: var(--muted); }

.checks { list-style: none; padding: 0; display: grid; gap: var(--space-2); }
.checks li { padding-left: 1.6em; position: relative; }
.checks li::before { content: "\2713"; position: absolute; left: 0; color: var(--accent); font-weight: 700; }

/* --------------------------------------------------------------- header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 4.5rem;
}
.nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--ink);
  text-decoration: none;
  margin-right: auto;
}
.nav__brand img, .nav__brand svg { height: 2.2rem; width: auto; }
.nav__toggle {
  display: none;
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 1.4rem;
  line-height: 1;
  padding: 0.35rem 0.6rem;
  cursor: pointer;
}
.nav__list {
  list-style: none;
  padding: 0;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.nav__link {
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.95rem;
  padding-block: 0.4rem;
  border-bottom: 2px solid transparent;
}
.nav__link:hover { color: var(--accent-deep); }
.nav__link--current { border-bottom-color: var(--accent-2); }
.nav__tel { font-weight: 700; color: var(--ink); text-decoration: none; white-space: nowrap; }

/* ---- Nav submenu (one level, CSS only) ----------------------------------------
   No JavaScript anywhere in this. main.js is site-owned and would only ever reach one
   site; behaviors.js loads solely when a page's SECTIONS declare a capability, and the
   nav is chrome that exists on every page including ones with no sections at all. So a
   scripted dropdown is either unsyncable or unloaded. :hover opens it for a mouse,
   :focus-within opens it for a keyboard, and below 880px it stops being a dropdown at
   all - it flattens into an indented sub-list inside the full-screen menu, which is the
   only behaviour that is safe on touch (a hover menu on a phone eats the first tap).

   The parent is a REAL LINK to a real hub page, never a dead toggle: the reader who taps
   it lands somewhere, and sync-chrome's current-nav marking works on it unmodified.
   Children carry plain `nav__link` for exactly that reason - sync-chrome collects nav
   hrefs with /class="nav__link" href="([^"]+)"/ and marks the exact match, so a child
   page highlights its own entry with no change to that tool. The parent then picks up a
   muted marker via :has(), which degrades to simply not highlighting. */
.nav__item { position: relative; }
/* The caret hangs off the LI, never off a modifier class on the link. sync-chrome collects
   nav hrefs with the exact literal `class="nav__link" href=`, so a parent written as
   `class="nav__link nav__link--parent"` would silently drop out of navHrefs and the hub page
   would be the one page in the site that cannot mark itself current. */
.nav__item--has-sub > .nav__link::after {
  content: "";
  display: inline-block;
  margin-left: 0.4em;
  vertical-align: 0.15em;
  border-left: 0.3em solid transparent;
  border-right: 0.3em solid transparent;
  border-top: 0.3em solid currentColor;
}
.nav__sub {
  list-style: none;
  margin: 0;
  padding: var(--space-2);
  position: absolute;
  top: 100%;
  left: calc(var(--space-2) * -1);
  z-index: 20;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.25rem);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}
.nav__item--has-sub:hover > .nav__sub,
.nav__item--has-sub:focus-within > .nav__sub {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.nav__sub .nav__link { border-bottom: 0; white-space: nowrap; }
/* Parent of the current page gets the marker its child is carrying. */
.nav__item--has-sub:has(.nav__link--current) > .nav__link { border-bottom-color: var(--accent-2); }

@media (max-width: 880px) {
  /* backdrop-filter on the header would make it the containing block for the fixed overlay
     below, collapsing `inset` against the ~4.5rem header instead of the viewport. Drop it
     on mobile so the full-screen menu resolves against the viewport. */
  .site-header { backdrop-filter: none; background: var(--bg); }
  .nav__toggle { display: block; }
  .nav__list {
    display: none;
    position: fixed;
    inset: 4.5rem 0 0 0;
    background: var(--bg);
    flex-direction: column;
    align-items: flex-start;
    padding: var(--space-5) var(--space-4);
    gap: var(--space-4);
    overflow-y: auto;
  }
  .nav__list[data-open="true"] { display: flex; }
  .nav__link { font-size: 1.3rem; }
  .nav__cta { margin-top: var(--space-3); }

  /* The dropdown stops being a dropdown. Inside the full-screen menu there is room to
     show the children outright, so they are always visible and never need a tap to
     reveal - which also means the parent link keeps its one job, going to the hub page. */
  .nav__item--has-sub { display: flex; flex-direction: column; align-items: flex-start; }
  .nav__item--has-sub > .nav__link::after { display: none; }
  .nav__sub {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    background: none;
    border: 0;
    border-left: 2px solid var(--line);
    border-radius: 0;
    box-shadow: none;
    margin-top: var(--space-3);
    padding: 0 0 0 var(--space-3);
    gap: var(--space-3);
  }
  .nav__sub .nav__link { font-size: 1.05rem; }
}

/* Mobile call bar (trades/clinics: enabled by adding the element to the page) */
.sticky-callbar { display: none; }
@media (max-width: 720px) {
  .sticky-callbar {
    display: flex;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 60;
  }
  .sticky-callbar a {
    flex: 1;
    text-align: center;
    padding: 0.9rem;
    font-weight: 700;
    text-decoration: none;
  }
  .sticky-callbar a:first-child { background: var(--accent); color: var(--btn-ink); }
  .sticky-callbar a:last-child { background: var(--bg-dark); color: var(--ink-inverse); }
  body:has(.sticky-callbar) { padding-bottom: 3.4rem; }
}

/* --------------------------------------------------------------- heroes */
.hero { padding-block: var(--space-7); }
.hero__sub { font-size: var(--text-sub); color: var(--muted); max-width: 48ch; margin-top: var(--space-3); }
.hero__reassure { font-size: 0.9rem; color: var(--muted); margin-top: var(--space-3); }

/* 1.1 split */
.hero--split .hero__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: var(--space-6);
  align-items: center;
}
.hero__media { position: relative; margin: 0; }
.hero__media img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.hero__media--arch img { border-radius: calc(var(--radius) + 45%) calc(var(--radius) + 45%) var(--radius) var(--radius); }
/* The hero frame is 4/5 portrait and crops from the CENTRE, which is wrong whenever the
   subject sits off to one side - a landscape shot of a band on stage left loses the band and
   keeps the empty half of the room. Put the focus where the subject actually is. */
.hero__media--focus-left img { object-position: left center; }
.hero__media--focus-right img { object-position: right center; }
.hero__media--focus-top img { object-position: center top; }
.hero__float-card {
  position: absolute;
  bottom: calc(-1 * var(--space-3));
  left: calc(-1 * var(--space-4));
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--space-3);
  font-size: 0.9rem;
  max-width: 18rem;
}

/* 1.2 centered editorial */
.hero--center .hero__copy { text-align: center; max-width: 46rem; margin-inline: auto; }
.hero--center h1 { font-size: clamp(2.5rem, 1.6rem + 4vw, 4.5rem); }
.hero--center .hero__sub { margin-inline: auto; }
.hero--center .cta-row { justify-content: center; }
.hero__media--wide { max-width: min(68rem, calc(100% - 2 * var(--space-4))); margin: var(--space-6) auto 0; }
.hero__media--wide img {
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
}

/* 1.3 full-bleed overlay */
.hero--bleed {
  position: relative;
  min-height: min(82vh, 46rem);
  display: grid;
  align-items: center;
  color: var(--ink-inverse);
  --muted: color-mix(in srgb, var(--ink-inverse) 78%, transparent);
}
.hero--bleed .hero__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero--bleed .hero__scrim {
  position: absolute;
  inset: 0;
  /* Two layers, and the flat one is load-bearing. sections.md 1.3 promises that contrast is
     guaranteed by the scrim and never by the photo, but the directional gradient alone is down
     to ~30% by the time the copy column ends - over a light photo (pale carpet, sky, snow) the
     subhead landed under 4.5:1. The flat base makes the promise true for any photo; the
     gradient still does the directional shaping on top of it. */
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--bg-dark) 88%, transparent) 0%,
      color-mix(in srgb, var(--bg-dark) 30%, transparent) 72%),
    color-mix(in srgb, var(--bg-dark) 45%, transparent);
}
.hero--bleed .hero__copy { position: relative; max-width: 40rem; }
.hero--bleed.hero--centered .hero__copy { text-align: center; margin-inline: auto; }
.hero--bleed.hero--centered .cta-row { justify-content: center; }
@media (max-width: 720px) {
  .hero--bleed { min-height: auto; padding-block: var(--space-7); }
  .hero--bleed .hero__scrim { background: color-mix(in srgb, var(--bg-dark) 72%, transparent); }
}

/* 1.4 dark band */
.hero--dark .hero__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: var(--space-6);
  align-items: center;
}
.hero--dark .hero__media img { box-shadow: none; border: 1px solid color-mix(in srgb, var(--ink-inverse) 12%, transparent); }

@media (max-width: 720px) {
  .hero--split .hero__grid, .hero--dark .hero__grid { grid-template-columns: 1fr; }
  .hero__float-card { position: static; margin-top: var(--space-3); max-width: none; }
  .cta-row > .btn, .cta-row > .btn--ghost { flex: 1 1 100%; text-align: center; }
}

/* ---------------------------------------------------- interior page top */
.page-header { padding-block: var(--space-6); }
.page-header h1 { font-size: clamp(2rem, 1.4rem + 2.5vw, 3rem); }
/* space-3, not space-2. At the h1 sizes this component uses, half a rem under a 3rem
   headline reads as the sub being stuck to it rather than following it. */
.page-header__sub { color: var(--muted); margin-top: var(--space-3); max-width: 55ch; }

/* ---------------------------------------------------------- card grids */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--space-4);
}
.card {
  position: relative;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--space-5);
  transition: transform 0.18s, box-shadow 0.18s;
}
.band--dark .card { background: color-mix(in srgb, var(--ink-inverse) 6%, transparent); }
.card:hover { transform: translateY(-4px); }
.card__icon {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: calc(var(--radius) * 0.8);
  background: color-mix(in srgb, var(--accent-2) 14%, transparent);
  color: var(--accent-deep);
  font-size: 1.4rem;
  margin-bottom: var(--space-3);
}
.card__title { margin-bottom: var(--space-2); }
.card__title a { color: inherit; text-decoration: none; }
.card__title a::after { content: ""; position: absolute; inset: 0; }
.card__body { color: var(--muted); margin-bottom: var(--space-3); }
.card--photo { padding: 0; overflow: hidden; }
.card--photo img { aspect-ratio: 16 / 9; object-fit: cover; width: 100%; height: auto; }
.card--photo .card__inner { padding: var(--space-4); }
.card--feature { grid-column: span 2; }
@media (max-width: 720px) { .card--feature { grid-column: span 1; } }
/* A card for something that has already happened. Still fully readable - it is evidence of a
   track record - but visibly behind the live ones, so last month's event is never mistaken
   for the next one. Dashed rather than faded: opacity would take the text below contrast. */
.card--past {
  background: var(--bg-alt);
  border-style: dashed;
  box-shadow: none;
}
.card--past .card__title, .card--past .card__body { color: var(--muted); }
.card--past .card__badge { background: transparent; color: var(--muted); border: 1px solid var(--line); }
/* NOTE: .card__badge--upcoming cannot live here. `.card__badge` is declared much further
   down this file, and a same-specificity modifier placed ABOVE its base class loses on
   source order - it rendered as the plain gold badge. It sits directly after the base
   instead. `.card--past .card__badge` above is fine: it is (0,2,0) and outranks both. */

/* Posts (blog teasers + dynamic /blog list) */
.card-grid--posts .card__body, .post-card p { color: var(--muted); }
.post-list {
  display: grid;
  gap: var(--space-4);
  /* Deliberate max of 3 per row (not auto-fit sprawl); reflows down on smaller screens. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 900px) { .post-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .post-list { grid-template-columns: 1fr; } }
.post-card {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.post-card__media img { aspect-ratio: 16 / 9; object-fit: cover; width: 100%; height: auto; }
.post-card__body { padding: var(--space-4); }
.post-card__meta { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent-2-deep); margin-bottom: var(--space-2); }
.post-card h2 { font-size: 1.2rem; margin-bottom: var(--space-2); }
.post-card h2 a { color: inherit; text-decoration: none; }
.post-card h2 a:hover { color: var(--accent-deep); }

/* ------------------------------------------------------- split feature */
.split-feature {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  align-items: center;
}
.split-feature + .split-feature { margin-top: var(--space-6); }
/* The text column is ALWAYS mixed content - eyebrow, heading, prose, often a list and a CTA
   row - and the reset zeroes every margin while restoring only `p + p`. So a heading sat
   flush against the paragraph under it, everywhere this component is used. `.flow` is the
   documented opt-in for mixed content, but a split-feature's text column qualifies by
   construction, so it opts in here rather than by somebody remembering a class. */
.split-feature > div > * + * { margin-top: var(--space-3); }
.split-feature > div > .eyebrow + * { margin-top: var(--space-2); }
.split-feature > div > * + h2, .split-feature > div > * + h3 { margin-top: var(--space-4); }
/* And AFTER a heading. Display headings run at line-height 1.12, so a 1rem margin reads as
   about half that much air and the paragraph still looks welded to the title. */
.split-feature > div > h2 + *, .split-feature > div > h3 + * { margin-top: var(--space-4); }
.split-feature:nth-of-type(even) .split-feature__media { order: -1; }
.split-feature__media { margin: 0; }
.split-feature__media img {
  width: 100%;
  /* height:auto is load-bearing, not tidiness. page-checklist REQUIRES width/height attrs on
     every <img>; the height attribute is a presentational hint, and with a height in play
     aspect-ratio is ignored and the image renders at its intrinsic pixel height. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.split-feature--bio .split-feature__media img { aspect-ratio: 4 / 5; }
/* A PORTRAIT photograph in the default 4/3 landscape frame is cropped from the centre, and
   on a group shot the centre of a portrait frame is everyone's midsection - the heads go.
   Put this on the <figure> when the source image is taller than it is wide. */
.split-feature__media--tall img { aspect-ratio: 4 / 5; }
@media (max-width: 720px) {
  .split-feature { grid-template-columns: 1fr; }
  .split-feature .split-feature__media { order: -1; }
}

/* ------------------------------------------------------------ stat band */
.stat-band { text-align: center; }
.stat-band__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-5); }
.stat { font-family: var(--display); }
.stat strong { display: block; font-size: clamp(2.5rem, 2rem + 2vw, 3.5rem); font-weight: 800; }
.stat span { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }

/* --------------------------------------------------------- testimonials */
.testimonials__agg { font-weight: 600; margin-bottom: var(--space-4); }
.quote-card footer { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-3); }
.quote-card footer img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.quote-card cite { font-style: normal; font-weight: 600; }
.quote-card cite span { display: block; font-weight: 400; font-size: 0.85rem; color: var(--muted); }
.quote-spotlight { text-align: center; max-width: 46rem; margin-inline: auto; }
.quote-spotlight blockquote { font-family: var(--display); font-size: clamp(1.4rem, 1.1rem + 1.4vw, 2rem); font-style: italic; }
.quote-spotlight figcaption { margin-top: var(--space-3); color: var(--muted); }
.quote-rail { display: flex; gap: var(--space-4); overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: var(--space-3); }
.quote-rail .quote-card { min-width: 22rem; scroll-snap-align: start; }

/* ------------------------------------------------------------------ faq */
.faq { max-width: 46rem; margin-inline: auto; }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__item summary {
  font-weight: 600;
  padding-block: var(--space-3);
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after { content: "+"; color: var(--accent); font-size: 1.4rem; font-weight: 400; transition: transform 0.18s; }
.faq__item[open] summary::after { transform: rotate(45deg); }
.faq__body { padding-bottom: var(--space-4); color: var(--muted); }

/* ---------------------------------------------------------------- steps */
.steps {
  list-style: none;
  counter-reset: step;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}
.steps__item { counter-increment: step; }
.steps__num {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent-2) 15%, transparent);
  color: var(--accent-deep);
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.2rem;
  margin-bottom: var(--space-3);
}
.steps__num::before { content: counter(step); }
.steps__item h3 { margin-bottom: var(--space-2); }
.steps__item p { color: var(--muted); }
/* The steps band's optional closing button. Hand-written pages centered this with an
   inline style; the renderer emits no inline styles, so the rule lives here - same shape
   as `.cta-band .cta-row` above it. Matches only a .cta-row that directly follows the
   list, so a steps band without one costs nothing. */
.steps + .cta-row { justify-content: center; margin-top: var(--space-5); }
@media (max-width: 720px) { .steps { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------- CTA band */
.cta-band { text-align: center; }
.cta-band__inner { max-width: 42rem; margin-inline: auto; }
.cta-band__inner p { margin-inline: auto; margin-top: var(--space-2); color: var(--muted); }
.cta-band .cta-row { justify-content: center; }
.cta-band--soft { background: var(--bg-alt); color: var(--ink); }

/* ------------------------------------------------ misc content patterns */
.prose-band .container > * { max-width: 60ch; }

/* prose-band variants. A page may legitimately need several prose bands in a row, and the
   reader should not meet the same rectangle each time. Both are SHAPES built from the
   existing tokens, so they inherit whatever palette the site's theme.css defines and stay
   correct on band--alt and band--dark. Neither changes the section's data. */

/* Two text columns. NOT .split-feature, which pairs copy with an image and leaves a
   half-width void when the image is missing. This flows ONE body across two columns, so it
   needs no second field and no media. The 60ch cap is lifted because the column itself is
   now the measure; break-inside keeps a paragraph from being sawn across the gutter, and a
   heading stays with the text it introduces. */
.prose-band--two-column .container { display: block; }
.prose-band--two-column .container > * { max-width: none; }
.prose-band--two-column .container > p,
.prose-band--two-column .container > ul,
.prose-band--two-column .container > ol {
  columns: 2;
  column-gap: var(--space-6);
}
.prose-band--two-column .container > * { break-inside: avoid; }
.prose-band--two-column .container > h2,
.prose-band--two-column .container > h3 { break-after: avoid; }

/* ---- Data table --------------------------------------------------------------
   The library had NO general table until 2026-08-18. `.ws-table` existed, but it is
   the worksheet's own thing - 0.85rem, boxed on all four sides, sized to be printed
   and filled in by hand - and reaching for it whenever a site has genuinely tabular
   content (an event calendar, an officer roster, a fee schedule) borrows a print
   artifact for a screen job. This is the screen job.

   Rules run horizontally only. A grid of boxes reads as a form; a stack of rules
   reads as a list you can scan down, which is what a reader does with a calendar.
   The wrapper scrolls on its own rather than letting the page scroll sideways -
   a table is the most common way a phone layout breaks, and `overflow-x: auto` on
   an ancestor is the fix that does not require guessing a breakpoint. */
.data-table-wrap { overflow-x: auto; }
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.data-table caption {
  caption-side: top;
  text-align: left;
  color: var(--muted);
  font-size: 0.9rem;
  margin-bottom: var(--space-3);
}
.data-table th, .data-table td {
  padding: 0.7rem 0.9rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.data-table thead th {
  font-family: var(--display);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom-width: 2px;
  white-space: nowrap;
}
.data-table th[scope="row"] { font-weight: 600; white-space: nowrap; }
.data-table tbody tr:last-child th, .data-table tbody tr:last-child td { border-bottom: 0; }
/* A row the reader should skim past rather than plan around (club closed, no event). */
.data-table tr.is-muted th[scope="row"], .data-table tr.is-muted td { color: var(--muted); }

/* ---- Month-grid calendar ------------------------------------------------------
   Drawn by js/calendar.js from an authored <ol class="cal-source"> of dated <li>. The
   list is the content and ships in the HTML; the grid is a nicer way to read it. So the
   source list is only hidden once the script has actually run and added the enhanced
   class - if the script never loads, the reader still gets the whole year as text. */
.cal { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); overflow: hidden; }
.cal__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-alt);
  border-bottom: 1px solid var(--line);
}
.cal__month { font-family: var(--display); font-size: 1.25rem; margin: 0; text-align: center; flex: 1; }
.cal__nav {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 1.3rem;
  line-height: 1;
  /* 2.75rem square: a comfortable touch target, which arrows usually are not. */
  min-width: 2.75rem;
  min-height: 2.75rem;
  cursor: pointer;
}
.cal__nav:hover:not(:disabled) { background: var(--accent); color: var(--btn-ink); border-color: var(--accent); }
.cal__nav:disabled { opacity: 0.35; cursor: default; }
.cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: var(--line); padding: 1px; }
.cal__dow {
  background: var(--bg-alt);
  padding: var(--space-2);
  text-align: center;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 700;
}
.cal__dow abbr { text-decoration: none; }
.cal__cell {
  background: var(--bg);
  min-height: 3.6rem;
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  align-items: flex-start;
}
.cal__cell--pad { background: var(--bg-alt); }
.cal__num { font-size: 0.85rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.cal__cell.is-event { background: color-mix(in srgb, var(--accent-2) 10%, var(--bg)); }
.cal__cell.is-event .cal__num { color: var(--ink); font-weight: 700; }
/* Today gets a ring rather than a fill, so it reads on top of an event tint. */
.cal__cell.is-today { box-shadow: inset 0 0 0 2px var(--accent); }
.cal__dots { display: flex; flex-wrap: wrap; gap: 0.2rem; }
.cal__dot { width: 0.45rem; height: 0.45rem; border-radius: 999px; background: var(--accent); display: inline-block; }
.cal__dot--dinner { background: var(--accent-2-deep); }
.cal__dot--meeting { background: var(--accent); }
.cal__dot--conference { background: var(--muted); }
.cal__dot--closed { background: var(--line); border: 1px solid var(--muted); }
.cal__list { padding: var(--space-4); border-top: 1px solid var(--line); }
.cal__events { list-style: none; padding: 0; display: grid; gap: var(--space-3); }
.cal__event { display: grid; grid-template-columns: 6.5rem 1fr; gap: var(--space-3); align-items: baseline; }
.cal__event-date { display: flex; align-items: center; gap: 0.4rem; font-weight: 700; font-size: 0.9rem; white-space: nowrap; }
.cal__event-body { display: flex; flex-direction: column; gap: 0.15rem; }
.cal__event-detail { color: var(--muted); font-size: 0.9rem; }
.cal__none { color: var(--muted); }
.cal__legend { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-3); font-size: 0.85rem; color: var(--muted); }
.cal__legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.cal-source--enhanced { display: none; }
@media (max-width: 560px) {
  .cal__cell { min-height: 2.9rem; padding: 0.3rem; }
  .cal__event { grid-template-columns: 1fr; gap: 0.15rem; }
}

/* Callout: set apart from the argument around it by a tint and a rule down the leading
   edge. border-inline-start, not border-left, so it follows the writing direction. */
.prose-band--callout .container {
  max-width: 60rem;
  background: var(--bg-alt);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
}
.prose-band--callout.band--alt .container { background: var(--bg); }
/* On a dark band the alt tint is invisible, so lift the block instead of tinting it. */
.prose-band--callout.band--dark .container {
  background: color-mix(in srgb, var(--ink-inverse) 10%, transparent);
  border-inline-start-color: var(--accent-2);
}

@media (max-width: 720px) {
  /* One column on a phone: two columns of 45ch prose in a 375px viewport is unreadable. */
  .prose-band--two-column .container > p,
  .prose-band--two-column .container > ul,
  .prose-band--two-column .container > ol { columns: 1; }
  .prose-band--callout .container { padding: var(--space-3) var(--space-4); }
}
/* start, not center: every page opens with a LEFT-aligned .page-header, and a centred 60ch
   column underneath a left-aligned H1 reads as a mistake rather than a choice. */
.prose-band .container { display: grid; justify-content: start; }
.logo-strip { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); justify-content: center; }
.logo-strip img { height: 2.4rem; width: auto; filter: grayscale(1); opacity: 0.7; }
.area-list { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); list-style: none; padding: 0; }
.area-list a { color: var(--muted); }
/* Grouped checklist: one heading over two or more named lists, side by side. Two lists is
   the shape this is for, so the columns are explicit rather than auto-fit - auto-fit would
   let a pair reflow to one column mid-range and read as two unrelated sections. */
.checklist__groups { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
.checklist__group > h3 { margin-bottom: var(--space-1); }
.checklist__group > p { color: var(--muted); margin-bottom: var(--space-2); }
@media (max-width: 720px) { .checklist__groups { grid-template-columns: 1fr; } }

.price-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-4); }
.price-cards .card__price { font-family: var(--display); font-size: 2rem; font-weight: 800; margin-bottom: var(--space-2); }

/* The recommended tier. Inverted rather than merely outlined, because a price ladder is
   scanned and a border is not a strong enough signal to survive that. Built from the same
   tokens band--dark uses, so it inverts correctly under any theme.
   Note this does NOT count against the dark-band budget: the budget rations whole BANDS,
   and one inverted card inside a light band is not the dramatic full-width inversion the
   cap exists to keep scarce. */
.price-cards .card--featured {
  background: var(--bg-dark);
  color: var(--ink-inverse);
  border-color: var(--bg-dark);
}
.price-cards .card--featured .card__title,
.price-cards .card--featured .card__price { color: var(--ink-inverse); }
.price-cards .card--featured .card__body,
.price-cards .card--featured .checks li { color: color-mix(in srgb, var(--ink-inverse) 88%, transparent); }
/* Real text, not ::before content - a generated string is unreadable to assistive tech and
   cannot be translated, and "Most popular" is information about the offer. */
.card__badge {
  display: inline-block;
  margin-bottom: var(--space-2);
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background: var(--accent-2);
  color: var(--ink);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* Dated, but not the one being highlighted: present and legible, clearly a step behind the
   filled gold badge. Must stay AFTER .card__badge - same specificity, so source order is
   what decides it. */
.card__badge--upcoming { background: transparent; color: var(--accent); border: 1px solid var(--accent); }

/* ---- Source attribution ------------------------------------------------------
   A quiet line naming where a fact came from when it did NOT come from the client.
   The factory forbids invented statistics; this is the affordance that lets a real
   external one be published honestly, with the reader one click from checking it.
   Deliberately small and muted - it is provenance, not content - but never below the
   muted-token contrast floor, because a citation nobody can read is decoration. */
.source-note { font-size: 0.8rem; color: var(--muted); margin-top: var(--space-3); }
.source-note a { color: inherit; text-underline-offset: 0.15em; }
.source-note a:hover { color: var(--accent-deep); }
.band--dark .source-note a:hover, .site-footer .source-note a:hover { color: var(--accent-2); }
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--space-3); }
.gallery img { aspect-ratio: 1; object-fit: cover; border-radius: var(--radius); width: 100%; height: auto; }
.credits-list { list-style: none; padding: 0; display: grid; gap: var(--space-2); color: var(--muted); }

/* ------------------------------ hours + location strip (persona D, §5.9) */
.hours-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-4) var(--space-5);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding-block: var(--space-4);
}
.hours-strip__label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--accent-2-deep);
  margin-bottom: var(--space-1);
}
.hours-strip__value { font-size: 1.15rem; font-weight: 600; line-height: 1.3; }
.hours-strip__value a { color: inherit; text-decoration: none; }
.hours-strip__value a:hover { text-decoration: underline; }
.hours-strip__note { font-size: 0.9rem; color: var(--muted); margin-top: var(--space-1); }
.band--dark .hours-strip__label { color: var(--accent-2); }
.band--dark .hours-strip { border-color: color-mix(in srgb, var(--ink-inverse) 18%, transparent); }

/* ------------------------------------------------- menu page (persona D, §5.10) */
/* One column only: the menu-list definition ships a single variant ("list") and the renderer
   emits the bare .menu-list. A .menu-list--wide two-column modifier lived here with nothing
   in the factory able to emit it, so it was removed rather than left as a promise. */
.menu-list { display: grid; gap: var(--space-6); max-width: 46rem; margin-inline: auto; }
.menu-group__title {
  padding-bottom: var(--space-2);
  border-bottom: 2px solid var(--accent-2);
  margin-bottom: var(--space-2);
}
.menu-group__note { color: var(--muted); font-size: 0.95rem; margin-bottom: var(--space-4); }
.menu-items { list-style: none; padding: 0; display: grid; gap: var(--space-4); }
.menu-item__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.menu-item__name { font-size: 1.05rem; margin: 0; }
.menu-item__head::after {
  content: "";
  flex: 1;
  order: 1;
  border-bottom: 1px dotted var(--line);
  transform: translateY(-0.25em);
}
.menu-item__price {
  order: 2;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.menu-item__desc { color: var(--muted); font-size: 0.95rem; margin-top: var(--space-1); }

/* --------------------------------------- standing disclosure band (§5.11) */
.notice {
  background: color-mix(in srgb, var(--accent-2) 14%, var(--bg));
  border-top: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  padding-block: var(--space-3);
  color: var(--ink);
}
.notice p { margin: 0; font-size: 0.95rem; max-width: 80ch; }
.notice a { color: inherit; }
/* Page-local repeat: sits INSIDE a .container, so it carries its own inline padding. */
.notice--page {
  padding-inline: var(--space-4);
  border: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
}

/* --------------------- latest-from-each-area hub grid (persona E, §5.12) */
/* Deliberately lighter chrome than .card: no border box, no shadow, just a rule. Six bordered
   boxes on a personal homepage read as a services grid, which is the one thing this persona
   must not look like. */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-5) var(--space-4);
}
.hub-item {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  border-top: 2px solid var(--accent-2);
  padding-top: var(--space-3);
}
.hub-item__area {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-family: var(--display);
}
.hub-item__area a { color: var(--ink); text-decoration: none; }
.hub-item__area a:hover { color: var(--accent-deep); }
.hub-item__latest { display: grid; gap: var(--space-1); }
.hub-item__meta { font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.hub-item__title { font-size: 1.15rem; line-height: 1.25; }
.hub-item__title a { color: inherit; text-decoration: none; }
.hub-item__title a:hover { color: var(--accent-deep); }
.hub-item__excerpt {
  color: var(--muted);
  font-size: 0.95rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.hub-item__more { margin-top: var(--space-1); }

/* ------------------------------------- designed empty state (§5.13) */
/* The component a sparse site cannot do without. No dashed frame, no icon, no spinner - those
   say "broken". A muted sentence in the owner's voice says "young". */
.empty-note {
  color: var(--muted);
  border-inline-start: 2px solid var(--line);
  padding-inline-start: var(--space-3);
  max-width: 46ch;
}

/* ---------------------------------------------------------- contact page */
.contact-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: var(--space-6); align-items: start; }
@media (max-width: 720px) { .contact-grid { grid-template-columns: 1fr; } }
.lead-form { display: grid; gap: var(--space-3); }
.lead-form label { font-weight: 600; font-size: 0.9rem; display: grid; gap: var(--space-1); }
.lead-form .opt { font-weight: 400; color: var(--muted); }
.lead-form input, .lead-form textarea {
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) * 0.8);
  padding: 0.7rem 0.9rem;
  background: var(--bg);
  color: var(--ink);
  width: 100%;
}
.lead-form__note { font-size: 0.85rem; color: var(--muted); }
.lead-form__error { background: color-mix(in srgb, var(--accent-2) 12%, transparent); color: var(--accent-2-deep); border-radius: var(--radius); padding: var(--space-3); }
.contact-details { display: grid; gap: var(--space-4); align-content: start; }
.contact-details address { font-style: normal; color: var(--muted); }
.hours { display: grid; grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-4); color: var(--muted); }
.hours dt { font-weight: 600; color: var(--ink); }
/* Honeypot field - visually gone, still in the form for bots to fill */
.field-website { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------------------- blog article */
.post-article { max-width: 65ch; margin-inline: auto; }
.post-article__hero {
  width: 100%;
  max-width: min(100%, 52rem);
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
}
.post-article h2, .post-article h3 { margin-top: var(--space-5); margin-bottom: var(--space-3); }
.post-article p { margin-block: var(--space-3); }
.post-article ul, .post-article ol { margin-block: var(--space-3); padding-left: 1.4rem; }
.post-article li { margin-block: var(--space-1); }
.post-article blockquote {
  border-left: 3px solid var(--accent-2);
  padding-left: var(--space-4);
  margin-block: var(--space-4);
  font-style: italic;
  color: var(--muted);
}
.post-article img { border-radius: var(--radius); margin-block: var(--space-4); height: auto; }
.post-article hr { border: none; border-top: 1px solid var(--line); margin-block: var(--space-5); }
.post-article pre {
  background: var(--bg-alt);
  border-radius: var(--radius);
  padding: var(--space-4);
  overflow-x: auto;
  font-size: 0.9rem;
}
.post-article code { background: var(--bg-alt); padding: 0.1em 0.35em; border-radius: 4px; font-size: 0.92em; }
.post-article pre code { background: none; padding: 0; }
.post-article__cta { text-align: center; }
.pager { display: flex; justify-content: space-between; margin-top: var(--space-5); }

/* --------------------------------------------------------------- footer */
.site-footer {
  background: var(--bg-dark);
  color: var(--ink-inverse);
  --muted: color-mix(in srgb, var(--ink-inverse) 72%, transparent);
  padding-block: var(--space-6) var(--space-4);
  margin-top: auto;
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--space-6);
  margin-bottom: var(--space-5);
}
@media (max-width: 720px) { .footer__grid { grid-template-columns: 1fr; } }
.footer__logo { font-family: var(--display); font-weight: 700; font-size: 1.2rem; margin-bottom: var(--space-2); }
.footer__blurb { color: var(--muted); font-size: 0.95rem; }
.footer__heading { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin-bottom: var(--space-3); font-family: var(--body); }
.footer__col ul { list-style: none; padding: 0; display: grid; gap: var(--space-2); }
.footer__col a { color: color-mix(in srgb, var(--ink-inverse) 85%, transparent); text-decoration: none; }
.footer__col a:hover { color: var(--accent-2); }
.footer__contact address { font-style: normal; color: var(--muted); margin-bottom: var(--space-2); }
.footer__tel { font-size: 1.15rem; font-weight: 700; color: var(--ink-inverse); text-decoration: none; }
/* A social link carries its platform mark next to the word. The glyph is inline SVG using
   currentColor, so it needs no request, inherits the link colour on light and dark bands
   alike, and is aria-hidden because the visible word beside it is already the label. */
.social-link { display: inline-flex; align-items: center; gap: 0.45rem; }
.social-icon { flex: none; }
.footer__legal {
  border-top: 1px solid color-mix(in srgb, var(--ink-inverse) 15%, transparent);
  padding-top: var(--space-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
  font-size: 0.85rem;
  color: var(--muted);
}
.footer__legal ul { list-style: none; padding: 0; display: flex; gap: var(--space-4); }
.footer__legal a { color: inherit; }
.footer__disclosure { font-size: 0.85rem; color: var(--muted); margin-top: var(--space-3); }

/* ---------------------------------------------------------------- print */
/* A `.no-print` escape hatch used to ride in this selector list. Nothing in the factory ever
   emitted it - not a renderer, not a hand-built page, not the chrome - so it was an opt-out
   no author could reach. The named bands below are the whole rule. */
@media print {
  .site-header, .site-footer, .cta-band, .sticky-callbar { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ============================================================================
   Phase 8b - carrier and variant styling. Everything below gives real rules to
   classes the section renderers already emit as carriers (the ARCHITECTURE.md
   open-items list) plus the three FirmaType hero layouts. Color use follows
   token-pair discipline: only pairs the audited components above already use
   in an equivalent context (--muted on --bg / --bg-alt, --accent-2-deep as
   accent text on light bands, --accent-2 as accent text inside band--dark,
   --accent-deep as link-tone text on light, --btn-ink on --accent). No new
   literal colors, so the catalog's per-theme AA audit carries over.
   ========================================================================== */

/* ------------------------------------------ gallery strip (office photos) */
/* FirmaType office-photo-strip: one row that scroll-snaps sideways, the same
   pattern as .quote-rail. Square crop comes from the base .gallery img rule. */
.gallery--strip {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-3);
}
.gallery--strip img {
  flex: 0 0 auto;
  width: min(20rem, 78vw);
  scroll-snap-align: start;
}

/* ------------------------------------- numbered short-pitch split feature */
/* FirmaType ShortPitchNumbered: each claim leads with a big zero-padded
   display-family numeral instead of a checkmark. Numeral color matches the
   base .checks checkmark (--accent), swapping to --accent-2 inside band--dark
   exactly as .eyebrow does. */
.split-feature--numbered-pitch .checks {
  counter-reset: pitch;
  gap: var(--space-4);
}
.split-feature--numbered-pitch .checks li {
  counter-increment: pitch;
  padding-left: 3.4rem;
}
.split-feature--numbered-pitch .checks li::before {
  content: counter(pitch, decimal-leading-zero);
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--accent);
}
.band--dark .split-feature--numbered-pitch .checks li::before { color: var(--accent-2); }

/* ---------------------------------------------------- practice-area cards */
/* FirmaType practice-area look, factory-ized the way .hub-item is: no border
   box, no shadow, just an accent top rule and tighter type. The transparent
   background keeps the card honest on band, band--alt and band--dark alike. */
.card-grid--practice-areas {
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-5) var(--space-4);
}
.card-grid--practice-areas .card {
  background: transparent;
  border: 0;
  border-top: 2px solid var(--accent-2);
  border-radius: 0;
  box-shadow: none;
  padding: var(--space-3) 0 0;
}
.card-grid--practice-areas .card:hover { transform: none; }
.card-grid--practice-areas .card__title { font-size: 1.1rem; }
.card-grid--practice-areas .card__body { margin-bottom: var(--space-2); }

/* ------------------------------------------------- checklist urgency note */
/* The quiet <em> becomes a distinct urgent tone: upright, semibold, accent-2
   ink. --accent-2-deep is the audited accent-text pair on --bg and --bg-alt
   (see .eyebrow); band--dark swaps to --accent-2 the same way .eyebrow does. */
.checklist__urgency {
  font-style: normal;
  font-weight: 600;
  color: var(--accent-2-deep);
}
.band--dark .checklist__urgency { color: var(--accent-2); }

/* --------------------------------------------------------- two-column faq */
/* CSS columns rather than grid: reading order stays top-to-bottom and the
   first-item-open behavior is untouched (it is a markup attribute). */
@media (min-width: 880px) {
  .faq--split {
    max-width: none;
    columns: 2;
    column-gap: var(--space-6);
  }
  .faq--split .faq__item { break-inside: avoid; }
}

/* ----------------------------------------------------------- fee schedule */
/* FirmaType FeesAndProcess: fees read as a hairline-ruled schedule, not
   marketing tiers. Cards flatten to rows - name and description on the left,
   the figure on the right in tabular numerals. Left-aligned under the band
   head, matching the .prose-band justify-content: start reasoning. */
.price-cards--fees {
  grid-template-columns: 1fr;
  gap: 0;
  max-width: 46rem;
}
.price-cards--fees .card {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--space-4);
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  padding: var(--space-4) 0;
}
.price-cards--fees .card:last-child { border-bottom: 1px solid var(--line); }
.price-cards--fees .card:hover { transform: none; }
.price-cards--fees .card > * { grid-column: 1; }
.price-cards--fees .card__title { font-size: 1.05rem; margin-bottom: var(--space-1); }
.price-cards--fees .card__price {
  grid-column: 2;
  grid-row: 1;
  font-size: 1.3rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-bottom: 0;
}
.price-cards--fees .card__body { font-size: 0.95rem; margin-bottom: var(--space-2); }
.price-cards--fees .checks { font-size: 0.95rem; }

/* -------------------------------------------------- outcome-forward cases */
/* FirmaType CaseResults: the outcome line (already <strong> in the markup)
   is the visual lead; the title drops to a small tracked label above it.
   --accent-deep is the audited link-tone text on light bands (.link-arrow);
   band--dark swaps to --accent-2 as .eyebrow does. */
.case-grid { grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); }
.case-grid .card__title {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-bottom: var(--space-2);
}
.case-grid .card strong {
  display: block;
  font-family: var(--display);
  font-size: clamp(1.4rem, 1.2rem + 0.9vw, 1.8rem);
  font-weight: 800;
  line-height: 1.15;
  color: var(--accent-deep);
  margin-bottom: var(--space-2);
}
/* Inside band--dark the card background is a 6% ink-inverse mix OVER bg-dark - lighter
   than the bg-dark the .eyebrow accent-2 pair was audited on, and accent-2 fails the 3:1
   large-text floor there under 4 of 7 themes (review P3, computed). ink-inverse is how
   band--dark already handles links: 8:1+ everywhere. */
.band--dark .case-grid .card strong { color: var(--ink-inverse); }

/* ------------------------------------------- 1.5 hero: utilitarian dense */
/* FirmaType HeroUtilitarianDense: scannable practical info above the fold.
   Deliberately small h1, two-column benefits checklist, and a 1/3 aside card
   that makes the call-now link the biggest thing on the right. */
.hero--utilitarian-dense { padding-block: var(--space-6); }
.hero--utilitarian-dense .hero__grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--space-6);
  align-items: start;
}
.hero--utilitarian-dense h1 { font-size: clamp(1.75rem, 1.35rem + 1.8vw, 2.5rem); }
.hero--utilitarian-dense .hero__sub { font-size: 1.05rem; margin-top: var(--space-2); }
.hero--utilitarian-dense .checks {
  margin-top: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
}
.hero__aside {
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  align-self: start;
}
.hero__aside .eyebrow { margin-bottom: var(--space-1); }
/* The big call-now link is the aside's only non-button link; selecting it
   structurally keeps the D5 markup contract to the classes named there. */
.hero__aside a:not(.btn) {
  display: inline-block;
  font-family: var(--display);
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 1.9rem);
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
  text-decoration: none;
}
.hero__aside a:not(.btn):hover { color: var(--accent-deep); }
.hero__aside .btn {
  display: block;
  width: 100%;
  text-align: center;
  margin-top: var(--space-3);
}

/* --------------------------------------- 1.6 hero: credentials anchored */
/* FirmaType HeroCredentialsAnchored: no photo - the hero IS the credentials.
   Restrained header over a hairline rule, then a fact rail of up to four
   cells (div-wrapped dt/dd pairs per the D5 contract; a dd may stand alone
   when the trust item carries no ":" label). */
.hero--credentials-anchored .container { max-width: min(var(--max-w), 64rem); }
.hero--credentials-anchored .eyebrow { letter-spacing: 0.3em; }
.hero__facts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5) var(--space-4);
  border-top: 1px solid var(--line);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
}
.hero__facts dt {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: var(--space-1);
}
.hero__facts dd {
  font-family: var(--display);
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.6rem);
  font-weight: 700;
  line-height: 1.2;
}
/* A label-less trust item renders as a dt with an empty dd (dl content-model
   conformance - review P3); the empty dd contributes nothing visually. */
.hero__facts dd:empty { display: none; }

/* ----------------------------------------------------- 1.7 hero: team led */
/* FirmaType HeroTeamLed: "we" framing - a 2/5 copy column against a 3/5 wide
   4/3 team figure. The original's headshot-mosaic branch needs a people list
   the hero definition does not carry; deferred per the growth ladder (D5). */
.hero--team-led .hero__grid {
  display: grid;
  grid-template-columns: 2fr 3fr;
  gap: var(--space-6);
  align-items: center;
}
.hero--team-led h1 { font-size: clamp(2rem, 1.4rem + 2.2vw, 3rem); }
.hero--team-led .eyebrow { letter-spacing: 0.25em; }
.hero__media--team img { aspect-ratio: 4 / 3; }

/* When the optional right-hand piece (aside or media) is omitted, the copy
   takes the full row instead of leaving a dead column. */
.hero--utilitarian-dense .hero__grid > :only-child,
.hero--team-led .hero__grid > :only-child { grid-column: 1 / -1; }

/* Hero collapse, consistent with the existing 720/880 breakpoints: the wide
   asymmetric grids stack at 880, the inner two-column lists fold at 720. */
@media (max-width: 880px) {
  .hero--utilitarian-dense .hero__grid,
  .hero--team-led .hero__grid { grid-template-columns: 1fr; }
  .hero__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .hero--utilitarian-dense .checks { grid-template-columns: 1fr; }
  .hero__facts { grid-template-columns: 1fr; }
}

/* --------------------------------------------------- small carrier notes */
/* Menu footnote (allergy notes, "Prices include tax"): small, muted, spaced
   under its group. Sizing the class stops the inner <small> from
   double-shrinking. */
.menu-list__note {
  margin-top: var(--space-3);
  font-size: 0.9rem;
  color: var(--muted);
}
.menu-list__note small { font-size: 1em; }

/* Gallery empty state: same visual treatment as .empty-note (5.13) - a muted
   sentence behind an inline-start rule. Declarations are duplicated rather
   than grouped so the 5.13 rule stays byte-identical to the pre-split file. */
.gallery__empty {
  color: var(--muted);
  border-inline-start: 2px solid var(--line);
  padding-inline-start: var(--space-3);
  max-width: 46ch;
}

/* Per-item hours note: small muted line under the value, sized explicitly so
   the <small> element renders predictably. */
.hours-strip__item-note {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: var(--space-1);
}

/* Site-wide announcement band: its own modest treatment apart from page-local
   notices - full-width, comfortable block padding, square corners (the inner
   .container carries the inline padding). */
.notice--announcement {
  padding-block: var(--space-3);
  padding-inline: 0;
  border-radius: 0;
}

/* ---------------------------------------------------------------- embeds
   Vetted third-party frames (video, map, booking, audio). The wrapper owns
   the aspect ratio - set inline per provider by the renderer - so the box is
   reserved before the provider responds and a slow embed cannot push the
   page down as it loads. The iframe simply fills it. */
.embed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--space-3);
}

/* One item per row at full container width: a single video or map that is
   the point of the band rather than one of several. */
.embed-grid--wide {
  grid-template-columns: 1fr;
}

.embed__item {
  margin: 0;
  min-width: 0; /* grid children default to min-content; without this a frame overflows */
}

.embed__frame {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface-2, rgba(0, 0, 0, 0.05));
}

.embed__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.embed__item figcaption {
  margin-top: var(--space-1);
  font-size: 0.9rem;
  color: var(--muted);
}

/* Designed empty state, matching .gallery__empty: a band whose content has
   not arrived yet says so rather than collapsing to a bare heading. */
.embed__empty {
  color: var(--muted);
  max-width: 46ch;
}

/* -------------------------------------------------- before and after pairs
   The DEFAULT is side by side and fully visible - that is what a reader with
   no JavaScript gets, and what the fidelity harness measures. The stacked
   comparison below only applies once behaviors.js has added .ba__frame--live,
   so the fallback can never be taken away by CSS alone. */
.ba-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--space-4);
}

.ba-grid--slider {
  grid-template-columns: 1fr;
}

.ba__item { margin: 0; min-width: 0; }

.ba__frame {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.ba__side, .ba__side--before, .ba__side--after { position: relative; margin: 0; }

.ba__side img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius);
  object-fit: cover;
}

.ba__label {
  position: absolute;
  left: var(--space-2);
  bottom: var(--space-2);
  padding: 0.15rem 0.5rem;
  border-radius: calc(var(--radius) / 2);
  background: rgba(0, 0, 0, 0.66);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.ba__item figcaption {
  margin-top: var(--space-2);
  font-size: 0.9rem;
  color: var(--muted);
}

/* Enhanced state: one frame, the after photo clipped to a draggable position. */
.ba__frame--live {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius);
  touch-action: pan-y; /* let a vertical swipe still scroll the page */
}

.ba__frame--live .ba__side {
  position: absolute;
  inset: 0;
}

.ba__frame--live .ba__side img {
  width: 100%;
  height: 100%;
  border-radius: 0;
}

.ba__frame--live .ba__side--after {
  /* --ba-pos is written by behaviors.js from the range input. */
  clip-path: inset(0 0 0 var(--ba-pos, 50%));
}

.ba__frame--live .ba__side--after::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--ba-pos, 50%);
  width: 2px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
}

/* The native range input IS the control - keyboard, touch and screen reader
   support come free. It is made invisible but never display:none, which would
   take it out of the accessibility tree along with the interaction. */
.ba__range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  appearance: none;
  background: transparent;
}

.ba__range:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.ba__empty { color: var(--muted); max-width: 46ch; }

/* ------------------------------------------------------------- carousel nav
   Built at runtime around a scrolling rail (behaviors.js). The rail scrolls
   perfectly well without these; they exist for a mouse with no horizontal
   wheel, which is the one input that cannot swipe, drag or arrow. */
.carousel { position: relative; }

.carousel__nav {
  position: absolute;
  top: 40%;
  z-index: 1;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

.carousel__nav:hover { background: var(--surface-2, var(--bg)); }
.carousel__nav--prev { left: calc(var(--space-2) * -1); }
.carousel__nav--next { right: calc(var(--space-2) * -1); }

@media (max-width: 40rem) {
  /* Touch has the swipe; the buttons would only cover the cards. */
  .carousel__nav { display: none; }
}

/* ------------------------------------------------------------------ lightbox
   Gallery images open full size. The grid image is wrapped in a real <button>
   at runtime so it is keyboard reachable and announced as activatable. */
.gallery__zoom {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  width: 100%;
  border-radius: var(--radius);
}

.gallery__zoom:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  background: rgba(0, 0, 0, 0.85);
}

.lightbox[hidden] { display: none; }

.lightbox__img {
  max-width: 100%;
  max-height: 90vh;
  width: auto;
  height: auto;
  border-radius: var(--radius);
}

.lightbox__close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__close:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .ba__frame--live .ba__side--after { transition: none; }
}

/* --------------------------------------------------- lead form: phase 9 fields
   The consent tick box reads as one line: box, then the wording beside it.
   align-items:start keeps the box level with the FIRST line when the wording
   wraps to several, which it usually does. */
.lead-form__consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2);
  align-items: start;
  font-size: 0.9rem;
  line-height: 1.45;
}

.lead-form__consent input[type="checkbox"] {
  width: auto;
  margin-top: 0.15rem;
}

/* Sits under the file input: what may be sent and how big. */
.lead-form__hint {
  display: block;
  margin-top: var(--space-1);
  font-size: 0.8rem;
  color: var(--muted);
}

/* Turnstile's own script draws and sizes the widget; this only gives it room
   in the form's flow. Present unconditionally so the class-coverage check has
   a rule to find, even on the sites that never render one. */
.cf-turnstile {
  margin-block: var(--space-2);
}

/* --------------------------------------------- lead form: inline validation
   Written by the lead-form behavior (behaviors.js) when a submit fails the
   native constraint check: aria-invalid on the control, one small message
   appended to its label. No renderer ever emits the class, so a no-JS page
   never carries it - the native bubbles do this job there. Color pairs are
   the audited set only: --accent-2-deep as accent text on --bg and --bg-alt
   (see .checklist__urgency), swapping to --accent-2 inside band--dark the
   way .eyebrow does, so the message reads on every band a form can sit on.
   Sized explicitly: the label is already 0.9rem, and a browser-default
   <small> inside it would double-shrink (see .menu-list__note). */
.lead-form__field-error {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-2-deep);
}
.band--dark .lead-form__field-error { color: var(--accent-2); }
.lead-form [aria-invalid="true"] { border-color: var(--accent-2-deep); }
.band--dark .lead-form [aria-invalid="true"] { border-color: var(--accent-2); }
/* The consent row is a two-column grid (box, wording): the appended message
   belongs under the wording, not in the checkbox column auto-placement would
   otherwise give it. */
.lead-form__consent .lead-form__field-error { grid-column: 2; }

/* ------------------------------------------------------- booking request
   The preferred-times form (booking-request section). It IS a .lead-form -
   inputs, labels, error banner and honeypot all come from the contact rules
   above - so this block styles only what is new: the slot fieldset, its
   two-column rows, the select (which the base .lead-form rule never covered),
   and the reassurance line. Color pairs are the audited set only: --line
   borders and --muted text on --bg / --bg-alt. */
.booking-form select {
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) * 0.8);
  padding: 0.7rem 0.9rem;
  background: var(--bg);
  color: var(--ink);
  width: 100%;
}
.booking-slots {
  min-width: 0; /* fieldsets default to min-content and would overflow the form grid */
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-3);
  display: grid;
  gap: var(--space-3);
}
.booking-slots legend {
  font-weight: 600;
  font-size: 0.9rem;
  padding-inline: var(--space-1);
}
.booking-slot { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
@media (max-width: 520px) { .booking-slot { grid-template-columns: 1fr; } }
/* display:grid above beats the UA's [hidden] display:none, so the behavior's
   hide has to be restated at the same strength or it silently does nothing. */
.booking-slot[hidden] { display: none; }
/* Runtime-built by behaviors.js (booking-slots): never rendered, still ruled. */
.booking-slots__add { justify-self: start; }
.booking-form__reassure { font-size: 0.85rem; color: var(--muted); max-width: 60ch; }

/* ------------------------------------------------------------- worksheet
   A print-first sheet (worksheet section): labelled prompts over ruled
   write-in lines, plus an optional hand-filled tracking table. On screen the
   sheet reads as a bordered card; in print it IS the page - see the print
   block below. The head above the sheet sits in a .flow wrapper, so the gap
   between them is set here. */
.flow + .booking-form, .flow + .embed__frame, .flow + .worksheet, .flow + .worksheet__print {
  margin-top: var(--space-5);
}
.worksheet {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-5);
  max-width: 52rem;
}
.worksheet__title { margin-bottom: var(--space-4); }
.ws-prompt { margin-block: var(--space-4); }
.ws-prompt__label { font-weight: 600; font-size: 0.9rem; margin-bottom: var(--space-2); }
/* A ruled line for a pen. Height leaves room for real handwriting; the rule
   uses --muted rather than --line so it survives a greyscale printer. */
.ws-lines { height: 2.1rem; border-bottom: 1px solid var(--muted); }
.ws-lines + .ws-lines { margin-top: 0; }
.ws-divider { border: none; border-top: 1px solid var(--line); margin-block: var(--space-4); }
.ws-table-wrap { overflow-x: auto; }
.ws-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.ws-table th, .ws-table td {
  border: 1px solid var(--line);
  padding: 0.45rem 0.6rem;
  text-align: left;
}
.ws-table th { font-weight: 600; }
.ws-cell { height: 2.1rem; }
.ws-day { font-weight: 600; white-space: nowrap; width: 1%; }
.worksheet__note { font-size: 0.85rem; color: var(--muted); margin-top: var(--space-4); }
/* Runtime-built by behaviors.js (print-page): never rendered, still ruled. */
.worksheet__print { margin-bottom: var(--space-3); }

/* Printing a worksheet. The chrome print rule near the top of this file
   already drops the header, footer, CTA bands and call bar on every page;
   this block adds what a hand-filled sheet needs: the consent banner and the
   print button itself gone, the card chrome stripped so the sheet fills the
   paper, black-on-white rules for greyscale printers, and page-break rules
   that keep every prompt with its write-in space and every table row whole. */
@media print {
  .cms-consent, .worksheet__print, .booking-slots__add { display: none !important; }
  .worksheet-band { padding-block: 0; background: #fff; }
  .worksheet {
    border: none;
    border-radius: 0;
    padding: 0;
    max-width: none;
    background: #fff;
    color: #000;
  }
  .ws-lines { border-bottom-color: #000; }
  .ws-table th, .ws-table td { border-color: #000; }
  .ws-table-wrap { overflow-x: visible; }
  .ws-prompt { break-inside: avoid; page-break-inside: avoid; }
  .ws-table tr { break-inside: avoid; page-break-inside: avoid; }
  .worksheet__note { color: #000; }
}
