/*
 Theme Name:   Full Circle Care
 Theme URI:    https://fullcirclecare.com
 Description:  Classic PHP theme for Full Circle Care (Dr. Leslie Peterson, N.D., P.C.).
 Author:       Full Circle Care Team
 Version:      1.0
 Text Domain:  fcc
*/

/* =========================================================================
   Full Circle Care — design system
   Ported verbatim from the prior Bricks-child-theme build (fcc-bricks-child),
   which itself ported docs/prototype/styles.css. This :root block is now the
   SINGLE source of truth for color tokens — there is no builder-side Style
   Manager to drift against (the prior build's dual-source palette problem
   does not exist here).

   Enqueued site-wide via functions.php.
   ========================================================================= */

:root {
  /* Palette: Cream + Rose */
  --bg: #FAF7F2;
  --bg-deep: #F2EBE0;
  --bg-card: #FFFFFF;
  --ink: #1F2937;
  --ink-soft: #4B5563;
  /* Darkened from the prototype's #8B8580 (a11y batch 3, approved 2026-07-25).
   * #8B8580 failed 1.4.3 at every text use: 3.41:1 on --bg, 3.08:1 on --bg-deep,
   * 3.64:1 on --bg-card, all needing 4.5:1 — every use is 11-13px, under the
   * 24px large-text threshold. #6F6A66 is the LIGHTEST value in the same warm
   * hue family that clears 4.5:1 on the worst of the three: 5.00 / 4.51 / 5.34.
   * The 4.51 is only a hundredth over — this value is pinned to --bg-deep
   * staying #F2EBE0. Recompute if that band ever changes. */
  --ink-mute: #6F6A66;
  --line: #E8E1D5;
  --line-soft: #EFE9DD;
  --accent: #C98B8E;        /* dusty rose */
  --accent-deep: #A86568;
  --accent-soft: #EBCFD0;
  --accent-tint: #F5E4E5;
  --highlight: #F2EBE0;

  /* Type */
  --font-display: "Newsreader", "Source Serif 4", Georgia, serif;
  --font-sans: "DM Sans", "Söhne", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "JetBrains Mono", "SFMono-Regular", Menlo, monospace;

  /* Scale */
  --r-sm: 6px;
  --r-md: 12px;
  --r-lg: 20px;
  --r-xl: 32px;
  --shadow-card: 0 1px 2px rgba(31, 41, 55, 0.04), 0 8px 24px rgba(31, 41, 55, 0.06);
  --shadow-lift: 0 1px 2px rgba(31, 41, 55, 0.04), 0 18px 40px rgba(31, 41, 55, 0.10);

  --container: 1240px;
  --gutter: clamp(20px, 4vw, 56px);
  --section-y: 112px;
}

/* =========================================================================
   Base layer
   Ported from the prototype's reset + global helpers (docs/prototype/styles.css)
   — the pieces held back from the Phase-1 as-is port (were a Bricks Theme-Styles
   / Style-Manager concern in the prior build). Minimal reset by design: per-
   section CSS carries explicit margins + grid/flex gaps, so semantic PHP
   (<p>/<h2>) does not drift on default UA margins.
   ========================================================================= */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "ss01", "cv11";
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; cursor: pointer; }

/* Eyebrow — mono label. Base rules (the .dot modifier lives below). */
/* Eyebrow (a.k.a. kicker) — the short label above a headline.
   ⚠️ CHANGED 2026-09-06 (Dr. Peterson via Daniel, approved). She did not
   NOTICE Home's "Is this you?" at all, and on being shown it said the line was
   valuable — it frames the section for the reader — and should be more visible.
   Applies globally, at her request: ~35 instances across 20 files.

   Size was the symptom, not the cause. Three properties compounded: wide
   letter-spacing plus uppercase at 11px reads as decorative TEXTURE rather
   than as words, so the eye classifies it as ornament and skips it — which is
   precisely what happened. Tracking was cut alongside the size bump; it costs
   more legibility at this size than the size does.

   ⚠️ THE COLOUR MOVE IS SAFE AND WAS CHECKED, not assumed. --ink-mute is
   PINNED (see its token comment: it clears 4.5:1 on --bg-deep by one
   hundredth). This does NOT touch that token — only what .eyebrow points at —
   and every ratio IMPROVES: 5.00→7.07 on --bg, 4.51→6.38 on --bg-deep,
   5.34→7.56 on --bg-card. Measured 2026-09-06, not estimated.

   Uppercase, the mono face and the .dot modifier are deliberately unchanged:
   they carry the design system, and the goal was legibility, not a restyle. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 500;
}

/* Nav link — header items (more layout in header.php). */
.nav-link {
  font-size: 14px;
  text-decoration: none;
  color: var(--ink);
  padding: 8px 12px;
  border-radius: 8px;
  transition: background .15s ease;
}
.nav-link:hover { background: var(--bg-deep); }

/* Reveal-on-scroll. js/reveal.js puts .js-motion on <html> as its first act, then
 * observes; the hidden state is gated on that class so a missing/blocked reveal.js
 * leaves every section visible instead of blanking the page. Never unscope this.
 * Tuning knobs (distance / duration / easing) live here — a retune stays CSS-only.
 * Per-element stagger comes from --reveal-delay, set inline in the templates. */
.reveal { transition: opacity .8s ease, transform .8s ease; transition-delay: var(--reveal-delay, 0ms); }
.js-motion .reveal {
  opacity: 0;
  transform: translateY(14px);
}
.js-motion .reveal.in { opacity: 1; transform: none; }

/* Scroll lock while the mobile drawer is open. */
body.menu-open { overflow: hidden; }

/* Global reduced-motion — disable reveals + transitions/animations site-wide.
 * Selector must stay at .js-motion .reveal specificity (0,2,0) or the gated
 * hidden state above outranks it and reduced-motion users get a blank page. */
@media (prefers-reduced-motion: reduce) {
  .js-motion .reveal,
  .js-motion .reveal.in { opacity: 1; transform: none; transition: none; }
  * { animation: none !important; transition: none !important; }
}

/* Layout helpers ---------------------------------------------------------- */
.section { padding-block: var(--section-y); }
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Anchor landing offset. The header is sticky, so any deep link would otherwise
 * drop its target underneath the bar — Home's service cards link straight to
 * /care/#{anchor_id}, so this is load-bearing, not hygiene. Global on [id] by
 * design: every in-page anchor on the site needs it, and nothing here nests a
 * scroll container that scroll-margin could disturb (the testimonials carousel
 * moves by transform, not scroll). 100px clears the header in BOTH states —
 * 92px at rest (48px logo + 2×22px pad + 1px rule) and 77px once .is-scrolled
 * shrinks the pad to 14px — and matches the sticky top used by .appr-aside /
 * .faq-intro / .svc-aside. Re-check it if the header's height changes. */
[id] { scroll-margin-top: 100px; }

/* Skip link — WCAG 2.4.1 Bypass Blocks (A). DOM: header.php, first focusable
 * element in <body>. Off-screen until focused rather than display:none, which
 * would remove it from the tab order and defeat the point.
 * It lands on #main, which inherits the [id] scroll-margin above, so the target
 * clears the sticky header on arrival — the two rules cooperate; don't give this
 * one its own offset. Colours are --ink on --bg-card (13.7:1) and it sits above
 * the header's z-index so it is never painted behind the frosted bar. */
.skip-link {
  position: absolute;
  left: 12px;
  top: -100px;
  z-index: 100;
  padding: 12px 20px;
  border-radius: var(--r-md);
  background: var(--bg-card);
  color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  font-size: 15px;
  transition: top .15s ease;
}
.skip-link:focus { top: 12px; }

/* <main> carries tabindex="-1" (every page template) so the skip link moves FOCUS,
 * not just the scroll position — a fragment jump alone leaves focus on <body> in
 * some browsers, so the next Tab would return to the nav the user just skipped.
 * -1 keeps it out of the tab sequence, so this outline can only ever appear from
 * that programmatic focus, where a ring around the entire page is noise. */
#main:focus { outline: none; }

/* Screen-reader-only utility. First use is the carousel's live region, but this
 * is a general helper — reach for it whenever AT needs text the design doesn't
 * show. NOT display:none or visibility:hidden: both remove the node from the
 * accessibility tree, which is exactly the opposite of the intent. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Typography -------------------------------------------------------------- */
.display {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 0.98;
  font-optical-sizing: auto;
  font-variation-settings: "opsz" 36;
  color: var(--ink);
}
.display em {
  font-style: italic;
  font-variation-settings: "opsz" 36;
  color: var(--accent-deep);
}
.h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(32px, 4.4vw, 56px);
  line-height: 1.04;
  letter-spacing: -0.02em;
}
.h2 em { font-style: italic; color: var(--accent-deep); }
.h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.15;
  letter-spacing: -0.015em;
}
.lede {
  font-size: clamp(17px, 1.4vw, 20px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 56ch;
  text-wrap: pretty;
}
.body { color: var(--ink-soft); }

/* Eyebrow accent-dot modifier (base .eyebrow is in the base layer above). */
.eyebrow.dot::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  background: var(--accent);
  border-radius: 50%;
  margin-right: 10px;
  vertical-align: middle;
  transform: translateY(-1px);
}

/* Buttons ------------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 22px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-decoration: none;
  border: 1px solid transparent;
  transition: transform .2s ease, background .2s ease, color .2s ease, border-color .2s ease;
  white-space: nowrap;
}
.btn-primary { background: var(--ink); color: var(--bg); }
.btn-primary:hover { background: var(--accent-deep); transform: translateY(-1px); }
.btn-accent { background: var(--accent); color: #fff; }
.btn-accent:hover { background: var(--accent-deep); transform: translateY(-1px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--ink); }
.btn-link {
  background: transparent;
  color: var(--ink);
  padding: 0;
  border-radius: 0;
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
}
.btn .arrow { display: inline-block; transition: transform .2s ease; }
.btn:hover .arrow { transform: translateX(3px); }

/* Card surface -------------------------------------------------------------- */
.card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

