/* Classy Dental skin (L3) — composes Anchor primitives.
   Brand colors come from Theme Colors (ac_colors). Only typography + polish here. */
:root {
	--heading-font: 'Playfair Display', Georgia, serif;
	--default-font: 'Libre Franklin', system-ui, -apple-system, sans-serif;
	--mono-font: 'EB Garamond', Georgia, serif;
	/* header/footer chrome fonts (Site Config also sets these; keep in sync) */
	--anchor-font-heading: 'Playfair Display', Georgia, serif;
	--anchor-font-body: 'Libre Franklin', system-ui, sans-serif;
}

/* --- Surface token: the cream -----------------------------------------
   --soc-cream #FBF9F5 is the warm off-white the home page's hero and its
   alternating bands are built on. It USED to be declared page-locally on
   `.home-page` in home.custom.css, which meant nothing outside that one
   element could see it: the reviews band had to fall back to
   --secondary-color, and the ten Phase 2 pages plus the 27 services opened on
   plain white instead. Unifying every non-home hero onto the cream (client,
   2026-08-12) makes it a SITE-WIDE surface, so it is declared here — once, in
   the shared sheet, on :root — rather than copied as a literal into each place
   that now needs it. home.custom.css no longer declares it and simply inherits.

   It is a SURFACE, not a brand colour, so it is not in the banned-hex rule's
   scope; every declaration that paints with it references the token.

   MEASURED ON THIS GROUND with bin/contrast.py (AA normal 4.5:1, large 3:1):
     #0d2440  --primary-color   14.86:1  PASS  (headings, hero h1)
     #4E5560  --text-color       7.15:1  PASS  (lead + body copy)
     #04807e  --accent-ink       4.54:1  PASS  (the small-caps hero eyebrow)
     #059e9c  --accent-color     3.13:1  LARGE TEXT ONLY — never small text
     #10c3cc  --button-color     2.06:1  FAIL as text; it is a FILL only, and
                                         the control's boundary is
                                         --btn-fill-edge #04807e at 4.54:1,
                                         which clears the 3:1 bar of WCAG 1.4.11
   --------------------------------------------------------------------- */