/* Image placeholder ----------------------------------------------------------- */
.imgph {
  position: relative;
  background: repeating-linear-gradient(
    135deg,
    var(--bg-deep) 0 14px,
    var(--bg) 14px 28px
  );
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  color: var(--ink-mute);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.imgph::after {
  content: attr(data-label);
  padding: 4px 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
}

/* Divider ------------------------------------------------------------------- */
.hr { height: 1px; background: var(--line); border: 0; margin: 0; }

/* Forms --------------------------------------------------------------------- */
/* ⚠️ UNUSED SINCE 2026-08-01 — kept deliberately. Removing the footer newsletter
 * column took the theme's only .input with it, so nothing in the markup carries
 * this class today. It stays because it is ported design-system furniture and
 * because the focus-ring reasoning below is contrast math nobody should have to
 * redo. Consequence to know before trusting it: the ring is no longer exercised
 * by any rendered page, so a cross-browser pass cannot confirm it — re-verify on
 * the first real field (newsletter restore, or any form PW-06 brings). */
.input {
  width: 100%;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  font: inherit;
  color: var(--ink);
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* Offset focus ring (a11y batch 3, approved 2026-07-25). The prototype killed the
 * UA outline and substituted a 3px --accent-tint ring, which is 1.23:1 on white —
 * effectively invisible, failing 2.4.7 and 1.4.11. --accent-deep carries the
 * criterion on every surface an .input can sit on: 4.44:1 on --bg-card, 4.16:1 on
 * --bg (the footer), 3.75:1 on --bg-deep, and 3.42:1 against the field's own
 * --line border.
 *
 * outline + outline-offset, NOT the box-shadow pair it was mocked up as: the gap
 * has to show whatever is actually behind the field, and the theme's only .input
 * today sits on the cream footer, where a hardcoded white gap would draw a halo.
 * Modern engines follow border-radius; older Safari squares the ring off, which
 * is an acceptable degradation for a focus indicator. */
.input:focus {
  outline: 3px solid var(--accent-deep);
  outline-offset: 2px;
  border-color: var(--accent-deep);
}

/* Selection ------------------------------------------------------------------- */
::selection { background: var(--accent-soft); color: var(--ink); }

/* =========================================================================
   Global chrome — site header (Phase 3)
   Sticky/frosted bar; transparent at top, frosted on scroll (js adds
   .is-scrolled at scrollY>24). Layout = 3-zone flex row via space-between.
   Collapses to a hamburger + drawer at <=920px. DOM: header.php.
   ========================================================================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background .25s ease, border-color .25s ease;
}
/* Frosted scrolled state. The prototype's .92 leaves only 8% of the blurred
   backdrop showing, so the blur is almost entirely masked and the frost reads as
   little more than a flat bar — judged too subtle on a calibrated display
   (2026-07-14). Lowered to .72, then to .65 on 2026-08-01: at .72 the bar does
   read cooler over the hero photo than over the cream, but only subtly, and the
   whole point of the effect is that it should be visible.

   ⚠️ CONTRAST BOUNDS THIS — it is not a free dial. Against the real hero photo,
   nav text #1F2937 at 14px clears 4.5:1 down to about .60 and FAILS at .50
   (4.40:1) over the darkest foliage. .65 sits inside that with room; do not go
   below .60, and recompute the whole range if the hero photo is ever swapped.
   Both figures are pessimistic by construction: the 12px blur averages the
   backdrop rather than sampling its darkest pixel, so the real composite under
   the text is lighter than the number assumes.

   Note the effect is content-dependent: frosting can only show what's behind it,
   so it stays quiet over flat cream and reads most over the hero headline, the
   symptom toggles, and the dark voices band. */
.site-header.is-scrolled {
  background: rgba(250, 247, 242, 0.65); /* --bg @ .65 — see contrast note above */
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom-color: var(--line);
}
.site-header-inner {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-block: 22px;
  transition: padding .25s ease;
}
.site-header.is-scrolled .site-header-inner { padding-block: 14px; }

.site-logo { display: inline-flex; align-items: center; }
.site-logo-img { height: 48px; width: auto; }
.site-logo-text {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 500;
  color: var(--ink);
}

.site-nav {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 4px;
}
/* Active nav item — accent-coloured label, replacing the near-invisible
   --bg-deep pill it used to be (that composited to 1.108:1 on --bg; approved
   this session). --accent-deep on --bg is 4.16:1 — a clear colour break from the
   --ink (near-black) siblings — and the 500 weight is a second, non-colour cue.
   aria-current="page" (header.php) carries the state for AT regardless of both.
   No background, so it doesn't collide with the --bg-deep hover pill. */
.nav-link.is-active { color: var(--accent-deep); font-weight: 500; }

.site-header-actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
}

/* Hamburger toggle (mobile only) */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  background: var(--bg-card);
  padding: 12px 14px;
  border-radius: 10px;
  color: var(--ink);
}
.nav-toggle .burger {
  display: block;
  position: relative;
  width: 20px;
  height: 14px;
}
.nav-toggle .burger span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.6px;
  background: currentColor;
  border-radius: 2px;
  transition: transform .2s ease, opacity .15s ease, top .2s ease;
}
.nav-toggle .burger span:nth-child(1) { top: 1px; }
.nav-toggle .burger span:nth-child(2) { top: 7px; }
.nav-toggle .burger span:nth-child(3) { top: 13px; }
.nav-toggle[aria-expanded="true"] .burger span:nth-child(1) { top: 7px; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .burger span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .burger span:nth-child(3) { top: 7px; transform: rotate(-45deg); }

/* Mobile drawer — hidden by default; shown at <=920 when body.menu-open */
.site-drawer { display: none; background: var(--bg); border-top: 1px solid var(--line); }
.site-drawer-inner {
  display: grid;
  gap: 6px;
  padding-block: 28px;
}
.site-drawer-link {
  font-family: var(--font-display);
  font-size: 28px;
  text-decoration: none;
  color: var(--ink);
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}
.site-drawer-cta { margin-top: 20px; justify-content: center; }

@media (max-width: 920px) {
  .site-nav,
  .site-cta { display: none; }
  .nav-toggle { display: inline-flex; }
  body.menu-open .site-drawer { display: block; }
}

/* =========================================================================
   Global chrome — site footer (Phase 3)
   4-col grid (Brand · Visit · Contact · Newsletter) → 2-col <=880 → 1-col
   <=540, over a mono legal bar. Content binds to ACF Site Options. DOM: footer.php.
   ========================================================================= */
.site-footer { padding-block: 80px; }
.site-footer a { transition: color .2s ease; }
.site-footer a:hover { color: var(--accent); }

.footer-grid {
  display: grid;
  /* Three tracks since the newsletter column was removed 2026-08-01 (see
     footer.php). Was 1.4fr 1fr 1fr 1.2fr — restore that with the column, not
     before, or the grid reserves a dead 1.2fr on the right.

     Widths are sized to the LONGEST LINE each column has to hold, at 1240px
     container / 56px gutter / 40px gap = 1048px of track:
       Brand  1.4fr ≈ 431px — .footer-blurb caps at 320px, so the slack is
                             deliberate breathing room around the logo.
       Hours  0.9fr ≈ 277px — longest line is "Wed, Sat & Sun closed", ~21ch.
       Visit  1.1fr ≈ 339px — longest is "925 Executive Park Dr Ste G", ~27ch,
                             and this one is load-bearing: the address carries a
                             MANUAL break after "Ste G" (stored in the ACF
                             textarea). If the track ever gets narrow enough for
                             that line to also wrap, the address renders as three
                             lines, not two. Don't drop this below ~1fr. */
  grid-template-columns: 1.4fr 0.9fr 1.1fr;
  gap: 40px;
}
@media (max-width: 880px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 540px) {
  .footer-grid { grid-template-columns: 1fr; }
}

/* Shared footer body text (14px --ink-soft — an F5 record deviation from the
   prototype's quieter 13px/--ink-mute; it covered the newsletter blurb too,
   until that column was removed 2026-08-01). */
.footer-text {
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.6;
}
.footer-text a { text-decoration: none; }

.footer-blurb { margin: 18px 0 0; max-width: 320px; }

/* Column eyebrow heads (base .eyebrow + spacing). */
.footer-head { margin-bottom: 12px; }
/* Second heading stacked inside one column (Contact, under Visit). Renamed from
   .footer-head-hours 2026-08-01: Hours moved to a column of its own and is now
   FIRST there, so the modifier no longer describes hours — it describes the
   position. Only add it to a heading that has content above it. */
.footer-head-stacked { margin-top: 22px; }

/* Hours WYSIWYG ships its own <p>; neutralize its default margin. */
.footer-hours p { margin: 0; }

/* Divider + legal bar. */
.footer-hr { margin: 64px 0 24px; }
.footer-legal {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}
.footer-legal-links { display: flex; gap: 18px; }

/* =========================================================================
   404 — not found (Phase 3)
   Centered block, min-height so the footer sits low on tall screens. Gutter on
   the section (not an inner .container) so it aligns with the chrome. DOM: 404.php.
   ========================================================================= */
.error-404 {
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--bg);
  padding: var(--section-y) var(--gutter);
}
.error-404-inner {
  max-width: 640px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}
.error-title {
  margin: 0;
  font-size: clamp(40px, 5vw, 64px);
  text-wrap: balance;
}
.error-message {
  margin: 0;
  max-width: 48ch;
  font-size: 18px;
  color: var(--ink-soft);
}
.error-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px 14px;
}

/* =========================================================================
   Home — hero-split
   DOM: .hero > .hero-grid > (.hero-col , .hero-media)
   ========================================================================= */

/* Section shell — gutter on the Section (matches .symc/.appr/.focus/.about);
   the inner .hero-grid carries max-width + centering so the left edge aligns
   with header/footer. No background: the page is already --bg.
   Top padding is a small 32px (prototype HeroSplit: paddingTop 32), NOT the
   full --section-y: the sticky in-flow header already occupies the top of the
   page, so a full top pad would double-count the header clearance (a glaring
   gap on mobile). Bottom keeps the standard --section-y. */
/* ⚠️ BOTTOM PADDING HALVED 2026-09-06, and it is a consequence of the
   .cred-strip background change directly below — not an independent taste call.
   While the strip was --bg-deep, the colour step did the separating and a full
   --section-y (112px) below the hero read correctly as a section break. With
   the strip now on --bg it groups WITH the hero, so the same 112px stopped
   reading as structure and started reading as a disconnect between a claim and
   the credentials backing it. Daniel spotted it on the deployed page.
   Kept as a calc on the token rather than a literal 56px, so it stays
   proportional if --section-y is ever retuned. The strip's own 22px top
   padding sits on top of this, for ~78px total. */
.hero { padding: 32px var(--gutter) calc(var(--section-y) / 2); }

.hero-grid {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
@media (max-width: 860px) {
  .hero-grid { grid-template-columns: 1fr; }
}

.hero-h1 { font-size: clamp(44px, 6.6vw, 92px); margin: 24px 0; }
.hero-lede { margin-bottom: 36px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.hero-stats {
  margin-top: 56px;
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  color: var(--ink-mute);
  font-size: 13px;
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
}
.stat-n {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1;
}
.stat-label { margin-top: 6px; }

.hero-media { position: relative; }
.hero-portrait {
  aspect-ratio: 4 / 5;
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  background: var(--bg-deep);
}
.hero-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

.hero-cred {
  position: absolute;
  left: -24px;
  bottom: 28px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 14px 18px;
  box-shadow: var(--shadow-card);
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 280px;
}
.hero-cred-mono {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--accent-tint);
  display: grid;
  place-items: center;
  color: var(--accent-deep);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  flex-shrink: 0;
}
.hero-cred-name { font-size: 14px; font-weight: 500; }
.hero-cred-role { font-size: 12px; color: var(--ink-mute); }

.hero-badge {
  position: absolute;
  right: -16px;
  top: 32px;
  background: var(--accent-tint);
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  padding: 10px 16px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-deep);
}

/* =========================================================================
   Home — credential strip (section 2)
   DOM: .cred-strip > .cred-strip-inner > 4× .cred-item
   ========================================================================= */
/* ⚠️ BACKGROUND CHANGED --bg-deep -> --bg, 2026-09-06 (Daniel spotted it).
   The strip sat on --bg-deep and so does .symc directly below it, so the two
   merged into one continuous deep band with a stray hairline through it — the
   strip stopped reading as its own device. Daniel's instinct was that it
   belongs WITH the hero; that is right, and the fix is colour rather than
   moving the DOM. On --bg it now groups with the hero above (whose claims
   these credentials support) and its bottom rule becomes a real transition
   into the deep symptom band instead of a line inside one colour.
   Its own hairline rules already give it structure; it never needed a fill.
   Contrast improves and was checked: .cred-item is --ink-soft, 6.38:1 on
   --bg-deep and 7.07:1 on --bg. Revert is this one line. */
.cred-strip {
  background: var(--bg);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 22px var(--gutter);
}
.cred-strip-inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
}
.cred-item {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* =========================================================================
   Home — symptom check (section 3, interactive)
   DOM: .symc-grid > (.symc-copy , .symc-list)
   Behavior: js/symptom-check.js. Default {0,2}.
   ========================================================================= */
.symc { background: var(--bg-deep); padding: var(--section-y) var(--gutter); }
.symc-grid {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: start;
}

.symc-h2 { margin: 20px 0; }
.symc-card {
  margin-top: 36px;
  padding: 24px 28px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.symc-card-top {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.symc-count {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--ink-mute);
}
.symc-message {
  font-family: var(--font-display);
  font-size: 26px;
  letter-spacing: -0.015em;
  margin-top: 10px;
  color: var(--ink-mute);
}
.symc-bar {
  margin-top: 18px;
  height: 4px;
  border-radius: 999px;
  background: var(--bg-deep);
  overflow: hidden;
}
.symc-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  transition: width .35s ease;
}
.symc-card .btn { margin-top: 22px; }

.symc-list { display: grid; gap: 10px; }
.symc-toggle {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 16px;
  width: 100%;
  text-align: left;
  padding: 18px 20px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink);
  font-size: 16px;
  font-family: inherit;
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.symc-toggle.is-on {
  border-color: var(--accent-deep);
  background: var(--accent-tint);
}
.symc-check {
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  background: transparent;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  transition: background .18s ease, border-color .18s ease;
}
.symc-toggle.is-on .symc-check {
  border-color: var(--accent-deep);
  background: var(--accent-deep);
}
.symc-check::after {
  content: "";
  width: 5px; height: 9px;
  border: solid #fff;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg) translateY(-1px);
  opacity: 0;
  transition: opacity .18s ease;
}
.symc-toggle.is-on .symc-check::after { opacity: 1; }

/* Mobile (<=880): stack to one column. .symc-left (copy + card) dissolves via
   display:contents so its two children become grid items alongside .symc-list,
   letting the reflection card — which summarizes the user's selections — sit
   AFTER the toggle list rather than above it. Reset the card's 36px top margin
   so the stacked spacing stays the uniform grid gap. */
@media (max-width: 880px) {
  .symc-grid { grid-template-columns: 1fr; }
  .symc-left { display: contents; }
  .symc-copy { order: 1; }
  .symc-list { order: 2; }
  .symc-card { order: 3; margin-top: 0; }
}

/* =========================================================================
   Home — approach steps (section 4, static — no JS)
   DOM: .appr-grid > (.appr-aside , .appr-steps)
   ========================================================================= */
.appr { background: var(--bg); padding: var(--section-y) var(--gutter); }
.appr-grid {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
}
.appr-aside { position: sticky; top: 100px; }
.appr-h2 { margin: 20px 0; }

.appr-steps { display: grid; gap: 24px; }
.appr-card {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 28px;
  align-items: start;
  padding: clamp(24px, 3vw, 36px);
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.appr-num {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  color: var(--accent-deep);
}
.appr-card-t { margin: 0 0 10px; }
.appr-card-d { margin: 0; max-width: 54ch; }

@media (max-width: 880px) {
  .appr-grid { grid-template-columns: 1fr; }
  .appr-aside { position: static; }
}

/* =========================================================================
   Home — services / clinical-focus grid (section 5, DYNAMIC)
   DOM: .focus-inner > (.focus-head , .focus-grid[loop] > .focus-card)
   Loop: inc/queries.php fcc_query( 'services' ) — post type Service,
   posts_per_page 6, orderby meta_value_num, meta_key order, order ASC.
   ========================================================================= */
.focus { background: var(--bg-deep); padding: var(--section-y) var(--gutter); }
.focus-inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}

.focus-head {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
  margin-bottom: 56px;
}
.focus-h2 { margin: 20px 0 0; max-width: 20ch; }

.focus-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
@media (max-width: 980px) {
  .focus-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .focus-grid { grid-template-columns: 1fr; }
}

.focus-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  height: 100%;
  min-height: 220px;
  padding: clamp(24px, 2.4vw, 36px);
  background: var(--bg);
}

.focus-ico {
  width: 28px; height: 28px;
  border-radius: 8px;
  background: var(--accent-tint);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.focus-ico::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent-deep);
}

.focus-card-t { margin: 0; font-size: 22px; }
.focus-card-t a {
  color: inherit;
  text-decoration: none;
  transition: color .18s ease;
}
.focus-card-t a:hover { color: var(--accent-deep); }
.focus-card-d { margin: 0; font-size: 15px; }

/* =========================================================================
   HOME · SECTION 6 — ABOUT-INTRO (Dr. Peterson intro band)
   DOM: .about-grid > (.about-media , .about-copy)
   ========================================================================= */
.about { background: var(--bg); padding: var(--section-y) var(--gutter); }
.about-grid {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
@media (max-width: 880px) {
  .about-grid { grid-template-columns: 1fr; }
}

.about-media { position: relative; }
.about-portrait {
  aspect-ratio: 4 / 5;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--bg-deep);
}
.about-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

.about-sig {
  position: absolute;
  right: -24px;
  bottom: 32px;
  max-width: 240px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 20px 22px;
  box-shadow: var(--shadow-card);
}
.about-sig-name {
  margin-top: 8px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 28px;
  line-height: 1.05;
  color: var(--accent-deep);
}
.about-sig-meta {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-mute);
}
/* Ease the overhang when stacked — the -24px desktop value exceeds the 20px
   mobile gutter floor and would push ~4px past the viewport (hairline
   horizontal scroll). -16px keeps a visible overhang while staying inside the
   gutter, matching the hero badge. Must come AFTER the base rule to win. */
@media (max-width: 880px) {
  .about-sig { right: -16px; }
}

.about-h2 { margin: 20px 0 24px; }
.about-body { margin-top: 18px; font-size: 16px; max-width: 58ch; }

.about-creds {
  margin-top: 36px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.about-cred { background: var(--bg-card); padding: 18px; }
.about-cred-k {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.about-cred-v { margin-top: 4px; font-family: var(--font-display); font-size: 18px; }
.about-cta { margin-top: 32px; }

@media (max-width: 520px) {
  .about-creds { grid-template-columns: 1fr; }
}

/* =========================================================================
   SHARED — TESTIMONIALS CAROUSEL (INTERACTIVE + DYNAMIC)   [Home §7 · About]
   DOM: .voices-inner > (.voices-head , .voices-viewport > .voices-track[loop] , .voices-dots)
   Loop: inc/queries.php fcc_query( 'testimonials' ) — featured, by order.
   Part: template-parts/testimonials.php
   Behavior: js/testimonials-carousel.js (enqueued site-wide, no-ops without
   .voices-track — so About needs no enqueue change).
   ========================================================================= */
.voices {
  background: var(--ink);
  color: var(--bg);
  padding: var(--section-y) var(--gutter);
}
.voices-inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}

.voices-head {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
  margin-bottom: 48px;
}
.voices .eyebrow { color: var(--accent-soft); }
.voices .eyebrow.dot::before { background: var(--accent-soft); }
.voices-h2 { margin-top: 20px; max-width: 18ch; color: var(--bg); }
.voices-h2 em { font-style: italic; color: var(--accent-soft); }

.voices-nav { display: flex; gap: 8px; }
.voices-btn {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: var(--bg);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.voices-btn:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}
.voices-btn::before {
  content: "";
  width: 14px; height: 12px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='14'%20height='12'%20viewBox='0%200%2014%2012'%20fill='none'%3E%3Cpath%20d='M1%206H13M13%206L8%201M13%206L8%2011'%20stroke='black'%20stroke-width='1.4'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='14'%20height='12'%20viewBox='0%200%2014%2012'%20fill='none'%3E%3Cpath%20d='M1%206H13M13%206L8%201M13%206L8%2011'%20stroke='black'%20stroke-width='1.4'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
.voices-prev::before { transform: scaleX(-1); }

/* Pause/play — WCAG 2.2.2 Pause, Stop, Hide (Level A). The carousel auto-advances
 * every 7s indefinitely, which requires a user-facing stop; prev/next only RESET
 * the timer, and prefers-reduced-motion is not a substitute (a system setting is
 * not a mechanism in the content). Sits in .voices-nav with prev/next rather than
 * beside the dots — that is where the other transport controls already are, and
 * it inherits .voices-btn wholesale, so it adds no new visual vocabulary.
 * The glyph swaps on .is-paused; js/testimonials-carousel.js owns that class AND
 * the aria-label, which must change together — the label is the only cue a screen
 * reader gets, since the icon is a background mask with no text. */
.voices-play::before {
  width: 12px; height: 14px;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='14'%20viewBox='0%200%2012%2014'%3E%3Crect%20x='1'%20y='1'%20width='3.4'%20height='12'%20rx='1'%20fill='black'/%3E%3Crect%20x='7.6'%20y='1'%20width='3.4'%20height='12'%20rx='1'%20fill='black'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='14'%20viewBox='0%200%2012%2014'%3E%3Crect%20x='1'%20y='1'%20width='3.4'%20height='12'%20rx='1'%20fill='black'/%3E%3Crect%20x='7.6'%20y='1'%20width='3.4'%20height='12'%20rx='1'%20fill='black'/%3E%3C/svg%3E");
}
.voices-play.is-paused::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='14'%20viewBox='0%200%2012%2014'%3E%3Cpath%20d='M2%201.6v10.8a1%201%200%200%200%201.5.85l8.2-5.4a1%201%200%200%200%200-1.7L3.5.75A1%201%200%200%200%202%201.6z'%20fill='black'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='14'%20viewBox='0%200%2012%2014'%3E%3Cpath%20d='M2%201.6v10.8a1%201%200%200%200%201.5.85l8.2-5.4a1%201%200%200%200%200-1.7L3.5.75A1%201%200%200%200%202%201.6z'%20fill='black'/%3E%3C/svg%3E");
}

.voices-viewport { overflow: hidden; }
.voices-track {
  display: flex;
  transition: transform .6s cubic-bezier(.2, .7, .2, 1);
}

.voices-slide {
  flex: 0 0 100%;
  margin: 0;
  padding: 8px 0;
}
.voices-quote {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 48px);
  line-height: 1.18;
  letter-spacing: -0.015em;
  font-style: italic;
  font-weight: 400;
  color: var(--bg);
  max-width: 24ch;
}
.voices-quote::before { content: "\201C"; color: var(--accent-soft); margin-right: 8px; }
.voices-quote::after  { content: "\201D"; color: var(--accent-soft); margin-left: 8px; }
.voices-cite {
  margin-top: 32px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-soft);
}