:root{ --soc-cream: #FBF9F5; }

/* =====================================================================
   ***** THE COLUMN ALIGNMENT SETTING — CHANGE IT HERE, NOWHERE ELSE *****

   CLIENT, 2026-08-17: "This should be a reusable rule in case I decide that I
   want things generally aligned center. I should be able to change it with one
   CSS rule, the base rule for those."

   THIS IS THAT ONE RULE. `start` puts every two-column band's shorter column at
   the TOP of its row; `center` centres it against the taller one. Change the
   single value below and every column construct on every authored page follows:

     .svc-band__grid   copy | aside note        (service pages)
     .svc-band__figure copy | illustration      (service pages)
     .svc-duo          two peer h3 columns      (service pages)
     .svc-split .row   copy | photograph        (service pages)

   Per-section exceptions are `u-align-center` / `u-align-start` on the section —
   they set this same property, so everything nested inside follows from one
   declaration. See section 4a for why it is a custom property rather than a
   shared selector list, and for why Bootstrap's `align-items-center` had to come
   out of the `.svc-split` markup before this could be authoritative.

   It is declared on `.dc-page` rather than `:root` so it cannot reach the
   header, the footer or any plugin markup — the wrapper every authored body
   opens with is exactly the scope this governs.
   ===================================================================== */
.dc-page{ --col-align: start; }

/* Editorial heading treatment */
.u-display, .u-title { letter-spacing: .2px; font-weight: 600; }
.eyebrow-text { letter-spacing: .18em; text-transform: uppercase; font-family: var(--mono-font); }

/* =====================================================================
   BUTTON SYSTEM — one base class, two variants. Nothing else.
   Every button on the site is `.btn-default` plus (optionally) a variant.
   Do NOT invent a new button class in page CSS; page CSS may only set
   margins/alignment on the element (e.g. `.closing-cta__btn{margin-top}`).

     .btn-default             filled sky blue, square (DEFAULT — every button)
     .btn-default.btn-ghost   outline (available; nothing uses it today)
     .btn-default.btn-sm      compact size (nav bar, mobile menu drawer)

   Ghost is context-aware: `--btn-ink` is inherited, so an outline button
   inside any dark band (.bg-leather / .service-hero / .section--inverse)
   flips to light teal automatically with no extra class in the markup.
   ===================================================================== */

/* --- Ink tokens ------------------------------------------------------ */
:root{
	/* RESTING fill is the client's sky blue (--button-color #10c3cc), which is a
	   LIGHT color: #FFFFFF on #10c3cc measures 2.16:1 and fails badly, so it takes
	   the dark navy ink instead (#0d2440 on #10c3cc = 7.22:1 PASS).
	   HOVER fill is the darker teal (--button-hover-color #04807e), which IS dark
	   enough for white (#FFFFFF on #04807e = 4.78:1 PASS) — so the ink flips.
	   This resting/hover ink split is the whole point of these four tokens; do not
	   collapse them back to a single ink. Same constraint Princeton hit with gold. */
	--btn-fill: var(--button-color);
	--btn-fill-ink: var(--button-text-color);
	--btn-fill-hover: var(--button-hover-color);
	--btn-fill-hover-ink: var(--white-color);
	/* EDGE — the button's BOUNDARY, and the reason this token exists.
	   WCAG 1.4.11 (non-text contrast) asks for 3:1 between an interactive
	   control and the colour BEHIND it, which is a different test from the label
	   contrast above and was the one this button failed. The fill is the client's
	   chosen #10c3cc and does not move; measured against the two grounds it
	   actually sits on it is #10c3cc on #FFFFFF = 2.16:1 FAIL and #10c3cc on
	   #F7F9FC = 2.05:1 FAIL, and 31 of the site's 32 solid buttons are on one of
	   those two. The border was `1px solid var(--btn-fill)` — the same colour as
	   the fill, so it drew no boundary at all and the control had no edge that
	   met the bar.
	   Pointing the edge at the existing hover teal fixes it without inventing a
	   colour or touching the fill: #04807e on #FFFFFF = 4.78:1 PASS and #04807e
	   on #F7F9FC = 4.53:1 PASS. It stays 1px, so no button changes size.
	   (The edge is NOT required to contrast with the fill it surrounds — 1.4.11
	   measures the control against its background, and fill+border read as one
	   object.) */
	--btn-fill-edge: var(--btn-fill-hover);
	/* FOCUS RING — 1.4.11 gives the focus indicator its own 3:1 requirement, and
	   the new edge must not be mistaken for it, so the ring is the navy instead
	   of a third teal: #0d2440 on #FFFFFF = 15.63:1 and on #F7F9FC = 14.82:1. The
	   2px offset keeps it clear of the #04807e edge (#0d2440 on #04807e = 3.27:1
	   even where they do touch). */
	--btn-focus-ring: var(--primary-color);
	/* Ghost, light sections: dark navy line + text. */
	--btn-ink: var(--primary-color);
	--btn-ink-hover-bg: var(--primary-color);
	--btn-ink-hover-text: var(--white-color);
}
/* Ghost, dark bands: light teal line + text. On the navy the computed
   color-mix resolves to #76cac9 on #0d2440 = 8.23:1 (was 6.46:1 on the green),
   and even raw --accent-color #059e9c now reaches 4.75:1 there instead of the
   old large-text-only 3.72:1. Hover flips to an accent fill with a near-black
   navy ink (the mix resolves to #071423 on #059e9c = 5.63:1). */
.bg-leather, .service-hero, .section--inverse, .cover, .ac-menu, .ac-header{
	--btn-ink: color-mix(in srgb, var(--accent-color) 55%, var(--white-color));
	--btn-ink-hover-bg: var(--accent-color);
	--btn-ink-hover-text: color-mix(in srgb, var(--primary-color) 55%, #000);
	/* On a dark band the solid button needs no drawn edge: the fill itself is the
	   boundary at #10c3cc on #0d2440 = 7.22:1, well past 1.4.11's 3:1. Handing the
	   edge back to the fill keeps the button flat teal on navy exactly as approved,
	   instead of ringing it in a darker teal that would only read as a smudge.
	   The navy focus ring would vanish here, so it flips to the same light teal the
	   ghost ink uses: the mix resolves to #76cac9 on #0d2440 = 8.23:1. */
	--btn-fill-edge: var(--btn-fill);
	--btn-focus-ring: color-mix(in srgb, var(--accent-color) 55%, var(--white-color));
}

/* --- Base (filled sky blue, square) -------------------------------------- */
/* Overrides the parent framework's pill: radius 100px -> 0, and 16px bold
   capitalize -> 13px uppercase letterspaced. */
.btn-default,
.ac-btn{
	position: relative;
	display: inline-flex;
	align-items: center;
	font-family: var(--default-font);
	font-size: 13px;
	font-weight: 600;
	line-height: 1;
	letter-spacing: .2em;
	text-transform: uppercase;
	background: var(--btn-fill);
	color: var(--btn-fill-ink);
	border: 1px solid var(--btn-fill-edge);
	border-radius: 0;
	padding: 20px 56px 20px 28px;
	text-decoration: none;
	cursor: pointer;
	overflow: hidden;
	transition: background .25s ease, color .25s ease, border-color .25s ease;
	z-index: 1;
}
.btn-default:hover,
.ac-btn:hover{
	background: var(--btn-fill-hover);
	border-color: var(--btn-fill-hover);
	color: var(--btn-fill-hover-ink);
}

/* Focus ring. Stated explicitly rather than left to the UA default, because the
   button now carries a 1px #04807e edge of its own and a browser's thin default
   outline sitting flush against it is easy to lose. `outline-offset: 2px` opens a
   gap of page background between edge and ring so the two never merge, and the
   ring is drawn in a different hue family from the edge (navy on light grounds)
   so "focused" is never confused with "just a border". `:focus-visible` only, so
   a mouse click does not paint it. */
.btn-default:focus-visible,
.ac-btn:focus-visible{
	outline: 2px solid var(--btn-focus-ring);
	outline-offset: 2px;
}

/* Arrow — inherited from the parent (\f061 rotated -45deg). Re-anchored to
   the new padding; it straightens on hover, same as the parent. */
.btn-default::before{
	font-size: 13px;
	transform: translate(-28px, -50%) rotate(-45deg);
}
.btn-default:hover::before{ transform: translate(-28px, -50%) rotate(0deg); }

/* Kill the parent's rotating diagonal sweep — the square system fills flat. */
.btn-default::after{ display: none; }

/* --- Variant: ghost outline ------------------------------------------ */
.btn-default.btn-ghost,
.ac-btn--ghost{
	background: transparent;
	color: var(--btn-ink);
	border-color: color-mix(in srgb, var(--btn-ink) 55%, transparent);
}
.btn-default.btn-ghost:hover,
.ac-btn--ghost:hover{
	background: var(--btn-ink-hover-bg);
	border-color: var(--btn-ink-hover-bg);
	color: var(--btn-ink-hover-text);
}

/* --- Variant: small (nav bar, mobile drawer) ------------------------- */
.btn-default.btn-sm,
.ac-btn--sm{
	font-size: 11.5px;
	letter-spacing: .22em;
	padding: 13px 46px 13px 24px;
}
.btn-default.btn-sm::before{ font-size: 11px; transform: translate(-24px, -50%) rotate(-45deg); }
.btn-default.btn-sm:hover::before{ transform: translate(-24px, -50%) rotate(0deg); }

/* --- Mobile: content CTAs span the column ----------------------------- */
/* The button is inline-flex with 28px/56px padding, so a two-word label like
   "Request a Visit" wraps inside a narrow box on a phone. Full width fixes
   that, and the arrow needs no adjustment — it is positioned `right: 0`
   against the button edge, so it simply moves out with it.
   `.btn-sm` is excluded on purpose: those are the nav bar and the mobile
   drawer pair, and the drawer lays its two out with `flex: 1` for a 50/50
   split that a forced 100% width would break. */
@media (max-width: 767px){
	.btn-default:not(.btn-sm){ width: 100%; }
}

/* --- Legacy aliases --------------------------------------------------- */
/* Older parent outline variants map onto .btn-ghost; `.btn-solid` is a no-op
   now that the filled sky-blue button IS the base. Keeps stale markup from resurrecting the pill. */
.btn-default.btn-highlighted,
.btn-default.btn-border,
.btn-default.btn-ghost-dark{
	background: transparent;
	color: var(--btn-ink);
	border-color: color-mix(in srgb, var(--btn-ink) 55%, transparent);
}
.btn-default.btn-highlighted:hover,
.btn-default.btn-border:hover,
.btn-default.btn-ghost-dark:hover{
	background: var(--btn-ink-hover-bg);
	border-color: var(--btn-ink-hover-bg);
	color: var(--btn-ink-hover-text);
}

/* Column gutters — the parent framework.css pins .row > * to a fixed 15px and
   ignores Bootstrap's gx-* gutter classes, so gx-4/gx-5 produce no extra space.
   Restore real gutters (overflow-safe: row negative margin matches column padding). */
.row.gx-4{ margin-left: -20px; margin-right: -20px; }
.row.gx-4 > *{ padding-left: 20px; padding-right: 20px; }
.row.gx-5{ margin-left: -28px; margin-right: -28px; }
.row.gx-5 > *{ padding-left: 28px; padding-right: 28px; }
@media (max-width: 991px){
	.row.gx-4, .row.gx-5{ margin-left: -15px; margin-right: -15px; }
	.row.gx-4 > *, .row.gx-5 > *{ padding-left: 15px; padding-right: 15px; }
}

/* Reusable teal-leather background — drop `.bg-leather` on any section for the
   dark leather look (Meet the Dentists, service sections, CTAs). Sets the
   background + light-on-dark text defaults so it's drop-in anywhere. */
.bg-leather{
	position: relative;
	background:
		linear-gradient(rgba(3,24,28,0.40), rgba(3,24,28,0.58)),
		url('/wp-content/uploads/2026/06/section-leather.avif') center/cover no-repeat;
	color: color-mix(in srgb, var(--white-color) 84%, transparent);
}
/* Light-on-dark text is the whole point of the utility, so it must win over any
   page-scoped color rule (e.g. `.service-page .content-header`) regardless of load order. */
.bg-leather h1, .bg-leather h2, .bg-leather h3,
.bg-leather .content-header, .bg-leather .u-display, .bg-leather .u-title{ color: var(--white-color) !important; }
.bg-leather p, .bg-leather .prose-serif{ color: color-mix(in srgb, var(--white-color) 82%, transparent) !important; }
.bg-leather .eyebrow-text{ color: var(--accent-color) !important; }

/* Offset frame edge — an accent-teal square sits BEHIND the image, shifted 10px left + 10px
   down, so a frame edge peeks at the bottom-left. Drop `.img-frame` on any image
   wrapper (works for <img> and background-image blocks); no bare, plain images. */
.img-frame{ position: relative; display: block; }
.img-frame::before{
	content: ''; position: absolute; inset: 0;
	background: var(--accent-color);
	transform: translate(-10px, 10px);
	z-index: 0; pointer-events: none;
}
.img-frame > *{ position: relative; z-index: 1; }

/* Inline prose links ---------------------------------------------------
   The framework base rule is `a{ text-decoration: none; }` with NO colour, so a
   link written inside body copy inherits the paragraph colour and renders as
   plain, unclickable-looking text. Every service page that has ever needed an
   inline link has re-declared the same fix in its own `_ac_custom_css`; this is
   that fix, once, for the whole child theme. Scoped to `.service-page` so it
   only touches editorial prose — nav, footer, buttons and whole-card links keep
   their own treatments. Page CSS prints at wp_head:99, after this file, so a
   page can still override it. */
.service-page .prose-serif a{
	color: var(--accent-color);
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1px;
	transition: color 0.3s ease;
}
.service-page .prose-serif a:hover{ color: var(--primary-color); }
/* On the leather band the body copy is already a muted near-white (set
   !important above), so the light-section hover would all but disappear —
   flip the hover to full white instead. */
.service-page .bg-leather .prose-serif a:hover{ color: var(--white-color); }

/* --- Partner / affiliation logo reel --------------------------------
   The `service-reel` Anchor Block, injected directly beneath the hero on every
   page and service by cd_inject_logo_reel() (functions.php). It used to be
   markup pasted into home.content.html and about-us.content.html and styled
   page-locally in home.custom.css; it is one block now, so its CSS is site-wide
   here for the same reason .soc-cta / .soc-revband are.

   WHITE ON PURPOSE. The hero above it is --soc-cream and the band under it is
   white; that cream → white step is the sectioning the client asked for, so
   this band must not take the cream too or the seam disappears.

   THE LOGO SIZING IS A DEFECT FIX, NOT DECORATION. The six affiliation files
   are wildly different intrinsic sizes — 3.webp is 1275x349 (3.65:1), 6.webp
   582x304, 5.webp 772x965 (PORTRAIT), against 300x167 for others. Sized by
   height alone the 3.65:1 one ran to 190px wide at h=52 and collided with its
   neighbours. `height` + `max-width` + `object-fit: contain` gives them a
   common optical height with a hard width cap: a logo wider than the cap
   letterboxes inside it at its true aspect ratio rather than being stretched,
   and no item can grow into the next. `flex: 0 1 auto` + `justify-content:
   space-between` then distributes what is left.
   ------------------------------------------------------------------- */
.partner-reel{
	background: var(--white-color);
	padding: 54px 0;
	border-bottom: 1px solid color-mix(in srgb, var(--primary-color) 10%, transparent);
}
/* The same rail the masthead uses (chrome.css: max-width 1440, padding 0 56),
   so the first logo starts on the header logo's left edge — 136px at 1600. */
.partner-reel__inner{ max-width: 1440px; margin: 0 auto; padding: 0 56px; }
.logo-reel{
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
	gap: 26px 40px;
}
.logo-reel__item{ flex: 0 1 auto; display: flex; align-items: center; justify-content: center; }
.logo-reel__img{
	display: block; width: auto; height: 56px; max-width: 150px; object-fit: contain;
	filter: grayscale(1); opacity: .62;
	transition: filter var(--transition-base) ease, opacity var(--transition-base) ease;
}
.logo-reel__item:hover .logo-reel__img{ filter: grayscale(0); opacity: 1; }
@media (max-width: 1200px){
	.logo-reel__img{ height: 46px; max-width: 124px; }
}
@media (max-width: 991px){
	.partner-reel{ padding: 40px 0; }
	.partner-reel__inner{ padding: 0 24px; }
}
@media (max-width: 767px){
	.partner-reel{ padding: 30px 0; }
	.logo-reel{ justify-content: center; gap: 22px 28px; }
	.logo-reel__img{ height: 38px; max-width: 104px; }
}

/* --- Card grids become a peek-slider on small screens ----------------
   Every icon-card grid on the site is the same shape: a Bootstrap `.row`
   whose columns each hold one `.ac-panel` card (care-card, jaw-card,
   promise-card, pillar-card, phase-card, correct-card, advantage-card).
   Rather than tag each one, this matches on that shape with `:has()`, so
   card grids added later pick the behaviour up with no markup change.

   The cards are deliberately narrower than the track: the sliver of the
   next card IS the affordance that tells you it swipes. CSS scroll-snap
   does the whole job, so there is no JS and nothing to measure on load. */
@media (max-width: 991px){
	.row:has(> [class*="col-"] > .ac-panel){
		flex-wrap: nowrap;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x mandatory;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
		padding-bottom: 6px;
	}
	.row:has(> [class*="col-"] > .ac-panel)::-webkit-scrollbar{ display: none; }
	.row:has(> [class*="col-"] > .ac-panel) > [class*="col-"]{
		flex: 0 0 46%;
		max-width: 46%;
		scroll-snap-align: start;
	}
	/* Cards keep a common height so the track reads as one row, not a ragged edge. */
	.row:has(> [class*="col-"] > .ac-panel) > [class*="col-"] > .ac-panel{ height: 100%; }
}
@media (max-width: 575px){
	.row:has(> [class*="col-"] > .ac-panel) > [class*="col-"]{
		flex: 0 0 84%;
		max-width: 84%;
	}
}

/* --- Icon card padding ------------------------------------------------
   The framework's .ac-panel ships 40px, which is generous for these short
   icon cards and costs a lot of height once they become a mobile slider.
   Scoped to the dental card variants rather than .ac-panel itself, because
   .ac-panel also backs blog items (which use --flush) and CTA boxes, and a
   bare .ac-panel override here would win over those on source order. */
.care-card,
.jaw-card,
.promise-card,
.pillar-card,
.phase-card,
.correct-card,
.advantage-card{ padding: 20px; }

/* --- Image-right rows lead with the image on mobile -------------------
   Half the split rows on the site are copy-left / image-right, so the copy
   comes first in the DOM and the image drops below it once the row stacks.
   That buries the visual and makes every one of those sections open with a
   wall of text. Where the image sits in the LAST column, pull it above the
   copy on small screens so a split row always leads with its image.

   `order` rather than `flex-direction: column-reverse`: .row carries
   `flex-wrap: wrap`, and wrapping along the column axis is a different
   (and messier) behaviour than wrapping along the row axis. Moving the one
   column is equivalent here and leaves the wrap model alone. */
@media (max-width: 991px){
	.row:has(> [class*="col-"]:last-child .img-frame) > [class*="col-"]:last-child,
	.row:has(> [class*="col-"]:last-child .media) > [class*="col-"]:last-child{
		order: -1;
	}
}

/* --- Service intro band ----------------------------------------------
   The editorial opener on every service page: rotated service name on the
   spine, eyebrow + heading in the main column, a hairline, then the lead
   prose and the enquiry form. Mirrors the home page's philosophy block,
   which is where the pattern was set — but lives here, in the theme, so
   all 13 pages share one definition instead of 13 copies of the old
   ornate-frame CSS. */
.svc-intro{ background: #fff; }
.svc-intro__grid{
	display: grid;
	grid-template-columns: 58px 1fr 1px 420px;
	gap: 0 56px;
	align-items: start;
}
.svc-intro__spine{
	writing-mode: vertical-rl; transform: rotate(180deg);
	font-family: var(--default-font); font-size: 11px; letter-spacing: .24em;
	text-transform: uppercase; color: color-mix(in srgb, var(--primary-color) 45%, transparent);
	justify-self: start; white-space: nowrap;
}
.svc-intro__title{
	font-family: var(--heading-font); font-weight: 500;
	font-size: clamp(34px, 3.75vw, 54px); line-height: 1.08;
	color: var(--primary-color); margin: 0;
}
.svc-intro .eyebrow-text{ margin-bottom: 18px; }
/* Hairline column — stretches to the tallest cell in the row. */
.svc-intro__rule{ background: color-mix(in srgb, var(--primary-color) 14%, transparent); align-self: stretch; }
.svc-intro__side .prose-serif{ font-size: 19px; margin: 0 0 18px; }
.svc-intro__side .prose-serif:last-child{ margin-bottom: 0; }
.svc-intro__form{ margin-top: 56px; }

@media (max-width: 1200px){
	.svc-intro__grid{ grid-template-columns: 40px 1fr 1px 340px; gap: 0 36px; }
}
@media (max-width: 991px){
	.svc-intro__grid{ grid-template-columns: 1fr; gap: 28px; }
	/* The spine and hairline are decorative framing; at one column they have
	   nothing to frame, and the rotated text would eat a full line of height. */
	.svc-intro__spine, .svc-intro__rule{ display: none; }
	.svc-intro__form{ margin-top: 40px; }
}

/* --- Contact card footer: privacy note, divider, follow ---------------- */
.contact-aside{ margin-top: 30px; text-align: center; }
.contact-aside__privacy{
	font-family: var(--default-font); font-size: 13.5px; line-height: 1.6;
	color: color-mix(in srgb, var(--primary-color) 62%, transparent);
	margin: 0 auto; max-width: 52ch;
}
.contact-aside__divider{
	border: 0; height: 1px; margin: 26px auto;
	background: color-mix(in srgb, var(--primary-color) 14%, transparent);
}
.contact-aside__follow .eyebrow-text{ margin-bottom: 14px; }
.contact-aside__social{ display: flex; justify-content: center; align-items: center; gap: 14px; }
.cd-social__link{
	display: inline-flex; align-items: center; justify-content: center;
	width: 42px; height: 42px; border-radius: 50%;
	border: 1px solid color-mix(in srgb, var(--primary-color) 20%, transparent);
	color: var(--primary-color);
	transition: background .25s ease, color .25s ease, border-color .25s ease;
}
.cd-social__link:hover{
	background: var(--primary-color); color: var(--white-color);
	border-color: var(--primary-color);
}

/* Footer legal row — sits beside the copyright line. */
.ac-footer__legal a{ color: inherit; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color .2s ease, color .2s ease; }
.ac-footer__legal a:hover{ color: var(--accent-color); border-bottom-color: var(--accent-color); }
.ac-footer__legal span{ opacity: .45; margin: 0 4px; }

/* =====================================================================
   GLOBAL CTA + REVIEWS BAND  (anchor_block `service-cta`)
   Markup: docs/soc/blocks/service-cta.html
   Rendered site-wide by ac_block('cta') immediately before the footer, so it
   is styled HERE and never in a per-page _ac_custom_css.

   NO BARE h1-h6 SELECTORS and no hardcoded brand hex — tokens only. The
   headline pair are DIVs carrying `cta-header` / `cta-subheader`; those classes
   are tag-agnostic by design, which is exactly why the CTA headline does not
   need to be (and is not) a heading element.

   TWO GROUNDS, AND THE ACCENT TOKEN INVERTS BETWEEN THEM.
   The accent is background-dependent: on navy `--accent-color` passes and
   `--accent-ink` fails; on the light surface the reverse is true. Measured with
   bin/contrast.py, all AA-normal (4.5:1) unless noted:

     PANEL 1, ground --primary-color #0d2440 (navy)
       #FFFFFF  title            15.63:1  PASS
       #DDE4EE  --on-dark  sub   12.21:1  PASS
       #10c3cc  --on-dark-accent  7.22:1  PASS   (phone link)
       #0d2440  on #10c3cc btn    7.22:1  PASS   (navy ink on the fill; never
                                                  white ink on this button)
       #A5B3C6  --on-dark-muted   7.34:1  PASS   (eyebrow, unused here)
     PANEL 2, ground --secondary-color #F7F9FC (cool near-white)
       #0d2440  title            14.82:1  PASS
       #04807e  --accent-ink     4.53:1   PASS   (accent words + eyebrow)
       #4E5560  --text-color     7.13:1   PASS

   `--accent-color` #059e9c on #F7F9FC is only 3.12:1, so it is NOT used for
   text on panel 2 — only as a solid decorative fill (the rule + the diamond),
   which carries no text. No color-mix() is used on any text color here: a
   previous accent color-mix composited to 2.74:1 and failed even the 3:1 bar.
   ===================================================================== */

/* --- Panel 1: the CTA, on navy ---------------------------------------- */
.soc-cta{ background-color: var(--primary-color); }
.soc-cta__inner{
	display: flex; flex-direction: column; align-items: center;
	text-align: center; gap: 18px;
}
.soc-cta__title{
	font-family: var(--heading-font);
	font-size: clamp(28px, 3.4vw, 44px); font-weight: 600; line-height: 1.2;
	color: var(--white-color); margin: 0; max-width: 20ch;
}
.soc-cta__title strong{ font-weight: 700; }
.soc-cta__sub{
	font-family: var(--default-font);
	font-size: clamp(17px, 1.5vw, 21px); font-weight: 500; line-height: 1.4;
	color: var(--on-dark); margin: 0;
}
.soc-cta__phone{
	color: var(--on-dark-accent); text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: border-color .2s ease;
}
.soc-cta__phone:hover,
.soc-cta__phone:focus-visible{ border-bottom-color: var(--on-dark-accent); }
/* .section--inverse would paint any nested <a> white; the phone link is the one
   deliberate exception, so it re-asserts the on-dark accent at higher weight. */
.soc-cta.section--inverse a.soc-cta__phone{ color: var(--on-dark-accent); }
.soc-cta__btn{ margin-top: 10px; }

/* --- Panel 2: the reviews, on the light surface ------------------------ */
.soc-reviews{ background-color: var(--secondary-color); }
.soc-reviews__head{
	display: flex; flex-direction: column; align-items: center;
	text-align: center; margin-bottom: 44px;
}
.soc-reviews__rule{
	display: block; width: 8px; height: 8px; margin-bottom: 20px;
	background: var(--accent-color); transform: rotate(45deg);
}
.soc-reviews__eyebrow{ font-size: 12px; color: var(--accent-ink); margin-bottom: 14px; }
.soc-reviews__title{
	font-family: var(--heading-font);
	font-size: clamp(26px, 3vw, 40px); font-weight: 600; line-height: 1.2;
	color: var(--primary-color); margin: 0;
}
.soc-reviews__accent{ color: var(--accent-ink); }
.soc-reviews__widget{ max-width: 1180px; margin: 0 auto; }

@media (max-width: 767px){
	.soc-reviews__head{ margin-bottom: 32px; }
	.soc-cta__inner{ gap: 14px; }
}

/* =====================================================================
   REVIEWS BAND — .soc-revband  (anchor_block `service-reviews`)
   ---------------------------------------------------------------------
   The band described in docs/soc/blocks/service-reviews.html. It is a PORT of
   two reference patterns, not a paraphrase of them:

     · the section head from `.hv2-svc__head` — eyebrow above the heading, the
       action set to the RIGHT of the heading rather than centred beneath it;
     · the overlap from `.hv2-close` — an opaque card set OVER the edge of the
       panel behind it. Here the panel is the dark band and the card holds the
       Trustindex widget, so the card begins inside the navy and extends down
       into the light section below it. That straddle is the whole effect.

   IT LIVES HERE, not in a page's _ac_custom_css, because a block is shared
   markup — the same reason .soc-cta / .soc-reviews above live here. Every
   selector is prefixed .soc-revband, a class no other template or page body
   uses, so it cannot reach the 27 .service-page bodies (or anything else).

   THE CARD IS A CORRECTNESS FIX, NOT DECORATION. Trustindex renders its review
   cards with TRANSPARENT backgrounds and its own dark body text. The previous
   revision put the widget straight onto the navy, which rendered the review
   copy as dark ink on a dark ground — unreadable. The opaque white card is what
   makes it legible.

   And the card must stay OUT of `.section--inverse`, which paints every
   descendant text element --white-color (primitives.css). Inside it the
   widget's dark text would be repainted white on a white card and would be
   invisible again in the other direction. `.section--inverse` is therefore on
   `.soc-revband__band` only, which contains no widget.

   GROUNDS AND RATIOS. Measured with bin/contrast.py; AA normal is 4.5:1, large
   3:1, and a control boundary 3:1 (WCAG 1.4.11).

   A · THE DARK HALF — --primary-color #0d2440 navy. Deliberately NOT
       --primary-deep #081829, which is --footer-bg: this band is the last thing
       before the footer, and matching the footer exactly would erase the seam.

         #FFFFFF  --white-color      15.63:1  PASS   (the h2)
         #10c3cc  --on-dark-accent    7.22:1  PASS   (the eyebrow — small
                                                      letter-spaced caps, so it
                                                      needs the normal bar)
         #10c3cc  button FILL on the band  7.22:1    PASS the 3:1 non-text bar —
                                                     the control is delineated
                                                     from its background before
                                                     --btn-fill-edge is considered
         #0d2440  --button-text-color on the #10c3cc fill  7.22:1  PASS (navy ink
                                                     on the light fill; never
                                                     white ink on this button)

       --accent-ink #04807e is 3.27:1 on this navy and FAILS, so it is not used
       here. The accent token inverts by ground: --accent-ink is the
       light-surface token, --on-dark-accent the dark-band one. Do not collapse
       them.

   B · THE LIGHT HALF — --secondary-color #F7F9FC, the theme's standard light
       section ground (it is what `.section--muted` paints). It is NOT the home
       page's cream. That WAS a hard constraint — --soc-cream used to be declared
       page-locally on `.home-page` and this block renders OUTSIDE that element
       (front-page.php echoes it after ac_render_raw_content), so the var
       resolved to nothing here. As of the 2026-08-12 hero unification the token
       is on :root and would resolve, but the choice stands on its own merits:
       --secondary-color is the theme's standard light section ground, it is what
       `.section--muted` paints, and at #F7F9FC against the #FBF9F5 the page's
       own closing band uses two sections up the difference is not perceptible.
       Nothing here needs changing; this note is kept so the next reader does not
       "fix" a constraint that has already been lifted.

   C · THE CARD — --white-color on that light half. Text sitting on it (whatever
       Trustindex renders) has the full white ground: #4E5560 --text-color
       7.52:1, #0d2440 --primary-color 15.63:1, #04807e --accent-ink 4.78:1, all
       PASS. The card's own edge against the light half is only 1.05:1 — a card
       is not a control and has no 1.4.11 duty, but it still has to READ as a
       card, so it gets a neutral --divider-color hairline and --shadow-strong
       rather than being left to a 1.05:1 seam. The overlap is what actually
       delineates it: the card's top sits on the navy at 15.63:1.
   ===================================================================== */

/* The two halves. The dark one's bottom padding is not whitespace for its own
   sake — it is the ground the card overlaps into, so it must exceed the card's
   negative top margin or the card would collide with the heading. 190 - 130
   leaves a 60px navy gap between the head and the card's top edge. */
/* GEOMETRY MEASURED OFF THE REFERENCE, 2026-08-12. The client supplied a
   screenshot of the band this one is modelled on beside a screenshot of ours;
   the three numbers below are read from that comparison rather than guessed.
     reference dark half   ~100px above the eyebrow, ~190px below the title
     reference card        straddles the seam ~40% inside / ~60% below
     reference card width  the full page rail — the card is NOT inset
   Ours had the right idea and the wrong proportions: a 76px top pad made the
   dark half read as a strip rather than a band, and the card was held to
   `max-width: 1180px` inside a 1328px rail, so it sat visibly narrower than
   the head above it and the straddle looked like a mistake instead of a move.
   190px of bottom padding is retained: it is the ground the card overlaps
   into and MUST stay larger than the card's negative top margin. */
/* PORTED FROM THE REFERENCE'S ACTUAL SOURCE, NOT FROM A SCREENSHOT.
   `.hv2-revs` is an Anchor Block in the Princeton install — post 27406618's
   `_ac_custom_css` in the `frameworkprinceton` database. It is not in that
   theme's files, which is why earlier revisions of this component "could not
   find it" and reconstructed the geometry by measuring a screenshot. Every
   value below is the reference's own:

       .hv2-revs      { background: var(--secondary-color); }
       .hv2-revs__band{ background: var(--primary-color); padding: 96px 0 190px; }
       .hv2-revs__card{ position: relative; z-index: 2; margin-top: -150px;
                        background: var(--white-color); border-radius: 0;
                        padding: 26px;
                        box-shadow: 0 26px 60px rgba(0, 40, 45, .16);
                        min-height: 120px; }
       @media (max-width: 767px){
         .hv2-revs__band{ padding: 64px 0 150px; }
         .hv2-revs__card{ margin-top: -120px; padding: 16px; }
       }

   THE LIGHT GROUND IS ON THE SECTION, AND THERE IS NO `__below` ELEMENT.
   That is not tidiness, it is what makes the pull work — see the note on
   `.soc-revband__card`. The dark band is the FIRST child; the card's rail is
   the second.

   190 - 150 = 40px of navy still showing between the heading and the card's
   top edge. The bottom padding MUST stay larger than the card's pull at every
   breakpoint or the card collides with the heading. Change one, re-check the
   other: 190/150 desktop, 150/120 at 767. */
/* ==== THE LIGHT HALF IS WHITE, AND IT MATCHES WHAT FOLLOWS ====
   CLIENT, 2026-08-19: "The reviews sit over the cream background, and then the
   next section is white. Please just make these white so they flow easier."

   They were right, and the previous note here had the reasoning backwards. It
   argued for cream on the grounds that the light half "meets an authored
   section instead of the footer and has to agree with the site's warm ground" —
   but the authored section it meets is `.soc-phil`, which declares no ground at
   all and therefore renders white. Measured in place: the run down the home
   page was WHITE (`.soc-drs`) -> NAVY -> CREAM -> WHITE (`.soc-phil`), so the
   cream strip under the card was the only warm band in the sequence and read as
   a stripe rather than as a ground. White makes it navy -> white, and the card's
   overhang lands on a ground that simply continues into the next section.

   THIS IS THE ONLY BAND THAT NEEDED IT. Two other review shapes exist and both
   were checked before changing anything: `.soc-reviews` (34 pages) sits between
   a navy band and the footer on --secondary-color, which is a deliberate hard
   seam and the last band on the page; `.section.reviews` (contact, testimonials)
   is already white and is followed by the dark `.visit` strip. Neither has the
   defect and neither is touched.

   IF THIS BAND EVER MOVES somewhere a cream or tinted section follows it, match
   that ground here — the rule is that the light half agrees with whatever comes
   next, not that it is any particular colour. */
.soc-revband{ background-color: var(--white-color); }
.soc-revband__band{ background-color: var(--primary-color); padding: 96px 0 190px; }

/* THE PADDING STAYS, and it is now doing only one job. `.hv2-revs` in the
   reference carries none, because there the next section shared the band's
   ground and the card's overhang simply landed on it. That is now true here too
   — but the padding is what stops the card's lower edge butting straight into
   `.soc-phil`'s first line, which is the "no space between things" defect fixed
   site-wide on 2026-08-18. It is whitespace, not a colour, and it must not go
   below the card's own pull at any breakpoint. */
.soc-revband{ padding-bottom: 100px; }

.soc-revband__head{
	display: flex; align-items: end; justify-content: space-between;
	flex-wrap: wrap; gap: 28px 48px; margin-bottom: 0;
}
/* (0,3,0) so it beats `.section--inverse .eyebrow-text` (0,2,0), which would
   otherwise paint this eyebrow plain white and lose the accent. */
.soc-revband__band.section--inverse .soc-revband__eyebrow{
	font-size: 12px; color: var(--on-dark-accent); margin-bottom: 14px;
}
/* The parent prints a brand-mark icon before every eyebrow; drop it so the
   label reads as plain letter-spaced caps, as the reference's do. */
.soc-revband .soc-revband__eyebrow::before{ display: none; }
.soc-revband__title{
	font-family: var(--heading-font);
	font-size: clamp(28px, 3.2vw, 44px); font-weight: 600; line-height: 1.14;
	color: var(--white-color); margin: 0; max-width: 18ch;
}
.soc-revband__btn{ flex: 0 0 auto; }

/* THE OVERLAP. A negative top margin on the FIRST child of the light half,
   which is the `.hv2-close` move expressed in flow rather than with absolute
   positioning: the card is pulled 130px up so its top 130px sit on the navy and
   the rest extends down into the light band. `position: relative` + z-index
   states the paint order explicitly rather than relying on the tree order that
   already happens to give the right answer. Nothing here may take
   `overflow: hidden` — that would clip the overhang and kill the effect. */
.soc-revband__card{
	/* THE PULL, AND WHY IT NEEDS THE BAND TO SIT FIRST.
	   A negative top margin on the FIRST in-flow child of a block collapses
	   through that block's top edge and moves the PARENT rather than the child.
	   An earlier revision wrapped this card in a `.soc-revband__below` half and
	   made it that half's first child, so this -150px dragged the entire light
	   half up: the dark band lost 150px of its bottom padding, and the card came
	   to rest exactly ON the seam with zero overlap. No amount of re-tuning the
	   two numbers could fix it, because every extra pixel of pull ate another
	   pixel of navy.
	   The reference has no lower-half element at all. The dark band is the FIRST
	   child of the section and this card's rail is the SECOND, so the margin has
	   nowhere to escape to and does exactly what it reads like it does. Do not
	   reintroduce a wrapper that makes this card a first child.
	   Nothing here may take `overflow: hidden` either — it would clip the 150px
	   that now deliberately hangs outside the band. */
	position: relative; z-index: 2;
	margin-top: -150px;
	background: var(--white-color);
	border-radius: 0;
	padding: 26px;
	box-shadow: 0 26px 60px rgba(0, 40, 45, .16);
	min-height: 120px;
}
/* THE WIDGET SLOT IS RESERVED, and that is load-bearing rather than cosmetic.
   Trustindex hydrates from cdn.trustindex.io AFTER paint: until it does, this
   div has no content and the card collapses to its padding (~90px). At 90px the
   card is SHORTER than the 130px it is pulled up by, so it would sit wholly
   inside the navy and the straddle — the entire point of the composition —
   would not exist until the third-party script happened to land. Reserving the
   slot makes the geometry a property of the CSS instead of a property of a
   remote script's timing, and it removes the layout shift when the widget does
   arrive. It is also what the reference pattern does: `.hv2-close__inner`
   carries a `min-height: 520px` for the same reason.

   320px is the shortest the three-column slider layout renders at; the widget
   grows past it freely, since this is a MIN. */
/* THE WIDGET'S FLOOR IS ON THE CARD, matching the reference's
   `min-height: 120px` — there is no separate widget wrapper any more.
   It exists because Trustindex hydrates from cdn.trustindex.io AFTER paint:
   until it lands the card has no content and collapses to its padding, and a
   card shorter than its own 150px pull would sit wholly inside the navy with no
   straddle at all until a third-party script happened to arrive. 120px keeps
   the geometry a property of this file rather than of a remote script's timing.
   It is a MIN — the widget grows past it freely — so do NOT raise it to
   "reserve space"; a tall floor only creates dead white space under the
   reviews, which is exactly the defect the client reported once already. */

/* The reference declares ONE breakpoint for this component, at 767. Its 991
   step existed only in our reconstruction; it is dropped so the two agree.
   150 - 120 = 30px of navy above the card, the same relationship the desktop
   pair holds. */
@media (max-width: 767px){
	.soc-revband__head{ align-items: flex-start; gap: 22px; margin-bottom: 0; }
	.soc-revband__band{ padding: 64px 0 150px; }
	.soc-revband__card{ margin-top: -120px; padding: 16px; }
	.soc-revband{ padding-bottom: 60px; }
}


/* =====================================================================
   FALLBACK PAGE-HEAD BAND — .cd-title-band
   ---------------------------------------------------------------------
   Emitted by cd_fallback_h1() (functions.php) ONLY on a page or `service`
   whose own body renders no <h1>. Today that is the 33 not-yet-authored
   URLs; each one loses this band automatically the moment it is authored
   with a real hero, so nothing here needs removing later.

   It deliberately borrows the authored heroes' treatment rather than
   inventing one: the same display face and weight, the same
   clamp(38px, 4.2vw, 60px) ramp, the same navy on the same cool
   near-white ground as `.soc-hero__title` (see e.g.
   docs/soc/content/testimonials.custom.css). The element itself carries
   the framework's `.hero-header`, so it also inherits --heading-font from
   primitives.css; the size is re-stated here because --fs-display (80px,
   flat) is tuned for a full-bleed hero and reads as shouting on a band
   this shallow.

   CONTRAST: --primary-color #0d2440 on --secondary-color #F7F9FC =
   14.82:1 PASS (measured with bin/contrast.py). No hardcoded hex — tokens
   only — and no bare h1 selector: the rule hangs off the class.
   ===================================================================== */
.cd-title-band{
	background-color: var(--secondary-color);
	border-bottom: 1px solid var(--divider-color);
}
.cd-title-band__title{
	font-size: clamp(38px, 4.2vw, 60px);
	font-weight: 600;
	line-height: 1.06;
	color: var(--primary-color);
	margin: 0;
}



/* =====================================================================
   UNIFIED SUBPAGE HERO — `.dc-page:not(.home-page) .soc-hero`
   ---------------------------------------------------------------------
   ONE hero for all 36 non-home URLs (client, 2026-08-12, verbatim: "every
   other page that's not the home page can just literally use the home page's
   setup … it doesn't use that cream background like it should … I would like
   to unify them and maybe have it be a minimum height of 50vh … It should have
   the H1 and the content in the buttons align left, just like the home page").

   WHAT WAS WRONG. /testimonials/ measured at 1600px: header logo left edge
   136px, hero <h1> left edge 265px — 129px out. Hero ground rgb(255,255,255)
   where the home page's is the cream. Hero min-height a fixed 682px. The 27
   service heroes were a third variant again, aligned to `.container` (165px at
   1600) by a `padding-left: max(15px, calc(96.153846% - 635px))` nudge. Three
   mechanisms, three answers, none of them the header's.

   WHAT REPLACES THEM. This block, and only this block. The per-page copies in
   about-us / contact / services / patient-financing / testimonials
   `_ac_custom_css` are deleted, and so is the `.service-page .soc-hero*` block
   that used to sit further up this file, nudge included. Nothing is layered on
   top of anything: there is one rule set and one alignment mechanism.

   SCOPE. `.dc-page:not(.home-page)` is (0,3,0). The home page carries
   `.home-page` on the same wrapper and is excluded by the `:not()`, so its
   full-height hero is untouched. The specificity is deliberate as well as
   descriptive: `_ac_custom_css` prints at wp_head 99, AFTER this sheet, so a
   leftover (0,2,0) `.testimonials-page .soc-hero` in some page's meta would
   otherwise win on source order. At (0,3,0) this wins on specificity instead,
   which makes the deletions hygiene rather than a load-bearing step.

   THE ALIGNMENT. The target is the masthead's content edge, which chrome.css
   sets with `max-width: 1440px; padding: 0 56px` on `.ac-masthead__inner`:

       L = max(56px, (W - 1440px) / 2 + 56px)   =   max(56px, W/2 - 664px)

   W is the VIEWPORT CONTENT width. `100vw` is the wrong way to get it — it
   includes the classic scrollbar, so it would misalign by half a scrollbar on
   Windows/Linux. `50cqw` is the right way: the hero is full-bleed, so its own
   inline size IS W, and a container query unit is measured from the container's
   content box with no scrollbar in it. `container-type: inline-size` on the
   hero is what makes `cqw` resolve to it.

   That also decouples the alignment from the 52fr/48fr split: the retired
   percentage identity (`96.153846% of the 52fr track`) had to be re-derived
   whenever the ratio moved. This one does not.

   HEIGHT. `min-height: 50svh` per the client's "minimum height of 50vh". svh
   rather than vh to match the home page hero, which is already on svh so mobile
   browser chrome cannot push the note badge below the fold.

   GROUND. --soc-cream, now a :root token (see the note at the top of this
   file). Ratios on it, measured with bin/contrast.py — AA normal 4.5:1,
   large 3:1, control boundary 3:1:

     #0d2440  --primary-color  on #FBF9F5  14.86:1  PASS  (the h1)
     #4E5560  --text-color     on #FBF9F5   7.15:1  PASS  (the lead)
     #04807e  --accent-ink     on #FBF9F5   4.54:1  PASS  (the eyebrow, which is
                                                    SMALL letter-spaced caps and
                                                    therefore needs the 4.5 bar,
                                                    not the 3.0 one)
     #059e9c  --accent-color   on #FBF9F5   3.13:1  LARGE ONLY — so it is used
                                                    for the textlink's 1px RULE
                                                    (a decoration under a link
                                                    whose own label is 15.63:1)
                                                    and for nothing textual
     #10c3cc  button FILL      on #FBF9F5   2.06:1  — not a text pairing. The
                                                    control's boundary is
                                                    --btn-fill-edge #04807e at
                                                    4.54:1, clearing 1.4.11's
                                                    3:1 bar on this ground.
   The note badge keeps its navy ground, where the accent token INVERTS:
     #10c3cc  --on-dark-accent on #0d2440   7.22:1  PASS  (label + link)
     #FFFFFF  --white-color    on #0d2440  15.63:1  PASS  (body)
   --accent-ink would be 3.27:1 there and is not used on the badge.
   ===================================================================== */
.dc-page:not(.home-page) .soc-hero{
	display: grid; grid-template-columns: 52fr 48fr; align-items: stretch;
	background: var(--soc-cream);
	min-height: 50svh;
	/* Makes 1cqw = 1% of this element's own inline size — see THE ALIGNMENT. */
	container-type: inline-size;
}
/* `.soc-hero--compact` is carried by 30 of the 33 authored bodies and used to
   mean "shorter than the full-viewport hero". The unified floor IS the short
   one, so the modifier now has nothing left to say; it is kept as a no-op
   rather than stripped from 30 page bodies for no rendered difference. */
.dc-page:not(.home-page) .soc-hero--compact{ min-height: 50svh; }
.dc-page:not(.home-page) .soc-hero__copy{
	display: flex; flex-direction: column; justify-content: center;
	min-width: 0;
	padding: 76px 56px 76px max(56px, calc(50cqw - 664px));
}
/* SMALL letter-spaced caps → --accent-ink #04807e on #FBF9F5 = 4.54:1 PASS.
   Both class names are matched: the Phase 2 and service bodies write
   `.soc-hero__eyebrow`, the home body writes `.eyebrow`, and about-us/services
   copied the home body. */
.dc-page:not(.home-page) .soc-hero__eyebrow,
.dc-page:not(.home-page) .soc-hero .eyebrow{
	font-family: var(--mono-font); font-size: var(--fs-eyebrow);
	font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
	color: var(--accent-ink);
	margin: 0 0 22px;
	text-box-trim: trim-start;
	text-box-edge: cap alphabetic;
}
/* The parent prints a brand-mark icon before `.eyebrow-text`; nothing here
   wants it. */
.dc-page:not(.home-page) .soc-hero__eyebrow::before,
.dc-page:not(.home-page) .soc-hero .eyebrow::before{ display: none; }
/* --primary-color #0d2440 on #FBF9F5 = 14.86:1 PASS. */
.dc-page:not(.home-page) .soc-hero__title{
	font-family: var(--heading-font);
	font-size: clamp(38px, 4.2vw, 60px); font-weight: 600; line-height: 1.06;
	color: var(--primary-color); margin: 0;
}
/* The italic accent word, where a body uses one. --accent-color #059e9c is
   3.13:1 on the cream — LARGE TEXT ONLY — which this clears: the title clamps
   to 38px minimum. */
.dc-page:not(.home-page) .soc-hero__title em{ font-style: italic; color: var(--accent-color); }
/* --text-color #4E5560 on #FBF9F5 = 7.15:1 PASS. Some bodies make the lead an
   h2 (outline requirement), so the rule may not assume a tag. */
.dc-page:not(.home-page) .soc-hero__lead{
	font-family: var(--mono-font); font-size: clamp(17px, 1.6vw, 21px);
	font-weight: 400; line-height: 1.55; text-transform: none;
	color: var(--text-color); margin: 28px 0 0; max-width: 44ch;
}
.dc-page:not(.home-page) .soc-hero__actions{
	display: flex; align-items: center; gap: 36px; margin-top: 40px; flex-wrap: wrap;
}
/* Text link with an accent rule — NOT a button. The button system is untouched;
   this block may only set margins on `.btn-default`. */
.dc-page:not(.home-page) .soc-hero__textlink,
.dc-page:not(.home-page) .soc-hero .soc-textlink{
	display: inline-flex; align-items: center; gap: 12px;
	font-family: var(--default-font); font-size: 13px; font-weight: 600;
	letter-spacing: .2em; text-transform: uppercase; text-decoration: none;
	color: var(--primary-color);
	padding-bottom: 8px; border-bottom: 1px solid var(--accent-color);
	transition: color var(--transition-base) ease, border-color var(--transition-base) ease;
}
.dc-page:not(.home-page) .soc-hero__textlink:hover,
.dc-page:not(.home-page) .soc-hero .soc-textlink:hover{ color: var(--accent-ink); border-color: var(--accent-ink); }
.dc-page:not(.home-page) .soc-hero__textlink:focus-visible,
.dc-page:not(.home-page) .soc-hero .soc-textlink:focus-visible{ outline: 3px solid var(--accent-ink); outline-offset: 4px; }
.dc-page:not(.home-page) .soc-hero__arw,
.dc-page:not(.home-page) .soc-hero .soc-arw{ font-size: 14px; line-height: 1; }

/* MEDIA — copy left, photo bleeding to the right edge. No floor of its own: the
   grid row is sized by `.soc-hero` above, and a min-height here would override
   that. */
.dc-page:not(.home-page) .soc-hero__media{ position: relative; background: var(--primary-color); }
.dc-page:not(.home-page) .soc-hero__media img{
	position: absolute; inset: 0; width: 100%; height: 100%;
	object-fit: cover; object-position: center 32%; display: block;
}
.dc-page:not(.home-page) .soc-hero__note{
	position: absolute; left: 0; bottom: 0; z-index: 2;
	background: var(--primary-color); color: var(--white-color);
	padding: 18px 22px; width: min(400px, calc(100% - 32px));
}
/* DARK GROUND — the token inverts. --on-dark-accent #10c3cc on the navy
   --primary-color #0d2440 = 7.22:1 PASS; --accent-ink would be 3.27:1 FAIL.
   NO margin-bottom and line-height 1.2: the client set exactly this in devtools
   on 2026-08-12 and asked for it to be applied. The 8px gap that was here made
   the two-line label sit oddly far from its body copy at narrow widths. */
.dc-page:not(.home-page) .soc-hero__note-label{
	display: block; font-family: var(--mono-font); font-size: var(--fs-eyebrow);
	font-weight: 600; letter-spacing: .18em; line-height: 1.2;
	text-transform: uppercase;
	color: var(--on-dark-accent);
}
/* #FFFFFF on #0d2440 = 15.63:1 PASS. */
.dc-page:not(.home-page) .soc-hero__note-body{
	display: block; font-family: var(--default-font); font-size: 15px;
	line-height: 1.5; color: var(--white-color);
}
.dc-page:not(.home-page) .soc-hero__note-link{
	color: var(--on-dark-accent); font-weight: 600; text-decoration: underline;
	text-underline-offset: 3px;
}
.dc-page:not(.home-page) .soc-hero__note-link:hover{ color: var(--white-color); }

/* THE FOUR LEGAL NOTICES have no `.soc-hero` — a policy document opens on a
   text-only `.legal-head` band, and inventing a photograph for it was rejected
   when they were authored. They still get the three things the client asked
   for: the cream ground, the 50svh floor, and the masthead's left edge. The
   band's inner element is the framework `.container` (max-width 1300, padding
   15px → 165px at 1600), so it is re-pinned to the 1440/56 rail here. This is
   the SAME target as the hero above, reached a different way because the
   element is centred-and-capped rather than a full-bleed grid track. */
.dc-page:not(.home-page) .legal-head{
	background-color: var(--soc-cream);
	border-bottom: 1px solid var(--divider-color);
	min-height: 50svh;
	display: flex; flex-direction: column; justify-content: center;
}
.dc-page:not(.home-page) .legal-head > .container{
	max-width: 1440px; padding-inline: 56px; width: 100%;
}

/* --- Responsive: one column, MEDIA FIRST (the home page's collapse) --- */
@media (max-width: 991px){
	.dc-page:not(.home-page) .soc-hero,
	.dc-page:not(.home-page) .soc-hero--compact{ grid-template-columns: 1fr; min-height: 0; }
	/* Below 980px the masthead's own padding drops to 20px (chrome.css), so the
	   copy follows it there rather than keeping the desktop 56. */
	.dc-page:not(.home-page) .soc-hero__copy{ order: 2; padding: 48px 20px 60px; }
	.dc-page:not(.home-page) .soc-hero__media{ order: 1; min-height: 0; aspect-ratio: 4 / 3; }
	.dc-page:not(.home-page) .soc-hero__note{ padding: 14px 16px; width: min(340px, calc(100% - 16px)); }
	.dc-page:not(.home-page) .soc-hero__actions{ gap: 26px; margin-top: 34px; }
	.dc-page:not(.home-page) .legal-head{ min-height: 0; }
	.dc-page:not(.home-page) .legal-head > .container{ padding-inline: 20px; }
}
@media (max-width: 767px){
	.dc-page:not(.home-page) .soc-hero__lead{ max-width: none; }
}

/* =====================================================================
   SERVICE PAGES — the shared `.service-page` furniture.

   WHY IT LIVES HERE AND NOT IN _ac_custom_css. The first two authored service
   pages carried this block twice, byte-identical, 388 of 397 lines the same.
   Across all 27 services that is ~10,000 duplicated lines and 27 edits for any
   future fix, so it is hoisted into the theme sheet once. Per-page
   `_ac_custom_css` now carries ONLY what is genuinely per-service.

   WHY IT CANNOT LEAK ONTO THE TEN PHASE 2 PAGES. Every selector below is
   scoped to `.service-page`, the wrapper class only single-service bodies
   carry (`<div class="dc-page service-page …-page">`). The Phase 2 pages
   carry `.dc-page` but never `.service-page`, so not one rule here matches
   them. The helpers in S1 were `.dc-page …` in the per-page files and were
   RE-SCOPED on the way in for exactly this reason — `.dc-page .section-head`
   would have matched all ten.

   LOAD ORDER, VERIFIED NOT ASSUMED. This sheet is enqueued at
   `wp_enqueue_scripts` and prints as `<link id="classy-dental-css">` in the
   head; `_ac_custom_css` prints from `ac_print_page_css()` at `wp_head` 99,
   measured at source line 521 of a rendered service page against line 200 for
   this file. A per-page rule of equal specificity therefore still wins on
   source order, which is what makes the S9 per-service overrides in the page
   files work.

   WHAT DELIBERATELY STAYED PER-PAGE — two rules, and only two:
       .ac-header{ position: sticky; … }
       html, body{ overflow-x: clip; }
   Both are GLOBAL selectors, not `.service-page` ones: they re-pin the
   masthead because this hero is light, and they undo chrome.css's
   `overflow-x: hidden`, which makes body a scroll container and silently
   kills the sticky. Neither can be scoped to `.service-page` (the header is
   outside the content wrapper) and neither may be hoisted unscoped — that
   would change the masthead on all ~30 unauthored URLs. `body.single-service`
   would cover the header but not `html`, and scoping `html` needs `:has()`.
   So they stay in each page's own CSS, exactly as the seven approved Phase 2
   pages already declare them. Four lines per page, and it keeps this sheet
   free of any global-element rule.

   CONTRAST — every pairing is measured with bin/contrast.py and recorded at
   its rule. The accent token INVERTS with its ground and that is not optional:
     light grounds  → --accent-ink #04807e  (4.78:1 on #FFFFFF, 4.53:1 on #F7F9FC)
                      --accent-color #059e9c is 3.29:1 / 3.12:1 → LARGE TEXT ONLY
     dark grounds   → --accent-color #059e9c (4.75:1 on #0d2440, 5.44:1 on #081829)
                      --accent-ink drops to 3.27:1 / 3.75:1 → FAILS there
   No bare h1-h6 selector: heading TAGS are fixed by the document outline, and
   the class decides how a heading looks.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1 · SHARED HELPERS — the same treatment home/about/services/contact/
   testimonials/patient-financing declare in their own S0, re-scoped from
   .dc-page to .service-page so it CANNOT reach those ten pages.
   --------------------------------------------------------------------- */
/* The parent prints a brand-mark icon before every eyebrow; drop it so the label
   reads as plain letter-spaced caps. LIGHT-SURFACE DEFAULT: eyebrows are 12px
   caps — SMALL text — so --accent-ink (4.78:1 white / 4.53:1 #F7F9FC PASS),
   never raw --accent-color (3.29:1 / 3.12:1 FAIL). Every band on a service page
   is light, so unlike home/services there is no dark-ground opt-out below. */
.service-page .eyebrow-text{ padding-left: 0; margin-bottom: 18px; color: var(--accent-ink); }
.service-page .eyebrow-text::before{ display: none; }
.service-page .eyebrow-center{ display: block; text-align: center; }

/* --primary-color #0d2440: 15.63:1 on #FFFFFF, 14.82:1 on #F7F9FC. Both PASS. */
.service-page .content-header{ color: var(--primary-color); }
/* The primitive capitalizes every word, which mangles real titles like
   "What are Dental Implants?". Keep the authored casing. */
.service-page .content-subtitle{ text-transform: none; color: var(--primary-color); }

/* --text-color #4E5560: 7.52:1 on #FFFFFF, 7.13:1 on #F7F9FC. Both PASS. */
.service-page .prose-serif{
	font-family: var(--mono-font);
	font-size: 18px; line-height: 1.65; color: var(--text-color);
}

.service-page .rule-center{
	display: block; width: 46px; height: 1px;
	background: var(--accent-color); margin: 0 auto 26px;
}
.service-page .rule-left{ margin-left: 0; margin-right: 0; }

/* --- THE ALTERNATION GROUND: cream, not the cool near-white --------------
   `.section--muted` paints --secondary-color #F7F9FC (primitives.css). Against
   #FFFFFF that is a 1.01:1 difference — this file's own section 6 note already
   concedes "the difference is not perceptible" — so the white/muted rhythm the
   27 service bodies are built on did not read as a rhythm at all. Every service
   page rendered as one undifferentiated white column from hero to footer, which
   is exactly what the client flagged: the section after the intro "could use
   that cream color to differentiate".
   Repointing the modifier at --soc-cream #FBF9F5 fixes all 27 pages at once
   instead of adding a second background class to ~90 sections. The token is
   already the site's warm surface (the hero rail, `.soc-close`, the reviews
   band), so this makes the service bodies agree with the chrome around them
   rather than introducing a colour.
   SCOPED TO `.service-page` on purpose: the ten Phase 2 pages and every
   unauthored URL keep the framework default.
   CONTRAST RE-MEASURED ON #FBF9F5 with bin/contrast.py — every pairing that
   previously cleared on #F7F9FC still clears, and by a slightly wider margin:
     #0d2440 --primary-color  14.86:1  PASS  (headings)
     #4E5560 --text-color      7.15:1  PASS  (body copy, .prose-serif)
     #04807e --accent-ink      4.54:1  PASS  (eyebrows, inline links) */
.service-page .section--muted{ background-color: var(--soc-cream); }

/* The tinted aside panel and the FAQ's `+` sat on the assumption that the band
   under them was #FFFFFF or #F7F9FC. On the cream the panel would vanish, so it
   takes white and keeps its accent edge — it still reads as a distinct object,
   now by being LIGHTER than its ground rather than darker.
   #4E5560 on #FFFFFF = 7.52:1 PASS; #04807e on #FFFFFF = 4.78:1 PASS. */
.service-page .section--muted .svc-note{ background: var(--white-color); }

.service-page .section-head{ text-align: center; max-width: 820px; margin: 0 auto 56px; }
.service-page .section-head .content-header{ margin: 0; }

/* ---------------------------------------------------------------------
   2 · HERO — the hero on a service page is the SHARED SUBPAGE HERO. Every
   rule that used to live here (`.service-page .soc-hero*`, ~105 lines) is gone:
   it is now one block scoped `.dc-page:not(.home-page)` further down this file,
   because the client asked for one hero across all 37 non-home URLs and two
   copies of it would be two things to keep in step. The old container-relative
   `padding-left: max(15px, calc(96.153846% - 635px))` nudge went with it — the
   unified rule aligns to the masthead rail instead, so the nudge is redundant
   rather than being layered under a second mechanism.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   3 · PROSE MEASURE — the single most important rule on a service page.
   These bodies run 4,000-13,000 characters. Unconstrained, a paragraph would
   set at ~150 characters per line on a desktop and be unreadable. Everything
   textual is held to a ~72ch measure.
   --------------------------------------------------------------------- */
.service-page .svc-prose{ max-width: 72ch; }
.service-page .svc-prose--center{ margin-left: auto; margin-right: auto; }
.service-page .svc-prose .prose-serif{ margin: 0 0 22px; }
.service-page .svc-prose .prose-serif:last-child{ margin-bottom: 0; }

/* THE OPENING BAND'S COPY IS CENTRED, and `--center` alone never did that.
   `.svc-prose--center` sets `margin-inline: auto` — it centres the BLOCK
   inside its container and says nothing about the text inside it, so every
   service page opened with a centred rule, a centred eyebrow and a centred h2
   sitting on top of a hard-left-ragged paragraph. The head was centred by
   `.section-head`; the body underneath it never was.
   CLIENT, 2026-08-12: the band under the hero reads centred on every service
   page, always. Scoped to `.svc-intro` rather than to `--center` itself, so a
   centred-block-but-left-text prose column stays available elsewhere.
   Link underlines and the 72ch measure are unaffected. */
.service-page .svc-intro .svc-prose{ text-align: center; }
/* Inline links inside body copy. --accent-ink on #FFFFFF = 4.78:1 and on
   #F7F9FC = 4.53:1, both PASS at this 18px size; raw --accent-color would be
   3.29:1 / 3.12:1 FAIL. Underlined, so the link is not signalled by colour
   alone (WCAG 1.4.1). */
.service-page .svc-prose a,
.service-page .svc-band__copy a,
.service-page .svc-band__lead a,
.service-page .svc-split__body a,
.service-page .svc-duo__col a,
.service-page .svc-list a,
.service-page .faq__a a{
	color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px;
	transition: color var(--transition-base) ease;
}
.service-page .svc-prose a:hover,
.service-page .svc-band__copy a:hover,
.service-page .svc-band__lead a:hover,
.service-page .svc-split__body a:hover,
.service-page .svc-duo__col a:hover,
.service-page .svc-list a:hover,
.service-page .faq__a a:hover{ color: var(--primary-color); }

/* ---------------------------------------------------------------------
   4 · CONTENT BAND — left-aligned head, copy column + optional aside.
   The workhorse layout for a service body h2 and its paragraphs.
   --------------------------------------------------------------------- */
.service-page .svc-band__head{ max-width: 760px; margin: 0 0 44px; }
.service-page .svc-band__head .content-header{ margin: 0; }
.service-page .svc-band__lead{ margin: 22px 0 0; }
.service-page .svc-band__tail{ margin-top: 44px; }
.service-page .svc-band__grid{
	display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
	gap: 30px 56px;
	align-items: var(--col-align);
}

/* ---------------------------------------------------------------------
   4a · COLUMN ALIGNMENT — ONE SETTING, `--col-align`.

   CLIENT, 2026-08-17: "This should be a reusable rule in case I decide that I
   want things generally aligned center. I should be able to change it with one
   CSS rule, the base rule for those. There might be sections where I want
   things to align start, so I could just override it, but there should be a
   base setting."

   SO THE SETTING IS A CUSTOM PROPERTY, DECLARED EXACTLY ONCE — the
   `.dc-page{ --col-align: start }` line at the TOP of this file, under its own
   banner. Change that one value to `center` and every two-column construct on
   every service page and every Phase 2 page re-aligns together: the copy/aside
   band, the copy/illustration band, the two-peer duo, and the image/copy split.
   Nothing else needs touching, and there is no second place where the answer is
   also written down.

   WHY A CUSTOM PROPERTY RATHER THAN A SHARED SELECTOR LIST. A selector list is
   one place to change the value but not one place to OVERRIDE it: an exception
   for a single band would need a new rule at higher specificity for each
   construct it contained. A custom property inherits, so an override is one
   declaration ON THE SECTION and every grid inside it follows — which is the
   second half of what the client asked for.

   THE OVERRIDES are `.u-align-center` and `.u-align-start`, and they set the
   property rather than `align-items`, so they compose the same way at any depth
   and cannot be beaten by a more specific rule further down this file.

   ==== WHY BOOTSTRAP'S `align-items-center` HAD TO COME OUT OF THE MARKUP ====
   The seven `.svc-split` bands carried `<div class="row align-items-center …">`.
   The parent theme ships Bootstrap, whose utility is declared
   `align-items: center !important` — so those bands were NOT governed by
   anything in this file, and flipping the base setting would silently have left
   seven sections behind. `!important` on a utility class cannot be outranked by
   specificity; the only fix is to stop using it. Each of those rows now carries
   `u-align-center` instead, which pins them to centred exactly as before while
   putting them under the token. Nothing moved on the page; the control moved.
   --------------------------------------------------------------------- */
/* The consumers. Scoped `.dc-page` rather than `.service-page` so the About
   page's two `.bio` rows — the same Bootstrap row, the same defect — are under
   the same one setting as the services. Any construct added later joins by
   adding one selector here, never by inventing a second base value. */
.dc-page .svc-duo,
.dc-page .svc-split .row,
.dc-page .bio .row{ align-items: var(--col-align); }

/* Per-section exceptions. They set the PROPERTY, so every construct nested in
   the section inherits the change from one declaration. */
.dc-page .u-align-center{ --col-align: center; }
.dc-page .u-align-start{ --col-align: start; }
/* A head that has moved INSIDE the copy column is no longer holding its own
   measure — the column is doing that — so the 760px cap comes off, and the gap
   to the first paragraph tightens from the 44px that separated a full-width
   head from a whole grid to the 28px that separates a heading from its own
   copy. */
.service-page .svc-band__copy .svc-band__head{ max-width: none; margin: 0 0 28px; }
.service-page .svc-band__copy .prose-serif{ margin: 0 0 22px; }
.service-page .svc-band__copy .prose-serif:last-child{ margin-bottom: 0; }
/* ---------------------------------------------------------------------
   4c · THE PROSE BAND, AND THE DEAD HALF IT USED TO LEAVE.

   CLIENT, 2026-08-17, over a screenshot of `.svc-band--prose`: "A section like
   this needs a photo. If there are none in the library, please use the pixabay
   skill and find some photos. I prefer infographic-style things for these, but
   I'm fine with a 3D image-type thing. You need to check all the service pages
   for issues like this."

   THE DEFECT. A prose band was a left-aligned head over a 72ch column and
   nothing else, so on a 1328px rail roughly 45% of the section was empty white
   to the right of every paragraph. 29 sections across 15 service pages.

   TWO FIXES, BECAUSE THERE ARE TWO CASES.

   `--figure` (8 sections) — the copy moves into the left column and an
   illustration takes the right. This is the shape the client asked for and it
   is used wherever an honest image exists for the subject.

   `--prose` (the remaining 21) — CENTRED, not padded. The client's own image
   rule from 2026-08-12 caps a page at two image-bearing sections per five body
   sections and says outright that "being under budget is preferred to padding a
   page with stock". Pixabay's dental inventory could not supply a truthful
   illustration for these subjects — see the report — and a generic laboratory
   photo next to a paragraph about saliva testing is exactly the filler that
   rule exists to prevent. So the column is centred on the rail instead: the
   dead half becomes two even margins, which reads as a deliberate measure
   rather than a missing column. It is the same treatment `.svc-intro` already
   carries on every service page.

   BOTH CAPS ARE 72ch SO THE TWO EDGES LINE UP. The head was capped at 760px and
   the prose at 72ch; centring two different widths would have left their left
   edges ~30px apart, which reads as a mistake rather than as centring.
   --------------------------------------------------------------------- */
.service-page .svc-band--prose .svc-band__head{ max-width: 72ch; margin-inline: auto; }
.service-page .svc-band--prose .svc-prose{ margin-inline: auto; }

/* --- `--figure`: copy one side, an illustration the other ---------------
   THE IMAGE IS NEVER CROPPED. These are anatomical diagrams and 3-D renders,
   not photographs — crop a cross-section of a tooth and you have removed the
   part that was explaining something. So the media panel sizes to the image
   with `object-fit: contain` on a tinted ground rather than filling a fixed
   aspect-ratio box the way `.svc-split__media` (a photograph) does.

   IT SITS ON --secondary-color #F7F9FC. Most of these illustrations are drawn
   on white, and white-on-white would leave the figure floating with no edge;
   the tint plus a hairline gives it the same object-ness `.svc-note` has. The
   panel is decoration behind an image that carries its own `alt`, so no
   contrast pairing arises.

   THE HEIGHT CAP is what stops the one portrait asset (a 934x1280 oral-anatomy
   diagram) from setting a section twice as tall as its own copy. Landscape
   assets never reach it. */
.service-page .svc-band__figure{ min-width: 0; }
.service-page .svc-figure{ margin: 0; }
.service-page .svc-figure__media{
	background: var(--secondary-color);
	border: 1px solid var(--divider-color);
	padding: 22px;
}
.service-page .svc-figure__media img{
	display: block; width: 100%; height: auto;
	max-height: 520px; object-fit: contain;
}
/* The figure column is the narrower of the two: the copy is the section and the
   illustration supports it. 1.35fr / 1fr is `.svc-band__grid`'s own split, so a
   figure band and an aside band set to the same rhythm down the page. */

@media (max-width: 991px){
	/* Stacked, so the illustration follows the copy it belongs to rather than
	   being squeezed beside it. The cap comes down with the viewport. */
	.service-page .svc-figure__media{ padding: 18px; }
	.service-page .svc-figure__media img{ max-height: 380px; }
}

/* --- The aside note: a tinted panel with an accent edge ---------------
   Ground is --secondary-color #F7F9FC on the white band, so the panel reads as
   a distinct object without a box-shadow. */
.service-page .svc-note{
	background: var(--secondary-color);
	border-left: 3px solid var(--accent-color);
	padding: 30px 30px 30px 27px;
}
/* Small caps on #F7F9FC -> --accent-ink 4.53:1 PASS. */
.service-page .svc-note__label{ margin-bottom: 16px; }
.service-page .svc-list{ list-style: none; margin: 0; padding: 0; }
/* --text-color #4E5560: 7.52:1 on white, 7.13:1 on #F7F9FC. Both PASS. */
.service-page .svc-list li{
	position: relative; padding: 12px 0 12px 24px;
	font-family: var(--default-font); font-size: 15.5px; line-height: 1.6;
	color: var(--text-color);
	border-top: 1px solid var(--divider-color);
}
.service-page .svc-list li:first-child{ border-top: 0; padding-top: 0; }
.service-page .svc-list li:last-child{ padding-bottom: 0; }
/* Decorative marker, not text — no contrast requirement, but it is the accent
   at full strength either way. */
.service-page .svc-list li::before{
	content: ''; position: absolute; left: 2px; top: 20px;
	width: 6px; height: 6px; background: var(--accent-color); transform: rotate(45deg);
}
.service-page .svc-list li:first-child::before{ top: 8px; }

/* ---------------------------------------------------------------------
   4d · STEPS — `.svc-steps`, for a band that was a wall of paragraphs.

   CLIENT, 2026-08-17, over the nine-paragraph implants band: "this is a good
   example of a section that SHOULD have been split into multiple sections OR
   turned into a manageable component like a tab section." Asked to choose, they
   picked numbered steps with everything visible over tabs.

   WHY NOT TABS, IN THEIR WORDS AND MINE. Tabs were the client's own example and
   they are the more compact answer, but a tab set shows one panel of five: four
   fifths of the copy would ship `display: none` on a port whose first rule is
   preserving the SEO content structure. Steps chunk the wall without hiding a
   sentence, and they need no JavaScript to read — the counter is CSS, so the
   component works in the editor preview, in an email export and with scripting
   off. The client chose this knowing the trade.

   WHAT IT FIXES, BEYOND LENGTH. Four of these walls turned out to contain
   FLATTENED HEADINGS — "Bone Grafting Procedures", "Frenectomy",
   "Post-treatment pain relief", "How does Ozone Therapy treatment work?" — real
   subheads in the source that the port demoted past `div` all the way into
   `p.prose-serif`, where they read as one-line paragraphs that trail off. They
   become step labels here. Not one word is rewritten: the label text is the
   flattened paragraph's own text, moved into the slot it always wanted.

   THE NUMBER IS A CSS COUNTER, NOT MARKUP. It is generated content behind
   `aria-hidden`, so it is decoration a screen reader never reads out as though
   it were part of the sentence, and renumbering after an edit is automatic —
   the practice can delete a step in the editor without leaving a gap at 03.

   `--plain` DROPS THE COUNTER for sets that are not a sequence. The oral-surgery
   implants band compares a bridge, a denture and an implant; numbering three
   alternatives would assert an order that the copy does not.
   --------------------------------------------------------------------- */
.service-page .svc-steps{
	display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: 26px;
	margin-top: 44px;
	counter-reset: svc-step;
}

/* THE GRID STATES ITS OWN BOTTOM MARGIN, symmetrical with its top. This is the
   seam the client actually reported — a closing paragraph welded to the bottom
   edge of the cards. The flow floor in section 11 would have caught it at 22px,
   but a component that holds itself 44px off what precedes it should hold the
   same distance from what follows; anything else reads as the grid drifting
   downward. `:not(:last-child)` so a band that ENDS on its steps does not push
   its own section padding out. */
.service-page .svc-steps:not(:last-child){ margin-bottom: 44px; }

/* WHEN A STEPPED BAND ALSO CARRIES A FIGURE, the head, the lead paragraph and
   the illustration form the top row and the steps run the FULL RAIL underneath.
   Left as an ordinary grid child, the step grid would drop into column one at
   1.35fr and the cards would be squeezed into two-thirds of the width for no
   reason — the figure has already had its say by then.

   Written as a negation rather than by listing `.svc-steps` and `.svc-band__tail`
   so that anything else placed at this level (a closing note, a callout added
   later) spans by default. In this grid the only things that legitimately hold a
   single column are the three named columns. */
.service-page .svc-band__grid > :not(.svc-band__copy):not(.svc-band__aside):not(.svc-band__figure){
	grid-column: 1 / -1;
}
/* The group label — a `div`, always, because it carries what used to be a
   flattened heading and the port's outline may not move. */
.service-page .svc-steps__label{ grid-column: 1 / -1; margin: 0 0 -8px; }

.service-page .svc-step{
	counter-increment: svc-step;
	background: var(--white-color);
	border: 1px solid var(--divider-color);
	border-top: 3px solid var(--accent-color);
	padding: 30px 30px 32px;
}
/* On a cream band the card ground stays white, which is what makes it a card;
   on a white band it needs the hairline to do the separating, which it does. */

/* THE NUMERAL. --accent-ink #04807e on --white-color #FFFFFF = 4.78:1 PASS —
   measured even though at 13px it is small text and therefore held to the
   stricter 4.5 bar. `decimal-leading-zero` so 01..09 all set to the same width
   and the cards' titles line up across a row. */
.service-page .svc-step__n{
	display: block;
	font-family: var(--default-font); font-size: 13px; font-weight: 600;
	letter-spacing: .18em;
	color: var(--accent-ink);
	margin-bottom: 14px;
}
.service-page .svc-step__n::before{ content: counter(svc-step, decimal-leading-zero); }
.service-page .svc-steps--plain .svc-step__n{ display: none; }
/* Without a numeral the title would sit hard against the accent edge. */
.service-page .svc-steps--plain .svc-step{ padding-top: 28px; }

/* --primary-color #0d2440 on #FFFFFF = 15.63:1 PASS. Sized between
   `.svc-card__title` (21px) and body copy: a step label names a stage, it does
   not open a section. */
.service-page .svc-step__title{
	font-family: var(--heading-font); font-size: 19px; font-weight: 600;
	line-height: 1.3;
	color: var(--primary-color);
	margin: 0 0 14px;
}
/* The step's own copy. Narrower than the 72ch band measure because the card is
   narrower; left to inherit, `.svc-prose`'s cap would never bind and the
   paragraph would simply fill the card. */
.service-page .svc-step .prose-serif{ margin: 0 0 16px; font-size: 17px; }
.service-page .svc-step .prose-serif:last-child{ margin-bottom: 0; }
.service-page .svc-step .svc-list{ margin-top: 4px; }

@media (max-width: 767px){
	.service-page .svc-steps{ gap: 20px; margin-top: 34px; }
	.service-page .svc-step{ padding: 26px 22px 28px; }
}

/* --- Card grid: an h3 + its list, repeated. Used where a service body lists
   sub-treatments under h3s. The h3 keeps its TAG; only the look is the card. */
.service-page .svc-cards{
	display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	gap: 26px;
}
.service-page .svc-card{
	background: var(--white-color);
	border: 1px solid var(--divider-color);
	border-top: 3px solid var(--accent-color);
	padding: 32px 30px;
}
/* --primary-color #0d2440 on #FFFFFF = 15.63:1 PASS. */
.service-page .svc-card__title{
	font-family: var(--heading-font); font-size: 21px; font-weight: 600; line-height: 1.25;
	color: var(--primary-color); margin: 0 0 20px;
}
/* A heading that is itself a link keeps the heading colour and takes an accent
   underline on hover, so it never reads as body-copy link text. */
.service-page .svc-card__title a{
	color: var(--primary-color); text-decoration: none;
	border-bottom: 1px solid var(--accent-color); padding-bottom: 2px;
	transition: color var(--transition-base) ease, border-color var(--transition-base) ease;
}
.service-page .svc-card__title a:hover{ color: var(--accent-ink); border-color: var(--accent-ink); }
.service-page .svc-card__title a:focus-visible{ outline: 3px solid var(--accent-ink); outline-offset: 3px; }

/* --- DARK CARD BAND — `.svc-band--dark` -------------------------------
   White cards on navy. CLIENT, 2026-08-12: a card grid is the one section
   shape that should punctuate the page rather than continue it, so every
   band that holds `.svc-cards` takes the dark ground and lets the cards do
   the reading. 15 sections across 14 service pages.

   THE MARKUP CARRIES `section--inverse` TOO, and that primitive is the whole
   difficulty here. It paints EVERY descendant text element white
   (`:is(.eyebrow-text, .content-header, h1..h6, p, li, a:not(.btn-default))`,
   primitives.css) at specificity (0,2,0) — including the copy INSIDE the white
   cards, which would be white ink on a white card: invisible, and the exact
   failure mode the reviews band was already fixed for once. The card therefore
   hands its own text back explicitly at (0,3,0), which beats the primitive on
   specificity rather than on source order. Do not "simplify" these four
   selectors away; the cards go blank if you do.

   Only the BAND is inverse. The cards are an opaque light island inside it and
   are measured on white, not on navy.
   CONTRAST, bin/contrast.py, on --primary-color #0d2440:
     #FFFFFF  the h2/h3 and the head's copy       15.63:1  PASS
     #10c3cc  --on-dark-accent, the eyebrow        7.22:1  PASS (small caps,
                                                            so it needs the
                                                            normal 4.5 bar —
                                                            --accent-ink is
                                                            3.27:1 here and
                                                            MUST NOT be used)
     #FFFFFF  the card's own ground               15.63:1  PASS as a boundary
   And inside the card, on #FFFFFF:
     #0d2440 --primary-color  15.63:1  PASS   (.svc-card__title)
     #4E5560 --text-color      7.52:1  PASS   (.prose-serif)
     #04807e --accent-ink      4.78:1  PASS   (inline links) */
.service-page .svc-band--dark{ background-color: var(--primary-color); }

/* The eyebrow's accent INVERTS with its ground — see the token note in this
   section's header. (0,3,0) so it beats `.section--inverse .eyebrow-text`,
   which would otherwise flatten it to plain white and lose the accent. */
.service-page .svc-band--dark .eyebrow-text{ color: var(--on-dark-accent); }
/* The hairline rule is decorative and has no contrast duty, but --accent-color
   #059e9c is muddy on the navy; the on-dark token keeps it legible. */
.service-page .svc-band--dark .rule-center{ background: var(--on-dark-accent); }

/* INLINE LINKS OUT ON THE BAND ITSELF — not inside a card, not inside a note.
   A `svc-band__tail` paragraph, a `svc-band__copy` column or a lead can sit
   directly on the navy, and its links were the one thing the dark band broke.
   The accent token INVERTS with its ground (see this section's header) and the
   S3 link rules hand every body link --accent-ink, which is the LIGHT-surface
   token: on navy it measures 3.27:1 and FAILS. Those rules are (0,2,1) and the
   framework's `.section--inverse :is(…, a:not(.btn-default))` is also (0,2,1),
   so they tie and the child sheet wins on load order — the failing colour was
   the one that shipped. Worse, their `:hover` is --primary-color, which IS the
   band, so the link disappeared entirely on hover.
   (0,3,1) settles it on specificity rather than on load order.
     #04807e --accent-ink     on #0d2440  3.27:1  FAIL  <- what it was
     #10c3cc --on-dark-accent on #0d2440  7.22:1  PASS  <- what it is
     #FFFFFF hover            on #0d2440 15.63:1  PASS
   The card and note rules below are (0,4,1) and still win inside their own
   opaque grounds, which is exactly what should happen. */
.service-page .svc-band--dark a:not(.btn-default){ color: var(--on-dark-accent); }
.service-page .svc-band--dark a:not(.btn-default):hover{ color: var(--white-color); }

/* THE CARDS HAND THEIR TEXT BACK. Each of these is (0,3,0) vs the primitive's
   (0,2,0). The border also has to change: --divider-color is
   rgba(13,36,64,.12), a navy tint that is invisible against a navy band, so
   the card's edge is the accent alone. */
.service-page .svc-band--dark .svc-card{
	border: 1px solid transparent;
	border-top: 3px solid var(--accent-color);
}
.service-page .svc-band--dark .svc-card .svc-card__title,
.service-page .svc-band--dark .svc-card .svc-card__title a{ color: var(--primary-color); }
.service-page .svc-band--dark .svc-card p,
.service-page .svc-band--dark .svc-card li{ color: var(--text-color); }
.service-page .svc-band--dark .svc-card a:not(.btn-default):not(.svc-card__title a){ color: var(--accent-ink); }

/* HOVER INSIDE A CARD, PINNED ON SPECIFICITY RATHER THAN ON LINE NUMBER.
   Without these two, `.svc-band--dark a:not(.btn-default):hover` (0,4,1) and
   `.svc-band--dark .svc-card .svc-card__title a` (0,4,1) TIE, and the only
   thing deciding the winner is that the card rule is written further down this
   file. That is a real trap: move either block, or hoist the band rule into a
   later sheet, and all 27 card links on other-services turn --white-color ON A
   WHITE CARD and disappear on hover. Specificity should decide this, not
   source order.
     .svc-card__title a:hover            (0,5,1)
     .svc-card a:not()…:not()…:hover     (0,6,2)
   Both clear the band's (0,4,1) outright, so the block is now order-independent.
   These also restore the hover colour on a card title link, which the dark-band
   rules had been swallowing — the only remaining hover signal there was the
   underline's border-color, so the link changed almost nothing on hover.
   Measured on the white card: --accent-ink 4.78:1 PASS, --primary-color
   15.63:1 PASS. */
.service-page .svc-band--dark .svc-card .svc-card__title a:hover{ color: var(--accent-ink); }
.service-page .svc-band--dark .svc-card a:not(.btn-default):not(.svc-card__title a):hover{ color: var(--primary-color); }

/* --- CARD ICON — hoisted out of three page files -----------------------
   `.svc-card__icon` was written independently into oral-surgery,
   periodontal-care and endodontics, byte-identical each time, each carrying a
   comment saying a fourth copy should trigger this hoist. Three is enough.
   FA 6.6.0 Free is enqueued site-wide by the parent (`ac-fontawesome`).
   The icon is DECORATION and every instance is `aria-hidden="true"` — the card
   title carries the meaning — so it has no 1.4.3 duty. It clears the bar
   anyway: at ~30px it sits inside the opaque white card, never on the navy,
   and --accent-ink #04807e on #FFFFFF is 4.78:1 PASS. (--accent-color #059e9c
   is 3.29:1 there and is LARGE-TEXT ONLY, so it is not used.)
   `<i>` is not in the `.section--inverse` white-out list, so nothing repaints
   these on a dark band. */
.service-page .svc-card__icon{
	display: block; font-size: 30px; line-height: 1;
	color: var(--accent-ink); margin: 0 0 20px;
}

/* A `.svc-note` aside caught inside a dark band takes the same treatment as
   the cards: an opaque white panel, its own ink restored. */
.service-page .svc-band--dark .svc-note{ background: var(--white-color); }
/* …UNLESS IT IS NESTED INSIDE A CARD, which is the case the rule above gets
   wrong. `.svc-note` is normally an aside sitting on the band, so painting it
   white is right. But a note INSIDE a `.svc-card` is already on white, so the
   rule above painted white on white and the panel lost its edge entirely —
   found on nutritional-testing-with-kbmo, whose three FIT panels are nested
   that way. (0,4,0) beats the (0,3,0) above and hands those back the tinted
   ground the note has on every light band, which is what it looked like before
   the section went navy. Contrast is therefore unchanged and still measured on
   #F7F9FC: #4E5560 7.13:1 PASS, #04807e 4.53:1 PASS. */
.service-page .svc-band--dark .svc-card .svc-note{ background: var(--secondary-color); }
.service-page .svc-band--dark .svc-note p,
.service-page .svc-band--dark .svc-note li{ color: var(--text-color); }
.service-page .svc-band--dark .svc-note .eyebrow-text{ color: var(--accent-ink); }

/* --- Two short h2 sections side by side, for bodies that end in a run of
   one-paragraph sections. */
.service-page .svc-duo{
	display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: 40px 56px;
}
.service-page .svc-duo__title{ margin: 0 0 20px; font-size: clamp(24px, 2.2vw, 32px); }
.service-page .svc-duo__col .prose-serif{ margin: 0 0 20px; }
.service-page .svc-duo__col .prose-serif:last-child{ margin-bottom: 0; }

/* ---------------------------------------------------------------------
   5 · SPLIT BAND — image one side, copy the other. Reuses the parent's
   .img-frame + .media--zoom exactly as home / about / services do.
   --------------------------------------------------------------------- */
.service-page .svc-split__body .content-header{ margin: 0 0 24px; }
.service-page .svc-split__body .prose-serif{ margin: 0 0 20px; max-width: 62ch; }
.service-page .svc-split__body .prose-serif:last-child{ margin-bottom: 0; }
.service-page .svc-split__media{ aspect-ratio: 3 / 2; }

/* ---------------------------------------------------------------------
   6 · FAQ — native <details>/<summary>. No JavaScript, no ARIA to keep in
   sync, keyboard-operable for free, and the open state is always truthful.
   (The source markup was a dead Divi accordion: <button aria-expanded="false">
   with its panel permanently visible underneath. See the HTML.)
   --------------------------------------------------------------------- */
.service-page .faq{ max-width: 860px; margin: 0 auto; }
.service-page .svc-faq__sub{ margin: 22px 0 0; }
.service-page .faq__item{
	border-top: 1px solid var(--divider-color);
	background: transparent;
}
.service-page .faq__item:last-child{ border-bottom: 1px solid var(--divider-color); }
/* --primary-color #0d2440 = 14.82:1 on the muted #F7F9FC band and 15.63:1 on
   the white one (.faq--onwhite). Both PASS.
   `list-style: none` + the WebKit pseudo kills both marker implementations; the
   +/- is drawn in ::after so the control has a visible affordance. */
.service-page .faq__q{
	list-style: none; cursor: pointer;
	position: relative; display: block;
	padding: 22px 46px 22px 0;
	font-family: var(--heading-font); font-size: 19px; font-weight: 600; line-height: 1.4;
	color: var(--primary-color);
	transition: color var(--transition-base) ease;
}
.service-page .faq__q::-webkit-details-marker{ display: none; }
.service-page .faq__q:hover{ color: var(--accent-ink); }
.service-page .faq__q:focus-visible{ outline: 3px solid var(--accent-ink); outline-offset: 3px; }
/* Rendered text, so it takes the text-safe token: --accent-ink #04807e =
   4.53:1 on #F7F9FC and 4.78:1 on #FFFFFF. Both PASS. */
.service-page .faq__q::after{
	content: '+'; position: absolute; right: 8px; top: 20px;
	font-family: var(--default-font); font-size: 24px; font-weight: 400; line-height: 1;
	color: var(--accent-ink);
}
.service-page .faq__item[open] .faq__q::after{ content: '\2013'; }
.service-page .faq__item[open] .faq__q{ color: var(--accent-ink); }
/* --text-color #4E5560 = 7.13:1 on #F7F9FC, 7.52:1 on #FFFFFF. Both PASS. */
.service-page .faq__a{ padding: 0 46px 26px 0; max-width: 68ch; }
.service-page .faq__a p{
	margin: 0; font-family: var(--mono-font); font-size: 17px; line-height: 1.7;
	color: var(--text-color);
}

/* ---------------------------------------------------------------------
   7 · CLOSING CTA — `.soc-close`, ONE component for the home page and all
   27 services. A LITERAL PORT of the reference `.hv2-close`
   (princeton-reference/home-v2.custom.css, section 6). Client note,
   2026-08-12: this component had been approximated three times and is to be
   COPIED, not redesigned. The reference is reproduced below beside each
   rule so any future edit can be diffed against it rather than re-invented.

       .hv2-close      { padding: 0 0 130px; background: var(--secondary-color); }
       .hv2-close__inner{ position: relative; min-height: 520px;
                          display: flex; align-items: center; }
       .hv2-close__media{ position: absolute; inset: 0;
                          background-size: cover; background-position: center; }
       .hv2-close__media::after{ content:''; position:absolute; inset:0;
                          background: rgba(0,0,0,.3); }
       .hv2-close__card { position: relative; z-index: 2;
                          background: var(--secondary-color);
                          padding: 66px 72px; max-width: 620px;
                          margin-left: 72px; }
       .hv2-close__card h2{ font-size: clamp(26px,2.64vw,38px); line-height:1.12; }
       .hv2-close__card .btn-default{ margin-top: 36px; }
       @media (max-width: 991px){
         .hv2-close__card{ margin: 0 16px; padding: 44px 28px; } }

   WHAT IT REPLACES, AND WHY EACH WAS WRONG
     · `.soc-close` (home): photo inset to the right 62% with a WHITE card
       beside it, the card carrying Find Us / Office Hours / button / phone.
       The reference photo fills the whole inner box and the card OVERLAPS it.
     · `.svc-cta` (27 services): photo painted on the SECTION, i.e. bleeding
       edge to edge, with a white card carrying an eyebrow, a paragraph, a
       button, a phone link and a 3px teal top border. The reference photo is
       inside the wrap and inset from the page edges, and the card holds a
       heading and one button — nothing else.

   THE MEDIA IS INSIDE THE WRAP. `.soc-close__media` is `inset: 0` on
   `.soc-close__inner`, which is a child of the 1440/56 wrap. So the photo's
   left edge is the page rail, NOT 0. This is the error the client could see
   from across the room, and it is why the wrap is a real element here rather
   than the section itself carrying the background.

   SURFACES. Section AND card are both --soc-cream #FBF9F5, exactly as the
   reference puts both on its own --secondary-color. The card is a TONAL
   panel, not a white one; it reads as a cut-out of the band laid over the
   photograph, which is the whole idea of the composition.

   TEXT NEVER SITS ON THE PHOTO — the card is fully opaque. Measured with
   bin/contrast.py on #FBF9F5 (AA: 4.5 normal, 3.0 large, 3.0 control edge):
     #0d2440  --primary-color  the h2 .soc-close__title  14.86:1  PASS
     #0d2440  --btn-fill-ink   on the #10c3cc fill        7.22:1  PASS
     #04807e  --btn-fill-edge  button boundary on card    4.54:1  PASS (1.4.11)
     #04807e  --accent-ink     (available, small text)    4.54:1  PASS
     #059e9c  --accent-color   (LARGE ONLY, unused here)  3.13:1  large-only
   Card edge vs photograph is NOT a WCAG pairing: it is a decorative panel
   and no text crosses it. For the record it is 1.99:1 worst case (the .3
   scrim caps a photo pixel at #B3B3B3 against the cream). The reference
   accepts exactly that and carries no shadow or border to reinforce it, so
   neither does this — the earlier navy drop shadow was one of the
   approximations being removed.

   RESOLUTION. The source is 596x335 and the media box is now the full wrap
   width, so `cover` upscales by wrapwidth/596: 2.15x @1280 content, 2.36x
   @1408. It holds to about 1400px and is visibly soft above that. The
   client owes a >=1600px file; do NOT substitute a different photograph.
   --------------------------------------------------------------------- */
/* PADDING IS SYMMETRIC, AND THAT IS A DELIBERATE DEPARTURE FROM THE REFERENCE.
   `.hv2-close` is `padding: 0 0 130px` — no top padding at all, so the media
   box butts against whatever section precedes it while 130px of ground shows
   beneath. On Princeton that band always followed a section that supplied its
   own bottom space, so the asymmetry never showed. Here it does: the band runs
   on 28 URLs, and after a white section the photo's top edge lands hard on the
   seam while the bottom floats, which reads as a mistake rather than a choice.
   CLIENT DECISION, 2026-08-12: equal padding top and bottom. This is the one
   value in section 7 that is NOT the reference's; everything else still is. */
.dc-page .soc-close{ padding: 130px 0; background: var(--soc-cream); }

/* `.soc-close__wrap` IS THE RAIL and is declared with `.dc-page .container`
   in section 9, not here — one set of numbers, one breakpoint, so the card's
   left edge and every body heading can never drift apart. */
.dc-page .soc-close__inner{
	position: relative; min-height: 520px; display: flex; align-items: center;
}
/* Decorative: the photograph carries no meaning the card does not, so it is
   a CSS background and correctly has no alt text. */
.dc-page .soc-close__media{
	position: absolute; inset: 0;
	background-size: cover; background-position: center;
}
/* The reference scrim, verbatim. It has no contrast obligation of its own —
   nothing is read off the photograph. */
.dc-page .soc-close__media::after{
	content: ''; position: absolute; inset: 0; background: rgba(0, 0, 0, .3);
}
.dc-page .soc-close__card{
	position: relative; z-index: 2; background: var(--soc-cream);
	padding: 66px 72px; max-width: 620px; margin-left: 72px;
}
/* STYLED BY CLASS, NOT BY TAG — and it has to be, because this headline is an
   <h2> on 10 URLs and a <div> on 18.

   WHY BOTH. The reference card is anchored by a substantial headline; without
   one it is a button floating in a large cream rectangle, which reads as
   broken rather than minimal. But 17 services and the home page have no
   closing heading in their pre-port outline, and inventing <h2>s for them
   would change an outline that is verified against a baseline. The project's
   own heading policy settles it (client, 2026-08-11; see the note at the top
   of the `soc-cta` block above and docs/soc/blocks/service-cta.html): a CTA
   headline is never document structure, so it is a DIV carrying a
   tag-agnostic style class. Those 18 therefore get
   `<div class="cta-header soc-close__title">` and the outline does not move.

   EVERY PROPERTY IS DECLARED HERE so the two render identically and neither
   base class (`content-header` on the h2s, `cta-header` on the divs) can leak
   a difference in. Measured on a rendered page, the <h2> computes to
   Playfair Display / 38px / 700 / 42.56px / rgb(13,36,64) / margin 0 — which
   is exactly what these six declarations reproduce. This mirrors how
   `.soc-cta__title` handles the same problem for the global band.

   #0d2440 --primary-color on the #FBF9F5 card = 14.86:1 PASS (bin/contrast.py). */
.dc-page .soc-close__title{
	font-family: var(--heading-font);
	font-size: clamp(26px, 2.64vw, 38px);
	font-weight: 700;
	line-height: 1.12;
	color: var(--primary-color);
	margin: 0;
}
.dc-page .soc-close__card .btn-default{ margin-top: 36px; }
/* A card with no heading (the 17 services whose pre-port outline records no
   closing heading) would otherwise inherit the button's 36px top margin
   against nothing. */
.dc-page .soc-close__card > .btn-default:first-child{ margin-top: 0; }

/* ---------------------------------------------------------------------
   8 · RESPONSIVE
   --------------------------------------------------------------------- */
@media (max-width: 1199px){
	.service-page .svc-band__grid{ gap: 26px 40px; }
	.service-page .svc-duo{ gap: 34px 40px; }
}

@media (max-width: 991px){
	.service-page .svc-band__grid{ grid-template-columns: 1fr; gap: 34px; }

	/* CLOSING CTA below the split point, verbatim from the reference's own
	   991px rule: the card loses its 72px offset and takes a 16px margin, so
	   the photograph becomes an even frame around it instead of a panel
	   beside it. `max-width: 620px` is deliberately KEPT — the reference
	   keeps it, and below ~650px the margins bind first anyway. */
	.dc-page .soc-close{ padding: 76px 0; }
	.dc-page .soc-close__card{ margin: 0 16px; padding: 44px 28px; }
}

@media (max-width: 767px){
	.service-page .svc-note{ padding: 24px 22px 24px 20px; }
	.service-page .svc-card{ padding: 26px 22px; }
	.service-page .faq__q{ font-size: 17.5px; padding-right: 38px; }
	.service-page .faq__a{ padding-right: 0; }
}

/* =====================================================================
   9 · ONE RAIL FROM MASTHEAD TO FOOTER — the body bands join the hero.

   WHAT WAS WRONG (client, 2026-08-12: "put the body sections on the same
   rail as the hero"). Measured at a 1600px viewport:

       masthead logo left edge ............ 136px   (.ac-masthead__inner,
                                                     chrome.css, 1440 / 56)
       hero <h1> left edge ................ 136px   (the unified hero above)
       first body <h2> left edge .......... 165px   (framework `.container`,
                                                     max-width 1300 + 15px)

   So every subpage's <h1> sat 29px left of the <h2>s beneath it and the
   page read as two grids stacked on each other.

   THE FIX, ONCE. `.container` is the inner element of every authored body
   band, so re-capping it to the masthead's 1440/56 puts all of them on the
   hero's rail in one declaration rather than 36 per-page nudges.

   WHY THESE NUMBERS ARE THE SAME RAIL, NOT A LOOKALIKE. A centred block of
   `max-width: M` with `padding-inline: P` puts its content edge at
   `max(P, (W - M)/2 + P)`. With M = 1440 and P = 56 that is
   `max(56, W/2 - 664)` — algebraically identical to the
   `max(56px, calc(50cqw - 664px))` the unified hero uses, and to the
   masthead's own centred 1440/56. Three elements, one rail, no magic
   numbers. The hero needs the container-query form only because it is a
   full-bleed grid rather than a centred capped box; this is not.

   SCOPE. `.dc-page` is the wrapper EVERY authored body opens with. The 27
   services carry `.dc-page service-page …-page` and the Phase 2 pages carry
   `.dc-page …-page` and never `.service-page`, so one selector covers both
   without touching that isolation — nothing here is `.service-page`-scoped
   and nothing here leaks the other way either. Unauthored URLs have no
   `.dc-page` wrapper and keep the framework default.

   THE MASTHEAD AND FOOTER ARE NOT TOUCHED. They are outside `.dc-page`,
   and neither uses `.container`: the masthead is `.ac-masthead__inner` and
   the footer is `.ac-container` (chrome.css). They already define the rail;
   this moves the body onto it.

   THE TWO GLOBAL BANDS ARE INCLUDED. `soc-cta` and `soc-revband` render
   from cd_the_cta_band() AFTER the body, i.e. OUTSIDE `.dc-page`, so
   `.dc-page .container` cannot reach them — and left out they would be the
   only two bands on the page still at 165px, which is the exact defect
   being fixed. They are named explicitly so the re-cap still cannot escape
   onto the ~30 unauthored URLs through a bare `.container`.

   THE CLOSING CTA'S WRAP IS IN THE LIST TOO. `.soc-close__wrap` is not a
   `.container` — it is the reference component's own `.wrap` — but it has to
   land on the identical rail or the photograph's left edge misses the grid by
   a few pixels at every width. Declaring it here rather than in section 7
   means there is exactly one set of rail numbers and one breakpoint for it.

   BELOW 991px the masthead's own padding drops to 20px (chrome.css), so
   the rail follows it there, exactly as the hero copy already does.
   ===================================================================== */
.dc-page .container,
.dc-page .soc-close > .soc-close__wrap,
.soc-cta > .container,
.soc-revband .container{
	max-width: 1440px; width: 100%; margin-inline: auto; padding-inline: 56px;
}

@media (max-width: 991px){
	.dc-page .container,
	.dc-page .soc-close > .soc-close__wrap,
	.soc-cta > .container,
	.soc-revband .container{ padding-inline: 20px; }
}

/* =====================================================================
   10 · SECTION LEAD — `.section-lead`, an eyebrow promoted to a headline.

   THE PROBLEM (client, 2026-08-17): "There are still so many instances of
   sections being led with simply this eyebrow text, which is unacceptable.
   There are instances where the eyebrow text is acceptable as a header or as
   a subheader of sections within a big section, but a standalone section
   should always have a big text header."

   WHY THE FIX IS CSS AND NOT A NEW <h2>. The whole port is bound to preserve
   the recorded H1-H3 outline of every URL — that is the SEO half of the brief,
   and the pages where this bites (testimonials, services, and the closing
   `.visit` band on nine URLs) are exactly the ones whose baseline records NO
   heading in the affected band. Adding an <h2> to fix the look would change
   the document outline this port exists to keep. So the client's own
   instruction is the mechanism: "If you need to make the eyebrow text into
   header text using CSS while maintaining the header hierarchy (because
   everything is class-based), you can manipulate it as you see fit. Then you
   can create a separate eyebrow text with new content if you like, with just
   a `div` wrapper so it doesn't interfere with the structure."

   So `.section-lead` is applied to the EXISTING `div.eyebrow-text` — a div,
   never a heading, so no outline moves — and it renders that div at headline
   size. A NEW `div.eyebrow-text` is then authored above it to carry the small
   label. Net effect on the document outline: zero elements added or removed
   from h1-h6, on every page it touches.

   WHY IT LIVES IN THE SHARED SHEET. It is now applied on eleven URLs. One
   definition here beats eleven copies in eleven `_ac_custom_css` blobs, and it
   is the same argument `.soc-close` and `.soc-cta` above are already settled
   on. It is inert until a body opts in by adding the class.

   TYPE. Deliberately the same face, weight and line-height as `.content-header`
   renders on these pages, so a promoted eyebrow and a real <h2> set
   identically and a reader cannot tell which band got which. The size is a
   clamp rather than the flat 46px `--fs-title` because these leads are short
   phrases in a centred head, not page titles.

   CONTRAST. --primary-color #0d2440 measures 15.63:1 on #FFFFFF and 14.82:1 on
   the cream #F7F9FC — both PASS by a wide margin. On a dark band the
   `.section--inverse` primitive already repaints `.eyebrow-text` white
   (primitives.css), and this rule sets no colour that would fight it: the
   `color` below is overridden there exactly as `.content-header`'s is.
   ===================================================================== */
.dc-page .eyebrow-text.section-lead{
	display: block;
	padding-left: 0;
	margin-bottom: 0;
	font-family: var(--heading-font);
	font-size: clamp(28px, 3.4vw, 46px);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: normal;
	text-transform: none;
	color: var(--primary-color);
}
/* The parent prints a brand-mark icon before every eyebrow. Page sheets
   already drop it for `.dc-page .eyebrow-text`, but this rule must not depend
   on a page sheet having been loaded. */
.dc-page .eyebrow-text.section-lead::before{ display: none; }

/* A promoted lead sits in the same slot a `.content-header` would, so it takes
   the same rhythm: the small eyebrow above it keeps its own 18px, and the lead
   itself pushes the copy beneath it away rather than carrying a top margin. */
.dc-page .section-head .eyebrow-text.section-lead,
.dc-page .svc-band__head .eyebrow-text.section-lead{ margin: 0 0 22px; }
.dc-page .section-head .eyebrow-text.section-lead:last-child,
.dc-page .svc-band__head .eyebrow-text.section-lead:last-child{ margin-bottom: 0; }

/* `.eyebrow-center` is a page-sheet helper that centres the small label; a
   centred head must centre the lead too. Stated here so the pairing works on
   any body that opts in, page sheet or not. */
.dc-page .eyebrow-center.section-lead{ display: block; text-align: center; }

/* --- The closing `.visit` band's own head ------------------------------
   `.visit` is the slim address / hours / CTA strip that closes nine URLs. It
   was led by its column labels ("Find Us", "Office Hours") and had no headline
   at all, which is the same defect as the reviews band — a standalone section
   with nothing but eyebrows in it. It now opens with a `.visit__head` holding
   one promoted div. The column labels stay exactly as they are: those ARE the
   "eyebrow as a subheader inside a bigger section" case the client called
   acceptable, and they label the data underneath them.

   SPECIFICITY, AND WHY THIS RULE IS FOUR CLASSES DEEP. Every page sheet carries
   `.<page> .visit .eyebrow-text{ colour: --accent-color }` at (0,3,0) to opt the
   band's labels back out of the light-surface eyebrow default. `_ac_custom_css`
   prints at wp_head 99, AFTER this file, so a (0,3,0) rule here would lose the
   tie and the promoted lead would come out teal at label spacing. `.dc-page
   .visit .eyebrow-text.section-lead` is (0,4,0) and wins outright, on all nine
   pages, with no per-page edit.

   CONTRAST on the band's --primary-deep #081829 ground:
     --on-dark #DDE4EE   13.99:1  PASS  (the value rows already use it)
   --accent-color would also pass at 5.44:1, but the lead is a headline and
   headlines on this site are ink, not accent. */
.dc-page .visit__head{ margin-bottom: 40px; }
.dc-page .visit .eyebrow-text.section-lead{
	margin: 0; color: var(--on-dark);
	font-size: clamp(26px, 2.8vw, 38px);
}

@media (max-width: 767px){
	.dc-page .visit__head{ margin-bottom: 30px; }
}

/* =====================================================================
   11 · FLOW SPACING — the safety net under every authored body.

   CLIENT, 2026-08-18, over a paragraph welded to the bottom of a card grid:
   "there's no space between the cards and this random line of text, which makes
   it seem like an accident. Please fix this anywhere it might be happening, not
   just in sections like this, but if there's a heading that has no space
   beneath it or cards that have no space above it."

   WHAT WENT WRONG, AND WHY A PATCH WOULD NOT HAVE HELD. Every spacing on this
   site is declared per component — a card grid states its own top margin, a
   paragraph inside `.svc-prose` states its own bottom margin. That works right
   up until a NEW pairing appears that no rule anticipated. The closing
   paragraph of the implants band is exactly that: it sits at `.container` level,
   where no `.prose-serif` margin rule reaches it, directly under a step grid
   that declares a top margin and no bottom one. Neither element was wrong.
   Nobody owned the seam between them.

   SO THIS DECLARES A DEFAULT FOR THE SEAM ITSELF. Inside the flow containers
   below, anything that follows a sibling gets a top margin — unless some other
   rule has an opinion, in which case that rule wins and nothing changes.

   ==== THE `:where()` IS THE ENTIRE SAFETY MECHANISM ====
   `:where()` contributes ZERO specificity, so this selector weighs (0,1,0) —
   the `.dc-page` and nothing else. Every component rule in this file is at least
   (0,2,0) and every page sheet is at least (0,2,0), so ALL of them outrank it.
   The rule can therefore only ever apply where the cascade was silent, which is
   the definition of the bug it fixes. It cannot re-space anything that was
   already deliberate.

   THE FLOOR IS 22px BECAUSE THAT IS ALREADY THE SITE'S PARAGRAPH RHYTHM
   (`.svc-prose .prose-serif` and its siblings all use it). Matching it exactly
   is what keeps this rule inert: adjacent sibling margins COLLAPSE to the larger
   of the two, so any seam that already had 22px or more does not move at all,
   and only the seams that had LESS — the accidents — come up to the floor.
   Anything that wants a bigger gap states it and wins.

   MEASURED, NOT ASSUMED. Every element in all 40 rendered bodies was computed
   twice, against the old sheet and this one, comparing the COLLAPSED gap rather
   than the declared margin. SIX element shapes move and nothing else does: the
   hero note's label/body pair (35 pages), the reported cards-to-paragraph seam
   (2 service pages), the services directory's first group, two seams on oral
   surgery that were sitting at 1.6px, and one list welded to its own lead-in
   sentence on tongue-tie. 41 element instances, out of every element on 40
   pages.

   THE CONTAINER LIST IS EXPLICIT rather than a bare `.dc-page > *`, because the
   hero, the masthead and the plugin blocks are laid out with grid and absolute
   positioning, where an inherited top margin is not a gap but a bug.
   ===================================================================== */
.dc-page :where(
	.container,
	.svc-prose,
	.svc-band__copy,
	.svc-band__aside,
	.svc-split__body,
	.svc-note,
	.svc-step,
	.legal-body
) > :where(:not(:first-child):not(.row):not([class*="anchor-block"])){
	margin-top: 22px;
}

/* `.row` IS EXCLUDED AND THAT IS NOT FUSSINESS. Bootstrap ships from the parent
   theme and gives every row `margin-top: calc(-1 * var(--bs-gutter-y))` — a
   NEGATIVE margin that cancels the gutter it adds to its own columns. Replacing
   it with a positive 28px does not add a gap, it un-does Bootstrap's gutter
   compensation and shifts every column in the row. The `.svc-split` and `.bio`
   bands are built on `.row`. Anchor Blocks are excluded for the same class of
   reason: they are plugin markup with their own full-bleed margin arithmetic
   (`margin-left: calc(50% - 50vw)`), and this sheet does not own their box. */

/* --- Seams the safety net cannot reach ------------------------------- */

/* The hero's pinned note: a label directly over its sentence, both `display:
   block` spans with no margin between them. They are not in a flow container
   (the note is absolutely positioned inside the hero), so they are named here.
   6px is a label-to-value gap, not a paragraph gap — it should still read as one
   object.

   SCOPED `.dc-page`, NOT `.dc-page:not(.home-page)`. It was written with that
   exclusion on 2026-08-18 by reflex, because the shared subpage-hero rules above
   all carry it — the home page brings its own hero. But the exclusion belongs to
   those rules, not to this one: the home hero renders the SAME note badge with
   the same two spans, and home.custom.css declares no margin between them
   either. So the fix skipped the one page most people land on. Caught on
   2026-08-19 only because the home page had been redirecting out of the audit
   crawl (it is https-only; the crawl asked for http and took the 301 as a
   failure). `.home-page .soc-hero__note-label` sets no margin-bottom, so this
   (0,2,0) rule reaches it without needing to escalate. */
.dc-page .soc-hero__note-label{ margin-bottom: 6px; }

/* A LIST THAT FOLLOWS ITS OWN LEAD-IN SENTENCE. `.svc-list` declares
   `margin: 0` at (0,2,0) — correct for a list that opens a `.svc-note`, wrong
   for one introduced by a paragraph ending in a colon, which is how the
   tongue-tie band uses it. The safety net cannot reach this: it is (0,1,0) and
   loses, which is exactly the behaviour that makes the net safe everywhere
   else. So the pairing is named. Equal specificity to the rule it corrects and
   declared after it, so it wins on source order rather than by escalating. */
.service-page :where(p, .prose-serif) + .svc-list{ margin-top: 18px; }

/* NOT DONE, AND DELIBERATELY SO — the legal bodies' bullet lists. The audit
   flagged `li` against `li` on four pages, but measuring the pair rather than
   trusting the flag showed the rows already carry `margin-bottom: 12px` from
   each page's own sheet, and the table of contents separates its rows with a
   hairline and the anchor's own padding. A rule here would have been inert CSS
   claiming to fix something that was never broken, which is worse than no rule:
   the next person reads it as load-bearing. The audit's finding was a false
   positive caused by the separation living on the child anchor, not the li. */