/* Dots row — resized in the a11y pass (batch 2, approved 2026-07-18) so a full
 * 24×24 target fits. gap 6→16px and the bar 8×4→10×5: 10 + 16 = 26px between
 * centres, the smallest spacing that lets 24px hit areas sit side by side WITHOUT
 * overlapping. Overlapping targets would be a regression, not a fix — a tap
 * between two bars becomes ambiguous. Don't tighten the gap below 16px without
 * shrinking the hit area to match. */
.voices-dots { display: flex; gap: 16px; margin-top: 36px; }
.voices-dot {
  position: relative;
  width: 10px; height: 5px;
  border-radius: 999px;
  /* 0.35 alpha, NOT higher. Raising it improves the dot against the band but
   * WORSENS it against the active dot, and 1.4.11 governs the state distinction:
   * at .35 both clear 3:1 (3.11:1 vs the band, 3.23:1 vs the --accent-soft active
   * dot); by .45 the active comparison has fallen to 2.41:1 and fails. A narrow
   * window — recompute BOTH ratios if either colour ever changes. */
  background: rgba(255, 255, 255, 0.35);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: width .3s ease, background .3s ease;
}
/* Hit area — WCAG 2.5.8 Target Size (AA, new in 2.2). Out of flow, so the row
 * keeps its exact height and the bars their exact spacing while the tappable box
 * is a full 24×24. Width is fixed rather than left:0/right:0 so the inactive and
 * active dots get the same target despite their different painted widths. */
.voices-dot::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 24px; height: 24px;
  transform: translate(-50%, -50%);
}
.voices-dot.is-active { width: 28px; background: var(--accent-soft); }

@media (prefers-reduced-motion: reduce) {
  .voices-track,
  .voices-dot { transition: none; }
}

/* =========================================================================
   HOME · SECTION 8 — FAQ ACCORDION (DYNAMIC)
   DOM: .faq-inner > .faq-grid > (.faq-intro , .faq-list > details.faq-item*)
   Loop: inc/queries.php fcc_query( 'faq' ) — fcc_faq posts, by `order`.
   Net-new CSS, authored from the prototype (sections.jsx `FAQ`).

   Mechanism divergence from the prototype (approved 2026-07-14): the prototype
   is a React button + max-height transition; here it is native <details>/
   <summary> with name="fcc-faq", which gives single-open-at-a-time for free and
   keeps every answer in the DOM and readable with JS off (FAQ copy is SEO-
   relevant, so max-height:0 answers were the wrong trade). No JS file. Cost:
   open/close is instant, not the prototype's .35s slide.
   ========================================================================= */
.faq { padding: var(--section-y) var(--gutter); }
.faq-inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}

.faq-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
}
.faq-intro { position: sticky; top: 100px; }
.faq-h2 { margin-top: 20px; margin-bottom: 20px; }

.faq-list { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }

/* Question row. list-style:none + ::-webkit-details-marker kill the native
   disclosure triangle (the latter for older Safari). */
.faq-q {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 24px 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(18px, 2vw, 24px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:focus-visible {
  outline: 2px solid var(--accent-deep);
  outline-offset: 4px;
  border-radius: var(--r-sm);
}

/* Plus → minus. Both bars are pseudo-elements on currentColor, so the open
   state's ink fill flips them to --bg with the icon's own color transition. */
.faq-icon {
  position: relative;
  width: 32px; height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.faq-icon::before,
.faq-icon::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  background: currentColor;
}
.faq-icon::before {
  width: 10px; height: 1.4px;
  transform: translate(-50%, -50%);
}
.faq-icon::after {
  width: 1.4px; height: 10px;
  transform: translate(-50%, -50%);
  transition: opacity .2s ease;
}
.faq-item[open] .faq-icon {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}
.faq-item[open] .faq-icon::after { opacity: 0; }

/* Answer. `answer` is a wysiwyg field, so it arrives as one or more <p> —
   keep the inner rhythm, neutralize only the outer edges. */
.faq-a {
  padding-bottom: 28px;
  padding-right: 56px;
  max-width: 64ch;
  font-size: 16px;
  color: var(--ink-soft);
}
.faq-a > :first-child { margin-top: 0; }
.faq-a > :last-child { margin-bottom: 0; }

@media (max-width: 880px) {
  .faq-grid { grid-template-columns: 1fr; }
  .faq-intro { position: static; }
}

/* =========================================================================
   SHARED — CTA FIT CALL (STATIC)          [Home §9 · Care · About]
   DOM: .fitcall-inner > .fitcall-card > (svg.fitcall-deco , .fitcall-body)
   Net-new CSS, authored from the prototype (sections.jsx `FitCallCTA`).
   Part: template-parts/cta-fit-call.php

   Always the LAST section on its page: padding-bottom is 0 by design (per the
   prototype), so the footer's own 80px top pad sets the gap. The card clips its
   decorative concentric circles via overflow:hidden.
   ========================================================================= */
.fitcall { padding: var(--section-y) var(--gutter) 0; }
.fitcall-inner {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}
.fitcall-card {
  position: relative;
  overflow: hidden;
  background: var(--bg-deep);
  border-radius: var(--r-xl);
  padding: clamp(40px, 6vw, 80px);
}
/* Decorative concentric rings + centre orb. Approved divergence from the
   prototype (2026-07-14, retuned 2026-07-14 on a calibrated display): the
   prototype's values are below the perceptual floor on --bg-deep and were never
   noticed there either. ALL paint knobs live here, not in the SVG — the markup
   carries geometry only, so a retune is a CSS-only deploy.

   The old container opacity (.85) is gone: multiplying it by the per-ring alphas
   made the real value of any ring hard to reason about (the outermost sat at an
   effective .21 while the markup claimed .25). Each ring now states its true
   final alpha. Reintroduce a container opacity only as a deliberate global dimmer.

   Ring alphas ramp outward-faint -> inward-strong, preserving the prototype's
   depth cue while clearing the floor. Mobile is the regression risk on any
   change here, not the target — it already read correctly at the old values. */
.fitcall-deco {
  position: absolute;
  right: -200px;
  top: -100px;
  width: 600px;
  pointer-events: none;
}
/* non-scaling-stroke pins the hairlines at 1.5 device px. Without it the base
   layer's svg{max-width:100%} downscales the 600px viewBox to card width on a
   phone, taking a 1px stroke sub-pixel — it antialiases away to nothing. */
.fitcall-ring {
  fill: none;
  stroke: var(--accent); /* Lever if still weak: --accent-deep #A86568. */
  stroke-width: 1.5;     /* Lever if still weak: 2 (device px, see above). */
  vector-effect: non-scaling-stroke;
}
.fitcall-ring-1 { opacity: .38; } /* r280, outermost — the faint one */
.fitcall-ring-2 { opacity: .51; } /* r200 */
.fitcall-ring-3 { opacity: .68; } /* r120, innermost */

/* Centre orb. NOT an opacity problem: the prototype's --accent-tint (#F5E4E5) is
   within 7/255 of --bg-deep (#F2EBE0) on every channel, so it is invisible here
   at ANY alpha, 1.0 included — same defect as the --line em-dashes we dropped.
   Fixed by moving the fill up a step to --accent-soft, which carries actual rose
   against this background. Adjust the FILL, never the opacity, if it needs work. */
.fitcall-orb { fill: var(--accent-soft); }
.fitcall-body { position: relative; max-width: 720px; }
.fitcall-h2 {
  margin-top: 20px;
  margin-bottom: 24px;
  font-size: clamp(36px, 5.4vw, 72px);
}
/* margin-top is left to collapse against the h2's 24px, matching the prototype. */
.fitcall-lede { margin-bottom: 36px; }

.fitcall-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}
/* The prototype separates these three with var(--line) em-dashes — ~1.1:1
   against --bg-deep, i.e. invisible. Dropped (2026-07-14): the 28px gap already
   separates them, and it avoids dashes stranding at line starts on wrap. */
.fitcall-meta {
  margin-top: 36px;
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  font-size: 13px;
  color: var(--ink-mute);
}

/* =========================================================================
   SHARED — PAGE HERO (STATIC)   [Care · About · Begin · Articles · Contact]
   DOM: .page-hero > .container > (.eyebrow.dot , h1.page-hero-h1 , p.page-hero-lede)
   Net-new CSS, authored from the prototype (pages.jsx `PageHero`, all inline).
   Part: template-parts/page-hero.php

   Not Home — Home opens with its own two-column .hero (see "Home — hero-split").
   Opening section on its page, so the top pad is a flat 56px rather than
   --section-y (same first-section rule the Home hero follows); the bottom pad
   is .6 of --section-y because every caller's next section pulls its own top
   pad to 0 and sits tight under the lede.

   .display carries no font-size (it's a face/weight/tracking mixin), so the
   size lives here with no cascade tie to break.
   ========================================================================= */
.page-hero { padding-block: 56px calc(var(--section-y) * 0.6); }
.page-hero-h1 {
  font-size: clamp(44px, 6.4vw, 88px);
  margin-top: 24px;
  margin-bottom: 24px;
  max-width: 18ch;
}

/* 0, not the prototype's 32px — an approved divergence (Daniel, 2026-07-16) and
   a bug fix rather than a taste call. The prototype's PageHero sets
   marginBottom:32 on the lede to separate it from its `cta` prop, and NONE of
   its five call sites pass a cta. So the 32px spaced a button that does not
   exist, and stacked on top of the section's own 67.2px bottom pad: the gap
   from lede to the next section measured 99px on every inner page. The
   prototype renders 99px too — it was never looked at (the same "ships things
   never exercised" pattern as the jump bar and the sub-perceptual decorations).
   Now 67px, which is the section rhythm the padding already intends.

   Explicit 0 rather than deleting the rule: the base layer is a MINIMAL reset
   and deliberately does not zero <p> margins (per-section CSS carries them), so
   dropping this line would hand the lede back the UA's ~1em bottom margin
   instead of removing it. Harness-verified: 99px -> 67px on desktop + mobile,
   with h1->lede unchanged at 24px (the h1's 24px bottom margin still collapses
   against the lede's UA margin-top, which is untouched here). */
.page-hero-lede { margin-bottom: 0; }

/* =========================================================================
   CARE — ANCHOR INDEX (DYNAMIC)
   DOM: .svc-jump > .container.svc-jump-inner > (.eyebrow.svc-jump-label , a.svc-jump-link*)
   Net-new CSS, authored from the prototype (pages.jsx `ServicesPage`, all inline).
   Part: template-parts/care/anchor-index.php

   Ruled --bg-deep strip under the page hero. The label owns its own line and the
   links start below it, wrapping freely.

   Divergence from the prototype, approved 2026-07-14. The prototype ran the
   label inline with the links (margin-inline-end:auto pushing them right). With
   six real titles that overflows at every width: the last link wraps alone onto
   a second row and lands directly beneath the label, reading as a misalignment
   rather than a wrap. flex-basis:100% on the label gives it the full first line
   — which is also what mobile already did once everything wrapped, so desktop
   and mobile now agree. Row gap is 14px (the .svc-detail-label eyebrow→content
   step) vs 24px between links.

   The dashed underline is a LITERAL #8B8580, NOT the prototype's --line and no
   longer --ink-mute: --line on --bg-deep composites to 1.10:1 — invisible — and
   this underline is the only thing marking these as links. #8B8580 is 3.08:1, a
   hairline subordinate to the 6.38:1 link text, which is the prototype's evident
   intent. Approved 2026-07-14. (Fourth sub-perceptual decoration found in this
   prototype; see the .fitcall-meta and .fitcall-orb notes.)

   Why it is hardcoded rather than tokenised (a11y batch 3, 2026-07-25): this was
   --ink-mute's only non-text use, and the token was darkened to #6F6A66 to satisfy
   1.4.3. That criterion governs TEXT — it does not reach a border, so the original
   value is still correct here, and following the token would darken this hairline
   until it competed with the link text it sits under. One carve-out did not
   justify a second token. Deliberately literal; do not "fix" it back to the token.
   ========================================================================= */
.svc-jump {
  background: var(--bg-deep);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.svc-jump-inner {
  padding-block: 22px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 14px 24px;
}
.svc-jump-label { flex-basis: 100%; }
.svc-jump-link {
  font-size: 13px;
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px dashed #8B8580; /* literal, not --ink-mute — see note above */
  padding-bottom: 2px;
  transition: color .15s ease, border-color .15s ease;
}
.svc-jump-link:hover { color: var(--ink); border-bottom-color: var(--ink); }
/* Clinician byline (internal-linking pass, 2026-08-29). flex-basis:100% gives
   it its own row under the wrapped links — the same mechanism .svc-jump-label
   uses above. The <a> keeps the strip's dashed-underline idiom rather than
   introducing a third link style into a 22px-tall band. */
.svc-jump-by {
  flex-basis: 100%;
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--ink-soft);
}
.svc-jump-by a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px dashed #8B8580; /* literal, matching .svc-jump-link above */
  padding-bottom: 2px;
  transition: border-color .15s ease;
}
.svc-jump-by a:hover { border-bottom-color: var(--ink); }

/* =========================================================================
   CARE — SERVICES LIST (DYNAMIC)
   DOM: .svc-section > .container.svc-list > article.svc-row* >
          ( .svc-aside > (.svc-num , h2.svc-h2 , p.svc-sum)
          , .svc-detail > (.svc-addr-block > ul.svc-addrs > li.svc-addr , .svc-labs , a.svc-cta) )
   Net-new CSS, authored from the prototype (pages.jsx `ServicesPage` — all
   inline styles plus one inline <style> block for the 880px collapse).
   Part: template-parts/care/services-list.php

   Top pad is .6 --section-y (the prototype pulls the list up under the jump
   strip); .section supplies the bottom pad.

   Row separation: the prototype branches on the array index (i === 0 ? none :
   32px + rule). Here it's the adjacent-sibling rule — same rendered result, no
   PHP counter, and the knob stays in CSS.

   .svc-num is uppercased in CSS, not by PHP strtoupper() — the title is bound
   from the CPT, so casing belongs in the style layer.

   .svc-h2 / .svc-sum resize .h2 / .lede at equal specificity (0,1,0), so they
   MUST stay after those base rules — source order is what breaks the tie.
   ========================================================================= */
.svc-section { padding-top: calc(var(--section-y) * 0.6); }
.svc-list {
  display: grid;
  gap: 80px;
}
.svc-row {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.3fr);
  gap: clamp(24px, 5vw, 72px);
  align-items: start;
}
.svc-row + .svc-row {
  padding-top: 32px;
  border-top: 1px solid var(--line);
}
.svc-aside { position: sticky; top: 100px; }
.svc-num {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-bottom: 16px;
}
.svc-h2 { font-size: clamp(30px, 3.6vw, 44px); }
.svc-sum {
  margin-top: 18px;
  font-size: 17px;
}
.svc-detail {
  display: grid;
  gap: 28px;
}
.svc-detail-label { margin-bottom: 14px; }
.svc-addrs {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 12px;
}
.svc-addr {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
  align-items: baseline;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line-soft);
}
.svc-addr-n {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--accent-deep);
}
.svc-addr-t { color: var(--ink); }
.svc-labs {
  background: var(--bg-deep);
  border-radius: var(--r-lg);
  padding: 22px 24px;
}
.svc-labs-label { margin-bottom: 10px; }
.svc-labs-t {
  margin: 0;
  font-size: 15px;
}
.svc-cta { justify-self: start; }

/* One column; the aside stops sticking. Stays AFTER the base rules above —
   equal specificity, so source order is what decides. */
@media (max-width: 880px) {
  .svc-row { grid-template-columns: 1fr; }
  .svc-aside { position: static; }
}

/* =========================================================================
   ABOUT — PORTRAIT + INTRO (STATIC)
   DOM: .ab-intro > .container > .ab-grid > (.ab-portrait , .ab-copy > (.ab-quote , .ab-attrib , .ab-body))
   Net-new CSS, authored from the prototype (pages.jsx `AboutPage`, all inline).
   Part: template-parts/about/portrait-intro.php

   Top pad 0: .page-hero already pulls its bottom pad to .6 of --section-y so
   this band sits tight under the lede (the Care arrangement).

   Namespace is .ab-, distinct from Home §6's .about- band: both hold a Dr.
   Peterson portrait, but that one is --r-xl with no shadow and carries the
   signature card, this one lifts. Don't merge them.
   ========================================================================= */
.ab-intro { padding-top: 0; }
.ab-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
/* AFTER the base rule — equal specificity, source order decides. */
@media (max-width: 880px) {
  .ab-grid { grid-template-columns: 1fr; }
}

.ab-portrait {
  aspect-ratio: 4 / 5;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--bg-deep);
  box-shadow: var(--shadow-lift);
}
.ab-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Display-face pull quote. Not .h2/.h3 — the prototype sizes it between them
   and it is a <p>, not a heading; giving it a heading class would tie it to a
   cascade it should not share. */
.ab-quote {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.6vw, 30px);
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.ab-attrib {
  margin-top: 28px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.ab-body {
  margin-top: 36px;
  font-size: 16px;
  max-width: 54ch;
}

/* =========================================================================
   ABOUT — PHILOSOPHY (STATIC)
   DOM: .phil > .container > (.eyebrow.dot , h2.phil-h2 , .phil-grid > .phil-card*5)
   Net-new CSS, authored from the prototype (pages.jsx `AboutPage`, all inline).
   Part: template-parts/about/philosophy.php

   auto-fit + minmax(280px, 1fr): the column count falls out of the available
   width, so this band needs no media query — harness-measured at five cards it
   lands 3-up (3+2) at 1280, 2-up (2+2+1) at 880, 1-up at 390. --gutter is the
   floor via .container.

   The 280px min sets a hard narrow floor: 280 + 2×20px (the --gutter clamp
   floor) = 320px, the narrowest width that still keeps both gutters. Measured
   exactly clean at 320; below that the card eats the right gutter rather than
   forcing a scrollbar (300px → 0 gutter, still scrollWidth == innerWidth), so
   it degrades quietly and no real device reaches it. Re-check if the min ever
   rises above 280.

   Compliance: card 03 names bioidentical hormone therapy — OPEN Utah-scope gate
   (§15), the third surface after Home approach 03 and Care service 02.
   ========================================================================= */
.phil { background: var(--bg-deep); }
.phil-h2 {
  margin-top: 20px;
  margin-bottom: 60px;
  max-width: 20ch;
}
.phil-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}
.phil-card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(22px, 2.4vw, 32px);
}
.phil-n {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--accent-deep);
  margin-bottom: 14px;
}
.phil-t {
  margin: 0 0 12px;
  font-size: 22px;
}
.phil-d {
  margin: 0;
  font-size: 15px;
}
/* Link row beneath the five cards (internal-linking pass, 2026-08-29). Wraps
   rather than shrinking: two pill buttons do not fit side by side at ~380px. */
.phil-links {
  margin-top: 44px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* =========================================================================
   ABOUT — TIMELINE (DYNAMIC)
   DOM: .tl > .container > (.eyebrow.dot , h2.tl-h2 , .tl-list > .tl-row* >
          (.tl-year , h3.tl-t , p.tl-d))
   Loop: inc/queries.php fcc_query( 'milestones' ) — every entry, by `order` ASC.
   Net-new CSS, authored from the prototype (pages.jsx `AboutPage` — all inline
   styles plus one inline <style> block for the 760px collapse).
   Part: template-parts/about/timeline.php

   Rules come from each row's own border-top, with the last row adding a bottom
   — so the list closes without a trailing element. The prototype sets that
   bottom border per-row via an index test (i === len-1); :last-child is the
   same thing declaratively, and survives the row count changing.

   The prototype wraps each row in a <Reveal> DIV, which would make .tl-row a
   grandchild; the port puts .reveal on .tl-row itself (the house pattern), so
   the rows are direct children and :last-child resolves against them.
   ========================================================================= */
.tl-h2 {
  margin-top: 20px;
  margin-bottom: 56px;
  max-width: 20ch;
}
.tl-row {
  display: grid;
  grid-template-columns: 120px 1fr 2fr;
  gap: clamp(20px, 4vw, 56px);
  padding-block: 32px;
  border-top: 1px solid var(--line);
  align-items: baseline;
}
.tl-row:last-child { border-bottom: 1px solid var(--line); }

/* The 120px track is fixed while the font clamps, so its capacity is finite.
   Harness-measured at the 40px clamp max (≥1333px viewport), in Newsreader:
   a 4-digit year is ~92px (28px headroom) and even a range like "2014–2016"
   fits at ~110px — but "2003–present" is ~130px and spills ~10px into the
   column gap (no scrollbar; the gap absorbs it). So: 4-digit years and ranges
   are safe, words in the year field are not. If an entry ever needs one, widen
   the track rather than shrinking the type. */
.tl-year {
  font-family: var(--font-display);
  font-size: clamp(28px, 3vw, 40px);
  color: var(--accent-deep);
  letter-spacing: -0.01em;
}
.tl-t { margin: 0; font-size: 22px; }
.tl-d { margin: 0; font-size: 15px; max-width: 60ch; }

/* Stacked: the 120px year column has nothing to align to, so the row becomes a
   tight vertical block. AFTER the base rule — equal specificity, source order
   decides. */
@media (max-width: 760px) {
  .tl-row {
    grid-template-columns: 1fr;
    gap: 8px;
    padding-block: 24px;
  }
}

/* =========================================================================
   BEGIN — BOOKING (STATIC + THIRD-PARTY EMBED)
   DOM: .begin-book > .container > (.bg-embed | .bg-cta) + .bg-below > (.bg-intro , .bg-steps)
   Net-new CSS, authored from the prototype (pages.jsx `BeginPage`, all inline).
   Part: template-parts/begin/booking.php

   Opening band on its page: top pad 0, so it sits tight under the page hero's
   lede (which pulls its own bottom pad to .6 --section-y for exactly this).

   LAYOUT REDESIGN (approved 2026-07-16). This band was originally a two-column
   grid — embed left, steps right — which is what the prototype drew around its
   intake form. It doesn't survive contact with a real Google embed: a 1fr column
   is 510px, and below ~800px of width Google's widget drops to its narrow,
   STACKED layout (the phone one), which is far too tall for any frame we set and
   scrolls internally. The fix was WIDTH, not height. So the embed spans the
   container, and the steps moved BELOW it into their own two columns. See
   .bg-below for why they didn't just go full-width too.

   .bg-embed keeps the card the prototype drew around the form — the form is
   gone (§8 routes booking to Google, off the WordPress layer) but the slot and
   the card port unchanged. Padding is deliberately tighter than the prototype's
   clamp(28px, 4vw, 44px) form padding: Google's iframe brings its own generous
   internal margins, so the drawn padding is trim only.
   ========================================================================= */
.begin-book { padding-block: 0 var(--section-y); }

.bg-embed {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: 12px;
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

/* THE HEIGHT KNOB. It deliberately overrides the height="600" Google ships in
   the snippet — author CSS beats a presentation attribute, so retunes stay
   CSS-only deploys and nobody re-pastes the snippet. Confirmed live, not
   assumed: with 640px set here the frame reported innerHeight 640, not 600.
   (The snippet's inline style is `border: 0` only. If Google ever ships a height
   in that style attribute, INLINE WINS and this rule silently stops applying —
   check that first if the height ever seems stuck.)

   720px is measured, not chosen. DevTools inside the cross-origin frame on the
   deployed page (2026-07-16, the only way to read it — from `top` the console
   gets a security error) returned scrollHeight 710 at a frame width of 1102px.
   720 fits that with a 10px buffer for subpixel/font rounding.

   Known-unverified, and the reason this knob is per-breakpoint by design: 710
   was measured at 1102px wide, but the embed renders from 921px up, and the
   iframe bottoms out at ~821px there. A narrower frame can wrap Google's header
   text onto more lines and grow taller, so the 921–1100 band may want its own
   height. It has NOT been measured. If a scrollbar shows up on a narrow desktop
   window, that's this — add a media query, don't raise the base.

   Still a compromise, not a solve: the widget's height is dynamic (the booking
   FORM step, after a time is picked, is a different view than the calendar), and
   auto-sizing needs postMessage cooperation Google does not offer. Cross-origin
   also means the font, color and spacing INSIDE the frame are Google's,
   permanently — not a missing setting; the only way to control that typography
   is to not use the embed. Don't go looking.

   max-width is pinned for a different reason: an embed wider than its container
   would force a page-level scrollbar. */
.bg-embed iframe {
  display: block;
  width: 100%;
  max-width: 100%;
  height: 720px;
  border: 0;
  border-radius: calc(var(--r-xl) - 12px);
}

/* The mobile stand-in for the embed — hidden here, shown at <=920px. Drawn as
   the same card the embed sits in, so the band's shape holds across the swap.

   max-width is not decoration. The card runs to the full container otherwise,
   and .btn is width:100% inside it — measured at a 920px viewport that produced
   a 772px-wide pill, which reads as a stretched bar rather than a button. Capped
   at 520px and centered, the button stays button-shaped across the whole range
   this variant serves (320px phone → 920px tablet); below ~560px the cap is
   moot and the card simply fills the column. */
.bg-cta {
  display: none;
  max-width: 520px;
  margin-inline: auto;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: clamp(24px, 6vw, 36px);
  box-shadow: var(--shadow-card);
  text-align: center;
}
.bg-cta-btn { justify-content: center; width: 100%; }
.bg-cta-note {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--ink-mute);
}

/* "Four steps", below the now-full-width embed. Two columns rather than one:
   the ruled step list is drawn for ~510px and reads as four near-empty bands if
   stretched to the full 1240px container, so it keeps its width on the right
   while the heading and the call block take the left. The call block gains from
   the move — it was previously buried under a tall column. */
.bg-below {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 72px);
  align-items: start;
  margin-top: clamp(40px, 5vw, 72px);
}

.bg-h2 {
  font-size: clamp(28px, 3.4vw, 40px);
  margin-top: 20px;
  margin-bottom: 32px;
}

/* Ruled step list. Each row draws its own bottom rule; the first adds a top one
   so the list is closed at both ends (the prototype's borderTop-on-index-0). */
.bg-steps { display: grid; }
.bg-step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 20px;
  padding-block: 22px;
  border-bottom: 1px solid var(--line);
}
.bg-step:first-child { border-top: 1px solid var(--line); }

.bg-step-n {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--accent-deep);
  color: var(--accent-deep);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 13px;
}
.bg-step-t {
  font-family: var(--font-display);
  font-size: 20px;
  letter-spacing: -0.01em;
  margin-bottom: 4px;
}
.bg-step-d { margin: 0; font-size: 14px; }

/* Call-direct block. hours_html is a wysiwyg (<p>-wrapped, one day per line),
   so the margins get flattened here rather than trusted. */
.bg-call {
  margin-top: 32px;
  padding: 22px;
  background: var(--bg-deep);
  border-radius: var(--r-lg);
}
.bg-call-label { margin-bottom: 8px; }
.bg-call-num {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.bg-call-num:hover { color: var(--accent-deep); }
.bg-call-hours {
  margin-top: 6px;
  font-size: 13px;
  color: var(--ink-mute);
  line-height: 1.6;
}
.bg-call-hours :where(p) { margin: 0; }

/* THE SWAP, and 920px is measured, not a round number. Daniel confirmed against
   Google's standalone page that the widget reflows side-by-side around 700–800px
   of iframe width. At a 921px viewport the gutter is 4vw = 36.8px, so the iframe
   lands at 921 - 73.7 - 24 (card padding) = ~823px — clear of the 800px ceiling
   with ~23px of margin. Any narrower and the embed would render in the stacked
   layout it cannot fit, which is the bug this redesign exists to kill.

   Below it the embed is replaced outright by a button to Google's own booking
   page (see the part's docblock). The layout failure above is only half of it:
   an iframe has NO SCROLLBAR ON TOUCH, so a phone shows a cut-off month with
   zero affordance that the calendar continues — on the site's only conversion
   surface. Google's standalone page is built for a phone; we can't beat it from
   inside a 320px box.

   The steps stack, and .bg-intro goes display:contents so its children become
   grid items directly — that's the established mobile-reorder pattern, and it's
   here to keep the reading order the band shipped with (heading → steps → call).
   Without it the call block would land between the heading and the steps.

   gap goes to 0 for the same reason it must: display:contents promotes the
   eyebrow, h2 and call block to grid items, so the column gap would start
   applying BETWEEN them — on top of margins that already space them. Zeroed, the
   elements' own margins do the work and the stack measures exactly as it did
   before this redesign. Nothing else needs the gap; single column has no columns
   to separate, and the embed→steps distance is .bg-below's margin-top.

   AFTER the base rules — equal specificity, source order decides. */
@media (max-width: 920px) {
  .bg-embed { display: none; }
  .bg-cta { display: block; }

  .bg-below {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .bg-intro { display: contents; }
  .bg-steps { order: 2; }
  .bg-call { order: 3; }
}

/* =========================================================================
   CONTACT — OFFICE INFO (STATIC chrome, ACF-bound NAP)
   DOM: .ct-office-section > .container > .ct-grid >
          ( .ct-card.ct-card-office
          , .ct-info > ( .ct-pair > .ct-card.ct-mini* , .ct-card.ct-card-hours ) )
   Net-new CSS, authored from the prototype (articles.jsx `ContactPage` first
   section), MINUS the SVG map. Part: template-parts/contact/office-info.php

   Opening content section on its page: padding-top 0 (pulled up tight under the
   page-hero, whose .6 --section-y bottom pad sets the gap).

   Layout rebalance after the map was dropped (Daniel, this session): the map was
   the left column; now the Office card takes that slot and the Phone+Email pair
   + Hours stack on the right. align-items:start (not the map era's stretch) — the
   columns are naturally uneven (the office card is shorter than the pair+hours
   stack) and that's fine for cards; stretch is also what let the map's
   aspect-ratio blow its width past the column, so it's gone with the map.

   .ct-card is the shared card shell (bg-card / line / --r-lg / clamp pad); the
   office, mini and hours cards override only what differs.
   ========================================================================= */
.ct-office-section { padding-top: 0; }
.ct-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: start;
}

.ct-info { display: grid; gap: 24px; align-content: start; }

.ct-card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(22px, 2.4vw, 32px);
}
.ct-card-eyebrow { margin-bottom: 14px; }

.ct-office-name {
  font-family: var(--font-display);
  font-size: 24px;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin-bottom: 8px;
}
.ct-office-addr { color: var(--ink-soft); font-size: 15px; line-height: 1.6; }
.ct-office-note {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  color: var(--ink-mute);
  line-height: 1.6;
}
.ct-office-maps { margin-top: 18px; display: flex; gap: 10px; flex-wrap: wrap; }
.ct-map-btn { padding: 10px 16px; font-size: 13px; }

.ct-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ct-mini { padding: 22px; }
.ct-mini .ct-card-eyebrow { margin-bottom: 10px; }
/* Phone/Email values are SANS — the card system's data font — not the
   prototype's serif display (Daniel, this session): the serif made them read as
   a different treatment from the address + hours cards, which are sans. Kept at
   16px/500/--ink (email 15px, a longer string) so the two tappable tel:/mailto:
   links still carry weight as the page's primary actions rather than dropping to
   plain body text; hover → accent is the link cue. */
.ct-mini-value {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
}
.ct-mini-value:hover { color: var(--accent-deep); }
.ct-mini-email { font-size: 15px; word-break: break-all; }

.ct-card-hours { padding: clamp(22px, 2.4vw, 28px); }
/* hours_html is one <p> per day (the footer renders the same field the same
   way). Mirrors .footer-hours: zero the paragraph margins and let line-height
   space the rows. */
.ct-hours { font-size: 14px; color: var(--ink-soft); line-height: 1.85; }
.ct-hours p { margin: 0; }

@media (max-width: 980px) {
  .ct-grid { grid-template-columns: 1fr; }
}

/* =========================================================================
   CONTACT — REACH TRIPTYCH (STATIC)
   DOM: .ct-tri-section > .container >
          ( .eyebrow.dot , h2.ct-tri-h2 , .ct-tri > .ct-tile* >
              (.ct-tile-num , h3.ct-tile-title , p.ct-tile-body , a.ct-tile-cta) )
   Net-new CSS, authored from the prototype (articles.jsx `ContactPage` second
   section + `ContactTile`, all inline + one inline 880px media query).
   Part: template-parts/contact/reach-triptych.php

   --bg-deep band (a full .section, so it keeps the section rhythm). The three
   tiles are equal-height flex columns so their CTAs bottom-align regardless of
   body length (margin-top:auto on the CTA).

   .ct-tri-h2 / .ct-tile-title / .ct-tile-body resize .h2 / .h3 / .body at equal
   specificity (0,1,0), so they MUST stay after those base rules — source order
   breaks the tie (the same pattern as .svc-h2 / .svc-sum).
   ========================================================================= */
.ct-tri-section { background: var(--bg-deep); }
.ct-tri-h2 { margin-top: 20px; margin-bottom: 48px; max-width: 20ch; }
.ct-tri {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2vw, 28px);
}
.ct-tile {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(24px, 2.4vw, 32px);
  height: 100%;
  display: flex;
  flex-direction: column;
}
.ct-tile-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--accent-deep);
  margin-bottom: 14px;
}
.ct-tile-title { margin: 0 0 14px; font-size: 24px; }
.ct-tile-body { margin: 0 0 24px; font-size: 15px; }
.ct-tile-cta { margin-top: auto; align-self: flex-start; font-size: 14px; }

@media (max-width: 880px) {
  .ct-tri { grid-template-columns: 1fr; }
}

/* =========================================================================
   CONTACT — NEW PATIENTS (STATIC — closing band)
   DOM: .ct-new-section > .container > .ct-new >
          ( .ct-new-text > (.eyebrow.dot , h3.ct-new-h3 , p.ct-new-body)
          , a.ct-new-cta )
   Net-new CSS, authored from the prototype (articles.jsx `ContactPage` final
   section, all inline + one inline 720px media query).
   Part: template-parts/contact/new-patients.php

   The LAST section on the page: padding-bottom 0, the footer's own top pad sets
   the gap (the shared cta-fit-call idiom). One --bg-card panel, text left / CTA
   right, stacking to text-over-CTA at 720px.

   .ct-new-h3 uses the .h2 class (display face) but a smaller clamp than the base
   — resized after .h2, equal specificity, source order wins.
   ========================================================================= */
.ct-new-section { padding-bottom: 0; }
.ct-new {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: clamp(32px, 5vw, 64px);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 32px;
  align-items: center;
}
.ct-new-eyebrow { margin-bottom: 12px; }
.ct-new-h3 { margin: 0 0 16px; font-size: clamp(28px, 3.4vw, 40px); }
.ct-new-body { margin: 0; max-width: 54ch; }
.ct-new-cta { flex-shrink: 0; }

@media (max-width: 720px) {
  .ct-new { grid-template-columns: 1fr; }
  .ct-new-cta { justify-self: flex-start; }
}

/* =========================================================================
   LEGAL — PROSE PAGES (STATIC)   [Privacy · Terms]
   DOM: .legal-hero > .container > (.eyebrow.dot , h1.legal-hero-h1 ,
          p.legal-hero-status)
        .legal-body > .container.legal-container > .legal-prose > (h2, p, ul>li, a)
   Parts: template-parts/legal-hero.php · page-privacy.php · page-terms.php

   Net-new CSS, translated from the prototype addendum (legal.jsx `LegalHero`
   / `LegalBody`, all inline styles + one inline <style> block). Two token
   fixes on port, per docs/prototype/README.md §10: legal.jsx styles h2 with
   var(--font-head), which this theme calls --font-display; and the body/link
   colours (--ink-soft, --accent-deep) already clear AA on their own (7.07:1 /
   4.16:1), so this page does not inherit the --ink-mute contrast fix from
   batch 3 — nothing here needed it.

   Opening section on its page (same first-section rule as .page-hero): flat
   56px top pad, not --section-y. Bottom pad is .5 of --section-y (JSX:
   `paddingBottom: calc(var(--section-y) * 0.5)`) — lighter than .page-hero's
   .6 because the next thing down is the prose body, not a full section band.
   ========================================================================= */
.legal-hero { padding-block: 56px calc(var(--section-y) * 0.5); }
.legal-hero-h1 {
  font-size: clamp(40px, 5.6vw, 72px);
  margin-top: 24px;
  margin-bottom: 16px;
}
.legal-hero-status { font-size: 14px; color: var(--ink-mute); margin: 0; }

.legal-body { padding-top: 0; padding-bottom: var(--section-y); }
.legal-container { max-width: 760px; }

.legal-prose h2 {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 500;
  margin: 40px 0 14px;
  color: var(--ink);
}
.legal-prose h2:first-child { margin-top: 0; }
.legal-prose p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0 0 16px;
}
.legal-prose ul { margin: 0 0 16px; padding-left: 20px; }
.legal-prose li {
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin-bottom: 8px;
}
.legal-prose a { color: var(--accent-deep); }
.legal-prose a:hover { color: var(--accent); }

/* =========================================================================
   ARTICLES — INDEX (DYNAMIC)
   DOM: .site-main >
          ( .page-hero
          , .section.art-featured-section > .container > a.art-featured >
              ( .art-featured-media[.imgph] , .art-featured-text >
                ( .art-meta , h2.art-featured-h2 , p.art-featured-dek , .art-byline ) )
          , .section.art-grid-section > .container >
              ( hr.art-grid-rule , .art-grid > a.art-card* )
          , nav.art-pagination
          , .section.art-empty            [only when there are no posts]
          , .cta band )
   Net-new CSS, authored from the prototype (articles.jsx `ArticlesPage`), MINUS
   the category filter row and the newsletter block — both dropped deliberately;
   home.php explains each. Templates: home.php + template-parts/articles/.

   Two shared pieces are reused rather than restyled: .imgph (the hatched
   empty-image placeholder, added by PHP only when no featured image is set) and
   .hr. The page hero is the same shared .page-hero every inner page uses.

   Whole-card links. Both .art-featured and .art-card are a single <a> wrapping
   image + text, so hover/focus is one target and the title carries the colour
   cue (--accent-deep, the house link-hover idiom — see .focus-card-t a:hover).
   Focus rings are left to the UA default, per the a11y batch 3 decision.

   The media wrapper — not the <img> — owns the aspect ratio, so a portrait,
   landscape or square upload all crop to the same 4:3 rather than reflowing the
   grid. object-fit: cover does the cropping; without it a tall image letterboxes
   and the card heights stop matching.
   ========================================================================= */

/* Pulled up under the page hero, whose .6 --section-y bottom pad sets the gap. */
.art-featured-section { padding-top: calc(var(--section-y) * 0.5); }

.art-featured {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
  text-decoration: none;
  color: inherit;
}

.art-featured-media,
.art-card-media {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--bg-deep);
}
.art-featured-media { border-radius: var(--r-xl); }
.art-card-media { border-radius: var(--r-lg); margin-bottom: 22px; }

.art-featured-media img,
.art-card-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Meta line — mono, uppercase, tracked. It does not borrow .eyebrow, because
 * .eyebrow.dot carries a leading marker this line does not want.
 * ⚠️ 2026-09-06: this comment used to say it "matches .eyebrow's treatment".
 * It no longer does, and that is deliberate rather than drift. .eyebrow was
 * bumped to 13px/0.12em because it FRAMES a section and readers were missing
 * it. This line is a byline and a reading time — metadata, which is genuinely
 * supposed to be quiet — so it stays at 11px/0.14em. Do not "resync" them. */
.art-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.art-meta-lead { color: var(--accent-deep); }
.art-meta-time,
.art-meta-sep { color: var(--ink-mute); }

.art-featured-h2 {
  font-size: clamp(32px, 4.4vw, 56px);
  margin: 0 0 18px;
  transition: color .2s ease;
}
.art-featured:hover .art-featured-h2,
.art-featured:focus-visible .art-featured-h2 { color: var(--accent-deep); }

.art-featured-dek { margin: 0 0 24px; }

.art-byline {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink-mute);
}
.art-byline-name { color: var(--ink-soft); }

/* Grid ---------------------------------------------------------------------- */
.art-grid-section { padding-top: 0; }
.art-grid-rule { margin-bottom: 56px; }

.art-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(28px, 3vw, 48px);
}

.art-card {
  display: block;
  text-decoration: none;
  color: inherit;
}
.art-card-meta { margin-bottom: 10px; }
.art-card-h3 {
  font-size: 24px;
  margin: 0 0 10px;
  transition: color .2s ease;
}
.art-card:hover .art-card-h3,
.art-card:focus-visible .art-card-h3 { color: var(--accent-deep); }
.art-card-dek { margin: 0 0 14px; font-size: 15px; }
.art-card-date { font-size: 12px; color: var(--ink-mute); }

/* Pagination ---------------------------------------------------------------- */
/* Renders only once a second page exists — see the note in home.php. */
.art-pagination {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--gutter) var(--section-y);
}
.art-pagination .nav-links {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
}
.art-pagination .page-numbers {
  display: inline-block;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-soft);
  text-decoration: none;
}
.art-pagination .page-numbers:hover { border-color: var(--ink); }
.art-pagination .page-numbers.current {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}

/* Empty state --------------------------------------------------------------- */
/* Not decoration. /articles/ is a redirect destination before it is a reading
 * list — the migration map sends most of the old site here — so an index with no
 * posts still has to read as a real page with somewhere to go. */
.art-empty { padding-top: calc(var(--section-y) * 0.5); }
.art-empty-inner { max-width: 62ch; }
.art-empty-lede { margin: 0 0 28px; }
.art-empty-actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* Responsive ---------------------------------------------------------------- */
/* 880 / 980 / 620 are the prototype's own collapse points for these two blocks
   (README "Responsive"), not new ones. */
@media (max-width: 980px) {
  .art-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 880px) {
  .art-featured { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .art-grid { grid-template-columns: 1fr; }
}

/* =========================================================================
   ARTICLES — SINGLE (DYNAMIC)
   DOM: .site-main > article.art-single >
          ( header.art-single-head > .container.art-single-head-inner >
              ( a.art-back , .art-meta , h1.art-single-h1 , p.art-single-dek
              , .art-single-byline > ( .art-monogram , .art-single-byline-text ) )
          , .container.art-single-hero-wrap > .art-single-hero[.imgph]
          , section.art-body-section > .container.art-body >
              ( aside.art-rail > ( .art-rail-label , nav > ul.art-toc )
              , .art-prose > ( post content , .art-cta )
              , .art-rail.art-rail-spacer ) )
        + section.section.art-more
   Net-new CSS, authored from the prototype (articles.jsx `ArticlePage`), MINUS
   the share buttons — article-body.php explains why they are not shipped.
   Templates: single.php + template-parts/articles/.

   .art-grid / .art-card in the "Keep reading" band are the INDEX's classes,
   reused unchanged so the two card treatments cannot drift apart. Only the band
   around them is styled here.

   The 180 / 700 / 180 body grid is the prototype's. The right column is empty
   and load-bearing: without it the prose sits 220px right of centre, which reads
   as a mistake rather than as a layout. Both rails hide at 1080px — the
   prototype's own collapse point — and the grid becomes a single 720px column.

   Prose measure is 700px, not the 1240px container: this is the only long-form
   reading surface on the site and line length is the whole point.
   ========================================================================= */

/* Masthead ------------------------------------------------------------------ */
.art-single-head { padding-top: 56px; padding-bottom: clamp(40px, 6vw, 80px); }
.art-single-head-inner { max-width: 880px; }

.art-back {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.art-back:hover { color: var(--ink); }
.art-back:hover .art-back-arrow { transform: translateX(-3px); }
.art-back-arrow { display: inline-block; transition: transform .2s ease; }

.art-single-meta { margin-top: 28px; margin-bottom: 24px; }

.art-single-h1 {
  font-size: clamp(36px, 5.4vw, 72px);
  margin: 0 0 28px;
  max-width: 20ch;
}
.art-single-dek { margin: 0 0 32px; font-style: italic; }

.art-single-byline {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}
/* Monogram — decorative, aria-hidden in the markup. --accent-deep on
 * --accent-tint is 4.63:1, so it clears 1.4.3 even though nothing requires it
 * to: a decorative element that happens to contain a legible letter should not
 * be the one illegible thing on the page. */
.art-monogram,
.art-cta-monogram {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-deep);
  font-family: var(--font-display);
  font-style: italic;
  flex-shrink: 0;
}
.art-monogram { width: 44px; height: 44px; font-size: 20px; }
.art-single-byline-text { display: grid; gap: 2px; }
.art-single-date { font-size: 12px; color: var(--ink-mute); }

/* Hero image ---------------------------------------------------------------- */
.art-single-hero-wrap { max-width: 1100px; }
.art-single-hero {
  aspect-ratio: 16 / 9;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--bg-deep);
}
.art-single-hero img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Body grid ----------------------------------------------------------------- */
.art-body-section { padding-block: clamp(56px, 7vw, 100px); }
.art-body {
  display: grid;
  grid-template-columns: 180px minmax(0, 700px) 180px;
  gap: 40px;
  justify-content: center;
}
/* Fewer than two h2s: no rail is rendered, so the grid is a single column. */
.art-body-norail {
  grid-template-columns: minmax(0, 700px);
  justify-content: center;
}

.art-rail { position: sticky; top: 100px; align-self: start; }
.art-rail-label { margin-bottom: 14px; }
.art-toc { list-style: none; padding: 0; margin: 0; display: grid; gap: 10px; }
.art-toc-link {
  display: block;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink-soft);
  text-decoration: none;
  border-left: 2px solid var(--line);
  padding-left: 12px;
  padding-block: 4px;
}
.art-toc-link:hover { color: var(--ink); border-left-color: var(--accent-deep); }

/* Prose --------------------------------------------------------------------- */
.art-prose { font-size: 18px; line-height: 1.7; color: var(--ink); }
.art-prose > p { margin: 0 0 20px; }

/* Standfirst + drop cap. Done in CSS on :first-of-type rather than by wrapping
 * the first paragraph in PHP: the editor writes ordinary paragraphs, and a
 * template that reaches into the body to re-tag the first one breaks the moment
 * a post opens with an image or a list instead. If that happens the rule simply
 * does not apply, which is the right failure. */
.art-prose > p:first-of-type {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 300;
  line-height: 1.5;
}
.art-prose > p:first-of-type::first-letter {
  float: left;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 76px;
  line-height: 0.9;
  margin-right: 12px;
  margin-top: 6px;
  color: var(--accent-deep);
}

.art-prose h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 32px;
  letter-spacing: -0.015em;
  line-height: 1.25;
  margin: 48px 0 16px;
  color: var(--ink);
}
.art-prose h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 24px;
  margin: 36px 0 12px;
  color: var(--ink);
}
.art-prose ul,
.art-prose ol { padding-left: 24px; margin: 24px 0; display: grid; gap: 8px; }
.art-prose a { color: var(--accent-deep); }
.art-prose a:hover { color: var(--accent); }
.art-prose img { max-width: 100%; height: auto; border-radius: var(--r-lg); }
.art-prose figure { margin: 32px 0; }
.art-prose figcaption {
  margin-top: 10px;
  font-size: 13px;
  color: var(--ink-mute);
  line-height: 1.5;
}

.art-prose blockquote {
  margin: 40px 0;
  padding-left: 28px;
  border-left: 2px solid var(--accent-deep);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 26px;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.art-prose blockquote p { margin: 0 0 12px; }
.art-prose blockquote p:last-child { margin-bottom: 0; }

/* Inline CTA card ----------------------------------------------------------- */
.art-cta {
  margin-top: 64px;
  padding: clamp(24px, 3vw, 36px);
  background: var(--bg-deep);
  border-radius: var(--r-lg);
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 20px;
  align-items: center;
}
.art-cta-monogram { width: 56px; height: 56px; font-size: 26px; }
.art-cta-h {
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 1.3;
  margin: 0 0 4px;
  color: var(--ink);
}
.art-cta-sub { font-size: 14px; color: var(--ink-mute); margin: 0; }
.art-cta-btn { flex-shrink: 0; white-space: nowrap; }

/* Keep reading -------------------------------------------------------------- */
.art-more { background: var(--bg-deep); }
.art-more-h2 { margin: 20px 0 48px; }

/* Responsive ---------------------------------------------------------------- */
@media (max-width: 1080px) {
  .art-body {
    grid-template-columns: minmax(0, 720px);
    justify-content: center;
  }
  .art-rail { display: none; }
}
@media (max-width: 720px) {
  .art-cta { grid-template-columns: 1fr; text-align: center; justify-items: center; }
  .art-prose > p:first-of-type::first-letter { font-size: 60px; }
  .art-prose blockquote { font-size: 22px; padding-left: 20px; }
}

/* =========================================================================
   PATIENT PORTAL — ACCESS + CAPABILITIES (STATIC)
   DOM: .pp-access > .pp-container >
          ( a.btn.btn-primary.pp-login , p.pp-newtab-note ,
            .pp-help > (h2.pp-help-title , ul.pp-help-list > li > a.btn.btn-link) ,
            p.pp-stuck )
        .pp-can > .pp-container >
          ( .eyebrow.dot , h2.pp-can-h2 , ul.pp-can-list > li.pp-can-item ,
            p.pp-can-note )
   Parts: template-parts/portal/portal-access.php
          template-parts/portal/portal-capabilities.php

   NET-NEW, and unlike every other section here it is NOT authored from the
   prototype — the prototype has no portal page (§8 approved building one).
   So this is composition from existing primitives on purpose: .btn/.btn-primary/
   .btn-link/.arrow, .eyebrow.dot, .h2/.h3, .body and the --bg-deep band idiom all
   come from the design system unchanged. What is new below is layout only.

   A measured column rather than the full container: this page is a short task
   (log in, or find the guide), and a 1080px-wide line of body text over three
   items reads as an empty page. Capped at 640px and centred.
   ========================================================================= */

.pp-container {
  max-width: 640px;
}

/* --- Access band ------------------------------------------------------- */

/* padding-top: 0 is the house rule for the first section after .page-hero, which
   already carries calc(--section-y * .6) = 67px of bottom pad — same override
   .ct-office-section and .begin-book use. Shipped without it 2026-08-23 and the
   two pads stacked to 179px, which Daniel correctly read as a hole.

   The bottom pad is halved (56px) rather than left at the full 112px: these two
   bands are one thought — get in, then what is in there — and the site's normal
   112+112 = 224px between sections reads as rhythm on a dense page and as
   emptiness on a short one. The --bg-deep band below keeps its full pad, so the
   colour block is unchanged. */
.pp-access {
  text-align: center;
  padding-block: 0 calc(var(--section-y) * 0.5);
}

.pp-login {
  font-size: 17px;
  padding: 16px 28px;
}

/* Sits directly under the button as its caption, so the gap is tight and the
   type drops a step — it is a disclosure, not body copy. Announcing the new tab
   in text is the WCAG 3.2.5 courtesy for a link that changes context. */
.pp-newtab-note {
  margin-top: 12px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-mute);
}

.pp-help {
  margin-top: 48px;
  padding-top: 32px;
  border-top: 1px solid var(--line);
}

.pp-help-title {
  margin-bottom: 16px;
}

.pp-help-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.pp-stuck {
  margin-top: 40px;
  color: var(--ink-soft);
}

.pp-stuck a {
  color: var(--accent-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- Capabilities band -------------------------------------------------- */

.pp-can {
  background: var(--bg-deep);
}

.pp-can-h2 {
  margin-top: 20px;
  margin-bottom: 28px;
}

/* A REAL bulleted list, and the third attempt at this band's grouping problem —
   the first two are worth knowing so nobody walks back through them.

   v1 shipped hairline rules copying the FAQ / timeline / services device. Those
   all sit on --bg where --line reaches 1.216:1; this band is --bg-deep, where the
   same token computes to 1.021:1. Invisible.
   v2 removed them and leaned on proximity, widening the row gap to 48px. That
   worked, and looked like a hole — 48px of nothing between one-line items, with
   the fragments reading as orphaned paragraphs rather than list entries.
   v3 (Daniel, 2026-08-23) is the obvious answer both attempts walked past: a
   BULLET. It marks the start of each item unambiguously, which is the whole job
   the rules and then the gap were doing badly — so the gap collapses from 48px to
   12px and a wrapped item can no longer read as two. It also fixes the copy: a
   verb phrase with no terminal punctuation reads as a fragment when it floats in
   space and reads correctly the moment a bullet precedes it.

   Matches the theme's existing list idiom (.art-prose ul: padding-left 24px,
   native markers, small gap) rather than inventing a fourth device. Markers are
   --accent-deep, NOT --accent: a bullet is a tiny filled dot, so the small area
   carrying the contrast has to clear comfortably. --accent is 2.34:1 on
   --bg-deep, --accent-deep is 3.75:1. */
.pp-can-list {
  margin: 0;
  padding-left: 24px;
  display: grid;
  gap: 12px;
}

.pp-can-item {
  padding: 0;
}

.pp-can-item::marker {
  color: var(--accent-deep);
}

.pp-can-note {
  margin-top: 28px;
  font-size: 15px;
  color: var(--ink-mute);
}

@media (max-width: 620px) {
  /* .btn sets white-space:nowrap, which is right for the short labels it was
     written for and wrong for this one — "Log in to the patient portal" cannot
     shrink, so on a 320px screen it would overflow its own pill. Released here
     only, with the flex centring doing the alignment once it wraps. */
  .pp-login {
    width: 100%;
    justify-content: center;
    white-space: normal;
    text-align: center;
  }
  .pp-help { margin-top: 36px; }
}

/* =========================================================================
   HOME §8.5 — ARTICLES BAND (DYNAMIC)
   DOM: .arth > .container > (.eyebrow , .arth-h2 , ul.arth-list > li.arth-item
        > a.arth-link , a.arth-all)
   Part: template-parts/home/articles-band.php

   ⚠️ NET-NEW, NOT PORTED. This section exists in no prototype and no Bricks
   screenshot — added 2026-08-29 (Daniel approved) by the internal-linking
   pass. Do not go looking for its spec in docs/prototype/.

   --bg-deep is deliberate: .faq above and .fitcall below BOTH default to
   --bg, so without it three sections would run together as one flat block.
   It also keeps Home's alternating deep/light rhythm (symc/appr/focus/about).

   The ruled list reuses the .faq-list idiom (border-top on the list, one
   border-bottom per item) rather than inventing a divider treatment.
   ========================================================================= */
.arth { background: var(--bg-deep); }
.arth-h2 {
  margin-top: 20px;
  margin-bottom: 40px;
  max-width: 20ch;
}
.arth-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}
.arth-item { border-bottom: 1px solid var(--line); }
/* display:block so the whole row is the hit target, not just the glyphs —
   WCAG 2.5.8 Target Size is comfortably met by the 22px block padding. */
.arth-link {
  display: block;
  padding: 22px 0;
  font-family: var(--font-display);
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.3;
  color: var(--ink);
  text-decoration: none;
  transition: color .15s ease;
}
.arth-link:hover { color: var(--accent-deep); }
.arth-all { margin-top: 36px; }
