/* Momma's Chiro - main stylesheet.
 *
 * Hand-written CSS only. No framework, no build step, no preprocessor.
 * Written append-only in numbered sections so later work never overrides
 * earlier work by accident.
 *
 * SECTIONS
 *   01  Design tokens
 */


/* ==================================================================
 * 01  DESIGN TOKENS
 *
 * Source of truth: Figma file vnCr4ZyZNI66s9qXq28VYL (Momma's Chiro).
 *
 * Figma defines only FIVE colour variables and ONE effect variable.
 * Everything else in this block is a deliberate addition, derived from
 * values the design actually uses, and every one is labelled with why.
 * Nothing here is invented.
 *
 * No --sds-* variables were carried over. The file defines none.
 * ================================================================== */

:root {

	/* ---- 1.1  Colour - MIRRORED ONE-TO-ONE FROM FIGMA VARIABLES ----
	 * These six are the entire published variable set. Names follow the
	 * Figma names (brand/pink -> --brand-pink) so the two stay legible
	 * against each other.
	 */
	--brand-pink:        #f7dce6;   /* Figma: brand/pink       */
	--brand-mint:        #beeadc;   /* Figma: brand/mint       */
	--brand-coral:       #fbdbd2;   /* Figma: brand/coral      */
	--brand-coral-deep:  #dd5a3f;   /* Figma: brand/coral-deep */
	--neutral-ink:       #33302f;   /* Figma: neutral/ink      */

	/* Figma effect variable "Main Card Shadow":
	 * DROP_SHADOW, #33302F26, offset (0,4), radius 30, spread -10.
	 * The 26 suffix is 15% alpha. */
	--shadow-card: 0 4px 30px -10px rgba(51, 48, 47, 0.15);

	/* The Checkout offer card's glow (Figma 917:2985): DROP_SHADOW,
	 * #FFBED6, offset (0,0), radius 70, spread -30. A pink HALO rather
	 * than a shadow - no offset, and the colour is brand pink rather
	 * than ink - which is why it is its own token and not a variant of
	 * --shadow-card. Its #ffbed6 is a fifth pink, deeper than
	 * --brand-pink (#f7dce6) and used nowhere else; it stays inside this
	 * token rather than becoming a colour of its own, because it is
	 * never painted as a fill. See section 26.1. */
	--shadow-offer-glow: 0 0 70px -30px #ffbed6;


	/* ---- 1.2  Colour - DELIBERATE ADDITIONS (no Figma variable) ----
	 * Each is a raw hex the design uses repeatedly. Without a token they
	 * end up hardcoded in several places, which is exactly what the
	 * build rules forbid.
	 */

	/* 27 uses across the homepage. This is the body and secondary text
	 * colour for the whole design - the single most-used colour after
	 * ink, and the most damaging one to leave untokenised. */
	--neutral-muted:     #6f6866;

	/* 11 uses. The light end of the coral CTA gradient. */
	--brand-coral-light: #f0917a;

	--accent-cyan:       #06add7;   /* About Us gradient; Homepage squiggle */
	/* The same hue taken down until it clears contrast on white: 5.8:1
	 * against the 4.5:1 text needs, where --accent-cyan itself manages
	 * only 2.6:1. Added 2026-09-04 for the header wordmark, which is the
	 * practice's name and has to be readable rather than merely on
	 * brand. */
	--accent-cyan-deep:  #0b6e8a;
	--accent-pink:       #ff7bac;   /* Prenatal squiggle                    */
	--accent-spring:     #00ffbb;   /* Postpartum squiggle                  */
	--accent-indigo:     #6652ff;   /* Pediatric squiggle                   */
	--steps-two:         #8fd6c2;   /* Online Booking System step "2."      */
	--border-field:      #d9d9d9;   /* form input borders - see section 25  */

	/* RESTORED 2026-08-21, both of them, for the Products page's card
	 * tints. Both were deleted on 2026-08-20 for having zero references,
	 * and that note said the reversal belonged to "the next page to want
	 * a mint panel". Figma tints Products cards 3 and 6 mint and cards 2
	 * and 5 periwinkle (frame 627:126), so this is that page. Same values
	 * as before, recovered from git history rather than re-read, then
	 * checked against the frame: they match.
	 *
	 * !! --brand-mint-deep is NOT --steps-two. #85d0bc against #8fd6c2 -
	 * near neighbours, different colours, and the note above already had
	 * to make that distinction once in the other direction. Two mints on
	 * the list is the truth about the design; collapsing them would be
	 * tidier and wrong. See section 31. */
	--brand-mint-deep:   #85d0bc;   /* Products card tint - see section 31 */
	--accent-periwinkle: #c8ceff;   /* Products card tint - see section 31 */

	/* --steps-two is the SECOND MINT, added 2026-08-20 for the Online
	 * Booking System's step numerals: Figma draws "1." in #beeadc, which
	 * is --brand-mint exactly, and "2." in #8fd6c2, which nothing on the
	 * site had. Checked against --brand-mint-deep before adding it -
	 * that token existed and was deleted in this section's own cleanup
	 * for having zero references, but its value was #85d0bc, so this is
	 * a new colour rather than a restore. One use today; see 23.1. */

	/* --accent-pink was a raw hex typed into section 17's squiggle rule
	 * until 2026-08-20. It is the second value the accent squiggle is
	 * stroked in, so it belongs beside --accent-cyan on the central list -
	 * a third page adding a third colour must add it HERE, not in a
	 * component. See .accent-squiggle in section 18. */

	/* Surfaces. */
	--surface-white:     #ffffff;
	--surface-off:       #f6f4f6;
	--surface-grey:      #f5f5f5;

	/* Scrim over photography behind light text. Read from the
	 * "Ready When You Are" section overlay. */
	--scrim:             rgba(63, 63, 63, 0.4);


	/* ---- 1.3  Gradients - DELIBERATE ADDITIONS ----
	 * Angles and stops copied from the design exactly. Figma exports
	 * angles at full float precision; they are rounded to 2dp here
	 * because the difference is far below one rendered pixel.
	 */
	--gradient-cta:        linear-gradient(90deg, var(--brand-coral-light), var(--brand-coral-deep));
	--gradient-about:      linear-gradient(90deg, var(--accent-cyan) 0.11%, var(--neutral-ink) 99.89%);

	/* REMOVED 2026-08-20, with the stops that fed them:
	 *   --gradient-mint       + --brand-mint-deep   (#85d0bc)
	 *   --gradient-periwinkle + --accent-periwinkle (#c8ceff)
	 *   --accent-sky          (#b3e3f8)
	 *
	 * !! THE TWO STOPS CAME BACK ON 2026-08-21 - --brand-mint-deep and
	 * --accent-periwinkle are on the list again, above, because the
	 * Products page tints its cards with them. The last paragraph of this
	 * note is what said to do that, and it held up. THE TWO GRADIENTS DID
	 * NOT come back: Products builds its own in section 31, at its own
	 * angle and stops, off these two colours plus --brand-pink.
	 * --accent-sky is still gone and still has no references.
	 *
	 * All five had ZERO references. The two gradients were read off the
	 * pillar cards, which ship as exported .webp instead (section 11), and
	 * off the hero credibility strip, which the design turns out not to
	 * fill at all (section 08b). Their stops existed only to feed them.
	 *
	 * They were kept on the grounds that they are published design values.
	 * That is a reason to record a value, not to define a token: an unused
	 * token reads as available vocabulary, and the next page to want a
	 * mint panel would have reached for one that no longer matches
	 * anything built. The numbers are still in Figma, which is where the
	 * design lives. Recovered from git history if ever needed. */


	/* ---- 1.4  Typography - DELIBERATE ADDITIONS ----
	 * Figma publishes no type variables. Families and sizes below are
	 * read from the design.
	 *
	 * Two families only: Baloo 2 for headings, Poppins for everything
	 * else. Weights listed are the ONLY weights the design uses, and
	 * are the only ones loaded from Google Fonts.
	 */
	--font-heading: "Baloo 2", "Trebuchet MS", sans-serif;   /* Bold 700 only        */
	--font-body:    "Poppins", "Helvetica Neue", Arial, sans-serif; /* 400/500/600/700 */

	/* Size scale.  APPROVED 2026-08-19 - replaces the 22px body lock and
	 * settles the heading step-down question that block left open.
	 *
	 * FOUR heading steps and one paragraph size. Every heading steps down
	 * by a FIXED amount at each breakpoint - H1 by 4, H2 by 3, H3 by 2,
	 * H4 by 1 - so the ladder is one decision rather than twenty, and
	 * every value lands on a whole pixel with no rounding to argue about.
	 *
	 *            1440+   1199    991     767     599
	 *   H1        46      42      38      34      30     step -4
	 *   H2        34      31      28      25      22     step -3
	 *   H3        26      24      22      20      18     step -2
	 *   H4        22      21      20      19      18     step -1
	 *   body      15      15      15      15      15     FLAT
	 *
	 * Body is FLAT at Figma's own 15px. It does not move at any width.
	 * The old rule that body went UP on tablet is superseded.
	 *
	 * The six sizes below body return to Figma's real values too. They
	 * had been inflated by 22/15 = 1.4667 only to stay in proportion with
	 * the 22px body lock; with body back at 15 that multiplier goes with
	 * it. Left inflated they would each have outsized a paragraph.
	 *
	 * !! These six are shared with the header and footer (.header-phone,
	 * .footer-phone, .footer-nav, .footer-heading, .footer-licence, the
	 * mobile menu). Their rendered type changes with this block. Approved
	 * 2026-08-19 - header.php and footer.php themselves are untouched.
	 *
	 * The responsive values live in section 1.4b. Headings step there;
	 * nothing else does.
	 */
	/* !! RE-SCALED 2026-09-01. Approved by Gerek. This supersedes the
	 * "back to Figma's own values" block above, which is left in place
	 * because its reasoning about the FIXED-STEP LADDER still governs -
	 * only the numbers move.
	 *
	 * THE HOUSE STANDARD, and the reason this is not a Figma question:
	 *
	 *   body   22px at desktop / laptop / tablet,  18px at mobile
	 *   H1     +10 on the drawn value
	 *
	 * Figma draws body at 15. It is a designer's artboard, not a spec -
	 * it sets direction, and sizing is a build decision. 15px running
	 * copy reads as small on a real desktop screen, which is exactly the
	 * complaint this block answers.
	 *
	 * SUB-BODY STEPS ARE RE-SCALED WITH BODY, not left where they were.
	 * They have to be: --type-small at Figma's 14 against a 22px body is
	 * not a step down, it is a different design. Each is Figma's own
	 * value x 22/15 = 1.4667, so the design's internal relationships
	 * survive the move intact. Note --type-ui stays ABOVE body, as it is
	 * in Figma - buttons are not body copy.
	 *
	 *   token           Figma   x1.4667   set to
	 *   --type-ui        16      23.47      23
	 *   --type-body      15      22.00      22   <- the locked value
	 *   --type-small     14      20.53      20   rounded DOWN, see below
	 *   --type-xs        13      19.07      19
	 *   --type-eyebrow   12      17.60      18
	 *   --type-micro     11      16.13      16
	 *
	 * --type-small rounds DOWN to 20 rather than up to 21. At 21 it would
	 * sit one pixel under body, which is not a step anybody can see.
	 *
	 * HEADINGS. H1 takes the +10 and the rest follow it proportionally,
	 * keeping the four-step ladder the block above established:
	 *
	 *   H1  46 -> 56     H2  34 -> 46     H3  26 -> 30     H4  22 -> 26
 *
 * !! H2 TOOK A SECOND RAISE on 2026-09-01, 40 -> 46, at Gerek's
 * request against the live page ("six to ten pixels larger" - this is
 * the +6 end). The ladder step stays -4.
 *
 * It costs the ladder some evenness: H1->H2 is now a 10px step and
 * H2->H3 a 16px one, where before they were 16 and 10. Section
 * headings are the most-read type on the site after body, so the
 * unevenness is spent there deliberately rather than pretending the
 * ladder is still uniform. H3 and H4 were left alone - they carry
 * stat figures and card titles, which nobody has said are small.
	 *
	 * The fixed per-breakpoint decrement widens with them - H1 -5, H2 -4,
	 * H3 -3, H4 -2, up from -4/-3/-2/-1. Steps that stayed narrow would
	 * have left a 40px H1 on a phone. See section 1.4b for the ladder.
	 *
	 * !! THIS RE-FLOWS EVERY PAGE, not just the homepage. Several rules
	 * further down this file were hand-measured against 15px body and a
	 * 1140 container - the pillar-card title fit (--type-h4-long below),
	 * the Prenatal measure notes in section 17, the line-length notes in
	 * section 19. They are being re-checked page by page, starting with
	 * the homepage. Anything not yet re-checked is flagged where it sits.
	 */
	--type-display:  64px;  /* UNTOUCHED  decorative opening quote mark   */
	--type-h1:       56px;  /* page H1              was 46, +10 by rule   */
	--type-h2:       46px;  /* section headings     was 34, then 40       */
	--type-h3:       30px;  /* sub-headings, stats  was 26                */
	--type-h4:       26px;  /* card titles          was 22                */
	--type-h4-long:  22px;  /* ONE named exception - see the note below   */

	--type-body:     22px;  /* running body copy - LOCKED, flat to 768    */
	--type-ui:       23px;  /* buttons, UI text                           */
	--type-small:    20px;  /* rounded down - see the note above          */
	--type-xs:       19px;
	--type-eyebrow:  18px;
	--type-micro:    16px;

	/* --type-h4 is new. h4 and the card titles previously borrowed
	 * --type-ui, a button size, which meant a heading level had no token
	 * of its own and could not be moved without moving every button with
	 * it. It now has one. */

	/* --type-h4-long exists for exactly one string:
	 *
	 *     "Postpartum chiropractic recovery"
	 *
	 * the third pillar card's title. It is 32 characters against 22 for
	 * the shortest of its two siblings, and that is the whole problem -
	 * the size is right, the words are long.
	 *
	 * !! RE-CUT AGAIN 2026-09-01, AND THE EXCEPTION HAS NOW DISSOLVED.
	 * The container moved 1140 -> 1400, which is the third time this one
	 * token has been moved by a container change. The card box grows with
	 * it: content 1352, less the two 16px gaps, over three flex: 1 1 0
	 * cards, less 20px padding each side = a 400px content box, up from
	 * 313.3.
	 *
	 * Re-measured on the same basis as the table below - Poppins 700, the
	 * real string, 17.5px of width per 1px of font size:
	 *
	 *     26px (--type-h4)  455.0px   overflows by 55.0  -> two lines
	 *     24px              420.0px   overflows by 20.0  -> two lines
	 *     23px              402.5px   overflows by  2.5  -> two lines
	 *     22px              385.0px   clears by    15.0  -> this token
	 *
	 * 22px, with 15.0px of clearance - the same order of margin every
	 * previous cut of this token was chosen for.
	 *
	 * !! IT IS NOW 4px BELOW ITS SIBLINGS, back to the gap the note below
	 * says was the original intent before the 1140 change forced it to 5.
	 *
	 * !! AND IT IS A DELETION CANDIDATE. At 22px it is one step of the
	 * ladder rather than an off-scale exception, so the token is no longer
	 * doing anything the ladder could not. NOT deleted in this pass: the
	 * homepage pillar row has not been visually re-checked at the new
	 * container yet, and deleting a token is not a change to make from
	 * arithmetic alone. Revisit when section 11 gets its pass.
	 *
	 * MEASURED IN THE BROWSER, not calculated. The pillar cards are
	 * flex: 1 1 0 with 20px padding inside the container, so each card's
	 * content box is the same at every viewport from 1200 up.
	 *
	 * !! THIS TOKEN WAS 18px AND HAD TO BE RE-CUT. It was sized against
	 * --container: 1200px, which gave a 1152px content box and a 333.3px
	 * card. --container moved to 1140 (664e84d) the commit AFTER this
	 * token landed, taking the card's content box to 313.3px - and 18px
	 * needs 315.0, so the title quietly went back to two lines and undid
	 * the fix. The original note said this held "from 1440 down to about
	 * 1145px"; the container is 1140, so it held nowhere.
	 *
	 * Re-measured at the real 313.3px box, in Poppins 700 - the face that
	 * loads, and the widest of the stack - by rendering the actual string
	 * rather than scaling a ratio:
	 *
	 *     22px (--type-h4)  385.0px   overflows by 71.7  -> two lines
	 *     20px              350.0px   overflows by 36.6  -> two lines
	 *     19px              332.5px   overflows by 19.1  -> two lines
	 *     18px              315.0px   overflows by  1.6  -> two lines
	 *     17px              297.5px   clears by    15.9  -> this token
	 *     16px              280.0px   clears by    33.4  -> unnecessary
	 *
	 * 17px is the largest size that fits, and its 15.9px of clearance is
	 * the same order as the 18.3px the 1200-era value was chosen for - a
	 * real margin, not the sub-pixel noise that got 19px rejected then.
	 *
	 * !! IT IS NOW 5px BELOW ITS TWO SIBLINGS rather than 4. That gap is
	 * the cost of the container change and it is a design judgement, not a
	 * measurement - worth a look before launch. The alternatives, all
	 * rejected as bigger changes than a bugfix should make: drop the card
	 * padding from Figma's 20, tighten the row gap, or shorten the string.
	 *
	 * FLAT, with no ladder of its own, because it is only ever read while
	 * the three cards sit in a row. Below 991 they stack full width, every
	 * title fits at the normal step, and section 11 hands this one back to
	 * --type-h4 so it matches its siblings.
	 *
	 * !! WHY THIS IS NOT THE PATTERN THE LADDER DELETED. Four scoped
	 * heading tokens were removed above (--type-h1-sm, --type-h2-sm,
	 * --type-h2-xs, --type-lead). They were a spread of near-duplicate
	 * heading steps that had accumulated one page at a time, and no one
	 * could say what distinguished them. This is not that: a single named
	 * use, approved deliberately on 2026-08-19, forced by one string's
	 * length rather than by a step in the design. If that string is ever
	 * shortened, this token and its modifier should be deleted, not
	 * reused for something else.
	 *
	 * !! KNOWN LIMIT, re-verified at the 1140 container. 17px needs
	 * 297.5px and the card's content box shrinks with the viewport:
	 * 313.3px from 1200 up, but 264px at 992. So this holds from 1440 down
	 * to roughly 1015px, and between there and 991 the title wraps again -
	 * a narrower bad band than the 18px value had, but still not none.
	 * At 992 the FIRST card's title wraps as well, so that band is uneven
	 * for reasons no type size can fix. Recorded rather than papered over.
	 *
	 * !! AND ONE MORE, newly measured: section 11 hands this token back to
	 * --type-h4 below 991 on the grounds that "every title fits at the
	 * normal step" once the cards stack full width. That is true at 768
	 * (665px box) and 600 (497px box) but NOT at 390, where the box is
	 * 287px and --type-h4 is 18px, needing 315. The third title wraps on a
	 * phone. Left alone deliberately: stacked, the cards are full width
	 * and a two-line title costs nothing, because the levelling problem
	 * this token exists to solve only exists while the three sit in a row.
	 * The claim in section 11 is what was wrong, not the behaviour. */

	/* REMOVED by this block, and gone from every file:
	 *   --type-h1-sm  (40px, Prenatal H1)      -> --type-h1,  46px
	 *   --type-h2-sm  (32px, Proof + Contact)  -> --type-h2,  34px
	 *   --type-h2-xs  (28px, five Prenatal H2s)-> --type-h2,  34px
	 *   --type-lead   (32px)                   -> never used, deleted
	 *
	 * The four-way heading spread those created is what the fixed ladder
	 * above replaces. The --type-lead / --type-h2-sm 32px collision that
	 * was flagged here resolves by both tokens ceasing to exist. */

	/* !! H3 and H4 both land on 18px at the 599 step. That is the one
	 * place the fixed-step rule produces a tie. Left as designed on
	 * purpose - to be judged against the live page, not pre-empted. */

	/* Header navigation size.
	 *
	 * DELIBERATELY ITS OWN TOKEN, not a reuse of --type-xs or any other
	 * page-scale value. The header is chrome, not content: it should be
	 * possible to re-scale the page type later without dragging the
	 * navigation along with it. Nothing outside section 04 may use this.
	 *
	 * Fixed at every breakpoint - the desktop nav is hidden below the
	 * hamburger takeover point anyway, so it has no mobile variant. */
	/* !! 16px -> 19px, 2026-09-01. This token existing separately is
	 * exactly what made the change safe - the nav moved without dragging
	 * any page type with it, and nothing outside section 04 reads it.
	 *
	 * It went up because at 16px against the new 22px body it had become
	 * the smallest text on the page, sitting under a 262px logo and a
	 * 23px phone number, and it read as undersized rather than as chrome.
	 *
	 * 19px, not 22px: the header should not compete with running copy.
	 * It matches --type-xs numerically, which is a coincidence and not a
	 * dependency - do not replace this with var(--type-xs).
	 *
	 * The space is there. The nav row grows 395 -> 469px, taking the
	 * header to ~1048px against 1352px available, so the gaps land near
	 * 152px each and the 1199 hamburger breakpoint is unaffected (the row
	 * needs 1080px against the 1152px a 1200px viewport gives). */
	--type-header-nav: 19px;

	/* 300 added 2026-09-01 for the header nav only - see the note on the
	 * Google Fonts enqueue in functions.php, which had to load the weight
	 * before this token could mean anything. A font-weight the page never
	 * loaded renders as the nearest one it did, silently. */
	--weight-light:    300;
	--weight-regular:  400;
	--weight-medium:   500;
	--weight-semibold: 600;
	--weight-bold:     700;

	/* Figma leaves line-height at "normal" on nearly every text layer,
	 * which is a design that was never specified rather than a decision.
	 * These are readability defaults, NOT values read from the file, and
	 * they are the one place in this block where a judgement was made.
	 * Flagged for confirmation before body copy is built. */
	--leading-heading: 1.18;   /* the one explicit value in the design */
	--leading-body:    1.6;    /* default, to be confirmed             */


	/* ---- 1.5  Radius - DELIBERATE ADDITIONS ----
	 * 24px is unambiguously the system radius: 36 uses against 1-2 each
	 * for the others.
	 */
	--radius:       24px;
	--radius-sm:    14px;
	--radius-field:  8px;   /* form inputs only - Contact and Booking both
	                         * draw them at exactly 8, and 14 on a 48px box
	                         * reads visibly rounder than the design. Two
	                         * confirmed users; see section 25.2. */
	--radius-pill:  9999px;
	/* --radius-md (20px) removed 2026-08-20 - zero references. */


	/* ---- 1.6  Layout - DELIBERATE ADDITIONS ----
	 *
	 * --container IS 1400px AS OF 2026-09-01. Approved by Gerek, and it
	 * deliberately leaves the Figma frame behind.
	 *
	 * What Figma actually draws, measured off the Header component
	 * (node 794:2607) rather than inferred: logo left edge x=150, nav
	 * group right edge x=1290, on a 1440 frame. So Figma's own content
	 * width is 1140 with a 150px gutter each side. The previous value
	 * was correct against the file.
	 *
	 * It is not the value this site is built to. Figma is a guide to
	 * intent, not a pixel spec - the designer sets direction and the
	 * build makes the sizing calls. The house standard is more room and
	 * larger type than a designer's 1440 artboard implies, so the
	 * container is set from that standard.
	 *
	 * !! NOTE THE PADDING. .container carries padding-inline: 24px, so
	 * the CONTENT BOX is always 48px narrower than this token:
	 *
	 *     --container      content box     visual gutter at 1440vw
	 *     1140 (was)       1092            150
	 *     1400 (now)       1352             44
	 *
	 * The old 1140 was therefore 48px narrower than Figma's own drawn
	 * content, not equal to it - the token was set to Figma's content
	 * width and then had padding taken out of the inside. The build was
	 * running 1092px wide against a design drawn at 1140. That is a real
	 * part of why the header felt cramped, and it is fixed here.
	 *
	 * Header fit at the new width: logo 262 + nav 395 + phone ~140 +
	 * the "Book Now" mini pill ~150 + three 16px gaps = ~995 against
	 * 1352 available. ~357px of slack, against roughly none before.
	 *
	 * --container-narrow is LEFT AT 1140 on purpose. It is the inner
	 * column inside already-padded sections (Prenatal, section 17), and
	 * widening the page container does not automatically mean widening
	 * a reading column. Section-by-section, deliberately, not by
	 * inheritance.
	 */
	--frame:            1440px;  /* design frame width, reference only */
	--container:        1400px;  /* content container - 1352px inside  */
	--container-narrow: 1140px;  /* inner width inside padded sections */

	/* --gutter removed 2026-08-20. Zero references, and its comment said
	 * "(1440 - 1200) / 2" against a value of 120 - arithmetic that stopped
	 * being true when --container moved to 1140, and that contradicted the
	 * prose four lines above it, which correctly says the gutter is 150.
	 * A wrong number nobody uses is worse than no number. */


	/* ---- 1.6b  MEASURE - THE READING WIDTH SCALE ----
	 *
	 * NEW 2026-08-20. Text measure now works the way type does: from a
	 * short central list, not from a number typed into a component.
	 *
	 * Before this there were EIGHT hard-coded reading widths spread across
	 * the stylesheet - 903, 840, 821, 806, 740, 704, 700, 1028 - and three
	 * of them were stale. They had been derived by multiplying Figma's own
	 * width by 22/15 = 1.4667, the factor that kept measures in proportion
	 * with the old 22px body lock. That lock is gone: body is back to
	 * Figma's own 15px, so the multiplier is 1.0 and those three simply
	 * return to the numbers Figma draws.
	 *
	 *     903  <- 616 x 1.4667   ->  --measure-body   616
	 *     821  <- 560 x 1.4667   ->  --measure-lead   560
	 *     704  <- 480 x 1.4667   ->  (see the note on --measure-lead)
	 *
	 * MEASURED, not assumed. Rendered body copy in Poppins 400 at 15px
	 * averages 7.36px per character, so these resolve to:
	 *
	 *     --measure-lead   560px   ~76 characters
	 *     --measure-body   616px   ~84 characters
	 *     --measure-head   700px   headings, so characters do not govern
	 *     --measure-card   840px   ~114 characters, card body - see below
	 *
	 * !! THE 821px "STANDING MEASURE CEILING" IS RETIRED, and it is worth
	 * saying why rather than just deleting it. It was itself a scaled
	 * number (560 x 1.4667) that got adopted as a general-purpose cap for
	 * blocks Figma gives no width to. At 22px body it was about 75
	 * characters, which is the top of the readable band and defensible. At
	 * 15px the SAME 821px is 112 characters - it did not get safer when
	 * the type shrank, it got considerably worse, and every block using it
	 * silently went too wide when the ladder landed.
	 *
	 * Its users take --measure-lead. 560px/76 characters sits at the top
	 * of the 45-75 band rather than 50 past it. A fifth token nearer 520
	 * was considered and rejected: 520 and 560 are not distinguishable by
	 * eye, and a spread of near-duplicate steps that nobody can tell apart
	 * is exactly what the type ladder was created to delete. One of them
	 * or the other, not both.
	 *
	 * --measure-card USED to stay wide on purpose - Figma's own 840 for
	 * the "Why One Adjustment" card. Superseded 2026-09-01: see its own
	 * note below. It is now at the 1100 reading ceiling with everything
	 * else.
	 *
	 * NOT EVERY WIDTH IS A MEASURE. 806 (the is/isn't comparison row),
	 * 740 (the AEO paragraph) and 1028 (the Is-This-Right-For-You inner
	 * frame) are component geometry Figma states directly, not reading
	 * caps, and they keep their own literals. */
	/* !! RE-SCALED 2026-09-01, x 22/15 = 1.4667. THIS IS A BUG FIX, NOT A
	 * PREFERENCE, and it is the direct consequence of body moving 15 -> 22.
	 *
	 * These tokens are DERIVED FROM BODY SIZE - the block above sets them
	 * by character count, at "7.36px per character in Poppins 400 at
	 * 15px". Raising body without raising them left every measure
	 * measuring the wrong thing:
	 *
	 *              held    chars at 15px    chars at 22px    now
	 *   lead       560          76               52          821
	 *   body       616          84               57          903
	 *   head       700          -                 -         1027
	 *   card       840         114               77         1232
	 *
	 * A 616px column of 22px text is 57 characters, well UNDER the 45-75
	 * band, which is what made the "Trained for This" section read as a
	 * narrow ribbon in a wide empty page. Gerek reported it as "the column
	 * is way too narrow" and he was reading a real defect.
	 *
	 * These are the same numbers the block above records as the widths
	 * that existed before 2026-08-20 - 903, 840, 821, 704, 700. They were
	 * removed as stale when body went to 15. With body back at 22 they
	 * are correct again, so this restores them rather than inventing
	 * anything: the multiplier tracks the body size, exactly as designed.
	 *
	 * !! EVERY CENTRED TEXT COLUMN ON EVERY PAGE GETS WIDER. That is the
	 * point, and it is also the risk - only the homepage has been looked
	 * at. Flagged for the page-by-page pass. */
	--measure-lead:  821px;   /* intro and closing lines, general prose   */
	--measure-body:  903px;   /* running body copy in a centred column    */
	--measure-head: 1027px;   /* headings that need a cap                 */
	/* !! 1232 -> 1100, 2026-09-01. It was the one token left ABOVE the
	 * reading ceiling Gerek set on the same day, at ~114 characters a
	 * line against --measure-wide's ~102.
	 *
	 * Its old note said it "stays wide on purpose - Figma's own 840 for
	 * the Why One Adjustment card... the design's decision rather than an
	 * artifact of the old multiplier. Recorded as wide, not corrected."
	 * The first half is still true and the conclusion is not: 840 was
	 * Figma's number against Figma's 1092 content box, and once the box
	 * became 1352 the x1.4667 rescale carried it to 1232, which is a
	 * width Figma never drew.
	 *
	 * !! IT NOW EQUALS --measure-wide, which makes it a merge candidate,
	 * NOT a merge. They still mean different things - "body inside a wide
	 * card" against "a full-width centred section" - and they are set
	 * from different reasoning even where they land on the same number.
	 * Collapsing them would lose that. Revisit if they are still equal
	 * after the page-by-page pass. */
	--measure-card: 1100px;   /* body inside a wide card - at the ceiling */

	/* NEW 2026-09-01. The full-width centred section - a heading and body
	 * that own the whole page rather than sitting in a reading column.
	 *
	 * Approved by Gerek at "1100 or 1200" for the "Trained for This" and
	 * "One System, Every Stage" sections, which are the two that use it.
	 * 1100 against the 1352px content box leaves 126px each side, so the
	 * block reads as deliberately inset rather than as an accident.
	 *
	 * !! IT IS WIDER THAN THE READING BAND, on purpose. At 22px this is
	 * about 102 characters a line against a 45-75 comfortable band, and
	 * the text is centre-aligned, which makes a long line harder to track
	 * back than a left-aligned one. That is a deliberate call to fill the
	 * page - recorded, not hidden, because it is the kind of thing that
	 * looks like an oversight to whoever reads this next. */
	--measure-wide: 1100px;   /* full-width centred section - see above   */


	/* ---- 1.7  Spacing - THE APPROVED SCALE ----
	 *
	 * THESE EIGHT VALUES ARE THE ONLY SPACING ALLOWED ON THIS SITE.
	 * Every padding, margin and gap must come from here. If a gap looks
	 * wrong, change it here and the whole site follows - never type a
	 * spacing number into a component.
	 *
	 *     8  12  16  20  24  32  40  48
	 *
	 * !! THIS IS A DELIBERATE SITE-WIDE DEVIATION FROM FIGMA.
	 * Approved by Gerek on 2026-08-18. The design's structural gaps run
	 * well past this ceiling - a 100px section rhythm, a 150px card
	 * padding, a 94px hero gap - and all of them are capped at 48 here.
	 * The trade is deliberate: exact Figma match is given up in exchange
	 * for a scale that is short enough to hold in your head and can be
	 * changed in one place. Several sections read measurably denser than
	 * the design as a result. That is the accepted outcome, not a bug.
	 * Logged as B5 in LAUNCH-CHECKLIST.md.
	 *
	 * NOTHING MAY EXCEED 48. That includes values built with calc() -
	 * two on-list tokens added together can breach the ceiling, which is
	 * exactly what .prenatal-fit__inner used to do.
	 *
	 * TIE RULE: a value landing exactly between two steps ROUNDS UP. One
	 * rule everywhere beats ten judgement calls. That is how 10 became
	 * 12, 14 became 16, 18 became 20 and 28 became 32.
	 *
	 * Removed in this pass: 2, 4, 6, 10, 14, 18, 28, 30, 50, 56 and 80.
	 *
	 * Comments elsewhere in this file still quote Figma's own numbers -
	 * "Figma draws this at 30", "the design's 100px rhythm". Those remain
	 * true statements about the DESIGN. Where the built value differs it
	 * is this scale's nearest step, and the comment says so.
	 *
	 * !! NOT EVERYTHING THAT LOOKS LIKE SPACING IS SPACING. This scale
	 * governs padding, margin and gap. Corner radii, animation offsets
	 * and fixed component dimensions are their own thing and must not be
	 * bent onto it - see the focus ring in section 02 and the sub-menu
	 * transform in section 04, which carry their own literals.
	 */
	--space-8:   8px;
	--space-12: 12px;
	--space-16: 16px;
	--space-20: 20px;
	--space-24: 24px;
	--space-32: 32px;
	--space-40: 40px;
	--space-48: 48px;

	/* ---- 1.7b  THE ONE PADDING THAT BREAKS THE CEILING ----
	 *
	 * --hero-content-padding is the single exception to the rule above.
	 * Both 56 and 60 sit past the 48px ceiling B5 established, and that
	 * is the point of it: it is a named, logged deviation rather than a
	 * value that drifted. Same precedent as --type-h4-long in the type
	 * scale and the ICPA navy in section 17 - one use, written down, with
	 * the reason attached.
	 *
	 * PROVENANCE. This value was read from the real Figma render by live
	 * DevTools inspection, not from a redline badge. That distinction is
	 * load-bearing: a badge on one side showed roughly 58, and the
	 * inspected value is the source of truth. Do NOT re-derive this from
	 * Figma's visual numbers - if it ever needs changing, inspect the
	 * render again.
	 *
	 * SCOPE. Homepage only, and read by exactly one selector:
	 * .hero__content-col--home in section 08b. The bare
	 * .hero__content-col is shared with the Prenatal hero and must not
	 * carry this - see the note on the modifier.
	 *
	 * The 0 on the left is not an omission. The content column sits to
	 * the right of the hero photograph, so its left edge is already held
	 * off by .hero__split's gap; the 60 on the right insets it from the
	 * container edge.
	 *
	 * RELEASED BELOW 768, where .hero__split turns the row into a column
	 * and the content column goes full width. Kept there it measured 24px
	 * of inset on the left against 84px on the right - 60px of empty
	 * strip down one side of a 342px phone screen. Section 08b's existing
	 * 767 block resets it to 0.
	 *
	 * Approved by Gerek 2026-08-19. Logged as B6 in LAUNCH-CHECKLIST.md.
	 */
	--hero-content-padding: 56px 60px 56px 0;

	/* ---- 1.8  Header metrics ----
	 * !! ITS ORIGINAL REASON IS GONE. This was published as a variable so
	 * the sticky header's height could feed a scroll-margin-top on anchor
	 * targets. The header is not sticky as of 2026-09-01, and that
	 * scroll-margin-top was never written on anything - checked before
	 * unsticking, not assumed.
	 *
	 * Kept because it is still the header's vertical padding and a
	 * sensible place to set it from. It is now just that, and no longer
	 * a scroll offset anything reads. */
	--header-pad-y: var(--space-16);
}


/* ---- 1.4b  RESPONSIVE LADDER ----
 * The four heading steps - and, as of 2026-09-01, body and the sizes
 * below it, at ONE breakpoint only.
 *
 * Fixed decrement per breakpoint: H1 -5, H2 -4, H3 -3, H4 -2. Widened
 * from -4/-3/-2/-1 when the desktop values went up in section 1.4; at
 * the old steps a 56px H1 would still have been 40px on a phone. Whole
 * pixels throughout, so there is nothing to round.
 *
 *            1440+   1199    991     767     599
 *   H1        56      51      46      41      36     step -5
 *   H2        46      42      38      34      30     step -4
 *   H3        30      27      24      21      18     step -3
 *   H4        26      24      22      20      18     step -2
 *   body      22      24      24      24      18     see the note below
 *
 * !! BODY IS NO LONGER FLAT. It was, at Figma's 15px. The house standard
 * is 22px at desktop, laptop AND tablet, dropping to 18px at mobile -
 * so there is exactly one body breakpoint, at 768, and tablet stays on
 * the desktop value deliberately rather than stepping with the headings.
 *
 * The five sizes below body drop with it by 18/22 = 0.818, the same way
 * they were scaled UP with it in section 1.4. The smallest three round
 * slightly above the strict factor so nothing lands under 14px on a
 * phone, where the strict result would be 13.09.
 *
 * Nothing moves at 1199, 991 or 599 except headings. If a size is not a
 * heading it appears in the 767 block and nowhere else.
 *
 * Breakpoints are the locked four: 1200 / 992 / 768 / 600, written as
 * max-width one pixel below each.
 * ================================================================== */
@media (max-width: 1199px) {
	:root {
		--type-h1: 51px;
		--type-h2: 42px;
		--type-h3: 27px;
		--type-h4: 24px;
	}
}

/* !! TABLET NOW HAS ITS OWN BODY SIZE, 2026-09-01, AND THE BAND MOVED.
 *
 * Gerek asked what size body copy is on a tablet, said it "seems way too
 * small, almost like it's 18 or 20", and asked for about +4. He was
 * reading it correctly, and the reason is worse than the number:
 *
 * THERE WAS NO TABLET STEP. Body had exactly two values - 22px above
 * 768 and 18px below - so the boundary landed in the middle of the iPad
 * range:
 *
 *     iPad Pro 11 portrait     834px  ->  22px
 *     iPad 10.9 portrait       820px  ->  22px
 *     iPad Mini portrait       744px  ->  18px   <- PHONE TYPE ON A TABLET
 *     iPhone Pro Max           430px  ->  18px
 *
 * An iPad Mini was getting the phone scale. Which tablet you picked up
 * decided whether you got 22 or 18, with nothing in between.
 *
 * So the fix is two changes, not one:
 *
 *   1. A real tablet step at 24px.
 *   2. The PHONE boundary moves 767 -> 599, so no tablet can fall into
 *      it. 600-767 is small-tablet and foldable territory; phones in
 *      portrait are 320-430.
 *
 *     >= 992   22px   desktop
 *     600-991  24px   tablet      <- new, and covers every iPad
 *     <= 599   18px   phone
 *
 * !! TABLET IS LARGER THAN DESKTOP. That looks like a mistake and is
 * not. A tablet is held closer than a monitor sits, and its measure is
 * narrower, so the same physical legibility needs more CSS pixels.
 * Gerek asked for 24 specifically. Recorded so nobody "corrects" it back
 * down to a monotonic ladder.
 *
 * The five sizes below body scale with it at 24/22 = 1.0909, the same
 * way they scale at 18/22 for the phone. Headings are unchanged - their
 * ladder is separate and nobody has said it is wrong.
 */
@media (max-width: 991px) {
	:root {
		--type-h1: 46px;
		--type-h2: 38px;
		--type-h3: 24px;
		--type-h4: 22px;

		--type-body:    24px;  /* tablet - Gerek's number               */
		--type-ui:      25px;  /* 23 x 1.0909 = 25.09                   */
		--type-small:   22px;  /* 20 x 1.0909 = 21.82                   */
		--type-xs:      21px;  /* 19 x 1.0909 = 20.73                   */
		--type-eyebrow: 20px;  /* 18 x 1.0909 = 19.64                   */
		--type-micro:   17px;  /* 16 x 1.0909 = 17.45                   */
	}
}

/* Headings only. The body group that used to live here has moved DOWN to
 * the 599 block - see the note above. 600-767 is a small tablet and now
 * takes the 991 block's 24px rather than the phone's 18. */
@media (max-width: 767px) {
	:root {
		--type-h1: 41px;
		--type-h2: 34px;
		--type-h3: 21px;
		--type-h4: 20px;
	}
}

/* The phone. */
@media (max-width: 599px) {
	:root {
		--type-h1: 36px;
		--type-h2: 30px;
		--type-h3: 18px;
		--type-h4: 18px;  /* ties with H3 - see the note in 1.4 */

		--type-body:    18px;  /* LOCKED - the mobile half of the rule  */
		--type-ui:      19px;  /* 23 x 0.818 = 18.82                    */
		--type-small:   17px;  /* 20 x 0.818 = 16.36, rounded up        */
		--type-xs:      16px;  /* 19 x 0.818 = 15.55, rounded up        */
		--type-eyebrow: 15px;  /* 18 x 0.818 = 14.73, rounded up        */
		--type-micro:   14px;  /* 16 x 0.818 = 13.09, floored at 14     */
	}
}


/* ==================================================================
 * 02  BASE
 *
 * Bare element fallbacks exist deliberately. Stage 1.5's rule is that
 * every heading carries a class OR the stylesheet provides element
 * fallbacks - this build chose fallbacks, so an unclassed heading can
 * never silently render at body size.
 * ================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--surface-white);
	color: var(--neutral-ink);
	font-family: var(--font-body);
	font-size: var(--type-body);
	font-weight: var(--weight-regular);
	line-height: var(--leading-body);
}

h1, h2, h3, h4, h5, h6 {
	margin: 0 0 var(--space-16);
	font-family: var(--font-heading);
	font-weight: var(--weight-bold);
	line-height: var(--leading-heading);
	color: var(--neutral-ink);
}

h1 { font-size: var(--type-h1); }
h2 { font-size: var(--type-h2); }
h3 { font-size: var(--type-h3); }
h4 { font-size: var(--type-h4); }
/* h5/h6 have no step of their own in the four-level scale, so they share
 * H4's token - the smallest heading size there is. That keeps them on the
 * central list rather than a typed-in number, and keeps them above body
 * copy at every width (22 down to 18, against a flat 15). Previously they
 * shared --type-body, which at 15px would now render them identical to a
 * paragraph. Neither page uses one today; this is the fallback. */
h5, h6 { font-size: var(--type-h4); }

p { margin: 0 0 var(--space-16); }

a { color: var(--brand-coral-deep); }

img, svg { max-width: 100%; height: auto; }

button { font: inherit; }

/* Visible focus on everything interactive. Built in the first time,
 * never retrofitted. */
:focus-visible {
	outline: 3px solid var(--brand-coral-deep);
	outline-offset: 2px;
	/* A literal, not a spacing token. This is a corner radius on a focus
	 * ring - not a gap between things - so the approved spacing scale does
	 * not govern it and must not be bent to fit it. */
	border-radius: 2px;
}


/* ==================================================================
 * 03  SHARED UTILITIES
 * ================================================================== */

.container {
	width: 100%;
	max-width: var(--container);
	margin-inline: auto;
	padding-inline: var(--space-24);
}

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

.skip-link {
	position: absolute;
	top: var(--space-8);
	left: var(--space-8);
	z-index: 100;
	padding: var(--space-8) var(--space-16);
	background: var(--surface-white);
	color: var(--neutral-ink);
	border-radius: var(--radius-sm);
	box-shadow: var(--shadow-card);
	text-decoration: none;
}
.skip-link:not(:focus):not(:active) {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}


/* ==================================================================
 * 04  SITE HEADER
 *
 * STATIC, not sticky. Navigation comes from the WordPress 'primary'
 * menu location - nothing here is hardcoded.
 * ================================================================== */

/* !! UNSTUCK 2026-09-01. This was `position: sticky; top: 0` since the
 * first build. Gerek's call: the header scrolls away with the page.
 *
 * position: relative, NOT static, because z-index only applies to a
 * positioned element and the header still has to paint above what
 * follows it - the dropdown sub-menus open over the hero, and the whole
 * bar sits over the fixed background layer from section 08.
 *
 * The shadow STAYS. Only the sticky behaviour was removed, so the bar
 * still reads as a distinct row while it is on screen.
 *
 * Nothing depended on the sticky: --header-pad-y was published for a
 * scroll-margin-top that was never written, and no anchor target on the
 * site sets one. Checked before removing rather than assumed. */
.site-header {
	position: relative;
	z-index: 50;
	background: var(--surface-white);
	box-shadow: var(--shadow-card);
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	/* 16px. This was a hard constraint when the container was 1140 and
	 * the CTA read "Book Your Appointment" - at 24px the row spilled past
	 * the container edge. It is not a constraint any more:
	 *
	 *   logo 262 + nav 395 + phone ~140 + "Book Now" pill ~150
	 *   + three 16px gaps = ~995 against 1352 available
	 *
	 * ~357px of slack. Left at 16 because it is a deliberate spacing
	 * value that looks right, not because it has to be - if the gap ever
	 * wants to grow, it now can. */
	gap: var(--space-16);
	padding-block: var(--header-pad-y);
}

/* margin-inline-end: auto is what right-packs the row, 2026-09-01.
 *
 * The row used to be justify-content: space-between across three items,
 * which spends the slack as TWO equal gaps and parks the nav in the
 * middle of the header. Gerek wants the nav grouped with the phone and
 * the button on the right, which is also how Figma draws it - logo at
 * x=150, then nothing until the nav group at x=708.
 *
 * An auto margin on the logo eats all the free space in one place, so
 * the gap lands after the logo and nav/actions sit together on the
 * --space-40 below. space-between is left on .site-header__inner: with
 * the auto margin absorbing the slack there is nothing left for it to
 * distribute, and it still does the right thing on the wrapped
 * two-row layout below 991, where it pushes the hamburger to the edge. */
/* align-items: center so the badge and the wordmark share a centre line
 * rather than sitting on a shared baseline, which a circle does not
 * really have. */
.site-header__logo {
	display: flex;
	align-items: center;
	gap: var(--space-12);
	flex-shrink: 0;
	margin-inline-end: auto;
	text-decoration: none;
}

/* !! THE HEADING FACE, NOT THE BODY FACE. Baloo 2 is what the badge's
 * own lettering is closest to, and it is the only face on this site with
 * the rounded, friendly weight the brand is built on - which is the
 * reason black type was rejected here in the first place.
 *
 * white-space: nowrap because "Momma's Chiro" breaking across two lines
 * beside a circle reads as a layout fault rather than as a lockup. */
.site-header__wordmark {
	font-family: var(--font-heading);
	font-size: 26px;
	font-weight: var(--weight-bold);
	line-height: 1;
	color: var(--accent-cyan-deep);
	white-space: nowrap;
}
/* !! SQUARE, AND SIZED BY BOTH AXES. The old value was `width: 262px;
 * height: auto` for a 524x44 horizontal lockup. The badge is 200x200, so
 * a width-only rule would have made it 262px tall and pushed the header
 * to nearly 300px.
 *
 * 72px is the size the wording inside the disc becomes readable at -
 * measured, not guessed: the inner "Momma's Chiro" band is 21% of the
 * badge, so it renders about 15px tall here. Below roughly 60px it stops
 * being legible and the mark reads as an anonymous blue dot.
 *
 * The header takes its height from its tallest child, so this alone
 * grows the bar from 72px to about 104px - the "10 to 20 pixels each
 * way" Gerek asked for, without touching any padding. */
.site-header__logo img { display: block; width: 72px; height: 72px; }

/* The gap between the nav and the phone number. Its own rule because it
 * is the one distance in the header that reads as deliberate now that
 * everything else is packed right - 40px separates two groups, where the
 * 28px inside the nav separates items within one group. */
.primary-nav { margin-inline-end: var(--space-40); }

.site-header__actions {
	display: flex;
	align-items: center;
	gap: var(--space-16);
	flex-shrink: 0;
}

/* Click-to-call. Present at every breakpoint. */
.header-phone {
	font-size: var(--type-ui);
	font-weight: var(--weight-bold);
	color: var(--neutral-ink);
	text-decoration: none;
	white-space: nowrap;

	/* OPTICAL NUDGE, 2026-09-01, and it is worth saying that this is not
	 * a centring bug being corrected.
	 *
	 * Measured against the real rendered glyphs - baseline derived from
	 * Poppins' own fontBoundingBox metrics, ink extents from canvas
	 * actualBoundingBox, not from the line box, which lies about where
	 * the letters actually are:
	 *
	 *     phone ink centre   35.9px
	 *     button box centre  36.0px
	 *
	 * 0.1px. Geometrically it is already dead centre.
	 *
	 * It still READS high, and the reason is the button, not the phone:
	 * .cta-button--icon carries `box-shadow: 0 6px 20px -6px`, a shadow
	 * offset 6px DOWNWARD. That weight below the pill drags its perceived
	 * centre lower than its geometric one, so a neighbour at true centre
	 * looks like it is floating.
	 *
	 * 3px, not the 5 Gerek estimated by eye - half the shadow's 6px
	 * offset, which is the amount the illusion is actually worth. Beyond
	 * about 3 it stops correcting an illusion and starts being visibly
	 * off-centre in the other direction on a screenshot.
	 *
	 * position/top rather than margin or padding so it moves the glyphs
	 * without changing the flex row's height or the click target. */
	position: relative;
	top: 3px;
}
.header-phone:hover { color: var(--brand-coral-deep); }

/* The booking CTA base.
 *
 * !! THE HEADER NO LONGER USES THIS ON ITS OWN. As of 2026-09-01 the
 * header button is `.cta-button .cta-button--icon .cta-button--mini` -
 * the same pill-plus-arrow component every other CTA on the site uses,
 * at the 36px mini size. It was the only button on the site without the
 * arrow, which is what made it look like a different component.
 *
 * The old note here said the header label was "Book Your Appointment"
 * and the menu label "Book Now", "two different strings, deliberately".
 * That is reversed: the header now says "Book Now" everywhere, which is
 * what buys the space, and the long string is left to the in-page CTAs
 * where there is room for it. Figma agrees - its header button is the
 * component literally named "Button / Circle-in-Bar / Coral (Glass) /
 * Mini", 113 x 36, arrow included.
 *
 * This base rule still supplies the gradient, radius, colour and hover
 * to all three, and is still used alone by the disabled/static variants
 * in sections 21 and 25. */
.cta-button {
	display: inline-block;
	padding: var(--space-12) var(--space-24);
	background: var(--gradient-cta);
	color: var(--surface-white);
	font-size: var(--type-xs);
	font-weight: var(--weight-semibold);
	line-height: 1.2;
	text-decoration: none;
	border: 0;
	border-radius: var(--radius-pill);
	white-space: nowrap;
}
/* ---- button interaction states ----
 *
 * !! brightness(1.05) IS NOT A HOVER STATE. That was the whole response
 * to hovering until 2026-09-02, and Gerek's report was exact: "I barely
 * could tell that the button's even working." A 5% brightness shift on a
 * mid-tone coral gradient is roughly one step of 8-bit colour - real, in
 * the sense that a colour picker can find it, and invisible to a person
 * deciding whether their click registered.
 *
 * There was also NO :active state at all, so pressing a button gave no
 * acknowledgement whatsoever. On the checkout button that is the worst
 * possible place for it: somebody who is not sure their click landed
 * clicks again, and on a payment form a second click is a thing people
 * genuinely worry about.
 *
 * Three states, deliberately different in KIND rather than in degree, so
 * they cannot be confused with each other:
 *
 *   hover  - lifts 1px, gains a shadow, brightens properly. Reads as
 *            "this is a thing you can press".
 *   active - drops back to the floor, shadow collapses, darkens. Reads
 *            as a physical press, and is the acknowledgement that was
 *            missing entirely.
 *   focus  - a visible ring, for anybody arriving by keyboard. It uses
 *            :focus-visible so it does not fire on mouse clicks.
 *
 * The shadow is tinted with the brand coral rather than neutral black:
 * a grey shadow under a coral gradient reads as dirt, a coral one reads
 * as glow. */
.cta-button {
	transition: filter .15s ease, transform .15s ease, box-shadow .15s ease;
}

.cta-button:hover {
	filter: brightness(1.12) saturate(1.05);
	transform: translateY(-1px);
	box-shadow: 0 6px 16px rgb(221 90 63 / 38%);
}

.cta-button:active {
	filter: brightness(.92) saturate(1.05);
	transform: translateY(0);
	box-shadow: 0 1px 3px rgb(221 90 63 / 30%);
}

.cta-button:focus-visible {
	outline: 3px solid var(--brand-coral-deep);
	outline-offset: 3px;
}

/* !! THE TRANSFORM IS THE ONLY PART THAT HAS TO GO. Somebody who asks
 * for reduced motion still needs to know their click landed, so the
 * colour and shadow changes stay and only the movement is dropped. */
@media (prefers-reduced-motion: reduce) {
	.cta-button {
		transition: filter .15s ease, box-shadow .15s ease;
	}

	.cta-button:hover,
	.cta-button:active {
		transform: none;
	}
}

/* ---- primary navigation ---- */
/* 32px between Tier-1 items, up from 12 on 2026-09-01.
 *
 * 12 was never a design value - it was the largest gap that fitted when
 * the row was fighting a 1092px content box, a 262px logo and a 455px
 * actions group carrying "Book Your Appointment". Both of those changed
 * (1352px box, ~150px button), so the constraint that produced 12 is
 * gone and the items were merely bunched, which is what Gerek reported.
 *
 * !! THIS WAS WRITTEN AS var(--space-28) FIRST AND SILENTLY DID NOTHING.
 * There is no --space-28 on the approved scale (8/12/16/20/24/32/40/48),
 * so the declaration was invalid and `gap` fell back to its initial
 * `normal` - which is ZERO for flex. The nav rendered with the five
 * labels touching. It was caught by measuring computed gap in the
 * browser, not by reading the CSS, because an undefined custom property
 * throws no error and shows nothing in the file.
 *
 * 32 is --space-32, a real step. 24 was the other candidate and reads
 * as too tight for a nav this wide; 40 is the group gap to the phone
 * number below, and using it here would flatten the distinction between
 * "space between items" and "space between groups". */
.primary-nav > ul {
	display: flex;
	align-items: center;
	gap: var(--space-32);
	margin: 0;
	padding: 0;
	list-style: none;
}
.primary-nav li { position: relative; }

.primary-nav a,
.primary-nav .menu-item > span {
	display: block;
	color: var(--neutral-muted);
	font-size: var(--type-header-nav);
	/* 300, not the inherited 400. Needs Poppins 300 in the font enqueue -
	 * without it this renders as 400 and looks like nothing happened. */
	font-weight: var(--weight-light);
	text-decoration: none;
	white-space: nowrap;
}
.primary-nav a:hover,
.primary-nav .current-menu-item > a { color: var(--brand-coral-deep); }

/* Dropdown parents with no page behind them render as a <span>, not an
 * anchor, so they are never a broken link. */
.primary-nav .menu-item > span { cursor: default; }

.primary-nav .sub-menu {
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 60;
	display: grid;
	gap: var(--space-8);
	min-width: 220px;
	margin: 0;
	padding: var(--space-16);
	background: var(--surface-white);
	border-radius: var(--radius-sm);
	box-shadow: var(--shadow-card);
	list-style: none;
	opacity: 0;
	visibility: hidden;
	/* A literal, not a spacing token: this is how far the menu slides
	 * while it fades in, not a gap between elements. Animation distance is
	 * its own concern and the spacing scale does not govern it. */
	transform: translateY(4px);
	transition: opacity .15s ease, transform .15s ease, visibility .15s;
}
.primary-nav li:hover > .sub-menu,
.primary-nav li:focus-within > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: none;
}

/* ---- hamburger ---- */
/* An icon-only square since 2026-09-01 - the word "Menu" moved to
 * .visually-hidden in the markup. 48px, so it stays a comfortable touch
 * target now that the label is no longer padding it out. */
.nav-toggle {
	display: none;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	padding: 0;
	background: none;
	border: 1px solid var(--brand-coral);
	border-radius: var(--radius-sm);
	color: var(--neutral-ink);
	font-size: var(--type-micro);
	cursor: pointer;
}
.nav-toggle__bars {
	display: grid;
	gap: 5px;                /* icon geometry, not layout spacing, so not
	                          * on the --space- scale */
	width: 26px;             /* 20 -> 26 now that the bars are the whole
	                          * control rather than a mark beside a word */
}
.nav-toggle__bars span {
	display: block;
	height: 2px;
	background: var(--neutral-ink);
	border-radius: var(--radius-pill);
}


/* ==================================================================
 * 05  MOBILE MENU
 *
 * A separate component with its own markup and behaviour. Hidden from
 * assistive tech and from tab order while closed.
 * ================================================================== */

.mobile-menu {
	position: fixed;
	inset: 0;
	z-index: 90;
	display: grid;
	grid-template-rows: auto 1fr;
	background: var(--surface-white);
	overflow-y: auto;
	visibility: hidden;
	opacity: 0;
	transition: opacity .2s ease, visibility .2s;
}
.mobile-menu[data-open="true"] { visibility: visible; opacity: 1; }

.mobile-menu__top {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-16);
	padding: var(--space-16) var(--space-24);
	border-bottom: 1px solid var(--surface-grey);
}
.mobile-menu__close {
	padding: var(--space-8) var(--space-16);
	background: none;
	border: 1px solid var(--brand-coral);
	border-radius: var(--radius-sm);
	color: var(--neutral-ink);
	font-size: var(--type-micro);
	cursor: pointer;
}

.mobile-menu__body { padding: var(--space-24); }

.mobile-menu ul { margin: 0; padding: 0; list-style: none; }
.mobile-menu > .mobile-menu__body > ul > li + li { margin-top: var(--space-8); }

.mobile-menu a,
.mobile-menu .menu-item > span {
	display: block;
	padding: var(--space-12) 0;
	color: var(--neutral-ink);
	font-size: var(--type-ui);
	text-decoration: none;
}
.mobile-menu .menu-item > span {
	color: var(--neutral-muted);
	font-weight: var(--weight-semibold);
}
.mobile-menu .sub-menu {
	padding-left: var(--space-16);
	border-left: 2px solid var(--brand-pink);
}
.mobile-menu .sub-menu a { font-size: var(--type-body); }

.mobile-menu__actions {
	display: grid;
	gap: var(--space-12);
	margin-top: var(--space-24);
	padding-top: var(--space-24);
	border-top: 1px solid var(--surface-grey);
}
/* !! display: flex IS THE FIX, AND IT IS THE POINT OF THIS RULE.
 *
 * `.mobile-menu a { display: block }` in this same section matches the
 * booking button - it IS an <a> inside .mobile-menu - and at one class
 * plus one element (0,1,1) it OUTRANKS `.cta-button--icon` (0,1,0).
 * So the button was not a flex row at all inside the drawer.
 *
 * What that looked like: the label centred on its own line by
 * text-align, and the arrow - which is its own flex container - falling
 * to a second line, hard against the left edge of a full-width pill.
 * Gerek's screenshot. Every other property was landing correctly, which
 * is why it looked like a spacing problem rather than a display one.
 *
 * `.mobile-menu__actions .cta-button` is (0,2,0) and wins. justify and
 * align then do what they were always meant to.
 *
 * The label is a step smaller than the header's 18px - Gerek read the
 * drawer button as slightly too big, and it is the one place the button
 * spans the full width, so the same size reads larger here. */
.mobile-menu__actions .cta-button {
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
}

/* !! THREE CLASSES, and the two declarations below are the reason.
 *
 * Written first as `.mobile-menu__actions .cta-button` - (0,2,0) - they
 * lost to `.cta-button.cta-button--mini` in the 767 block, which is the
 * SAME specificity and sits LATER in the file. Measured: the label
 * stayed 18px and the padding stayed --mini's asymmetric 20-left /
 * 8-right, which pushed the label-and-arrow pair exactly 6px right of
 * the pill's centre. Both silent.
 *
 * That is the fourth source-order loss in this build. The pattern is
 * always the same - a narrow override written earlier in the file than
 * the general rule it means to beat.
 *
 * 17px: a step under the header's 18. Gerek read the drawer button as
 * slightly too big, and this is the one place the pill spans the full
 * width, so the same size reads larger here.
 *
 * Symmetrical padding: --mini's 20/8 exists to balance an arrow sitting
 * at the END of a shrink-to-fit pill. Centred inside a full-width one,
 * that offset only pushes the pair off-centre. */
.mobile-menu__actions .cta-button.cta-button--mini {
	font-size: 17px;
	padding: var(--space-12);
}

/* Centred, not left-aligned. `.mobile-menu a { display: block }` makes
 * this a full-width block whose text then starts at the left edge, under
 * a centred button - which is what Gerek saw. Same specificity fix. */
.mobile-menu__actions .header-phone {
	display: block;
	text-align: center;
	/* No optical nudge here. The 3px in section 04 corrects for the
	 * header button's drop shadow sitting beside it; in the drawer the
	 * phone is on its own line with nothing to align to. */
	position: static;
	top: auto;
}

body.is-menu-open { overflow: hidden; }


/* ==================================================================
 * 06  SITE FOOTER
 *
 * Carries NAP and the licence. These are compliance requirements, not
 * decoration. The licence belongs to Dr. Aimee personally - never to
 * the practice name and never to the legal entity.
 *
 * SECOND REBUILD 2026-09-01. The first one kept Figma's three link
 * columns and came out tall and loose; Gerek's read was "too big, too
 * spread out", and the columns only repeated the header nav. This is the
 * sister site's footer shape - HOURS | BRAND | MAP over a legal line -
 * carrying Momma's content, in Figma's colour.
 *
 * !! THE COLOUR IS FIGMA'S, SAMPLED, AND THE TYPE IS INK. Do not
 * re-invert it to white. The Figma footer node (776:4141) was rendered
 * and its pixels read: #8ad5ee at the top, #0daed7 at the bottom.
 * Measured against that actual gradient:
 *
 *     text colour       on #8ad5ee   on #0daed7   AA 4.5?
 *     white              1.64         2.61        FAIL
 *     --neutral-ink      8.00         5.02        PASS
 *     --neutral-muted    3.34         2.09        FAIL
 *
 * White on Figma's own blue is 1.64:1 - about light grey on white, and
 * visible in Figma's own render, where the link columns nearly vanish.
 * Ink keeps the bright blue AND stays readable, and it matches the rest
 * of the site, which is ink-on-pale everywhere else.
 *
 * --neutral-muted is unusable here at 2.09. Every secondary tone is INK
 * AT FULL STRENGTH, with hierarchy carried by size, weight and
 * letterspacing. Fading ink toward the background is what fails.
 *
 * !! THE FOOTER HAS ITS OWN TYPE SCALE, six properties scoped to
 * .footer-panel alongside the colours. The page scale was too big here -
 * --type-small is 20px, which is body copy, not footer chrome. Same
 * argument as --type-header-nav in section 04: the footer is chrome, and
 * re-scaling the page should not drag it along. Nothing outside this
 * section may read these.
 *
 * !! IF YOU CHANGE THE PANEL COLOUR, RE-MEASURE EVERY TEXT TONE. At
 * these hues text looks legible on a screen it fails on.
 * ================================================================== */

.site-footer {
	/* No background of its own. The panel carries the colour, and the
	 * page shows around it. */
	margin-top: var(--space-48);
	padding-block: var(--space-48);
}

/* THE PANEL. The properties below are the whole footer vocabulary -
 * nothing in this section names a colour or a size directly.
 *
 * 180deg, matching the direction Figma's own fill runs. An earlier draft
 * used a 135deg diagonal, which put its lightest point in the top left,
 * where the headings sit - the one corner that had to be readable was
 * the one being lightened. */
.footer-panel {
	/* Sampled from Figma node 776:4141. Raw hex, deliberately: these are
	 * one component's measured fill, not brand tokens, and promoting them
	 * to :root would imply the rest of the site may use them.
	 * --accent-cyan (#06add7) is close to the bottom stop but is not it. */
	--footer-from:      #8ad5ee;
	--footer-to:        #0daed7;
	--footer-ink:       var(--neutral-ink);
	--footer-rule:      rgba(51, 48, 47, 0.18);
	--footer-chip:      rgba(255, 255, 255, 0.55);
	--footer-mark:      rgba(255, 255, 255, 0.28);

	/* Footer type scale. Smaller than the page scale on purpose - see the
	 * section header. Roughly the sizes the sister site's footer uses. */
	--footer-heading-size: 14px;
	--footer-text-size:    16px;
	--footer-phone-size:   24px;
	--footer-legal-size:   14px;

	position: relative;
	overflow: hidden;             /* clips the oversized mark */
	isolation: isolate;           /* keeps the mark behind, without z-index on every child */
	/* Asymmetric on purpose. 48 top, 32 bottom.
	 *
	 * Gerek: the three columns sat too close to the panel's top edge and
	 * wanted "40 to 60 pixels total of top space". 32 was the whole gap.
	 * 48 is --space-48, inside that band, and the extra weight above the
	 * content is what makes the panel read as deliberate rather than as
	 * something the content was poured into.
	 *
	 * The bottom stays 32 because the legal line already carries its own
	 * 20px above the hairline - matching the top would have put nearly
	 * 70px under the last line of type. */
	/* !! 80px SIDES, NOT 40. Gerek asked for the hours to come in about
	 * 40px from the left and the map the same from the right, to tighten
	 * the row without changing the blue box itself.
	 *
	 * Done as the PANEL'S padding rather than by insetting the two outer
	 * columns, which was the obvious route and is worse. The grid is
	 * `1fr auto 1fr`, so padding the columns individually would shrink
	 * the outer tracks and drag the badge off the panel's centre line -
	 * the middle column sits where it does precisely because the two
	 * outer tracks are equal. Padding the panel moves the whole content
	 * box in and leaves that symmetry untouched.
	 *
	 * It also keeps the legal row aligned with the columns above it,
	 * because that row lives inside the same padding. Insetting the
	 * columns alone would have left "Contact" and the copyright hanging
	 * 40px outside the hours and the map.
	 *
	 * Desktop only in effect - the 991 and 767 queries below set their
	 * own 24px and 20px, and 80px on a 375px phone would leave 167px of
	 * usable width. */
	padding: var(--space-48) 80px var(--space-32);
	border-radius: var(--radius);

	/* !! THE ARTWORK IS TWO LAYERS NOW, AND THE FLATTENED ONE IS GONE.
	 *
	 * The first delivery (media 233) was a single 2400x720 panel with the
	 * monogram baked in. Two things were wrong with it, and neither could
	 * be fixed at this end:
	 *
	 *   - The mark was fixed at whatever size the export chose. Gerek's
	 *     note on the rendered page was "almost a little too big and not
	 *     fully in focus", and nothing in CSS can resize part of a
	 *     picture.
	 *   - A 3.33:1 band cannot fill a phone's tall panel. `cover` showed
	 *     10% of its width at eleven times magnification.
	 *
	 * The designer re-cut it as media 235 (gradient, no mark) and media
	 * 234 (the monogram alone, white on transparency, 1566x860). The
	 * softness is still there - it is a blur in the Figma layer, not an
	 * export fault, and the separated file has it too. But softness
	 * scales: drawn smaller, the blur shrinks with it, so sizing the mark
	 * down answers both halves of the complaint at once.
	 *
	 * Layer order is monogram, gradient artwork, CSS gradient - each one
	 * a fallback for the one before. If either upload ever 404s the
	 * footer loses a watermark, not its brand colour.
	 *
	 * !! THE MARK IS SIZED BY WIDTH, NOT HEIGHT. Height was the obvious
	 * choice and it is wrong: the panel is a wide band on desktop and a
	 * tall column on a phone, so a height-relative mark is small where
	 * there is room for it and enormous where there is not. A percentage
	 * of WIDTH tracks the panel the way the eye expects at both ends.
	 *
	 * The file carries its own opacity - its alpha peaks at 36 of 255,
	 * about 14% - so nothing here fades it further. */
	background-image: var(--footer-mono-image), var(--footer-bg-image), linear-gradient(180deg, var(--footer-from) 0%, var(--footer-to) 100%);
	background-repeat: no-repeat, no-repeat, no-repeat;
	/* 52% of the panel width, up from 42% - Gerek on the rendered page:
	 * "only like one-third of the page... should be a little taller or a
	 * little wider". At 52% it draws about 703x386 in a 438px-tall panel,
	 * so it fills the height the way a watermark should without touching
	 * the edges.
	 *
	 * Anchored 6% in from the right rather than 3%. Growing the mark
	 * extends it leftward into the open middle of the panel, which is
	 * what should happen - but the last few percent were tucking under
	 * the map card on the right, and a watermark hidden behind an opaque
	 * element is one nobody sees. */
	background-position: right 6% center, center, center;
	background-size: 52% auto, cover, auto;

	color: var(--footer-ink);
	box-shadow: var(--shadow-card);
}

/* !! `:not(.cta-button)` IS LOAD-BEARING, AND WITHOUT IT THE FOOTER'S
 * BOOK NOW BUTTON IS REPAINTED BLACK.
 *
 * ".site-footer a" scores one class plus one element, which BEATS the
 * bare ".cta-button" rule that gives every button its white label. So
 * the footer's coral pill was drawn with --footer-ink (#33302f) on the
 * gradient - near-black text on orange, while every other button on the
 * site is white. Gerek spotted it on the rendered page 2026-09-04.
 *
 * hbchiropractic already carried this exclusion, and its comment
 * describes the identical bug - the two footers were written from the
 * same shape and only one had been corrected.
 *
 * Any future rule that colours a bare "a" inside the footer must exclude
 * this class too, or it comes straight back. */
.site-footer a:not(.cta-button) { color: var(--footer-ink); }

/* ---- the mark ----
 * Figma sets a very large, very faint monogram behind the footer. There
 * is no monogram asset - the only mark is the horizontal lockup - so it
 * is SET AS TYPE in the display face the brand already uses. That is why
 * it is a <span> and not an <img>.
 *
 * White, not ink: on the blue a dark ghost reads as a smudge, a white
 * one reads as a watermark, which is what Figma draws.
 *
 * clamp() so it scales with the panel rather than sitting at one pixel
 * size and looking wrong at both ends. Decorative, so it carries no
 * contrast obligation; pointer-events none so it can never eat a click. */
/* !! HIDDEN SINCE THE ARTWORK ARRIVED, AND KEPT RATHER THAN DELETED.
 * The monogram is now baked into the background image, so drawing it as
 * type as well put two MCs on the panel - the same doubling the
 * back-to-top plugin guards against when a theme ships its own arrow.
 *
 * The span and this rule stay because the image is the thing most likely
 * to change: if it is ever swapped for one without the monogram, or
 * dropped back to the plain gradient, restoring the watermark is
 * deleting one line rather than rebuilding a component. The comment
 * below still explains why it is type and not an <img>, which was true
 * and is worth keeping. */
.footer-panel__mark { display: none; }

.footer-panel__mark {
	position: absolute;
	right: -0.06em;
	bottom: -0.3em;
	z-index: -1;
	font-family: var(--font-heading);
	font-size: clamp(180px, 24vw, 330px);
	font-weight: var(--weight-bold);
	line-height: 1;
	letter-spacing: -0.04em;
	color: var(--footer-mark);
	pointer-events: none;
	user-select: none;
}

/* ---- the three zones ----
 *
 * HOURS | BRAND | MAP, the sister site's arrangement.
 *
 * The map column is a FIXED 280px and the other two share what is left.
 * A map does not benefit from extra width the way a text column does -
 * past about 300px it is just more grey - and pinning it means the
 * address beneath it sets its own measure instead of stretching.
 *
 * align-items: start so the three zones top-align. Stretch would make
 * the hours list and the map the same height as the tallest, which puts
 * the address a long way below the map it belongs to. */
.footer-panel__main {
	display: grid;
	/* 1fr auto 1fr, NOT `1fr auto 280px`. With the map track fixed, the
	 * hours column absorbed all the slack - 692px for 161px of content -
	 * and the brand column ended up parked just left of the map instead
	 * of in the middle. Two equal outer tracks put the logo on the
	 * panel's true centre line, which is the whole point of the sister
	 * site's arrangement. The map's own 280px moves onto .footer-zone--map
	 * and is right-aligned inside its track. */
	grid-template-columns: 1fr auto 1fr;
	align-items: start;
	gap: var(--space-40);
}

.footer-zone--map {
	justify-self: end;
	width: 280px;
}

/* !! THE HOURS SIT 20px LOWER THAN THE OTHER TWO COLUMNS, ON PURPOSE.
 *
 * The row is top-aligned and the hours column is the shortest - 161px
 * against 259 for the map - so it ended flush with the top and left all
 * 98px of its slack in one lump underneath. Gerek's read on the rendered
 * page was to close that up.
 *
 * 20px down splits it: 20 above and 78 below rather than 0 and 98, which
 * reads as a column sitting in its space instead of one that stopped
 * early. Deliberately NOT centred in the track - "OFFICE HOURS" is a
 * heading, and headings across three columns should start near the same
 * line, so this is a nudge rather than an alignment change.
 *
 * min-width so it never reaches the stacked layout, where the columns
 * run one under another and 20px would just be a gap. */
@media (min-width: 992px) {
	.footer-zone--hours {
		margin-top: 20px;
	}
}

.footer-zone--brand {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-24);
	text-align: center;
}

/* !! .footer-brand__cta AND ITS BUTTON OVERRIDES ARE GONE - the phone
 * and booking button were removed from the footer on 2026-09-04.
 *
 * They made the middle column 344px against 161 for the hours and 259
 * for the map, so the panel was as tall as this column and the left one
 * ended in a 183px hole. A three-column panel is as tall as its tallest
 * column, so moving the pair between columns only ever moved the
 * problem - taking them out hands the height back to the map.
 *
 * The practice's phone number is still in the header on every page and
 * on the contact page; this removed a duplicate, not the number.
 *
 * .footer-phone below is kept deliberately even though nothing in the
 * footer uses it now: --footer-phone-size and its hover are named in the
 * section header at the top of this file, and an unused class costs
 * nothing while a dangling reference costs a search. */

.footer-heading {
	margin: 0 0 var(--space-12);
	font-family: var(--font-body);
	font-size: var(--footer-heading-size);
	font-weight: var(--weight-semibold);
	letter-spacing: .1em;
	text-transform: uppercase;
	/* FULL INK, no alpha. Fading it toward the blue is what fails - see
	 * the section header. The heading is already separated from what
	 * follows by uppercase, letterspacing and weight; the colour step was
	 * never the signal. */
	color: var(--footer-ink);
}

/* ---- hours ----
 * A two-column grid over the <dl>: days first, time second, one row per
 * pair. The pairing is real structure, which is why the markup is a
 * description list - a grid can lay it out and a screen reader can
 * announce it as pairs.
 *
 * auto / 1fr, not two halves: the day labels are the fixed thing and the
 * times should start just after the widest of them.
 *
 * !! THE GAP WAS var(--space-4) AND THE WHOLE SHORTHAND DIED. There is
 * no --space-4 on the approved scale (8/12/16/20/24/32/40/48) and ONE
 * invalid value invalidates the entire declaration, so column-gap fell
 * back to `normal` - 0 for grid - and every day label rendered flush
 * against its time: "Mon, Wed, Thu9am-12pm". No error, nothing visible
 * in the file. Found by reading computed style in the browser. */
.footer-hours {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: var(--space-8) var(--space-16);
	margin: 0;
	font-size: var(--footer-text-size);
}
.footer-hours dt { font-weight: var(--weight-semibold); }
.footer-hours dd { margin: 0; }

/* ---- brand column ---- */
/* !! BOTH LOGO SLOTS NOW USE assets/img/logo-badge.webp - the circle
 * mark, blue disc with white type. The two older lockups are still in
 * the theme and are no longer referenced:
 *
 *   logo.webp        524x44, light blue on transparent. Retired because
 *                    it measured 2.1:1 against white and the word
 *                    "Chiro" was effectively invisible.
 *   footer-logo.webp 524x52, white on transparent, painted black here
 *                    with filter: brightness(0). Retired with it.
 *
 * They are kept rather than deleted: the badge is a very old asset with
 * no source file, and if it ever has to be abandoned these are the only
 * other marks that exist. */
/* !! THE CIRCLE BADGE, AND THE brightness(0) FILTER HAD TO GO WITH IT.
 * That filter existed to paint a WHITE lockup black, which was the only
 * way to get ink type from the one asset available. The badge is already
 * full colour - a blue disc with white type - so leaving the filter in
 * place would have rendered it as a solid black circle.
 *
 * !! SIZED SQUARE, AND SIZED BY BOTH AXES. The old rule was width-only
 * against a 524x52 lockup. The badge is 200x200, so `width: 320px` alone
 * would have made it 320px TALL as well and blown the footer column
 * apart.
 *
 * 176px, against 320 for the lockup it replaces. That reads as larger
 * rather than smaller despite the smaller number, because a disc fills
 * its box where a wordmark is mostly empty space - 176x176 of mark
 * against 320x32. It is also comfortably inside what the file supports:
 * 176 CSS px is 352 real pixels on a retina screen against a 200px
 * source, and this is a flat hard-edged graphic, which is the kind that
 * upscales cleanly. Checked at 2x before committing to it.
 *
 * There is no larger copy anywhere - see the note in header.php. */
.footer-brand__logo {
	display: block;
	/* 176 -> 144. Gerek on the rendered footer: "reduce the size by a
	 * lot... not too much smaller but definitely smaller", having found
	 * the panel out of proportion once the disc replaced the wordmark.
	 * A disc fills its box where a lockup is mostly air, so like-for-like
	 * pixel counts mislead here - 144 still reads larger than the 320x32
	 * lockup it replaced. */
	width: 144px;
	height: 144px;
	/* On top of the column's 24px gap, so the badge has 32px beneath it
	 * and sits apart from the group below rather than in the same rhythm
	 * as its members. */
	margin-bottom: var(--space-8);
}

.footer-phone {
	font-size: var(--footer-phone-size);
	font-weight: var(--weight-bold);
	text-decoration: none;
	white-space: nowrap;
}
.footer-phone:hover { color: var(--brand-coral-deep); }

.footer-social {
	display: flex;
	align-items: center;
	gap: var(--space-12);
	margin: 0;
	padding: 0;
	list-style: none;
}
/* 46px, up from 40, with a 24px glyph up from 20 - Gerek asked for "a
 * hair bigger". It also clears the 44px minimum comfortable touch
 * target, which the old 40 did not. */
.footer-social a {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	border-radius: 50%;
	/* A white chip with an ink glyph - the same device as the trust
	 * badges elsewhere on the page, a light disc carrying a dark mark,
	 * rather than a new pattern. */
	background: var(--footer-chip);
	color: var(--footer-ink);
	transition: background-color .18s ease, transform .18s ease;
}
.footer-social a:hover {
	background: var(--surface-white);
	transform: translateY(-2px);
}
.footer-social svg { display: block; }

/* ---- map column ----
 * .map-embed (section 24) supplies the radius, the clip and the shadow.
 * Only the box belongs here. 275x192 is Figma's own thumbnail; the
 * ratio is kept and the width rounded to 280. */
.footer-map {
	aspect-ratio: 275 / 192;
	width: 100%;
}

.footer-address {
	/* No bottom margin - the Get Directions link that used to follow it
	 * is gone, so this is the last thing in the column. */
	margin: var(--space-12) 0 0;
	font-style: normal;
	font-size: var(--footer-text-size);
	line-height: var(--leading-body);
}

/* .footer-directions and .footer-directions__arrow WERE HERE and are
 * deleted, not commented out - Gerek cut the link on 2026-09-01 as the
 * tallest thing in the map column, and it had no other user. The
 * homepage Contact section still has a full Get Directions button, and
 * the map itself opens Google Maps when clicked.
 *
 * mommaschiro_directions_url() survives: front-page.php now reads from
 * it, which was the last inline copy of that URL. */


/* ---- legal line ----
 * One hairline, then the quietest type in the footer. This is the
 * compliance block, so it is held at FULL ink rather than dimmed. */
.footer-panel__legal {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-12);
	margin-top: var(--space-32);
	padding-top: var(--space-20);
	border-top: 1px solid var(--footer-rule);
	font-size: var(--footer-legal-size);
}
.footer-panel__legal p { margin: 0; }

/* !! TWO STACKED LINES, NOT ONE LONG ONE. Gerek, 2026-09-02, looking at
 * the rendered footer: "too much going on in the same line."
 *
 * He was right, and the build credit was what tipped it over. The legal
 * line already carried a practitioner's name, a state licence number and
 * a copyright notice; adding "Website by iTech Valet" made four separate
 * facts, middot-separated, running the full width beside the footer nav.
 * At that length the eye stops parsing it as anything.
 *
 * Split the way hbchiropractic already had it - credential on top,
 * copyright and credit beneath, right-aligned as a block. 2px of gap
 * rather than a paragraph's worth: they are one thought in two lines,
 * not two paragraphs. */
.footer-legal-block {
	display: grid;
	gap: 2px;
	text-align: right;
}

/* Kept visually distinct so nobody edits it casually. */
.footer-licence { color: var(--footer-ink); }
.footer-licence strong { font-weight: var(--weight-semibold); }
.footer-licence__sep { opacity: .45; margin-inline: var(--space-8); }

/* !! THE BUILD CREDIT MUST NOT LOOK LIKE AN ADVERT. Gerek's brief was
 * "same font size, same everything else - make it look blended in", so
 * it inherits colour from the legal line rather than taking the site's
 * link colour, which would put a coral word in a grey sentence and read
 * as the one thing on the page selling something.
 *
 * `color: inherit` rather than naming --footer-ink, so it keeps matching
 * if the legal line is ever recoloured. It is still a link: it underlines
 * on hover and on keyboard focus, which is the whole of what tells
 * somebody it can be clicked. */
.footer-credit__link {
	color: inherit;
	text-decoration: none;
}

.footer-credit__link:hover,
.footer-credit__link:focus-visible {
	text-decoration: underline;
}

.footer-nav ul {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-20);
	margin: 0;
	padding: 0;
	list-style: none;
}
/* NO UNDERLINE. Gerek: "the Contact and Privacy Policy don't need to be
 * underlined, that just looks bad." They are the only two links in the
 * legal line and they sit alone at the end of the page, so nothing
 * around them can be confused for a link - the underline was carrying no
 * information. It comes back on hover, where it confirms the target
 * rather than advertising it. */
.footer-nav a {
	font-size: var(--footer-legal-size);
	color: var(--footer-ink);
	text-decoration: none;
}
.footer-nav a:hover {
	color: var(--brand-coral-deep);
	text-decoration: underline;
	text-underline-offset: 4px;
}


/* ==================================================================
 * 07  RESPONSIVE
 * Breakpoints 1200 / 992 / 768. One pass, not per component.
 * ================================================================== */

/* Hamburger takeover at 1199px, moved LATER from 1280px after the nav
 * dropped to 16px.
 *
 * The binding constraint is the container, not the viewport: .container
 * caps at 1200px, so the space available to the header row is an
 * identical width at 1200px wide and at 1440px wide, so the row fits at
 * any viewport from 1200px up and at none below, where the container
 * starts shrinking with the window. Hence 1199.
 *
 * !! RE-MEASURED 2026-08-19. This note used to say the row needs 1144px
 * against 1152px available - 8px of slack - and that the breakpoint must
 * be re-measured if the menu changes. Two things changed underneath it:
 * the type ladder shrank the header text, and --container moved to 1140.
 * Measured at the current container the row is 1022px (logo 262 + nav 395
 * + actions 333 + two 16px gaps) against 1092px available, so the slack
 * is about 70px, not 8. Still worth re-measuring if the menu grows, but
 * it is no longer the tight constraint this warning described. */
@media (max-width: 1199px) {
	.primary-nav { display: none; }
	.nav-toggle { display: inline-flex; }
}

/* Two-row header on small screens.
 *
 * Everything stays present at every breakpoint, as required: the phone,
 * click-to-call, and the full "Book Your Appointment" wording. They
 * simply move to their own row rather than being dropped or abbreviated.
 * Row 1: logo + hamburger.  Row 2: phone + CTA. */
/* The layout breakpoint is 991px, NOT 767px. At exactly 768 the single
 * row still overflows - measured - so the wrap has to start above the
 * tablet boundary. The TYPE breakpoint stays at 767 so tablet keeps the
 * desktop 22px body copy, which is a separate decision. */
/* ---- footer, 991 ----
 * Three zones side by side needs about 900px of content before the map's
 * fixed 280 starts squeezing the hours list. Below that the map moves
 * under the other two and spans the full width, which keeps it readable
 * instead of shrinking it to a postage stamp. */
/* !! ONE COLUMN AND A NEW ORDER FROM 991 DOWN, 2026-09-01.
 *
 * Gerek: on tablet and mobile the brand block goes FIRST - logo, social,
 * phone, Book Now - then Office Hours, then the map. Desktop keeps the
 * three-across arrangement.
 *
 * The reasoning holds up: on a narrow screen the footer is read top to
 * bottom rather than scanned, so it should open with who this is and how
 * to book, not with a table of opening times. On desktop the eye takes
 * all three at once and the brand belongs in the middle.
 *
 * `order` on grid items rather than reordering the markup - the DOM
 * order is the desktop order and there is no focus-order problem, since
 * each zone is a self-contained block and the tab sequence still runs
 * through each one in turn. */
@media (max-width: 991px) {
	/* Top 32 -> 48. Gerek asked for "another 20px of space from the top
	 * to the start of the logo"; 52 is not on the approved scale
	 * (8/12/16/20/24/32/40/48) and 48 is the step that exists, 4px short
	 * of the ask and indistinguishable from it. It also matches the
	 * desktop panel's own top padding, so the inset is now the same at
	 * every width. Sides and bottom unchanged. */
	.footer-panel { padding: var(--space-48) var(--space-24) var(--space-32); }
	.footer-panel__main {
		grid-template-columns: 1fr;
		justify-items: center;
		gap: var(--space-32);
	}
	.footer-zone--brand { order: 1; }
	.footer-zone--hours { order: 2; }
	.footer-zone--map   { order: 3; }
	/* Capped rather than full width. At 834 an uncapped map is 786px
	 * wide and, at the 275:192 ratio, 549 tall - it would be the single
	 * largest thing in the footer by a wide margin. 420 keeps it a
	 * reference rather than a feature. Below 420 it simply fills the
	 * column, so a phone is unaffected. */
	.footer-zone--map {
		justify-self: center;
		width: 100%;
		max-width: 420px;
		display: block;
	}
	.footer-address { margin-top: var(--space-12); }
	.footer-panel__mark { font-size: clamp(160px, 30vw, 260px); }
}

@media (max-width: 991px) {
	.site-header__inner {
		flex-wrap: wrap;
		row-gap: var(--space-12);
		column-gap: var(--space-12);
	}
	.site-header__logo    { order: 1; }
	.nav-toggle           { order: 2; }
	.site-header__actions {
		order: 3;
		flex-basis: 100%;
		justify-content: space-between;
		gap: var(--space-12);
	}

	/* ---- tablet header sizing, 2026-09-01 ----
	 *
	 * The two-row header has been in place since the first build but was
	 * never SIZED for - it inherited the desktop values and only the
	 * layout changed. On a tablet that left a 262px logo, a 23px phone
	 * number and a 40px button floating in a much shorter row, which is
	 * what Gerek reported as "the header is not working".
	 *
	 * Row 2 is the whole width and holds two controls, so both can be
	 * bigger here than they are on desktop, where they compete with a
	 * five-item nav. */
	.site-header__logo img { width: 68px; height: 68px; }
	.site-header__wordmark { font-size: 24px; }

	.header-phone { font-size: 26px; }

	/* Between .cta-button--mini and the full size. --mini's declarations
	 * are later in the file than this block, so they win on source order
	 * unless matched selector-for-selector - hence the two-class forms. */
	.cta-button.cta-button--mini {
		gap: var(--space-12);
		padding: var(--space-8) var(--space-8) var(--space-8) var(--space-24);
		font-size: 20px;
	}
	/* !! THREE CLASSES, NOT TWO. Section 21 declares
	 * `.cta-button--mini .cta-button__icon { width: 24px }` and sits
	 * LATER in the file. Media queries add no specificity, so the
	 * two-class form here lost on source order and the icon silently
	 * stayed 24px while the label around it grew to 20 - the button
	 * measured 40px tall instead of 48. Adding .cta-button takes this to
	 * three classes and it wins. Caught by measuring the rendered button,
	 * not by reading the rule. */
	.cta-button.cta-button--mini .cta-button__icon,
	.cta-button.cta-button--mini .cta-button__icon svg {
		width: var(--space-32);
		height: var(--space-32);
	}
}

/* !! THESE TWO .cta-button RULES NO LONGER REACH THE HEADER BUTTON.
 * They are earlier in the file than .cta-button--mini (section 21), and
 * media queries add no specificity, so --mini's own padding and
 * font-size win on source order. That is correct - the mini pill is
 * already header-sized and does not want tightening again.
 *
 * They are kept, not deleted: .cta-button is still used bare by the
 * static and disabled variants, and by any future plain button, and
 * those do still want to tighten on a phone. Recorded here so the next
 * person does not read them as dead code and remove them. */
@media (max-width: 767px) {
	/* Logo 190 -> 240. Gerek reported it as far too small to read on a
	 * tablet and a phone; the old values were sized against a header row
	 * that still had to fit a phone number, a full-length CTA and the
	 * word "Menu" beside the hamburger. Two of those three are gone.
	 *
	 * The .header-phone override that used to sit here is DELETED. It set
	 * --type-micro - 14px, the smallest type anywhere on the site, on the
	 * one control a person on a phone is most likely to tap. It now steps
	 * down gently from the desktop 23 instead. */
	.site-header__logo img { width: 62px; height: 62px; }
	.site-header__wordmark { font-size: 22px; }
	.header-phone { font-size: 22px; }
	.cta-button   { padding: var(--space-8) var(--space-16); font-size: var(--type-micro); }

	/* A step down from the tablet size, still up on where it was. Gerek:
	 * "the mobile Book Now is not that bad, so you can go up a little" -
	 * so this is a nudge, not the tablet jump. */
	.cta-button.cta-button--mini {
		padding: var(--space-8) var(--space-8) var(--space-8) var(--space-20);
		font-size: 18px;
	}
	/* Three classes, same reason as the 991 block above. */
	.cta-button.cta-button--mini .cta-button__icon,
	.cta-button.cta-button--mini .cta-button__icon svg {
		width: 28px;
		height: 28px;
	}

	/* ---- footer, 767 ----
	 * One column, everything centred. The map keeps its full width, which
	 * on a phone is the widest it has ever been - it is the one element
	 * that gets easier to read as the screen narrows. */
	.site-footer { padding-block: var(--space-32); }

	.footer-panel { padding: var(--space-48) var(--space-20) var(--space-32); }

	/* The single column and the brand/hours/map order both come from the
	 * 991 block above. Only the text alignment is new here. */
	.footer-panel__main { text-align: center; }

	/* The hours list stays LEFT-ALIGNED inside a centred column. Centring
	 * a two-column day/time list breaks the alignment that makes it
	 * scannable - the times stop sharing a left edge. */
	.footer-zone--hours { text-align: left; }

	/* !! THE ARTWORK STAYS ON A PHONE NOW, AND SPLITTING IT IN TWO IS
	 * WHAT MADE THAT POSSIBLE. The flattened version had to come off
	 * here: a 3.33:1 band filling a 327x1019 panel showed 10% of its
	 * width at eleven times magnification. The gradient layer no longer
	 * carries the mark, so `cover` cropping it is now harmless - a
	 * gradient looks like itself at any crop - and the monogram is
	 * positioned independently.
	 *
	 * It is sized up here because 42% of a phone's width is a stamp
	 * rather than a watermark, and moved off the right edge to centre,
	 * where a narrow column has room for it. */
	/* !! 44% DOWN, NOT 78% - THE MARK WAS LANDING BEHIND THE MAP. On a
	 * phone the panel stacks brand, then hours, then map (see the order:
	 * rules above), so 78% put the watermark under the one opaque,
	 * full-width element in the footer and it may as well not have been
	 * drawn. Gerek: "it's behind the map, so you can't really see it."
	 *
	 * The hours are the middle third and are open type on flat colour,
	 * which is what a watermark wants to sit behind. */
	.footer-panel {
		background-position: center 44%, center, center;
		background-size: 72% auto, cover, auto;
	}

	.footer-panel__legal {
		flex-direction: column;
		align-items: center;
		text-align: center;
		gap: var(--space-12);
		margin-top: var(--space-24);
	}

	/* !! THE STACKED BLOCK MUST FOLLOW THE ROW, NOT FIGHT IT. Its
	 * desktop `text-align: right` is right-aligned against a footer nav
	 * on the left; here the whole row is a centred column, and a block
	 * still pushing its text right inside it reads as a mistake rather
	 * than as alignment. The two lines stay stacked - only where they
	 * sit changes. */
	.footer-legal-block { text-align: center; }

	/* Pulled in and shrunk. At the desktop offsets the mark hung far
	 * enough off the corner that only a sliver showed, which reads as a
	 * rendering fault rather than as texture. */
	.footer-panel__mark {
		font-size: clamp(140px, 44vw, 220px);
		right: -0.04em;
		bottom: -0.26em;
	}
}

@media (max-width: 599px) {
	.site-header__logo img { width: 56px; height: 56px; }

	/* !! THE WORDMARK GOES, IT DOES NOT SHRINK. Below 600px the header
	 * row also carries a hamburger, and at any readable size the name
	 * beside the badge pushes that off the edge. The badge still carries
	 * the practice's name inside the disc, so nothing is lost that a
	 * visitor can read at this width anyway - and the <span> stays in the
	 * markup, so screen readers and crawlers still get the name. */
	.site-header__wordmark { display: none; }
	.header-phone { font-size: 20px; }
	/* The label stays in full. Only the box around it tightens. */
	.cta-button { padding: var(--space-8) var(--space-12); }
}


/* ==================================================================
 * 08  PAGE BACKGROUND
 *
 * ONE fixed layer behind the whole homepage. It replaces two earlier
 * systems that both moved on scroll: the hero's own parallax box, which
 * bled a measured 291px past the hero to match Figma's layer bounds,
 * and the tiled wash that ran across the seven white sections. Neither
 * survives - see the note at the end of this section.
 *
 * A position: fixed ELEMENT, never background-attachment: fixed. Fixed
 * attachment is broken or badly janky on iOS Safari and several Android
 * browsers, and it forces a repaint on every frame even where it works.
 *
 * z-index -1 rather than lifting the rest of the page to 1. This works
 * because <html> declares no background, so <body>'s --surface-white
 * propagates to the canvas, and the canvas paints BEHIND negative
 * z-index content. Verified in the browser, not assumed. Two things
 * follow from it, both wanted:
 *   - body keeps its white, which becomes a free fallback if the image
 *     ever fails to load.
 *   - Every in-flow element is in front by default, so the four opaque
 *     panels (About's gradient, Contact's and What to Expect's cards,
 *     Ready's photo) and the footer hide the layer with no code of
 *     their own. Nothing needs a z-index.
 *
 * What DOES need changing is the other direction: eight sections used
 * to paint --surface-white and would hide the layer completely. Their
 * fills are gone. <body> still supplies the white underneath.
 *
 * height: 100lvh, not inset: 0. On iOS Safari the layout viewport grows
 * and shrinks as the URL bar collapses; anchoring to the LARGE viewport
 * keeps the layer a constant size, so the artwork does not rescale
 * halfway through a scroll.
 *
 * cover, so the picture crops rather than distorts. What that crops is
 * a real trade-off and is handled per breakpoint below.
 *
 * NO JAVASCRIPT. A layer that never moves needs none - both parallax
 * blocks are deleted from main.js. prefers-reduced-motion stops being
 * a concern here too: there is no motion left to reduce.
 * ================================================================== */

/* !! THE ELEMENT IS 8% WIDER THAN THE VIEWPORT ON PURPOSE, AND IT IS
 * COVERING FOR A DEFECT IN AN ASSET RATHER THAN A DESIGN CHOICE.
 *
 * Gerek reported "a grey line like a shadow on the left side" on the
 * desktop. Measured by reading the artwork's own pixels:
 *
 *     booking-parallax-background-2048x1287.webp   71px grey border on
 *       the LEFT edge only, luminance 246 against 255 inside. 3.47% of
 *       its width. Right, top and bottom are clean. Used by SEVEN pages.
 *
 *     parallax-background-scaled-2048x1377.webp    clean. The home page
 *       uses this one, which is exactly why the line never showed there.
 *
 * At 50% opacity that 246 lands at ~250 against a pure white page - a
 * 5-value difference, invisible in a screenshot and plainly visible on a
 * good monitor, which is how Gerek found it and I did not.
 *
 * left: -4% with width: 108% hangs 4% of the viewport off EACH side, so
 * the image's 3.47% grey edge falls outside the viewport at any width.
 * Symmetric rather than shifted left, so the artwork stays centred and
 * the composition does not move.
 *
 * !! THIS IS A PATCH. The real fix is a clean re-export of that one
 * .webp with the grey border trimmed; then this can go back to
 * left: 0 / width: 100%. Flagged for Gerek. Harmless on the clean home
 * page asset in the meantime - it scales a decorative layer by 8%. */
.page-bg {
	position: fixed;
	top: 0;
	left: -4%;
	width: 108%;
	height: 100lvh;
	z-index: -1;
	pointer-events: none;

	/* --page-bg-image is set on the element in front-page.php so that
	 * every uploads URL on this page stays in that one file - the same
	 * arrangement --pillar-card-bg uses in section 11. */
	background-image: var(--page-bg-image);
	background-repeat: no-repeat;
	background-size: cover;

	/* Wide viewports crop the TOP AND BOTTOM and keep the full width, so
	 * centre is right: all three motifs survive. See the breakpoint
	 * below for what happens when that flips. */
	background-position: center center;

	opacity: 0.5;
}

/* Below 991px the crop flips axis and gets severe. The artwork is
 * 2048x1377 (1.49); once the viewport is narrower than that ratio,
 * cover scales to HEIGHT and cuts the sides off instead. Measured, with
 * a centred position:
 *
 *   834x900   62% of the width visible - pink circle GONE, mint 53%
 *   390x844   31% of the width visible - pink circle GONE, mint 14%
 *
 * A centred crop on a phone therefore lands on the middle of the
 * picture, which is almost entirely empty white. Anchoring right
 * instead pins the visible window to the mint glow, which is the one
 * motif big enough to read at that size:
 *
 *   834x900   window covers 38-100% of the image - all of the mint
 *   390x844   window covers 69-100% - 78% of the mint
 *
 * The pink circle and the triangle are lost either way at 390. This
 * chooses to lose them and keep something, rather than centre on
 * nothing. */
@media (max-width: 991px) {
	.page-bg { background-position: right center; }
}


/* ==================================================================
 * 08b  HERO
 *
 * Two-column split: photo left (~45%), content right (~55%).
 * Trust bar spans the full hero width.
 *
 * No background of its own - the fixed layer in section 08 shows
 * through here, which is the whole point of it.
 * ================================================================== */

.hero {
	position: relative;
	padding-top: var(--space-48);
}

/* Container lifted above blobs */
.hero__inner {
	position: relative;
	z-index: 1;
	padding-bottom: var(--space-48);
}

/* Two-column flex */
.hero__split {
	display: flex;
	align-items: center;
	gap: var(--space-48);
}

/* Photo column */
.hero__photo-col {
	flex: 0 0 45%;
	max-width: 45%;
}
.hero__image {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--radius);
	object-fit: cover;
}

/* Content column */
.hero__content-col {
	flex: 1;
	min-width: 0;
}

/* Homepage only, and the modifier stays even though the reason for it
 * has changed.
 *
 * It existed because .hero__content-col was SHARED - the Prenatal hero
 * reused it, so putting this padding on the bare class would have leaked
 * onto a second page. As of 2026-08-20 that is no longer true: Prenatal
 * moved to .page-hero in section 18, and .hero__content-col is now the
 * homepage's alone.
 *
 * Kept anyway. .hero and its parts are still the pattern any non-service
 * page would reach for, and this padding is a logged deviation above the
 * 48px ceiling (B6) that must not travel to one by accident. A modifier
 * says "homepage" out loud; a bare class would quietly offer it to the
 * next page that reuses the hero.
 *
 * Value and its provenance: --hero-content-padding in section 1.7b. It
 * exceeds the 48px spacing ceiling on purpose. */
.hero__content-col--home {
	padding: var(--hero-content-padding);
}

/* H1 */
.hero__title {
	margin-bottom: var(--space-20);
	font-size: var(--type-h1);
	line-height: var(--leading-heading);
}

/* The squiggle that used to live here is now .accent-squiggle in section
 * 18.4. It moved because it could not be recoloured where it stood: the
 * stroke was baked into a background-image data URI, CSS cannot
 * interpolate a custom property into a url(), and Prenatal had therefore
 * been carrying a second full copy of the SVG with one hex changed. As a
 * mask the shape is carried once and the colour is --squiggle-color,
 * which defaults to this page's --accent-cyan. Geometry, including the
 * derived 0.084em offset, is unchanged. */

/* Body paragraph */
.hero__body {
	margin-bottom: var(--space-32);
	color: var(--neutral-muted);
}

/* CTA button wrapper.
 * No bottom margin any more - the AEO paragraph that used to follow it
 * has moved out of this column into the credibility block. */
.hero__cta-wrap {
	margin-bottom: 0;
}

/* ---- CTA: pill + detached circular icon ----
 *
 * Figma component "Button / Circle-in-Bar / Coral (Glass)" (525:709) is
 * two nested parts, not one solid pill with an inline arrow:
 *
 *   outer  gradient #f0917a -> #dd5a3f, radius 9999, overflow clip,
 *          shadow 0 6px 20px -6px rgba(221,90,63,0.35)
 *   glass  height 52, padding 6px 6px 6px 28px, gap 10,
 *          overlay gradient rgba(255,255,255,0.07) -> transparent
 *   arrow  40x40 circle, fill #fff at 70%, chevron stroke #DD5A3F 2.2
 *
 * Declared here rather than beside .cta-button in section 04 so the
 * shared header button is left exactly as it was. These modifiers are
 * only ever applied inside the hero.
 *
 * The 52px height falls out of the geometry: 40px icon + 6px padding
 * top and bottom. Every value below is a design token that already
 * existed; none were added. */
.cta-button--icon {
	display: inline-flex;
	align-items: center;
	gap: var(--space-12);
	padding: var(--space-8) var(--space-8) var(--space-8) var(--space-32);
	font-size: var(--type-ui);
	background-image:
		linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0)),
		var(--gradient-cta);
	box-shadow: 0 6px 20px -6px rgba(221, 90, 63, 0.35);
}

.cta-button__label {
	white-space: nowrap;
}

.cta-button__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: var(--space-40);
	height: var(--space-40);
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.7);
	color: var(--brand-coral-deep);
}

/* AEO paragraph — SEO context copy, intentionally small.
 *
 * Figma node 525:711 sits inside "Credibility Intro" (525:710), NOT in
 * the right-hand text column: centred, 740px wide, 50px above the badge
 * row. It has moved in the markup to match.
 *
 * Figma draws it at 10px and the locked scale has no 10px step, so this
 * paragraph has to borrow one. It took --type-micro until the type
 * ladder landed and that token returned to Figma's 11px; at 11px across
 * a 740px centred measure it read as a mistake rather than fine print.
 * It takes --type-xs (13px) instead - the smallest step that still
 * reads at this width. Approved 2026-08-19.
 *
 * This size is shared: the Prenatal page's AEO paragraph is this same
 * component and restates only its width, so it follows from here. */
/* !! 740 -> 960px, 2026-09-01. Gerek asked for three lines instead of
 * four, and for the column to widen to get there.
 *
 * MEASURED, by stepping the width 740 -> 1200 in 10px increments in the
 * browser and counting rendered line boxes at the real 19px:
 *
 *     740 - 900    four lines
 *     910          three lines   <- first width that breaks to three
 *     920 - 1200   three lines
 *
 * 960, not 910. 910 is the exact edge: it clears by a few pixels, and
 * any copy edit - one longer word, a swapped comma - silently puts the
 * fourth line back. 960 buys 50px of headroom, which is about five
 * characters of slack.
 *
 * A LITERAL, not a --measure- token, and deliberately so. The measure
 * scale is keyed to character count for READING copy; this is centred
 * fine print sized to a line count Gerek asked for by eye. Borrowing
 * --measure-head (1027) would have fitted but would have meant a
 * heading cap governing a paragraph, and the next person to move that
 * token would silently reflow this. If the copy changes, re-measure
 * this number rather than trusting it. */
.hero__aeo {
	max-width: 960px;
	margin-inline: auto;
	margin-bottom: var(--space-48);
	font-size: var(--type-xs);
	color: var(--neutral-muted);
	text-align: center;
}

/* ---- Credibility block (Figma "Credibility Intro", 525:710) ----
 *
 * No band of its own. In the file the badges float straight over the
 * hero background and its blobs; the periwinkle gradient that used to
 * fill this strip is not in the design. --gradient-periwinkle was kept
 * defined in section 01 on the grounds that it is a published design
 * value, and was removed on 2026-08-20 once it had gone a whole second
 * page without a single reference. The value lives in Figma. */
.trust-bar {
	position: relative;
	z-index: 1;
	padding-bottom: var(--space-48);
}

/* 32px between cards - Figma's 30, snapped. */
.trust-bar__inner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-32);
	flex-wrap: wrap;
}

/* Each stat is its own white card.
 * Figma: background #fff, radius 24, padding 20, and the published
 * "Main Card Shadow" effect variable - which --shadow-card already
 * mirrors exactly (0 4px 30px -10px rgba(51,48,47,0.15)). */
.trust-badge {
	display: flex;
	align-items: center;
	gap: var(--space-12);
	padding: var(--space-20);
	background: var(--surface-white);
	border-radius: var(--radius);
	box-shadow: var(--shadow-card);
}

.trust-badge__icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}

/* The three badge icons are real WebP assets, not inline SVG. Each <img>
 * carries its own width/height attributes (sized per asset, so nothing is
 * distorted and the box is reserved before the image loads). height:auto
 * keeps the intrinsic ratio if the width is ever constrained. */
.trust-badge__img {
	display: block;
	max-width: 100%;
	height: auto;
}

/* Figma gap is 5px. --space-8 is the smallest step the approved scale
 * has, so it rounds up to that under the tie rule. (This comment used to
 * name --space-4, which B5 removed from the scale - the rule below has
 * always said --space-8.) */
.trust-badge__text {
	display: flex;
	flex-direction: column;
	gap: var(--space-8);
}

.trust-badge__value {
	font-family: var(--font-heading);
	font-size: var(--type-h3);
	font-weight: var(--weight-bold);
	line-height: 1.1;
	color: var(--neutral-ink);
}

/* Figma draws the label at 12px Poppins Regular. On the locked scale
 * 12px maps to --type-eyebrow (18px), not --type-micro (Figma's 11px). */
.trust-badge__label {
	font-size: var(--type-eyebrow);
	font-weight: var(--weight-regular);
	color: var(--neutral-muted);
	line-height: 1.3;
}

/* ---- 08 responsive ---- */

/* !! THE HERO NOW STACKS AT 991, NOT 767. Moved 2026-09-01.
 *
 * The 42%/58% split used to hold all the way down to 768, and it does
 * not survive a tablet. Measured at 834 - iPad portrait, the single most
 * common tablet width there is:
 *
 *   photo column   42% = 320px, squeezed well under its natural size
 *   text column    ~440px, which puts the H1 on SIX lines
 *     "Prenatal / Chiropractic Care / and Pediatric / Chiropractic in /
 *      Huntington / Beach"
 *
 * Gerek reported the header as broken on tablet and did not raise this,
 * because his device is narrower than 768 and was already getting the
 * stacked layout. Anything between 768 and 991 was not.
 *
 * The stacked layout was already written and already correct - it was
 * just gated 224px too low. Nothing here is new; the breakpoint moved
 * and the 42% rule that fought it is deleted. */
@media (max-width: 991px) {
	.hero {
		padding-top: var(--space-40);
	}
	.hero__split {
		flex-direction: column;
		gap: var(--space-32);
	}
	/* The row is gone, so the hero's padding exception goes with it. Held
	 * here the column measured 24px of inset on the left against 84px on
	 * the right, because the value is deliberately 0 on the left - it
	 * relies on the photo column being beside it. Full width, that reads
	 * as a mistake, so it resets and the container's own 24px gutters
	 * take over evenly. See --hero-content-padding in section 1.7b. */
	.hero__content-col--home { padding: 0; }

	/* The CTA centres, the copy does not. The heading and paragraph read
	 * better ranged left - a centred three-line paragraph is harder to
	 * track back to. The button is a single object, so it can sit on the
	 * axis alone.
	 *
	 * !! text-align ON THE WRAPPER, not margin-inline: auto on the button.
	 * That was the first attempt and it did nothing, measurably: the
	 * button sat 26px left of centre with computed margins of 0.
	 * .cta-button--icon is display: inline-flex, so the button is an
	 * INLINE-LEVEL box, and auto margins only centre block-level boxes -
	 * on an inline box `auto` computes to 0. Its parent .hero__cta-wrap
	 * is display: block, so text-align on the block is what centres it.
	 *
	 * !! IT LIVES AT 991, WHICH COVERS TABLET AND MOBILE, AND THAT IS
	 * DELIBERATE. It landed here by accident first - the hero's stacking
	 * rules had moved to this block earlier the same day and took the
	 * anchor with them - and Gerek had asked for "on mobile". It was
	 * moved to 767 to match the words, and he then said he had seen it
	 * centred on tablet and wanted it kept: "centered is right".
	 *
	 * So it is back at 991 ON PURPOSE. Do not narrow it to 767 again to
	 * make it match the original request - the request was superseded.
	 * The tablet and mobile heroes are the same stacked layout and the
	 * button belongs on the axis in both. */
	.hero__cta-wrap { text-align: center; }

	.hero__photo-col {
		flex: none;
		max-width: 100%;
		width: 100%;
	}
	.hero__image {
		max-height: 400px;
		object-position: top center;
	}
	.hero__inner {
		padding-bottom: var(--space-40);
	}
}

/* Phone-only. These were in the same block as the stacking rules above
 * and have been split out - they are about a 390px viewport, not about
 * the hero being one column, and applying them from 991 would have
 * shrunk the CTA and the trust badges on a tablet for no reason. */
@media (max-width: 767px) {
	/* H1 needs no override here any more. --type-h1 steps itself down the
	 * ladder in section 1.4b (46/42/38/34/30), which is what the old
	 * clamp(28px, 7.5vw, ...) was standing in for. */
	/* Cards centre once they wrap; flex-start was for the old flat text. */
	.trust-bar__inner {
		justify-content: center;
		gap: var(--space-20);
	}
	.trust-badge__value { font-size: var(--type-ui); }

	/* Keep the pill inside a 390px viewport: the desktop geometry is
	 * 28+label+10+40+6, which overflows once the label wraps. */
	.cta-button--icon {
		padding-left: var(--space-20);
		font-size: var(--type-body);
	}
	.cta-button__icon {
		width: var(--space-32);
		height: var(--space-32);
	}
	.cta-button__icon svg { width: 32px; height: 32px; }

	.hero__aeo { margin-bottom: var(--space-32); }
	.trust-bar { padding-bottom: var(--space-40); }
}

@media (max-width: 599px) {
	.trust-badge__value { font-size: var(--type-body); }
}


/* ==================================================================
 * 09  TESTIMONIALS
 *
 * Figma node 525:866 (1130 x 213). Two columns: a fixed 387px intro
 * column, then a row of 356px review cards.
 *
 * !! The card row OVERFLOWS its own frame in Figma. Four 356px cards
 * plus three 10px gaps measure 1454px, and the row starts at x=443
 * inside an 1130px frame - 1897px of content in an 1130px box. The
 * frame is drawn, not laid out, so that overflow is read here as a
 * horizontal scroller: Figma's card geometry is kept exactly and the
 * row scrolls. Nothing was resized to "make it fit", which would have
 * meant inventing widths the design never states.
 *
 * All type lands on existing scale tokens; none were added.
 * Figma 34 -> --type-h2, 64 -> --type-display (which section 01
 * already describes as the decorative opening quote mark), 14 ->
 * --type-small, 13 -> --type-xs, 12 -> --type-eyebrow.
 *
 * !! Card CONTENT is placeholder. See the TODO block in front-page.php
 * and the pre-launch blocker in LAUNCH-CHECKLIST.md. Styling here is
 * final; the words are not.
 * ================================================================== */

.testimonials {
	/* 100px is the measured section gap in the design - Testimonials
	 * sits at y=2099.4 and the section above ends at 1999.4. Left as a
	 * literal rather than tokenised until the rhythm is confirmed across
	 * the remaining sections. */
	padding-block: var(--space-48);
}

.testimonials__inner {
	display: flex;
	align-items: flex-start;
	gap: var(--space-48);
}

/* ---- intro column ---- */
.testimonials__intro {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	flex: 0 0 387px;
	max-width: 387px;
}

/* Served at 180x133 (2x of Figma's 90x66.7) from the media library. */
.testimonials__icon {
	display: block;
	width: 90px;
	height: auto;
}

.testimonials__title {
	margin: 0;
	line-height: 1.03;   /* Figma: 35px leading on a 34px face */
}

/* Figma keeps this on one line (14px in a 387px column, nowrap). On the
 * locked scale it is 20px, which wraps to two lines, so the star aligns
 * to the FIRST line instead of the centre of the block. */
.testimonials__rating {
	display: flex;
	align-items: flex-start;
	gap: var(--space-8);
	margin: 0;
	font-size: var(--type-small);
	color: var(--neutral-muted);
}

/* (line-box 1.6em - star 0.75em) / 2 - centres the 15px star optically
 * against the first line rather than the paragraph. */
.testimonials__star {
	display: flex;
	flex-shrink: 0;
	margin-top: 0.42em;
}

/* ---- card row ----
 * The vertical padding gives the card shadow room inside the scroll
 * container, which would otherwise clip it (overflow-x also clips the
 * cross axis). The negative margin cancels it so layout is unchanged. */
.testimonials__list {
	display: flex;
	align-items: stretch;
	gap: var(--space-12);
	flex: 1 1 auto;
	min-width: 0;
	margin: 0;
	padding: 0;
	padding-block: var(--space-12) var(--space-32);
	margin-block: calc(var(--space-12) * -1) calc(var(--space-32) * -1);
	list-style: none;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;

	/* Scrollbar hidden in all three engines. Scrolling itself is NOT
	 * disabled - touch, keyboard and wheel all still work, and main.js
	 * adds mouse drag, which is the one route a hidden scrollbar takes
	 * away. The grab cursor replaces the scrollbar as the affordance. */
	scrollbar-width: none;        /* Firefox */
	-ms-overflow-style: none;     /* legacy Edge / IE */
	cursor: grab;
}

.testimonials__list::-webkit-scrollbar { display: none; }   /* Chrome, Safari */

/* Set by main.js for the duration of a drag. */
.testimonials__list.is-dragging {
	cursor: grabbing;
	user-select: none;
	-webkit-user-select: none;
	scroll-behavior: auto;
}

/* The scroller carries tabindex="0" so it is keyboard-reachable; give it
 * a visible focus ring rather than letting the UA outline sit under the
 * card shadows. */
.testimonials__list:focus-visible {
	outline: 2px solid var(--brand-coral-deep);
	outline-offset: 4px;
	border-radius: var(--radius-sm);
}

.testimonial-card {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	flex: 0 0 356px;
	max-width: 356px;
	padding: var(--space-24);
	background: var(--surface-white);
	border-radius: var(--radius);
	box-shadow: var(--shadow-card);
	scroll-snap-align: start;
}

/* Figma draws a 64px glyph on 64px leading inside a 34px-tall box, so the
 * mark is deliberately cropped and the box is shorter than the type. The
 * leading must stay at 1 (=64px): dropping it to 34px pulls the glyph up
 * and opens a ~15px hole between the mark and the quote. */
.testimonial-card__mark {
	display: block;
	height: 34px;
	font-family: var(--font-heading);
	font-size: var(--type-display);
	font-weight: var(--weight-bold);
	line-height: 1;
	color: var(--brand-pink);
}

.testimonial-card__quote {
	margin: 0;
	font-size: var(--type-small);
	font-weight: var(--weight-medium);
	color: var(--neutral-ink);
}

.testimonial-card__name {
	margin: 0;
	font-size: var(--type-xs);
	font-weight: var(--weight-bold);
	color: var(--neutral-ink);
}

.testimonial-card__source {
	margin: 0;
	font-size: var(--type-eyebrow);
	color: var(--neutral-muted);
}

/* ---- 09 responsive ---- */

/* Stack at tablet, not phone. Side by side, a 768px viewport leaves only
 * 368px for the card row once the 387px intro column and the gap are
 * taken - less than a single 356px card. Stacking hands the row the full
 * container width, so two cards show. */
@media (max-width: 991px) {
	.testimonials__inner {
		flex-direction: column;
		gap: var(--space-32);
	}
	.testimonials__intro {
		flex: none;
		width: 100%;
		max-width: 100%;
	}
	.testimonials__list { width: 100%; }
}

@media (max-width: 767px) {
	.testimonials { padding-block: var(--space-48); }
	.testimonials__inner { gap: var(--space-24); }
	/* One card sits comfortably inside a 390px viewport with the
	 * container gutter, with the next one peeking to signal the scroll. */
	.testimonial-card {
		flex-basis: 280px;
		max-width: 280px;
	}
}


/* ==================================================================
 * 10  TRAINED FOR THIS
 *
 * Figma node 525:731 (1130 x 460.4, at x=150 y=890). One centred
 * column: icon, H2, three paragraphs, 16px between each. Nothing here
 * is a card - the frame has no fill, no border and no shadow.
 *
 * Figma does set border-radius: 24px on that frame, but with no fill
 * there is nothing for it to round, so it is deliberately not built.
 * The frame also carries no vertical padding of its own: the icon sits
 * at y=0 and the last paragraph ends exactly on the frame's bottom
 * edge. All vertical air comes from the Content Column's gap.
 *
 * MEASURE. Figma draws the text column at a fixed 616px against a 15px
 * body face, and body IS 15px, so the measure is Figma's own number and
 * needs no conversion: --measure-body, section 1.6b.
 *
 * !! It was 903px until 2026-08-20, which was 616 x 22/15 - the factor
 * that kept measures in proportion with the old 22px body lock. That lock
 * was retired by the type ladder and the multiplier went with it, but
 * this number was left behind, so the column had been running about 47%
 * wider than the design for two commits.
 *
 * All type lands on existing tokens; none were added. Figma 34 ->
 * --type-h2, 15 -> --type-body, #6f6866 ->
 * --neutral-muted. Figma leaves the H2 at line-height "normal", so the
 * house --leading-heading applies rather than an override.
 *
 * !! SECTION RHYTHM - needs a decision. The gap between sections in the
 * Content Column is a consistent 100px (Credibility ends at y=790 and
 * this starts at 890; this ends at 1350.4 and One System starts at
 * 1450.4). Section 09 already spends that as padding-block: 100px on
 * the section itself, and this follows that precedent for consistency -
 * but two neighbours each contributing 100px sum to 200px, twice the
 * design. Fixing it properly means halving BOTH to 50px, which means
 * editing section 09, which is out of scope for this change. Flagged,
 * not silently diverged from.
 * ================================================================== */

/* No background, like every other section on this page - the fixed
 * layer in section 08 shows through, and <body> supplies the white
 * underneath it. Needs no z-index: in-flow content is already in front
 * of a z-index -1 layer. */
.trained {
	padding-block: var(--space-48);
}

.trained__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-16);
	text-align: center;
}

/* Served at 176x163 (2x of Figma's 88 x 81.4) from the media library.
 *
 * !! 88 -> 140px, 2026-09-01, Gerek's call - roughly the 1.5x he asked
 * for ("if it was 100, make it 150"). 140 rather than 132 because it is
 * the number that holds its own against a 46px heading directly beneath
 * it; at 132 the icon still read as a small decoration on a wide block.
 *
 * !! IT IS NOW BEING UPSCALED. The file is 176px wide, so at 88 it was
 * served at exactly 2x for retina and was pin-sharp. At 140 the 2x point
 * would be 280, so a high-DPI screen is now interpolating - Gerek checks
 * on an iPad and iPhone, which are the screens where that shows first.
 * It is a line illustration on white, which is the most forgiving case,
 * but if it looks soft the fix is a larger export from the media library
 * rather than a smaller number here. */
.trained__icon {
	display: block;
	width: 140px;
	height: auto;
}

/* --measure-wide, not --measure-body. This is a full-width centred
 * section rather than a reading column - see the token's own note. */
.trained__title,
.trained__body {
	margin: 0;
	max-width: var(--measure-wide);
}

.trained__body {
	color: var(--neutral-muted);
}

/* ---- 10 responsive ---- */

@media (max-width: 767px) {
	.trained { padding-block: var(--space-48); }
}


/* ==================================================================
 * 11  ONE SYSTEM, EVERY STAGE  (service pillars)
 *
 * Figma node 525:771 (1130 x 549, at x=150 y=1450.4). Centred column:
 * H2, intro, a row of three 366px cards, closing line, two CTAs.
 * Frame gap 40px; card row gap 16px; card padding 20px, inner gap 12px.
 *
 * CTAs reuse .cta-button--icon from section 08 unchanged. Figma confirms
 * they are the same component instance as the hero CTA
 * ("Button / Circle-in-Bar / Coral (Glass)", 525:864 / 525:865): same
 * 52px height, 6px 6px 6px 28px padding, 10px gap and 40px arrow. No new
 * button CSS was written.
 *
 * CARD BACKGROUNDS. Figma will not surrender the pink card's fill -
 * queried at 525:771, 525:775 and 525:776, it never appears, because it
 * sits beneath the dot-texture image fill on the same node and only the
 * image is emitted. The periwinkle and mint cards DO resolve, and match
 * --gradient-periwinkle and --gradient-mint exactly. Rather than invent
 * a pink gradient, all three cards use the exported pillar-card-*.webp
 * from the media library, which carry the gradient and the dot texture
 * together. Approach approved by Gerek 2026-08-16.
 *
 * Those exports bake in the drop shadow, so they carry a transparent
 * margin the card must not show. The card face was measured out of the
 * actual pixels (canvas readback, not by eye): 738 x 390 inside 819 x
 * 470, margins L40 R41 T32 B48 - a clean 2x of Figma's 366 x 194. The
 * background-size and background-position below frame exactly that face
 * and crop the baked shadow away; the real shadow is then --shadow-card,
 * Figma's own published effect.
 *
 *   size-x  819 / 738 = 110.98%      pos-x  40 / (819-738) = 49.38%
 *   size-y  470 / 390 = 120.51%      pos-y  32 / (470-390) = 40.00%
 *
 * MEASURE. Figma sets the intro and closing lines at 560px against a
 * 15px face, which is the face body actually uses - so this is Figma's
 * own number, --measure-lead. It was 821 (560 x 22/15) until 2026-08-20;
 * see the note in section 10.
 *
 * All type lands on existing tokens; none were added. Figma 34 ->
 * --type-h2, 16 -> --type-ui, 15 -> --type-body, 14 -> --type-small.
 * ================================================================== */

.pillars {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

.pillars__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-40);
	text-align: center;
}

/* --measure-wide so this section matches "Trained for This" directly
 * above it, which Gerek asked for explicitly. The two are consecutive
 * full-width centred blocks and a width change between them reads as a
 * mistake. Was --measure-lead. */
.pillars__intro,
.pillars__closing {
	margin: 0;
	max-width: var(--measure-wide);
	color: var(--neutral-muted);
}

.pillars__closing { font-size: var(--type-small); }

/* ---- card row ---- */

.pillars__list {
	display: flex;
	align-items: stretch;
	gap: var(--space-16);
	width: 100%;
	margin: 0;
	padding: 0;
	list-style: none;
}

.pillar-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-12);
	flex: 1 1 0;
	min-width: 0;
	padding: var(--space-20);
	border-radius: var(--radius);
	box-shadow: var(--shadow-card);

	/* --pillar-card-bg is set per card in front-page.php so that every
	 * uploads URL on the page stays in one file. */
	background-image: var(--pillar-card-bg);
	background-size: 110.98% 120.51%;
	background-position: 49.38% 40%;
	background-repeat: no-repeat;
}

/* Each illustration has its own width (238 / 203 / 230) but all three
 * are 85 tall, so height is what gets pinned. Served at 2x. */
.pillar-card__illustration {
	display: block;
	width: auto;
	height: 85px;
	max-width: 100%;
}

/* Poppins Bold 16px in Figma, not a Baloo heading - the element default
 * has to be overridden rather than inherited. */
.pillar-card__title {
	margin: 0;
	font-family: var(--font-body);
	/* These are real <h3> elements. They used to borrow --type-ui, a
	 * button size, which is why they sat below every heading step.
	 *
	 * They take the H4 step, not H3, and that is deliberate. At --type-h3
	 * (26px) two of the three titles wrapped to a second line - "Prenatal
	 * chiropractic care" and "Postpartum chiropractic recovery" - while
	 * "Pediatric chiropractic" stayed on one, so the body copy in a row of
	 * three matched cards started at three different heights. 22px puts
	 * the first two back on a single line. Approved 2026-08-19.
	 *
	 * It does NOT fit the third - "Postpartum chiropractic recovery" is
	 * simply too long for a 333.3px box at any heading size the ladder
	 * offers. That one title carries the --long modifier below. */
	font-size: var(--type-h4);
	font-weight: var(--weight-bold);
	line-height: var(--leading-body);
	color: var(--neutral-ink);
}

/* The one long title. See --type-h4-long in section 01 for the measured
 * reasoning and for why this is a named exception rather than a new
 * heading step. Applied to a single <h3> in front-page.php. */
.pillar-card__title--long {
	font-size: var(--type-h4-long);
}

.pillar-card__text {
	margin: 0;
	font-size: var(--type-small);
	color: var(--neutral-ink);
}

/* ---- CTA pair ---- */

.pillars__cta-wrap {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-24);
}

/* ---- 11 responsive ---- */

/* Three 366px cards need 1130px. Below that the illustrations (up to
 * 230px wide) start shrinking out of their shared 85px height, so the
 * row stacks rather than squeezes - same breakpoint as section 09. */
@media (max-width: 991px) {
	.pillars__list { flex-direction: column; }
	.pillar-card { flex: none; }

	/* The row is gone, so the exception is too: the long title hands back
	 * to --type-h4 rather than sitting smaller than its two siblings for
	 * no reason.
	 *
	 * !! CORRECTED. This used to claim "every title fits at the normal
	 * step" once stacked. Measured at the 1140 container, that is true at
	 * 768 (665px content box) and 600 (497px) but false at 390, where the
	 * box is 287px and --type-h4 is 18px, needing 315 - the third title
	 * wraps. Deliberately not fixed: full-width stacked cards do not have
	 * the unlevel-row problem this exception exists to solve, so a second
	 * line there costs nothing. */
	.pillar-card__title--long { font-size: var(--type-h4); }
}

@media (max-width: 767px) {
	.pillars { padding-block: var(--space-48); }
	.pillars__inner { gap: var(--space-32); }
	.pillars__cta-wrap { gap: var(--space-16); }
}


/* ==================================================================
 * 12  PROOF, NOT PROMISES
 *
 * Figma node 539:1232 (1200 x 781.7, at x=115 y=3389.4). One centred
 * column, gap 14px: H2, two paragraphs, the YouTube mark, a fanned trio
 * of Shorts thumbnails, then the "Isn't for Everyone" H3 block and a
 * row of three office photos.
 *
 * This frame is 1200 wide, not the 1130 sections 10 and 11 use, so it
 * spans the full container.
 *
 * MEASURE. Full container width - a DELIBERATE EXCEPTION to the
 * standing 821px ceiling, for this section only. Gerek 2026-08-16.
 *
 * Every text layer here is the full 1200px frame width; the design fits
 * each paragraph on a single line at 14px, and the frame is wider than
 * the container, so the text simply fills the container. That is Figma's
 * own proportion for this section, which is why the cap is lifted here.
 *
 * !! The 821px rule still applies everywhere else. Do not read this as
 * a change to the rule - sections 11, 13, 14 and 15 keep their caps.
 * !! It lands around 95 characters per line, wider than comfortable
 * reading, which was the reason for the cap in the first place. Kept
 * because matching Figma's layout was the explicit instruction.
 *
 * !! The H3 is 21px in Figma and the locked scale has no 21px step.
 * --type-h3 (24px) is used as the same semantic level rather than
 * adding a token for one heading. A 3px difference, flagged.
 *
 * Type: Figma 32 -> --type-h2 (34), 21 -> --type-h3, 14 -> --type-small.
 * Shadow is Figma's published Main Card Shadow -> --shadow-card.
 * Image pixel sizes are NOT scaled by the type factor, matching how
 * sections 10 and 11 treat their artwork.
 * ================================================================== */

.proof {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

.proof__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-16);
	text-align: center;
}

.proof__subtitle {
	margin: 0;
	font-size: var(--type-h3);   /* Figma 21px - see note above */
}

.proof__body {
	margin: 0;
	/* No max-width. See the MEASURE note above - this section is the one
	 * exception to the 821px ceiling. */
	font-size: var(--type-small);
	color: var(--neutral-muted);
}

.proof__youtube {
	display: flex;
	flex-shrink: 0;
}

/* ---- Shorts trio ----
 *
 * ONE IMAGE, NOT THREE CARDS. Figma fans three 128.68 x 193.019 cards
 * inside a 398.29 x 207.73 group, rotated -6.33deg and +7.13deg with a
 * 4-5px overlap, and this was built that way in CSS until 2026-08-17.
 * The asset that arrived is that whole arrangement already composited
 * into a single 752x467 picture - the fan, the rotation, the rounded
 * corners and the shadows are all baked into the pixels.
 *
 * So the CSS that used to construct the effect is gone rather than
 * layered on top of it. Rebuilding the rotation here would have turned
 * an already-fanned graphic on its side; keeping the 2:3 portrait slots
 * would have cropped a 1.61 landscape picture down to its middle third.
 *
 * 376px is half the file's natural width, which lands within a pixel of
 * the 377px the three-card group actually measured, so the section's
 * rhythm is unchanged. Below that it scales with the column.
 */
/* !! 376 -> 470px, 2026-09-01. Gerek's +25%, and it is inside the
 * grain budget he attached to the request.
 *
 * The density maths, since this is the one image on the page where it
 * is close. The file is 752px wide naturally:
 *
 *     displayed 376   752/376 = 2.00x   pixel-perfect on a retina screen
 *     displayed 470   752/470 = 1.60x   still oversampled, no upscaling
 *     displayed 752   752/752 = 1.00x   the point it would start to soften
 *
 * 470 is still supplying more pixels than a 2x screen strictly needs at
 * that size, so nothing is being invented - it is simply less headroom
 * than the old 2.00x. On a photographic collage that difference is not
 * visible. The hard ceiling is 752, and anything approaching it should
 * come with a bigger export instead.
 *
 * The old 376 was chosen as "half the file's natural width, within a
 * pixel of the 377px the three-card group measured in Figma". That tie
 * to Figma's group width is deliberately broken here - the section is
 * being sized against the wider container by eye, not against the
 * artboard. */
.proof__shorts-image {
	display: block;
	width: 100%;
	max-width: 470px;
	height: auto;
	margin-inline: auto;
}

/* ---- office photo row ----
 * Figma: 294 / 293 / 294 wide by 257 tall in a 909px row, gap 14,
 * radius 24. Equal thirds in practice, so they flex.
 *
 * The row is 909 of the frame's 1200, not full width, and artwork is
 * not scaled by the type factor - so 909px is kept literally rather
 * than stretched to the container. */
.proof__gallery {
	display: flex;
	align-items: stretch;
	gap: var(--space-16);
	width: 100%;
	max-width: 909px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.proof-gallery__item {
	flex: 1 1 0;
	min-width: 0;
}

.proof-gallery__image {
	display: block;
	width: 100%;
	height: 100%;
	aspect-ratio: 294 / 257;
	object-fit: cover;
	border-radius: var(--radius);
}

/* ---- 12 responsive ---- */

@media (max-width: 767px) {
	.proof { padding-block: var(--space-48); }

	/* The Shorts graphic needs no breakpoint of its own: width 100% with
	 * a max-width already fits it to the column, and 342px of usable
	 * width on a phone is under the 376px cap, so it simply scales. */

	/* !! THE STACK MOVED TO 599, and this is the fix for what Gerek
	 * reported as the office photos being "too big on tablet by a lot".
	 *
	 * The old rule stacked them at 767. That number was chosen against a
	 * phone - "three across a phone leaves 108px each, too small to read
	 * a room in" - and it is still right about a phone. But 767 is not a
	 * phone: a tablet lands inside this block too, and stacking there
	 * gave each photo the FULL column, roughly 780px wide and 680 tall.
	 * Three of those is most of a screen of office photography, which is
	 * not what a supporting detail row should be.
	 *
	 * The row now survives down to 600, where three across is about 168px
	 * each. Small, but these are atmosphere, not information, and a small
	 * photo of a room still reads as a room. Below 600 the original
	 * argument holds and they stack. */
}

@media (max-width: 599px) {
	.proof__gallery {
		flex-direction: column;
		align-items: center;
	}
	/* Capped and centred rather than full-bleed. Stacked at the full
	 * column each photo was 342px wide and 299 tall on a phone; 320
	 * takes a little of that back without shrinking them to thumbnails.
	 * Gerek on mobile: "not so much" - so this is a trim, not a cut. */
	.proof-gallery__item {
		width: 100%;
		max-width: 320px;
	}
}


/* ==================================================================
 * 13  READY WHEN YOU ARE  (closing CTA band)
 *
 * Figma node 525:942 (1130 x 340, at x=150 y=4760.1). Unlike sections
 * 10-12 this frame really is a card: it has a fill (#f6f4f6 ->
 * --surface-off), radius 24, and overflow clip. The photo and scrim
 * rectangles inside it are 1200 wide against the frame's 1130, so they
 * deliberately bleed and are clipped.
 *
 * Layers, bottom to top:
 *   525:943  photo        section-background-photo.webp
 *   525:944  scrim        rgba(63,63,63,0.4) -> --scrim
 *   525:945  content      column, gap 18, centred, 340 tall
 *
 * --scrim already existed in section 01, where it is documented as
 * having been read from this exact overlay. First use.
 *
 * The photo is the pre-cropped 2280 x 680 export - exactly 2x of a
 * 1140 x 340 band, so it needs no repositioning, only object-fit. Its
 * own corner rounding is baked at roughly 12px once scaled to display
 * width, which sits inside the 24px clip here and so is never visible.
 *
 * !! The H2 is Poppins Bold in Figma, not Baloo 2 - the only heading on
 * the page that uses the body face. The element default has to be
 * overridden rather than inherited.
 *
 * MEASURE. Figma sets the paragraph at 480px against a 15px face. It was
 * built at 704 (480 x 22/15, the old body-lock factor) and takes
 * --measure-lead now - 560px, the nearest step on the measure scale in
 * section 1.6b. 80px wider than Figma's own 480, which is inside the
 * scale's tolerance and avoids a fifth near-duplicate token.
 *
 * CTA reuses .cta-button--icon from section 08 unchanged - Figma
 * confirms the same component instance and the same label as the hero.
 * ================================================================== */

.ready {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

.ready__panel {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 340px;
	overflow: hidden;
	border-radius: var(--radius);
	background: var(--surface-off);
}

.ready__photo {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.ready__scrim {
	position: absolute;
	inset: 0;
	background: var(--scrim);
}

.ready__content {
	position: relative;   /* lifts the content above photo and scrim */
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-20);
	padding: var(--space-40) var(--space-24);
	text-align: center;
	color: var(--surface-white);
}

.ready__title {
	margin: 0;
	font-family: var(--font-body);   /* Figma: Poppins Bold, not Baloo */
	font-weight: var(--weight-bold);
	color: inherit;
}

.ready__body {
	margin: 0;
	max-width: var(--measure-lead);
	color: inherit;
}

/* ---- 13 responsive ---- */

@media (max-width: 767px) {
	.ready { padding-block: var(--space-48); }
	.ready__content {
		gap: var(--space-16);
		padding: var(--space-40) var(--space-20);
	}
}


/* ==================================================================
 * 14  ABOUT US
 *
 * Figma node 525:906 (1200 x 370, at x=115 y=2412.4). Sits between
 * Testimonials and Proof on the page; numbered 14 because this file is
 * append-only and section order here is authoring order, not page
 * order.
 *
 * A gradient card: radius 24, padding-left 80, padding-top 40, gap 20,
 * photo left and a left-aligned text block right. There is deliberately
 * NO bottom padding - the photo runs flush to the card's bottom edge,
 * and the 370 height is 40 + the photo's 330.
 *
 * !! GRADIENT DIRECTION. The MCP output converts this fill to
 * "bg-gradient-to-b" (180deg), but the rendered design is plainly
 * left-to-right - cyan at the left edge, ink at the right - and
 * --gradient-about in section 01 was read off this same fill as 90deg.
 * The token is trusted over the conversion. Flagged.
 *
 * Text is #f5f5f5, which is already tokenised as --surface-grey. Using
 * a surface token as a foreground colour reads oddly, but it is the
 * exact value the design specifies, and adding a near-duplicate token
 * for one use would be worse.
 *
 * !! Copy comes from Figma's text layers, not the copy document, which
 * has no entry for this section. See B3 in LAUNCH-CHECKLIST.md.
 *
 * Type: Figma 34 -> --type-h2, 15 -> --type-body. CTA reuses
 * .cta-button--icon from section 08 unchanged.
 * ================================================================== */

.about {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

.about__panel {
	display: flex;
	gap: var(--space-20);
	padding-left: var(--space-48);
	padding-top: var(--space-40);
	overflow: hidden;
	border-radius: var(--radius);
	background: var(--gradient-about);
}

/* Runs flush to the card's bottom edge, exactly as Figma draws it.
 * align-self is what does it: the panel has no bottom padding, and at
 * this type scale the text column is TALLER than the 330px photo, so
 * the photo would otherwise float above the edge. In Figma the photo is
 * the taller of the two and sets the card height; here it is not.
 *
 * 294px is the asset's own width - see the note in front-page.php on
 * why it is not Figma's 354. */
.about__photo {
	display: block;
	flex: none;
	align-self: flex-end;
	width: 294px;
	height: auto;
}

.about__content {
	display: flex;
	flex-direction: column;
	/* flex-start, not the default stretch: .cta-button--icon is
	 * inline-flex and a stretched flex child would pull it to the full
	 * column width instead of sizing to its label. */
	align-items: flex-start;
	justify-content: center;
	gap: var(--space-16);
	flex: 1 1 auto;
	min-width: 0;
	/* Figma: 48px, and the approved scale's top step is exactly 48. (This
	 * comment used to read "the scale has 40 and 50 but no 48" - written
	 * before B5 capped the scale, and wrong in both directions since.) */
	padding: var(--space-48);
	color: var(--surface-grey);
}

.about__body {
	margin: 0;
	max-width: var(--measure-lead);
	color: inherit;
}

/* ---- 14 responsive ---- */

/* At 768 the 294px photo leaves only ~302px of text, which is narrower
 * than the phone layout gets. So the card stacks here rather than
 * squeezing - the same breakpoint sections 09 and 11 use. */
/* !! column-reverse, NOT column. Rewritten 2026-09-01.
 *
 * It stacked photo-on-top / text-below, and Gerek's read was that the
 * photo got cut off - which it did. The panel has NO bottom padding by
 * design (see the section header: on desktop the photo runs flush to the
 * card's bottom edge and sets the card's height). Stacked photo-first,
 * that same zero landed under the TEXT instead, and the photo sat in the
 * middle of the card with its torso cropped by nothing in particular.
 *
 * Reversing it puts the design's intent back at every width: text and
 * button at the top, photo at the bottom, flush to the edge with zero
 * beneath it. Exactly what it does on desktop, rotated.
 *
 * DOM ORDER IS UNCHANGED - the photo is still first in the markup and
 * only the visual order flips. Safe here because the photo is not
 * focusable, so the tab order still runs through the text to the CTA in
 * reading order. It would NOT be safe if the photo were a link.
 *
 * The text centres because it is now a full-width block above a centred
 * photo; left-aligned it would have hung off one side of it. */
@media (max-width: 991px) {
	.about__panel {
		flex-direction: column-reverse;
		align-items: center;
		padding: var(--space-40) var(--space-32) 0;
	}
	.about__photo {
		align-self: center;
		width: 100%;
		max-width: 294px;
	}
	.about__content {
		width: 100%;
		align-items: center;
		text-align: center;
		/* No top padding - the panel's own 40 already supplies it. The
		 * bottom 32 is the gap down to the photo, and is the only thing
		 * separating the two now that the photo owns the card's edge. */
		padding: 0 0 var(--space-32);
	}
}

@media (max-width: 767px) {
	.about { padding-block: var(--space-48); }
	.about__panel { padding-inline: var(--space-20); }
}


/* ==================================================================
 * 15  NEW PATIENTS
 *
 * Figma node 525:912 (1130 x 407, at x=150 y=2882.4). Sits between
 * About Us and Proof on the page; numbered 15 because this file is
 * append-only.
 *
 * A centred H2, a lead line, an is/isn't comparison row and the CTA,
 * sitting DIRECTLY ON THE PAGE like sections 10 and 11.
 *
 * !! THE CARDS WERE ON THE WRONG ELEMENT UNTIL 2026-09-01, and the note
 * below records exactly how that happened. This section used to be one
 * big white card holding everything, and the two comparison columns had
 * no card at all. It is now the other way round: the panel is plain and
 * each comparison point is its own white card with the published
 * shadow. That is what Figma draws and what Gerek asked for.
 *
 * The old reasoning is left in place two paragraphs down because it is
 * a good example of a defensible decision that was still wrong - the
 * observation was right (two white cards on a white card are invisible)
 * and the conclusion drawn from it was backwards. Faced with a
 * contradiction between an outer fill and an inner one, it deleted the
 * inner cards rather than asking which one the design meant. The inner
 * ones were the ones carrying meaning.
 *
 * !! Figma gives this card an ad-hoc drop shadow of
 * 0 4px 15px rgba(51,48,47,0.15) rather than its own published "Main
 * Card Shadow" (0 4px 30px -10px, same colour). The same inconsistency
 * appears on the third pillar card in section 11, and as there, the
 * published effect wins so every card on the page matches. Flagged.
 *
 * THE ORIGINAL NOTE, kept as the record of the mistake:
 *   "The two comparison columns carry bg-white and radius 24 in Figma,
 *    but they sit on a white card, so both are invisible and neither is
 *    built."
 * Superseded 2026-09-01. The columns are built, and the outer card is
 * the thing that is gone.
 *
 * !! Copy comes from Figma's text layers, not the copy document, which
 * has no entry for this section. See B3 in LAUNCH-CHECKLIST.md.
 *
 * !! The first bullet carries a manual line break in Figma ("...every
 * step, " / "built around your stage"). That is a layout artifact, not
 * content, so the string is single and wraps on its own.
 *
 * Type: Figma 34 -> --type-h2, 16 -> --type-ui, 15 -> --type-body.
 * Comparison row is 806px in Figma, already under the standing 821px
 * measure ceiling, so it is kept literally. CTA reuses
 * .cta-button--icon from section 08 unchanged.
 * ================================================================== */

.expect {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

/* The white card, its 24px radius, its 48px padding and its shadow are
 * all .card + .card--roomy + .card--raised in the markup, including the
 * 32/24 padding steps this rule used to carry its own copies of. Only the
 * layout is left. */
.expect__panel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-16);
	text-align: center;
}

.expect__title,
.expect__body {
	margin: 0;
	max-width: var(--measure-lead);
}

.expect__body { color: var(--neutral-muted); }

/* ---- is / isn't comparison ---- */

/* 806 -> 956px, 2026-09-01.
 *
 * 806 is Figma's own number, measured against Figma's 1140 container -
 * it is 71% of the content width, not an absolute. The container is
 * 1352 now, so holding 806 literally would have quietly narrowed the row
 * to 60% and left two small cards floating in a wide empty band. 956 is
 * the same 71% of 1352, so Figma's proportion is what carries over
 * rather than its pixel count.
 *
 * This matters more now than it did: with the outer card gone there is
 * no longer a white panel behind the row giving it edges, so the row's
 * own width is the only thing setting its relationship to the page. */
.expect__compare {
	display: flex;
	/* stretch, not flex-start. flex-start let each column size to its own
	 * text, which was invisible while neither had a card and is glaring
	 * now that both do - the left statement runs to two lines and the
	 * right to one, so the two boxes finished at different heights and
	 * read as unfinished. Figma draws them equal. stretch is the flex
	 * default, so this is really the removal of an override.
	 *
	 * Safe at the 767 stack: flex-direction goes to column there, where
	 * stretch means full width, which is what the stacked layout wants. */
	align-items: stretch;
	gap: var(--space-20);
	width: 100%;
	max-width: 956px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* NOW A CARD. Carries .card + .card--raised in the markup, so the white
 * fill, the 24px radius and Figma's published Main Card Shadow all come
 * from the shared component in section 18 rather than being restated
 * here. Only the layout and the padding belong to this rule.
 *
 * Padding 20 -> 32. At 20 the text sat almost on the card edge, which
 * was invisible while the card was not being drawn and obvious the
 * moment it was. 32 is --space-32, the step below .card--roomy's 48;
 * 48 would have made two short statements into very tall boxes. */
.expect-point {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-20);
	flex: 1 1 0;
	min-width: 0;
	padding: var(--space-32);
}

.expect-point__badge {
	display: block;
	width: 26px;
	height: 26px;
}

.expect-point__text {
	margin: 0;
	font-size: var(--type-ui);
	color: var(--neutral-muted);
}

/* ---- 15 responsive ---- */

@media (max-width: 991px) {
	/* Nothing. This block used to say "padding steps with .card--roomy in
	 * section 18" - the panel no longer carries .card--roomy, so there is
	 * no padding here to step. The cards on .expect-point step at 767. */
}

/* Two columns inside a phone-width card leaves each statement about 15
 * characters per line, so the comparison stacks. */
@media (max-width: 767px) {
	.expect { padding-block: var(--space-48); }
	.expect__compare { flex-direction: column; }
	/* 24, not the old 12/20. These are cards on a phone now, and a card
	 * with 12px of vertical padding reads as a box someone forgot to
	 * finish. Still under the desktop 32. */
	.expect-point { padding: var(--space-24); }
}


/* ==================================================================
 * 16  CONTACT
 *
 * Figma node 525:925 (1130 x 389, at x=150 y=4271.1). Sits between
 * Proof and Ready on the page; numbered 16 because this file is
 * append-only.
 *
 * White card, radius 24, padding-right 50, flex row. A 620px details
 * column (padding 48, gap 16, left aligned) with a 460 x 320 map to its
 * right.
 *
 * !! SHADOW ADDED. Figma gives this card a white fill and NO shadow. In
 * Figma that still reads as a card because the page frame behind it is
 * off-white; here the section background is white, so a shadowless
 * white card would have no visible edge at all and the 24px radius
 * would be invisible with it. --shadow-card is applied so the design's
 * intent survives, matching every other card on this page. Flagged.
 *
 * !! MAP NOT BUILT. Figma's right-hand column is a 460 x 320 static
 * OpenStreetMap image with radius 24 and shadow 0 6px 20px
 * rgba(0,0,0,0.12). No such asset is in the media library, so the
 * details column takes the full card for now. Those are the measured
 * values for whoever adds it; approved alt text already exists in the
 * Contact page copy document, option 4. Note that OSM raster tiles
 * carry an ODbL attribution requirement. See B4 in LAUNCH-CHECKLIST.md.
 *
 * !! HOURS ROW NOT BUILT - a locked decision overrides Figma's
 * placeholder. See the note in front-page.php and B4.
 *
 * Type: Figma 32 -> --type-h2 (34), 15 -> --type-body, 11 -> --type-micro
 * (11px, Figma's own - this label is the size that token is named for).
 * CTA reuses .cta-button--icon from section 08 unchanged.
 * ================================================================== */

.contact {
	padding-block: var(--space-48);   /* see the rhythm note in section 10 */
}

/* White card, radius and shadow are .card + .card--raised in the markup.
 * NO padding modifier: this panel has no uniform padding, only the
 * right-hand inset below, because .contact__details supplies its own. */
.contact__panel {
	display: flex;
	align-items: center;
	justify-content: center;
	padding-right: var(--space-48);
}

.contact__details {
	display: flex;
	flex-direction: column;
	/* flex-start so .cta-button--icon sizes to its label rather than
	 * being stretched by the default align-items: stretch. */
	align-items: flex-start;
	gap: var(--space-16);
	flex: 1 1 auto;
	min-width: 0;
	/* Figma: 48px. No 48 step on the spacing scale - same call as
	 * sections 14 and 15. */
	padding: var(--space-48);
}

.contact__row {
	display: flex;
	flex-direction: column;
	gap: var(--space-8);
}

.contact__label {
	margin: 0;
	font-size: var(--type-micro);
	font-weight: var(--weight-medium);
	color: var(--neutral-ink);
}

.contact__value {
	margin: 0;
	font-size: var(--type-body);
	font-style: normal;   /* <address> defaults to italic */
	color: var(--neutral-ink);
}

.contact__phone { color: inherit; }

/* ---- map ----
 * Figma draws a 460 x 320 static OpenStreetMap image at x=620 in the
 * 1130 frame, radius 24, shadow 0 6px 20px rgba(0,0,0,0.12). No such
 * asset exists in the media library, so this is a live Google Maps
 * embed sized and styled to that same box. 620 + 460 = 1080, which is
 * exactly the panel's 1130 less its 50px right padding, so the widths
 * still resolve the way the design intends.
 *
 * ONLY THE SIZE IS LEFT HERE. The radius, the shadow, the overflow clip
 * and the iframe reset moved to .map-embed in section 24.2 when the
 * Thank You page needed the same chrome at a different size. Same
 * declarations, moved rather than copied; this element carries both
 * classes now. */
.contact__map {
	flex: none;
	width: 460px;
	aspect-ratio: 460 / 320;
}

/* ---- 16 responsive ---- */

/* Details and a 460px map will not sit side by side much below the
 * desktop frame, so the card stacks and the map goes full width. */
@media (max-width: 991px) {
	.contact__panel {
		flex-direction: column;
		align-items: stretch;
		padding-right: 0;
		padding-bottom: var(--space-32);
	}
	.contact__details { padding: var(--space-32); }
	.contact__map {
		width: auto;
		margin-inline: var(--space-32);
	}
}

@media (max-width: 767px) {
	.contact { padding-block: var(--space-48); }
	.contact__panel { padding-bottom: var(--space-24); }
	.contact__details { padding: var(--space-24); }
	.contact__map { margin-inline: var(--space-24); }

	/* Phone and Get Directions centre; the address does not.
	 *
	 * .contact__details is a flex column with align-items: flex-start, so
	 * nothing inside it is full width by default - centring the text
	 * alone would do nothing. Each item is stretched to the column and
	 * then centred within itself.
	 *
	 * The ADDRESS row is deliberately left out. It is two lines on a
	 * phone, and a centred two-line address gives every line a different
	 * left edge - harder to read, and harder to copy by eye. The phone
	 * number and the button are each a single object, so they centre
	 * cleanly. */
	.contact__row--phone {
		align-self: stretch;
		text-align: center;
	}
	.contact__details .cta-button {
		align-self: center;
	}
}


/* ==================================================================
 * 17  PRENATAL PAGE  (/prenatal/)
 *
 * Source of truth: Figma frame 448:565, "(unverified) Prenatal
 * Chiropractic Care - Option B: Accent Only" (1440 x 4312).
 *
 * Everything in this section is scoped under .prenatal, set on <main>
 * in page-prenatal.php, so nothing here can reach the homepage. The one
 * exception is documented at the rule that makes it.
 *
 * Section numbers below are the PAGE's section numbers (1-9 top to
 * bottom), not further main.css sections. Section 1 - the Page Hero and
 * everything tied to it - is not built yet; see page-prenatal.php.
 * ================================================================== */

/* ---- page section 1: Page Hero (449:1413) ----
 *
 * Built after sections 2-9; it was held on the hero photograph.
 *
 * .hero itself is deliberately NOT used. It carries padding-top 80 and
 * its own mobile step, and Figma puts this hero 10px below the header
 * (Frame 27 starts at y=78 against a 68px header). Taking the class
 * would have meant overriding both and restating a media step for
 * nothing, so this section carries its own two properties instead.
 * .hero__split, .hero__content-col, .hero__title, .hero__title-accent,
 * .hero__body and .hero__cta-wrap are all reused as they are. */
/* !! NO overflow CONTROL HERE, AND THAT IS DELIBERATE.
 *
 * The first version carried `overflow-x: clip; overflow-y: visible` to
 * stop the 1180px backdrop widening the page while still letting it
 * bleed downward. In Chrome that combination made the ENTIRE section
 * stop painting - photo, backdrop and all - while every element still
 * reported correct dimensions in the DOM. Removing it restored the
 * render immediately, which is how it was found.
 *
 * It was never needed. The backdrop is anchored `right: 0`, so on a
 * viewport narrower than the image its left edge simply goes negative,
 * and content extending past the LEFT origin does not create horizontal
 * scroll - only content extending right does. Verified at all three
 * breakpoints: no horizontal overflow anywhere. */
.prenatal {
	/* Everything this page varies from the shared .page-hero defaults,
	 * declared in ONE place so Pediatric can copy the block and change the
	 * numbers. All six are read by section 18. */
	--squiggle-color:     var(--accent-pink);
	--hero-backdrop-w:    1180px;
	--hero-backdrop-w-sm:  700px;
	--hero-photo-w:        476px;
	--hero-photo-ratio:  476 / 408;
	--hero-watermark-w:    256px;
}

/* The hero's top space and its three mobile fixes USED TO LIVE HERE,
 * scoped to .prenatal while only this page had been reviewed. Gerek
 * approved the Prenatal page and asked for the same on Pediatric and
 * Postpartum 2026-09-01, so all four moved up to .page-hero itself in
 * section 18.6 and the scope is gone. Nothing was re-decided on the way
 * up - the values are the ones approved here.
 *
 * The blend below stays scoped, and that is not an oversight: it exists
 * because THIS page has a .page-bg under its hero. The other two do
 * not, so there is nothing for them to blend with. */

/* !! THE HERO BACKDROP HAS TO BLEND NOW THAT THERE IS A PAGE BEHIND IT.
 *
 * Found by looking, 2026-09-01, the moment .page-bg landed on this page.
 * prenatal-hero-background-shapes-pink.webp is FULLY OPAQUE - sampled
 * across the whole file, every pixel is alpha 255. That never mattered
 * while the page under it was plain white, because the image's own
 * ground IS near-white (mean 246.7). Put a tinted, moving layer under
 * it and the same opacity becomes a 1180 x 814 rectangle stamped over
 * that layer: a hard vertical seam down the page at the backdrop's left
 * edge - x=260 on a 1440 viewport - with the parallax mint on one side
 * of it and the backdrop's flat white on the other, and a matching
 * horizontal cut where the image ends.
 *
 * multiply is the fix, and it is the right one rather than a trick: the
 * image is dark shapes on a near-white ground, and multiplying by white
 * is the identity. So the ground disappears and only the pink shapes
 * survive, drawn over whatever the parallax layer is showing. The seam
 * cannot exist because there is no longer an edge to see.
 *
 * It does NOT touch the hero photograph. The photo is painted after
 * this element, so it is not part of this element's backdrop and takes
 * no blending - confirmed on the live page, not assumed.
 *
 * STILL SCOPED TO .prenatal after the hero's other rules were promoted
 * to the component, and that is the decision rather than a leftover:
 * Pediatric and Postpartum share .page-hero and have NO .page-bg under
 * them, so they have nothing to blend with and nothing to fix. The day
 * either of them gets the background layer, it needs this line with it.
 *
 * DEGRADES SAFELY. A browser that refuses to blend paints the image as
 * it does today, which is the seam - visible, not broken. */
.prenatal .page-hero__backdrop {
	mix-blend-mode: multiply;
}

/* The flattened backdrop - shapes plus the 50% linen wash. Anchored to
 * the right because that is where Figma's group sits once it is clipped
 * to the frame edge, and drawn at its own 1x size.
 *
 * z-index -1 with no stacking context on the section above, so it paints
 * behind the page's in-flow content rather than only behind this
 * section's - which is what lets it carry on under section 2, as the
 * file does. */
/* Figma: counter axis MIN and gap 0 - the separation between the two
 * columns is the text column's own 56px padding, not a flex gap. Both
 * differ from .hero__split's centre/56, so both are restated. Because
 * the gap is 0 at every width by design, its media steps are not - there
 * is nothing for them to step. */
/* Figma draws the separator as its own "/" text node. Here it is a
 * ::before so a screen reader is not made to read it out, and the trail
 * is a real ordered list. The 8px gap plus the 8px margin reproduce
 * Figma's even 8/8 spacing either side of the slash. */
/* .hero__title supplies --leading-heading, which is Figma's 118% exactly.
 * Only the size and the bottom margin differ - the column's 16px gap does
 * the spacing here, where the homepage's hero uses margins. */
/* The squiggle is .hero__title-accent's, unchanged except for its
 * colour: this page strokes #ff7bac where the homepage strokes
 * --accent-cyan. Same path, same 4px width, same round cap - the data
 * URI below is character for character the section 08b one with the
 * stroke swapped, so the two cannot drift in shape. */
/* Figma: a fixed 476 wide against the homepage's percentage split, which
 * is why .hero__photo-col is not reused. */

/* ---- page section 1 responsive ----
 * Both breakpoints now live on .page-hero in section 18, driven by this
 * page's --hero-* custom properties at the top of this section. Nothing
 * page-specific is left to restate. */


/* ---- page section 2: AEO paragraph (448:569) ----
 *
 * The paragraph itself is the shared .hero__aeo component. Only its
 * width differs from the homepage, so only the width is restated.
 *
 * .prenatal-aeo__text is a single class, the same weight as .hero__aeo,
 * and wins purely because this section is appended after section 08b.
 * That is the whole point of the append-only rule - no !important and no
 * inflated selector needed. It is NOT scoped under .prenatal because it
 * is already a page-specific class name; adding the scope would only
 * raise the specificity above the mobile override in section 08b that
 * this paragraph is meant to inherit.
 *
 * !! WIDTH: Figma draws the text box 1093px wide starting at x=150 - so
 * it is flush with the 1140px content column on the left and stops 47px
 * short of it on the right. That asymmetry is an auto-width text node,
 * not a designed inset: the text inside is centre-aligned and no other
 * element on the page uses a 1093px measure. Read here as "spans the
 * content column", which is --container-narrow. The 47px is recorded
 * rather than reproduced.
 */
.prenatal-aeo__text {
	max-width: var(--container-narrow);
	/* !! RHYTHM FIX, 2026-08-20. .hero__aeo carries margin-bottom: 48px,
	 * and section 08b's 767 block steps that to 32. This page has no
	 * padding of its own between the AEO paragraph and the section under
	 * it, so that borrowed margin WAS the boundary - which meant one gap
	 * on this page silently dropped to 32 on a phone while its six
	 * siblings held 48, and section 4's comment claimed that did not
	 * happen. The gap now belongs to the next section's .section class,
	 * where every other boundary on the page already lives. */
	margin-bottom: 0;
}

/* 48px of clear space above, matching the 48px the shared component
 * already carries below it. Figma draws this gap at 50 (hero ends 495,
 * paragraph starts 545, first content section starts 625); 48 is the
 * ceiling of the approved scale.
 *
 * This lives on the wrapper rather than the paragraph so that when the
 * hero lands it can be replaced by the hero's own bottom padding without
 * touching the shared component. */

/* ---- page section 3: "Your Body Is Changing Fast." (449:2211) ----
 *
 * Row: Figma 449:2211, HORIZONTAL, gap 94 (built at 48 - the scale's
 * ceiling), counter axis CENTER, packed
 * left. .hero__split already supplies flex + centred items + the mobile
 * column stack, so only the gap is restated.
 *
 * !! Restating a shared class means restating its media steps too. This
 * file is append-only, so an unconditional .prenatal-changing__row rule
 * here beats section 08b's `@media (max-width:991px) .hero__split { gap:
 * 32px }` on source order alone - media queries add no specificity. The
 * tablet gap below is not decoration; without it the 48px gap would
 * survive all the way down to the mobile stack. */
.prenatal-changing__row {
	gap: var(--space-48);
}

/* !! A SUPPLIED COMPOSITE WAS TRIED HERE AND REVERTED, 2026-08-18.
 * prenatal-credential-card-pink.webp bakes this panel's gradient, dots,
 * corners and shadow into one 835 x 369 raster, and section 6's fan was
 * swapped to its own composite on the same day. This panel could not
 * take the same treatment: it is not a fixed shape. Body copy is locked
 * at 22px against Figma's 15px, so the panel grows with its text, and
 * stretching a fixed raster onto it elongated the dot matrix - 1.40:1 at
 * desktop, 2.68:1 at tablet, 4.72:1 at 390px, where the dots read as
 * vertical dashes. Gerek's call was to revert; the distinction is that a
 * fan is fixed-aspect artwork and a growing text panel is not.
 *
 * If the composite is ever wanted here it needs an export built for a
 * growing box - a tiling dot layer over a separate gradient, or a
 * 9-sliceable frame. The CSS below verifies to 2/255 against the node
 * and reflows correctly at every width, which the raster did not. */

/* 62px of Figma's 1140 row is trailing slack (795 + 94 + 189.3 =
 * 1078.3), because the panel is a fixed width packed to the left. The
 * panel keeps that fixed 795 ceiling and is allowed to shrink below it,
 * which is what carries the row down through tablet. */
.prenatal-changing__panel {
	position: relative;
	/* isolation, not z-index juggling: it makes the panel a stacking
	 * context so the two decorative layers below can sit at z-index -1 -
	 * above the panel's own background, beneath its text - without
	 * escaping to fight anything else on the page. */
	isolation: isolate;
	flex: 1 1 795px;
	max-width: 795px;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-16);   /* Figma auto-layout gap */
	padding: var(--space-32) var(--space-48);
	border-radius: var(--radius);   /* Figma 24, exact */
	/* The bottom edge of Figma's fill is #f7dce6 across its whole width -
	 * --brand-pink, exactly. It is the base here and the wash sits on it. */
	background-color: var(--brand-pink);
	/* DROP_SHADOW radius 30, offset 0/4, spread -10, #33302f at 15%.
	 * That is --shadow-card to the digit. (The Figma MCP code output
	 * reports this shadow with spread 0; the file itself says -10. The
	 * file wins - the conversion drops the spread.) */
	box-shadow: var(--shadow-card);
}

/* ---- the panel's two decorative layers ----
 *
 * !! FIGMA USES A SHADER FILL HERE, AND CSS HAS NO EQUIVALENT.
 * 682:128's fill is type SHADER - a four-point mesh gradient - not a
 * linear or radial one. That is why it cannot be copied as a gradient
 * and why the MCP code output omits it entirely. Its four control
 * colours, read from the file, are:
 *     #ffbed6   saturated pink   (top left)
 *     #f8f8f8   near white      (top right)
 *     #f7dce6   --brand-pink    (bottom left AND bottom right)
 *
 * Reproduced as a base fill plus one masked wash: left-to-right pink to
 * near-white across the top, faded out downwards to reveal the pink
 * base.
 *
 * VERIFIED, not eyeballed. The built panel was rendered at 1:1, sampled
 * against a 1:1 render of the Figma node at eight points (three corners
 * plus both mid-edges and three edge midpoints), and the worst error at
 * any point on any channel is 2/255. Three of the eight match exactly.
 * A linear vertical fade turns out to be what the mesh does.
 *
 * It is still an approximation of an effect CSS cannot express, and a
 * designer should confirm it before launch - but the numbers say the
 * approximation is not the weak link. */
.prenatal-changing__panel::before,
.prenatal-changing__panel::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	pointer-events: none;
}

/* The wash. mask-image is the load-bearing part: without it the wash
 * would cover the pink base at full strength for the panel's whole
 * height and both bottom corners would come out wrong. */
.prenatal-changing__panel::before {
	background: linear-gradient(to right, #ffbed6, #f8f8f8);
	-webkit-mask-image: linear-gradient(to bottom, #000, transparent);
	        mask-image: linear-gradient(to bottom, #000, transparent);
}

/* The dot matrix, built in CSS per Gerek's direction rather than shipped
 * as the raster tile Figma uses.
 *
 * Every number is measured off that tile (imageHash 5396437087...,
 * scaleMode TILE, scalingFactor 0.5): the source is a 512px tile of 4px
 * dots on a 24px pitch, and at 0.5 scale that draws as 2px dots on a
 * 12px pitch. Both figures were confirmed a second way, by clustering
 * the pixels of the rendered node - 2px boxes, rows at 0/12/24/36/48.
 *
 * !! DOT COLOUR is rgb(61,46,34) at 15.7% alpha, straight from the
 * tile's own pixels. It is a warm brown and it is NOT --neutral-ink
 * (#33302f), so no token fits and none was added for a single 2px dot.
 * Worth knowing the two Figma exports disagree on the alpha - the tile
 * says 40/255, the rendered node says 30/255 - because a 2px dot loses
 * peak alpha to antialiasing. The tile is the source, so 40/255 stands.
 *
 * Dots are centred in their cells rather than phase-locked to the
 * corner the way the tile is. At a 12px pitch the phase is invisible.
 *
 * The 0.118 alpha is the RENDERED value, not the tile's 0.157, and it
 * was arrived at by measurement: at 0.157 the CSS dot came out 26%
 * hotter than Figma's, because a hard-stopped radial-gradient is crisper
 * than a bitmap that has been scaled down by half. At 0.118 the peak
 * contrast against the panel matches Figma's to within 0.1 of 25.2
 * (measured in the left padding strip, which has no glyphs in it).
 *
 * What still differs: about 4.7 percentage points more faint-skirt
 * pixels than Figma, because the CSS circle's falloff profile is not a
 * bitmap's. Tightening the outer stop does not move it - the measure is
 * driven by dot count, not radius - so it is left alone rather than
 * fitted to noise. On a texture whose peak is 25/255 it is not visible. */
.prenatal-changing__panel::after {
	background-image: radial-gradient(
		circle at center,
		rgba(61, 46, 34, 0.118) 0.9px,
		rgba(61, 46, 34, 0) 1.15px
	);
	background-size: 12px 12px;
}

.prenatal-changing__body {
	color: var(--neutral-muted);
}
/* The panel's 32px bottom padding - Figma's 30, snapped. Leaving the last
 * paragraph's own 16px margin on top of it would make the padding
 * lopsided. */
.prenatal-changing__body p:last-child {
	margin-bottom: 0;
}

.prenatal-changing__illustration {
	display: block;
	flex: none;
	width: 189px;
	height: auto;
}

/* ---- page section 3 responsive ---- */

/* !! 75/25 AT DESKTOP, GEREK'S CALL 2026-09-01: "I do not want it to be
 * a 50/50. I want it to be 75/25, where the icon is 25%."
 *
 * It was never literally 50/50 - the panel was a fixed 795 and the
 * illustration a fixed 189 - but the SLACK is what he was reading. The
 * container moved to 1400 on 2026-09-01, so at 1440 the row's content
 * box is 1352 and those three fixed values (795 + 48 + 189 = 1032) left
 * 320px of empty space trailing off the right. The panel sat left, the
 * illustration floated in the middle of nothing, and the pair read as
 * unbalanced. That is the "not balanced" and the "too small" in the
 * same observation: the illustration was not small, the gap beside it
 * was large.
 *
 * Both columns now take a share of the row instead of a fixed width,
 * and the 48px gap is split evenly out of the two shares so 75 + 25
 * still adds up to the full row: 75% - 24 and 25% - 24. At 1440 that
 * is a 990px panel and a 314px illustration against the old 795 and
 * 189 - the illustration is two-thirds larger, which is the "bigger"
 * that was asked for, and the row now fills its container edge to edge.
 *
 * !! ASSET RESOLUTION, worth knowing rather than acting on.
 * pillar-illustration-prenatal.webp is 379 x 478. At the old 189 slot
 * it was a clean 2x; at 314 it is 1.21x, so on a retina display it is
 * softer than it was. It is still a DOWNSCALE at every width - the
 * container caps at 1400, so the column never exceeds 314 and the file
 * is never stretched past 1:1 - and it is flat line art, which is the
 * most forgiving thing to resample. A 758px-wide re-export would make
 * it crisp again if Gerek wants it; flagged, not invented.
 *
 * MIN-WIDTH, not an unconditional rule. Gerek's instruction on tablet
 * and mobile was "do not change", and this file is append-only - an
 * unconditional rule here would beat the two max-width blocks below on
 * source order and silently take the phone with it. A 992 floor cannot
 * reach them at all. */
@media (min-width: 992px) {
	.prenatal-changing__panel {
		/* Replaces flex: 1 1 795px / max-width: 795px above. The 24 is
		 * half of the row's 48px gap. */
		flex: 0 0 calc(75% - var(--space-24));
		max-width: calc(75% - var(--space-24));
	}

	/* Replaces the fixed 189px width above. flex-basis is the width on a
	 * row axis, and height: auto on the element keeps the ratio, so the
	 * intrinsic 379 x 478 is preserved. */
	.prenatal-changing__illustration {
		flex: 0 0 calc(25% - var(--space-24));
		width: calc(25% - var(--space-24));
	}
}

@media (max-width: 991px) {
	/* See the note on the row above: this restores section 08b's tablet
	 * gap, which source order would otherwise hand back to 48px. */
	.prenatal-changing__row { gap: var(--space-40); }

	/* At tablet the row still fits exactly (557 + 40 + 189 = 786, the
	 * full inner width) but the panel is by then some 950px tall, and a
	 * 239px illustration centred against that reads as a gap rather than
	 * as a pair. Top-aligning it puts it beside the heading, which is the
	 * relationship the desktop frame actually shows. Gerek's direction
	 * 2026-08-17, chosen over stacking early.
	 *
	 * align-self on the child, not align-items on the row: .hero__split's
	 * centring is still correct for the panel itself. The 32px offset
	 * matches the panel's own top padding so the illustration lines up
	 * with the cap height of the heading rather than with the panel edge. */
	/* !! THE ILLUSTRATION LEADS THE STACK, 2026-09-01. Gerek, on all
	 * three service pages at once: "after the hero, we have the colored
	 * box, then the icon, and then the next section starts with another
	 * icon. On both tablet and mobile the icons are right after each
	 * other. Reverse it, so the icon comes first and then the colored
	 * box follows - that way we are breaking up the icon versus the
	 * content."
	 *
	 * He is describing a collision the desktop layout hides. Side by
	 * side, this illustration sits to the RIGHT of the panel and the next
	 * section's centred mark is a whole section away. Stacked, the
	 * illustration lands at the BOTTOM of this section and that mark at
	 * the TOP of the next one, so the two pieces of line art end up
	 * adjacent with only a section boundary between them - two drawings
	 * in a row and no words. Leading with it puts the panel's copy
	 * between them instead.
	 *
	 * order: -1 rather than reordering the markup. Source order is
	 * panel-then-illustration on all three pages and that is correct for
	 * a screen reader and for desktop: the panel carries the H2 that
	 * names the section, and the illustration is decorative and
	 * aria-hidden. Moving the <img> in the template would put a hidden
	 * decoration ahead of the heading in the document for the sake of one
	 * breakpoint. A visual order property is exactly the tool for a
	 * visual-only reversal.
	 *
	 * 991, not 767: "on both tablet and mobile", and 991 is where all
	 * three of these rows stack, so it is the same boundary the collision
	 * appears at. */
	.prenatal-changing__illustration {
		order: -1;
		/* !! align-self AND margin-top BOTH CHANGE, and the rule they
		 * replace was already stale. It read `flex-start` plus a 32px top
		 * offset, with a note explaining that at tablet the row "still
		 * fits exactly" and the illustration should top-align BESIDE the
		 * heading. That was true when .hero__split stacked at 767; it
		 * stacks at 991 now, so the row has been a column at this width
		 * for some time and the rule has been ranging a lone drawing
		 * against the left edge with a 32px hole above it.
		 *
		 * Centred and flush, matching what Pediatric and Postpartum
		 * already do with theirs. */
		align-self: center;
		margin-top: 0;
	}
}

@media (max-width: 767px) {
	/* .hero__split turns the row into a column here, so the panel stops
	 * competing for width and the illustration centres under it. Figma
	 * draws no mobile frame for this page, so the stack order - panel
	 * first, illustration second - follows the document's reading order
	 * and the homepage's precedent, and is a build decision rather than
	 * something the design states. */
	/* The second of section 08b's two gap steps, restored for the same
	 * source-order reason as the tablet one above. Without it the stacked
	 * column would keep the 40px tablet gap instead of .hero__split's 32. */
	.prenatal-changing__row { gap: var(--space-32); }

	.prenatal-changing__panel {
		flex: 1 1 auto;
		max-width: 100%;
		/* 56px of side padding inside a 390px screen leaves a 230px
		 * measure. Figma has no mobile value to copy, so this drops to
		 * the container's own gutter step. */
		padding: var(--space-32) var(--space-24);
	}
	/* Back to centred once the row is a column - and the tablet block's
	 * 32px top offset has to be cleared with it, or it would stack on top
	 * of the column gap. */
	.prenatal-changing__illustration {
		align-self: center;
		margin-top: 0;
	}
}


/* ---- page section 4: "Why One Adjustment Isn't..." (650:2220) ----
 *
 * The layout is entirely .trained__inner from section 10 - centred flex
 * column, 16px gap, centred text - and the icon is .trained__icon at its
 * existing 88px. Nothing about either needed changing, so nothing here
 * restates them. Three rules only: the section's rhythm, the card, and
 * the two measures.
 *
 * !! SECTION RHYTHM, and a deliberate improvement on section 10.
 * Inside Figma's Frame 1 these sections are 50px apart, not the
 * homepage's 100px: section 3 ends at y=329 and this starts at 379; this
 * ends at 758.4 and section 5 starts at 808.4. Section 10 spends its
 * rhythm as padding-block on BOTH neighbours, which is why its own
 * comment flags that two adjacent sections sum to double the design's
 * gap. That trap is avoided here by spending the gap on ONE side only -
 * padding-top, no padding-bottom - so each 48px boundary is contributed
 * by exactly one section and the sum is what the file draws. Section 2's
 * wrapper already works this way. */
/* Figma: SOLID white, radius 24, padding 30/150, no shadow and no
 * border. See the note in page-prenatal.php on why a white card on a
 * white body is built even though it currently renders as nothing.
 *
 * Figma's side padding here is 150. Capped to 48 by the approved scale.
 * This changes the card's content box but NOT the reading width: the
 * max-width: 840px on .prenatal-why__title / __body a few rules below
 * caps the measure independently of this padding, so the text still sets
 * at Figma's 840. The breakpoint steps below are now nearly no-ops, kept
 * so the intent survives if the ceiling is ever revisited. */
/* Figma draws both text blocks at 840 inside the 1140 frame. Kept
 * literal rather than scaled - see the MEASURE note in
 * page-prenatal.php. Overrides .trained__title / .trained__body's 903px,
 * which is the homepage's scaled measure and wrong for this card.
 *
 * At the 1200px container the card's content box is 852 rather than
 * Figma's 840, so the cap leaves a 156px side gap against the file's
 * 150. Six pixels, from the container being 60px wider than the frame. */
.prenatal-why__title,
.prenatal-why__body {
	max-width: var(--measure-card);
}

/* ---- page section 4 responsive ---- */

@media (max-width: 767px) {
	/* Side padding now steps with .card--pad in section 18, which is where
	 * every card on the site gets the same treatment. */
}


/* ---- page section 5: "The Webster Technique" (650:2266) ----
 *
 * Navy card, two columns: a fixed 160px mark stack left, a filling text
 * column right, gap 100, tops aligned. See page-prenatal.php for why no
 * shared row class is reused here - both candidates would have cost more
 * in overrides than the row costs to write.
 *
 * Same one-sided rhythm as sections 2 and 4: 48px on top, nothing on the
 * bottom, so each boundary is contributed by exactly one section. Figma
 * agrees - section 4 ends at y=758.4 and this starts at 808.4. */
.prenatal-webster__panel {
	display: flex;
	align-items: flex-start;   /* Figma counter axis MIN, not centre */
	gap: var(--space-48);
	padding: var(--space-32) var(--space-48);
	border-radius: var(--radius);
	/* ICPA's brand navy. Deliberately not a token - see the note in
	 * page-prenatal.php. */
	background: #00378b;
	/* Figma's "Main Card Shadow" effect style, which is --shadow-card
	 * exactly. (The MCP code output renders it as a Tailwind drop-shadow
	 * with a 15px radius and no spread; the file says radius 30, spread
	 * -10, and the file wins. Same conversion loss as section 3.) */
	box-shadow: var(--shadow-card);
	/* Set once here so the heading and paragraphs can inherit it. Without
	 * this the base h1-h6 rule would paint the heading --neutral-ink and
	 * it would all but vanish against the navy. Borrowed from how
	 * .about__content carries colour for its own dark card. */
	color: var(--surface-white);
}

/* Figma: 160 wide, vertical, gap 20, items MIN.
 *
 * Fixed because 160 is the width the design states, not for any
 * technical reason - both files are 2x, so they could shrink without
 * softening if the column ever needed to give ground. Worth knowing if
 * the tablet measure below is ever revisited. */
.prenatal-webster__marks {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-20);
	flex: none;
	width: 160px;
}
.prenatal-webster__marks img {
	display: block;
	width: 100%;
	height: auto;
}

/* Figma gives this flex-[1_0_0], so the 768px width in the file is
 * derived, not designed: 1140 - 56 - 160 - 100 - 56. It therefore needs
 * no max-width, and at the 1200px container it simply resolves to 780
 * instead. Nothing to scale and nothing to cap. */
.prenatal-webster__content {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	flex: 1 1 0;
	min-width: 0;
}

.prenatal-webster__body {
	margin: 0;
	color: inherit;
}

/* ---- page section 5 responsive ---- */

@media (max-width: 991px) {
	/* Was a 100px gutter, now 48. Still steps to 40 here so the two-column
	 * sections stay in step at this width. */
	.prenatal-webster__panel { gap: var(--space-40); }
}

@media (max-width: 767px) {
	.prenatal-webster__panel {
		flex-direction: column;
		gap: var(--space-32);
		/* Matches sections 3 and 4 so all three cards share one gutter. */
		padding-inline: var(--space-24);
	}
	/* !! THE MARKS AND THE HEADING NOW CENTRE, AND THIS REVERSES AN
	 * EARLIER DECISION ON PURPOSE. This block used to carry a note saying
	 * the marks stay left-aligned because Figma aligns the column to MIN
	 * on desktop and draws no mobile frame, so left was "the design's own
	 * answer" and centring would be inventing one.
	 *
	 * That reasoning was sound and the conclusion was still wrong. Figma
	 * has no mobile frame for this card at all, so there was never a
	 * design answer to defer to - only a desktop one being carried down
	 * to a width it was not drawn for. Gerek looked at it on a phone
	 * 2026-09-01 and asked for both centred. That is the missing frame,
	 * and it beats an inference from the desktop layout.
	 *
	 * THE BODY COPY STAYS RANGED LEFT. Only the icon and the title were
	 * asked for, and three centred paragraphs of running text at this
	 * measure are materially harder to read - the eye loses the left edge
	 * on every return. A centred mark and heading over left-set body is a
	 * pattern this site already uses; centred body copy is not. */

	/* align-self on the marks column rather than align-items on the
	 * panel: the panel's flex-start is what ranges the text column left
	 * and that has to stay. The column is a fixed 160px, so centring it
	 * centres both the wordmark and the seal inside it as a unit, which
	 * keeps their 20px stack alignment to each other intact. */
	.prenatal-webster__marks {
		align-self: center;
	}

	/* The heading only. .prenatal-webster__body carries no alignment, so
	 * it keeps the inherited left and is not dragged along by a rule on
	 * the content column. */
	.prenatal-webster__content .section__title {
		text-align: center;
	}
}


/* ---- page section 6: "Backed by 285+ Five-Star Reviews" (677:2053) ----
 *
 * White card like section 4, but horizontal: a 639px text column in
 * flow, and the stat fan positioned absolutely over the card's right
 * side - which is what Figma does, and why the section's 80px
 * auto-layout gap is inert in the file. See page-prenatal.php.
 *
 * The fan is anchored with `right`, not `left`. Figma's absolute x=720
 * only works at exactly 1140; anchoring to the right edge keeps the
 * design's real relationship - fan tucked into the card's right side,
 * overhanging the padding - at any card width. */
.prenatal-proof {
	/* The fan's two geometry numbers, hoisted so the text column below can
	 * be sized FROM them instead of being a second literal that has to be
	 * kept in step by hand. They are dimensions and a position offset, not
	 * spacing, so the approved 48px scale does not govern them - same
	 * footing as the focus ring's radius and the sub-menu's transform.
	 *
	 * !! WHY THEY ARE HERE AND NOT ON .prenatal-proof__fan. The 767 block
	 * at the end of this section overrides the fan's width to 100%. These
	 * have to be declared somewhere that block still wins over, which
	 * means the section root, not the fan itself. */
	--fan-w:     425px;
	--fan-inset:  30px;
}

.prenatal-proof__card {
	position: relative;
	display: flex;
	align-items: center;
	/* Figma: no effect on this frame, so no .card--raised. Section 4's
	 * card is the same. */
}

/* !! WAS A LITERAL 639px, AND THAT IS WHAT BROKE.
 *
 * Figma draws this column at 639 inside its 1140 frame, and the build
 * took that number literally. It fitted while --container was 1200: the
 * card was 1152 wide, the fan's left edge landed at 697, the text ended
 * at 687, and there was 10px of clearance nobody had to think about.
 *
 * --container moved to 1140 (664e84d). The card is 1092 now, so the fan
 * moved 60px LEFT to 637 while the text column, being a literal, did not
 * move at all - it still ended at 687. The absolutely-positioned fan is
 * painted after the text in document order, so it printed a 50px band of
 * artwork straight over the ends of the paragraph lines at every desktop
 * width. Measured in the browser, not calculated: 50.0px box overlap,
 * 49.5px to the widest rendered glyph run.
 *
 * So the width is DERIVED now rather than restated. It is whatever is
 * left of the card once the fan's lane is taken out of it:
 *
 *     100%              the card's content box (1092 - 48 - 48 = 996)
 *   - --fan-w           the composite's own width          425
 *   - --fan-inset       how far the fan is held off the card edge  30
 *   + --space-48        the card's right padding, which the fan
 *                       deliberately overhangs, so it is not lost twice
 *   - --space-24        clearance between the two. Figma's own gap here
 *                       is 25 (720 - 695); 24 is the scale's nearest
 *                       step and the difference is under a pixel.
 *
 * At the current container that resolves to 565px with 24px of clearance
 * - verified in the browser at 1440 and 1200. The point is not the
 * number: it is that the fan and the text can no longer be moved
 * independently, so the next container change cannot reopen this.
 *
 * !! The 1199 block below MUST release this cap. The calc assumes the fan
 * is beside the text; once the card stacks, the fan's lane is no longer
 * being reserved and the same expression goes negative - it collapsed the
 * column to 0 and pushed the page into horizontal overflow at 390 when
 * this was first tried without the reset. */
.prenatal-proof__text {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	width: 100%;
	max-width: calc(100% - var(--fan-w) - var(--fan-inset) + var(--space-48) - var(--space-24));
	/* !! INK, not muted. 677:2055 is #33302f where every other body block
	 * on this page is #6f6866. Drawn that way in the file. */
	color: var(--neutral-ink);
}

.prenatal-proof__body {
	margin: 0;
}

/* ---- the fan ----
 *
 * One supplied composite now, not three CSS cards - see the note in
 * page-prenatal.php. Everything the old build needed for the cards,
 * their rotations, the star, the stat type and the second shader fill
 * has gone with them.
 *
 * Group 10 is 390 x 255.242 in the file and its right edge sits 30px in
 * from the card's right edge; the composite is 425 x 288 because it
 * carries about 17px of shadow bleed per side, so the box is that much
 * larger and the drawn artwork still lands where Figma draws it.
 *
 * Centred vertically for the same reason as before: the text column runs
 * far past the file's 442 at the locked body size, so the file's fixed
 * top offset would pin this to the top and leave a hole under it. */
.prenatal-proof__fan {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	right: var(--fan-inset);
	width: var(--fan-w);
}

.prenatal-proof__cards {
	display: block;
	width: 100%;
	height: auto;
}

/* ---- page section 6 responsive ---- */

/* !! A NEW BREAKPOINT, and it is the container's own width, not a guess.
 * The absolute fan and the 639px text column only coexist while the card
 * is at least about 1094 wide (695 of text plus 425 of fan minus nothing,
 * plus the 30 inset). The card is the container minus 48, so that is a
 * viewport near 1142 - below it the two collide. 1199 is the first round
 * number under the 1200px container maximum, so the row survives only at
 * full container width and stacks everywhere below, which is exactly the
 * range where Figma's geometry stops fitting. */
@media (max-width: 1199px) {
	.prenatal-proof__card {
		flex-direction: column;
		gap: var(--space-40);
	}
	/* max-width, not just width: the desktop cap is a calc that reserves
	 * the fan's lane, and the fan is not in that lane any more once the
	 * card stacks. Left in place it resolves negative as the card narrows
	 * and takes the text column to zero. */
	.prenatal-proof__text { width: 100%; max-width: 100%; }
	.prenatal-proof__fan {
		position: static;
		transform: none;   /* clears the desktop centring translate */
		align-self: center;
	}
}

@media (max-width: 767px) {
	.prenatal-proof__card {
		gap: var(--space-32);
		/* padding-inline steps with .card--pad in section 18. */
	}
	/* 425 will not fit a 294px column, and the composite can no longer
	 * drop its two decorative cards the way the CSS build did, so the
	 * whole picture scales down instead. That is the accepted cost of
	 * the swap. */
	.prenatal-proof__fan {
		width: 100%;
		max-width: var(--fan-w);
	}
}


/* ---- page section 7: "A Waiting Room Built for Moms" (448:575) ----
 *
 * White card, radius 24, a flat 20px of padding, no shadow, no gap:
 * photo and text column sit directly against each other and the text
 * column supplies its own 48px of side padding.
 *
 * See page-prenatal.php for why .about__panel and friends were measured
 * against this and not reused. */
.prenatal-waiting__panel {
	display: flex;
	align-items: flex-start;   /* Figma counter axis MIN */
}

/* Figma: 480 x 340, radius 24, image fill at scaleMode FILL - which is
 * object-fit: cover. The asset is 960x680, a clean 2x, so it is never
 * upscaled.
 *
 * Fixed height rather than auto, because the fill is a crop: at the
 * locked type scale the text column comes out around 344 against this
 * 340, so the two still finish within a few pixels of each other and
 * the card keeps the proportions the design draws. */
.prenatal-waiting__photo {
	display: block;
	flex: none;
	width: 480px;
	height: 340px;
	object-fit: cover;
	border-radius: var(--radius);
}

/* Figma: fills the remaining width AND the card height (both sizing
 * modes FILL), vertical, gap 16, side padding 48, contents centred on
 * the main axis. align-self is what reproduces the vertical FILL against
 * the panel's flex-start. */
.prenatal-waiting__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;
	gap: var(--space-16);
	flex: 1 1 0;
	min-width: 0;
	align-self: stretch;
	/* Figma: 48px, and the approved scale's top step is exactly 48 - the
	 * same call as .about__content in section 14. (Both comments used to
	 * read "the scale has 40 and 50 but no 48", written before B5 capped
	 * the scale and wrong in both directions since.) */
	padding-inline: var(--space-48);
	color: var(--neutral-muted);
}

/* Ink, against the column's muted body colour - the two differ here,
 * which is why the colour is not simply inherited for both. */
.prenatal-waiting__title {
	color: var(--neutral-ink);
}

.prenatal-waiting__body {
	margin: 0;
}

/* ---- page section 7 responsive ---- */

@media (max-width: 991px) {
	/* 480 of photo plus 632 of text needs 1112, and the card only offers
	 * 746 here. Stacks rather than squeezing, the same call section 14
	 * makes at this width for the same reason. */
	.prenatal-waiting__panel {
		flex-direction: column;
		gap: var(--space-24);
	}
	/* Full width once stacked, aspect held by the ratio rather than by
	 * the fixed height, so the crop never changes shape. */
	.prenatal-waiting__photo {
		width: 100%;
		height: auto;
		aspect-ratio: 480 / 340;
	}
	.prenatal-waiting__content {
		align-self: stretch;
		padding-inline: 0;
	}
}

/* No mobile block. The panel's padding is already Figma's 20 - it never
 * needed stepping down the way sections 3 to 6 did from 56 and 150 - and
 * the stacked layout above already holds at 390. Nothing to restate. */


/* ---- page section 8: "Is ... Right for You?" (593:132) ----
 *
 * Figma frame has NO fill and no shadow - only padding 30/56 and a
 * border-radius 24 with nothing to round. The radius is not built, the
 * same call section 10 makes about the homepage's equivalent frame.
 *
 * The comparison row is .expect__compare and .expect-point from section
 * 15, reused whole. Everything below only adds what Figma puts on top of
 * that component; nothing restates it. Notably the padding is NOT
 * restated, so section 15's own mobile step down to 12px/20px still
 * applies here - the source-order trap from section 3 is avoided by
 * simply not touching the property. */
/* Figma's inner frame: vertical, gap 40, both axes centred, 1028 wide
 * inside the 56px padding.
 *
 * !! THE calc() HERE WAS THE ONE PLACE THE 48 CEILING COULD BE BEATEN
 * WITHOUT AN OFF-LIST VALUE. It used to read
 * calc(var(--space-24) + var(--space-56)) - the container's own gutter
 * plus Figma's inset - which computed to 80, and would still have come
 * to 72 after the 56 was capped. Two on-list tokens, one off-list
 * result. It is a single --space-48 now: the ceiling applies to what a
 * value COMPUTES to, not just to what it is named. */
.prenatal-fit__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-40);
	padding-inline: var(--space-48);
	text-align: center;
}

.prenatal-fit__title {
	/* Figma's inner width. Component geometry, not a reading measure, so
	 * it keeps its literal rather than taking a --measure token. */
	max-width: 1028px;
}

/* !! MEASURE FOR THE PROSE. Figma sizes the subhead at its full 1028.
 * Three of the four blocks below are document prose that Figma has no
 * slot for at all, so there is no drawn width to honour for them.
 *
 * They took the old 821px standing ceiling, which was retired on
 * 2026-08-20 - at 15px body it measured 112 characters a line. They take
 * --measure-lead now, 560px and about 76.
 *
 * !! CONSEQUENCE, accepted rather than worked around: the three body
 * paragraphs go from two lines to three, so the section is taller at
 * desktop. Readability at the real body size wins over the old width.
 *
 * !! CORRECTED 2026-08-20, measured on the deployed page. An earlier
 * version of this note claimed the SUBHEAD had gone from one line to two.
 * It had not, and it does not: .prenatal-fit__lead sets at its own
 * content width of 495px, which is inside both the old 821 cap and the
 * new 560 one, so it is a single line either way and this change never
 * touched it. The note was written from the diff rather than from the
 * render. What moved was the body prose. */
.prenatal-fit__lead,
.prenatal-fit__body {
	margin: 0;
	max-width: var(--measure-lead);
	color: var(--neutral-muted);
}

/* Figma's own 806 max-width lives on .expect__compare already. This only
 * needs to stop the <ul> stretching wider than the prose above it. */
.prenatal-fit__compare {
	width: 100%;
}

/* What this page adds to the shared component: Figma gives both columns
 * a white fill, radius 24 and the Main Card Shadow, where the homepage's
 * pair sit bare. justify-content matters because the two columns are
 * equal height and their text is not - without it the shorter column's
 * contents ride to the top. */
.prenatal-fit__point {
	/* The white card itself is .card + .card--raised in the markup. This
	 * only adds what the shared component cannot know: the two columns are
	 * equal height and their text is not, so without this the shorter
	 * column's contents ride to the top.
	 *
	 * !! NO PADDING HERE OR ON .card. .expect-point supplies it, including
	 * a mobile step in section 15, and section 18 is appended - any
	 * padding there would beat that media query on source order. */
	justify-content: center;
}

.prenatal-fit__cta {
	display: flex;
	justify-content: center;
}

/* ---- page section 8 responsive ---- */

@media (max-width: 767px) {
	/* Down to the container's own gutter, matching sections 3 to 6. The
	 * comparison row's own stacking at this width comes from section 15
	 * and is left alone. */
	.prenatal-fit__inner { padding-inline: var(--space-24); }
}


/* ---- page section 9: closing CTA banner (448:581) ----
 *
 * The homepage's .ready component, reused whole - panel, photo, scrim,
 * content column, title and body all carry over from section 13, along
 * with its mobile step-down, which is left untouched deliberately.
 *
 * Four values differ from the homepage's instance, and they are the only
 * four restated here. .ready itself is NOT used: it carries the
 * homepage's 100px padding-block rhythm, where this page spends 50px on
 * one side only. Figma agrees - section 8 ends at 3199.4 and this starts
 * at 3249.4. */
.prenatal-cta__panel {
	/* Figma 440 against the homepage's 340. */
	min-height: 440px;
	/* #e6e6e6 against the homepage's --surface-off. It sits behind an
	 * opaque full-bleed photo and is never seen; carried anyway so the
	 * panel degrades to the design's own grey rather than a different
	 * page's if the photo ever fails to load. Not tokenised - one use,
	 * invisible, and no existing token matches. */
	background: #e6e6e6;
}

/* rgba(10,10,10,0.4) here against --scrim's rgba(63,63,63,0.4) on the
 * homepage. Same 40% opacity, a markedly darker base - this banner's
 * photograph is brighter and needs more suppression to hold white text.
 * --scrim is documented in section 01 as having been read off the
 * homepage's overlay specifically, so it is not redefined; this page
 * states its own value instead. */
.prenatal-cta__scrim {
	background: rgba(10, 10, 10, 0.4);
}

/* Figma sets the heading box at 700. Not scaled: the heading face is
 * 34px in both the file and the build, so the drawn measure transfers
 * directly. .ready__title has no cap of its own - the homepage's
 * heading is short enough not to need one - so this is an addition
 * rather than an override. */
.prenatal-cta__title {
	max-width: var(--measure-head);
}

/* !! LINKS ON THE SCRIM. The added "Have questions first?" line is the
 * only inline link this component has ever carried - the homepage's
 * instance has none - so nothing had set a colour for one here, and the
 * base rule painted them --brand-coral-deep. Against a 40%-scrimmed
 * photograph that lands somewhere near 2:1 and is genuinely hard to
 * read.
 *
 * Fixed the way the footer already fixes it: one blanket rule turning
 * links white inside the dark region, mirroring
 * `.site-footer a { color: var(--surface-white) }`. The underline is
 * left at the browser default, exactly as the footer's nav links leave
 * it, so the links stay distinguishable from the copy around them.
 *
 * .cta-button sets its own colour and its own text-decoration: none, so
 * the button in this panel is unaffected. */
.prenatal-cta__panel a {
	color: var(--surface-white);
}


/* ==================================================================
 * 18  SHARED COMPONENTS  (extracted 2026-08-20)
 *
 * Stage 2.5 found that Prenatal wrote 52 classes and that none of them
 * could be used by a third page, because every one was named after the
 * page it first appeared on rather than after the pattern it is. This
 * section is that correction, made with two pages of evidence behind it
 * and before Pediatric adds a third set.
 *
 * The rule this follows is the master build document's: a section is an
 * instance of a pattern, so name the pattern. Nothing here is invented -
 * every declaration is lifted from a rule that already existed and was
 * already proven on a built page.
 *
 * PER-PAGE VARIATION IS A CUSTOM PROPERTY, NOT A NEW CLASS. The hero's
 * backdrop width, photo width and accent colour differ per service page
 * and are set on the element in the template, which is the arrangement
 * --pillar-card-bg and --page-bg-image already use so that every
 * per-page value stays in that page's own file.
 *
 * !! THIS SECTION IS APPENDED, so it wins on source order against
 * everything above it. That is the point for the classes that replace
 * an earlier rule - but it is also a hazard, and two components below
 * are shaped by it. Read the notes on .card and .page-hero__row.
 * ================================================================== */


/* ---- 18.1  .section - the vertical rhythm ----
 *
 * ONE-SIDED, and that is the whole design. The homepage spends its
 * rhythm as padding-block on BOTH neighbours, so every boundary is paid
 * for twice and comes out at double the design's gap - section 10's own
 * comment flags it. Prenatal fixed that by spending the gap on one side
 * only, and this is that fix promoted to a class: each boundary is
 * contributed by exactly one section, so the sum is what the file draws.
 *
 * Held at every width deliberately. Figma has only a 1440 frame, and
 * stepping the rhythm down on one page but not another is how the two
 * drift apart.
 *
 * !! NOT FOR THE HERO. A page hero sits 10px below the header, not 48 -
 * see .page-hero, which carries its own top padding and must not take
 * this class as well. */
/* !! padding-BLOCK, not padding-top. Changed 2026-09-01 and it answers
 * a question Gerek asked directly: is the spacing between H2 sections
 * the same across pages? It was not, and it was exactly half.
 *
 * MEASURED on the live site:
 *
 *     home page   every section  padding 48 top / 48 bottom  -> 96px apart
 *     every other page           padding 48 top / 0  bottom  -> 48px apart
 *
 * So the pages built on .section were running at half the home page's
 * rhythm. "It feels a little tight" was reading a real 48px difference,
 * not a preference.
 *
 * 48/48 rather than a single 96 top: it matches how the home page's own
 * sections are built, so the two stay in step if the value ever moves,
 * and a section keeps its breathing room whether or not something
 * follows it. */
.section {
	padding-block: var(--space-48);
}


/* ---- 18.2  .section__title ----
 *
 * margin only. NOT font-size, and that is the finding rather than an
 * omission: 14 rules across both pages each restated
 * `font-size: var(--type-h2)`, and all 14 sit on a real <h2>, which
 * section 02's element fallback already sizes to exactly that. Every one
 * of those declarations was dead code. Stage 1 chose bare element
 * fallbacks over mandatory heading classes; this is that decision
 * finally being trusted.
 *
 * The same is true of .page-hero__title and <h1>. */
.section__title {
	margin: 0;
}

/* Dark panels - About, Ready When You Are, and Prenatal's Webster card -
 * set a colour on the panel and let their heading inherit it, rather
 * than letting the base h1-h6 rule paint --neutral-ink onto navy. */
.section__title--inverse {
	color: inherit;
}


/* ---- 18.3  .card ----
 *
 * Generalised from six near-identical rules across both pages:
 * .expect__panel, .contact__panel, .prenatal-why__card,
 * .prenatal-proof__card, .prenatal-waiting__panel, .prenatal-fit__point.
 * All six painted white, all six rounded to --radius; they differed only
 * in padding and in whether they carried the shadow.
 *
 * !! .card CARRIES NO PADDING, and that is deliberate rather than an
 * oversight - it is the one place this component's shape had to be
 * argued rather than copied.
 *
 * Padding could not go on the base class, for two independent reasons:
 *
 *   - .contact__panel has no uniform padding at all. It sets only
 *     padding-right, because its details column supplies its own. A
 *     default on .card would have added padding the design does not
 *     have.
 *   - .prenatal-fit__point sits on top of .expect-point, which already
 *     has padding AND a mobile step down to 12/20 in section 15. This
 *     section is appended, so ANY padding here would beat that media
 *     query on source order - media queries add no specificity - and the
 *     mobile step would have died silently. That is the same trap
 *     section 3's row note describes, and the same one that broke the
 *     proof section's fan during the a66e147 fix.
 *
 * So padding is opt-in, one modifier each, and a card that already has
 * padding from somewhere else simply takes none of them.
 *
 * !! .trust-badge and .testimonial-card also paint white at --radius and
 * are deliberately NOT folded in. Both carry enough layout of their own
 * that they would need the base class plus an override for nearly every
 * property, which is more code and less clarity, not less. Confirmed as
 * a decision, not an oversight. */
.card {
	border-radius: var(--radius);
	background: var(--surface-white);
}

/* Figma's published "Main Card Shadow", which --shadow-card mirrors
 * exactly. Opt-in because three of the six cards are drawn without it. */
.card--raised {
	box-shadow: var(--shadow-card);
}

/* The common case: Figma's 30/56 on the Prenatal cards, snapped to the
 * approved scale. */
.card--pad {
	padding: var(--space-32) var(--space-48);
}

/* A flat 20 all round - the waiting-room panel, where the photo and the
 * text column sit directly against the card edge. Figma's own value, not
 * a capped one. */
.card--flush {
	padding: var(--space-20);
}

/* 48 all round - the New Patients panel. */
.card--roomy {
	padding: var(--space-48);
}

/* ---- 18.3 responsive ----
 *
 * !! THESE STEPS ARE NOT OPTIONAL DECORATION, they are the reason the
 * padding modifiers can exist at all.
 *
 * Every page that had one of these cards also had a mobile step tightening
 * its side padding to the container's own gutter - .expect__panel in
 * section 15, .prenatal-why__card and .prenatal-proof__card in section 17.
 * Those live in EARLIER sections, and section 18 is appended, so an
 * unconditional padding here beats all of them on source order and the
 * cards would sit at desktop padding on a phone.
 *
 * Measured when exactly that happened during this extraction: the Prenatal
 * "Why One Adjustment" card grew 98px taller at 390 and the proof card 63px,
 * because both were holding 48px of side padding where they used to hold 24
 * and the text rewrapped inside a narrower box.
 *
 * So the steps belong to the component, and the page-level overrides they
 * replace are deleted rather than left to lose silently. */
@media (max-width: 991px) {
	.card--roomy { padding: var(--space-32); }
}

@media (max-width: 767px) {
	/* Down to the container's own gutter, so a card's inner edge lines up
	 * with the page's. */
	.card--pad   { padding-inline: var(--space-24); }
	.card--roomy { padding: var(--space-24); }
}


/* ---- 18.4  .accent-squiggle ----
 *
 * The Figma "Squiggle Accent" vector (525:707 / 525:708), drawn under a
 * word in a heading.
 *
 * !! THE SVG IS NOW A MASK, NOT A BACKGROUND IMAGE, AND THAT IS THE
 * WHOLE REASON THIS COMPONENT EXISTS.
 *
 * It used to be a background-image data URI with the stroke colour baked
 * into the SVG markup itself. CSS cannot interpolate a custom property
 * into a url(), so the Prenatal page could not recolour it - it had to
 * carry a SECOND, character-for-character copy of the same data URI with
 * one hex changed, and that hex was typed straight into the rule.
 * Pediatric would have been a third copy and a third raw colour, which
 * is exactly what the project's central-list rule forbids.
 *
 * As a mask the shape is carried once and the colour is a real token:
 * the SVG paints the AREA, --squiggle-color paints the INK. A fourth
 * page changes one custom property.
 *
 * The path, viewBox, 4px width and round cap are unchanged from the
 * exported asset - two full sine cycles. Only the stroke is now white,
 * which is a mask concern rather than a design one: an SVG used through
 * mask-image is read as alpha in Chrome and as luminance elsewhere, and
 * white is both fully opaque and fully luminous, so it resolves
 * identically under either reading. Black would have vanished under the
 * second.
 *
 * mask-image with the -webkit- prefix is already used in this file, on
 * .prenatal-changing__panel::before, so this is an established technique
 * here rather than a new dependency.
 *
 * GEOMETRY IS UNCHANGED from .hero__title-accent, including the derived
 * vertical offset. Figma centres the squiggle on the BOTTOM EDGE OF THE
 * LINE BOX, and Baloo 2 has an unusually tall content box - 1.6087em
 * measured against a 1.18em line box - so an inline ::after anchored to
 * the content box sits about 15px too low and strikes through the next
 * line once the heading wraps:
 *
 *   bottom = (content 1.6087em - lineBox 1.18em) / 2 - (height 0.26em / 2)
 *          = 0.0843em
 *
 * Sized in em so it tracks the heading down the responsive ladder. */
.accent-squiggle {
	position: relative;
	display: inline;
	white-space: nowrap;
}
.accent-squiggle::after {
	content: '';
	position: absolute;
	bottom: 0.084em;
	left: 0;
	right: 0;
	height: 0.26em;

	/* The ink. --accent-cyan is the default because the homepage's is the
	 * original instance; a page wanting another colour sets
	 * --squiggle-color on the heading or any ancestor. */
	background: var(--squiggle-color, var(--accent-cyan));

	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 179.001 11.9975' preserveAspectRatio='none'%3E%3Cpath d='M2.00047 5.99876C31.1671 -7.85764 60.3338 19.8552 89.5005 5.99876C118.667 -7.85764 147.834 19.8552 177 5.99876' stroke='%23ffffff' stroke-width='4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
	        mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 179.001 11.9975' preserveAspectRatio='none'%3E%3Cpath d='M2.00047 5.99876C31.1671 -7.85764 60.3338 19.8552 89.5005 5.99876C118.667 -7.85764 147.834 19.8552 177 5.99876' stroke='%23ffffff' stroke-width='4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
	-webkit-mask-size: 100% 100%;
	        mask-size: 100% 100%;
	-webkit-mask-repeat: no-repeat;
	        mask-repeat: no-repeat;
}


/* ---- 18.5  .breadcrumb ----
 *
 * Was welded inside the Prenatal hero as .prenatal-hero__breadcrumb.
 * Every page below root needs one - seventeen of the nineteen in the
 * locked sitemap - so it is its own component and carries no hero
 * geometry.
 *
 * Figma draws the separator as its own "/" text node. Here it is a
 * ::before so a screen reader is not made to read it out, and the trail
 * stays a real ordered list. The 8px gap plus the 8px margin reproduce
 * Figma's even 8/8 spacing either side of the slash. */
.breadcrumb ol {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--type-xs);   /* Figma 13 */
	color: var(--neutral-muted);
}
.breadcrumb li + li::before {
	content: "/";
	margin-right: var(--space-8);
}
.breadcrumb a { color: inherit; }
.breadcrumb [aria-current="page"] {
	color: var(--neutral-ink);
	font-weight: var(--weight-medium);   /* Figma: Poppins Medium */
}


/* ---- 18.6  .page-hero - the service-page hero ----
 *
 * Pediatric and Postpartum are the same page shape as Prenatal: a
 * watermark, a breadcrumb, an H1 with a squiggled word, a subhead, a
 * CTA, a photograph on the right, and a decorative backdrop bleeding off
 * the right edge. This is that shape, with the things that differ per
 * page exposed as custom properties.
 *
 *   --hero-backdrop-w     backdrop width at desktop   (Prenatal 1180px)
 *   --hero-backdrop-w-sm  backdrop width below 768    (Prenatal  700px)
 *   --hero-photo-w        photo column width          (Prenatal  476px)
 *   --hero-photo-ratio    photo aspect ratio          (Prenatal 476/408)
 *   --hero-watermark-w    watermark width             (Prenatal  256px)
 *   --hero-watermark-w-sm watermark cap below 768     (default   300px)
 *   --squiggle-color      accent ink, see 18.4        (Prenatal pink)
 *
 * !! .hero AND .hero__split ARE NOT REUSED, and both were tried. .hero
 * carries the homepage's 48px top padding and its own mobile step, where
 * Figma puts a service-page hero 10px below the header. .hero__split
 * centres its columns on the cross axis and carries a 48/32/32 gap
 * ladder, where this row aligns to the top and has NO gap at all - the
 * separation between the columns is the text column's own padding.
 * Taking either would have meant overriding more than it supplied.
 *
 * !! THE MOBILE COLUMN STACK IS RESTATED HERE rather than inherited.
 * .hero__split's 767 block is what used to turn this row into a column,
 * and this component no longer carries that class, so the behaviour has
 * to be its own. Dropping it is how a hero silently stays side-by-side
 * on a phone. */
/* !! THE TOP PADDING IS NO LONGER FIGMA'S 12, AND THAT IS A DIRECTION
 * RATHER THAN A DRIFT. Figma starts Frame 27 at y=78 against a 68px
 * header, which is 10px of air, and the component was built at 12.
 * Gerek read the live page and said "we need about 100px of top space
 * from the top of the section down to the columns."
 *
 * .page-hero__row adds its own 20px of block padding, so 80 + 20 = 100
 * exactly. 80 is written as 48 + 32 rather than as a literal so it stays
 * on the approved --space- scale (8/12/16/20/24/32/40/48) instead of
 * minting a value the scale does not have - the same arithmetic the
 * Reviews page uses for its 48 + 48 = 96.
 *
 * ON THE COMPONENT, NOT ON ONE PAGE. It landed scoped to .prenatal on
 * 2026-09-01 while only that page had been reviewed; Gerek approved
 * Prenatal and asked for the same on Pediatric and Postpartum the same
 * day, so it moved here. All three service heroes now start at 100 and
 * cannot drift apart.
 *
 * THE BACKDROPS DO NOT MOVE WITH IT. .page-hero__backdrop is anchored to
 * the section's own top edge on all three pages, so the type slides down
 * INTO the decorative shapes rather than away from them - which is where
 * each page's artwork is thickest. Checked on all three, not assumed. */
.page-hero {
	position: relative;
	padding-top: calc(var(--space-48) + var(--space-32));
}

/* The flattened backdrop - shapes plus their wash, drawn at 1x.
 *
 * Anchored right because that is where Figma's group sits once clipped
 * to the frame edge, and z-index -1 with no stacking context on the
 * section, so it paints behind the page's in-flow content rather than
 * only behind this section's - which is what lets it carry on under the
 * section below, as the design does.
 *
 * !! NO overflow CONTROL, DELIBERATELY. An earlier build carried
 * `overflow-x: clip; overflow-y: visible` to stop the backdrop widening
 * the page. In Chrome that combination made the entire section stop
 * painting - photo, backdrop and all - while every element still
 * reported correct dimensions in the DOM. It was never needed: the
 * backdrop is anchored right, so on a narrow viewport its LEFT edge goes
 * negative, and content extending past the left origin creates no
 * horizontal scroll. Only content extending right does. */
.page-hero__backdrop {
	position: absolute;
	z-index: -1;
	top: 0;
	right: 0;
	width: var(--hero-backdrop-w, 1180px);
	max-width: none;
	height: auto;
	pointer-events: none;
}

/* Figma: counter axis MIN, gap 0. The gap is 0 at every width by design,
 * so unlike .hero__split there is nothing for it to step. */
.page-hero__row {
	display: flex;
	align-items: flex-start;
	gap: 0;
	/* Figma's own 20px hero padding. Block axis only: the inline 20 would
	 * fight .container's 24px gutter and end up narrower than the site
	 * standard on a phone. Costs 4px against the file. */
	padding-block: var(--space-20);
}

.page-hero__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;   /* Figma primary axis CENTER */
	gap: var(--space-16);
	align-self: stretch;
	flex: 1;
	min-width: 0;
	padding: var(--space-20) var(--space-48);
}

.page-hero__watermark {
	display: block;
	width: var(--hero-watermark-w, 256px);
	height: auto;
}

/* margin only. The <h1> element fallback in section 02 already supplies
 * --type-h1 and --leading-heading, which is Figma's 118% exactly - see
 * the note on .section__title. The column's 16px gap does the spacing
 * here, where the homepage hero uses margins. */
.page-hero__title {
	margin: 0;
}

.page-hero__body {
	margin: 0;
	color: var(--neutral-muted);
}

/* Figma: a fixed width against the homepage hero's percentage split,
 * which is why .hero__photo-col is not reused either. */
.page-hero__photo-col {
	flex: 0 0 var(--hero-photo-w, 476px);
	max-width: var(--hero-photo-w, 476px);
}

.page-hero__photo {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: var(--hero-photo-ratio, 476 / 408);
	object-fit: cover;
	border-radius: var(--radius);
}

/* ---- 18.6 responsive ---- */

@media (max-width: 991px) {
	/* A fixed 476 photo inside a 738px row leaves the text 262px. The
	 * photo goes proportional here instead, at the same 42% the homepage
	 * hero uses at this width. */
	.page-hero__photo-col {
		flex: 0 1 42%;
		max-width: 42%;
	}
	.page-hero__content { padding-inline: var(--space-32); }
}

@media (max-width: 767px) {
	/* ---- the mobile pass, 2026-09-01 ----
	 *
	 * Three fixes Gerek read off his phone on the Prenatal page. They
	 * shipped scoped to .prenatal that day and moved here the same day,
	 * when he approved that page and asked for the same on Pediatric and
	 * Postpartum. All three pages draw the same watermark-over-H1-over-
	 * CTA-over-photo stack at this width, so all three had all three
	 * problems. */

	/* 1. "A little bit too much space from the header to the first icon."
	 *
	 * The 100px above is right on a desktop and too much on a 390px
	 * screen, where it is a far bigger share of the first view - it
	 * pushed the H1 most of the way down before a word had been read.
	 * 48 + the row's 20 = 68 here, still well clear of the 32 these
	 * heroes shipped with. Tablet keeps the full 100. */
	.page-hero { padding-top: var(--space-48); }

	/* Restated, not inherited - see the note on the component. */
	.page-hero__row { flex-direction: column; }

	/* 2. "The button and the next image are right on top of each other."
	 *
	 * They were, at exactly 0px - measured. The row is built with gap: 0
	 * because on desktop the separation between its two columns is the
	 * text column's own 48px side padding, and that padding is
	 * deliberately zeroed just below so the copy lines up with the
	 * container gutter. So the stacked column had nothing left holding
	 * the two apart and every service page's photo sat flush against its
	 * CTA. The gap applies to that one boundary and nowhere else: in the
	 * stacked state this row has exactly two children. */
	.page-hero__row { gap: var(--space-32); }

	.page-hero__photo-col {
		flex: none;
		max-width: 100%;
		width: 100%;
	}
	/* The container's own 24px gutter is the whole margin on a phone. */
	.page-hero__content { padding: 0; }

	/* 3. "I want the icon centered, not left justified."
	 *
	 * align-self on the watermark alone, NOT align-items on the column.
	 * The column is what ranges the H1 and the subhead left and both stay
	 * there - a centred multi-line heading is harder to track back to.
	 * Individual flex items opt out; the column does not change its mind.
	 *
	 * !! AND IT GETS BIGGER, 2026-09-01: "the hero section icon needs to
	 * be centered and possibly a little bit larger." Centred it already
	 * was - measured at 195 of 390 on all three pages - so this is the
	 * second half of that sentence.
	 *
	 * The three pages draw this watermark at three different widths,
	 * because Figma draws three different marks: Prenatal 256, Postpartum
	 * 249, Pediatric 220. Those ratios are worth keeping on a desktop and
	 * are not worth keeping on a 390px screen, where the question is
	 * simply how much of the column the mark should hold. One answer for
	 * all three: as wide as the column allows, capped at 300.
	 *
	 * 300 is a 17-36% increase depending on the page, and every one of
	 * the three files still draws at better than 1.4x - they are 512,
	 * 498 and roughly 440 wide against a 300px slot. width: 100% with the
	 * cap rather than a flat 300 so the mark can never outgrow a narrower
	 * column than today's 342. */
	.page-hero__watermark {
		align-self: center;
		width: 100%;
		/* A KNOB rather than a flat 300 as of 2026-09-01. Postpartum's
		 * mark needs a lower ceiling than the other two and the reason is
		 * the file, not the design - see --hero-watermark-w-sm on
		 * .postpartum in CSS 34. Every other page takes the 300. */
		max-width: var(--hero-watermark-w-sm, 300px);
	}

	/* 4. THE HERO CTA CENTRES, on all three service pages, 2026-09-01:
	 * "the hero section Book Appointment button needs to be centered on
	 * all three on mobile only."
	 *
	 * !! TWO SELECTORS BECAUSE THE THREE PAGES DO NOT AGREE ON THE
	 * MARKUP, and that is worth knowing rather than tidying blind.
	 * Prenatal wraps its button in .hero__cta-wrap - inherited from the
	 * homepage hero it was built from - while Pediatric and Postpartum
	 * put the <a class="cta-button"> straight into the column. Both are
	 * direct flex children of .page-hero__content, so both take
	 * align-self and both land on the same axis. Unifying the markup is
	 * the real fix and is a template change on a page Gerek has approved;
	 * recorded, not done.
	 *
	 * !! align-self, NOT the text-align trick .hero__cta-wrap already
	 * carries at 991. That trick works on the HOMEPAGE because its column
	 * stretches its children to full width, so a centred inline-flex
	 * button has room to move. This column is align-items: flex-start, so
	 * the wrap shrinks to exactly the button's width and text-align has
	 * nothing to centre inside - which is why the button was still left
	 * on Prenatal despite that rule applying to it. Measured: 289px wide
	 * at x=24 on all three.
	 *
	 * 767, not the 991 that rule uses: "on mobile only", and at 834 these
	 * heroes are still two columns where a centred button would float
	 * away from the copy it belongs to. */
	.page-hero__content > .hero__cta-wrap,
	.page-hero__content > .cta-button {
		align-self: center;
	}
	/* 1180px of decorative backdrop on a 390px screen is all blur and no
	 * shape, so it scales back to something that still reads as an accent
	 * behind the type. */
	.page-hero__backdrop { width: var(--hero-backdrop-w-sm, 700px); }
}


/* ==================================================================
 * 19  PROSE  (added 2026-08-20)
 *
 * Base styling for running copy that comes out of the editor rather
 * than out of a component: the Privacy Policy & Terms of Service page
 * now, and About, Articles, FAQ and Momma's Village after it.
 *
 * WHY THIS SECTION EXISTS. Before it, main.css carried bare element
 * rules for exactly a, body, button, h1-h6, html, img, p and svg.
 * Every one of ul, ol, li, dl, dt, dd, blockquote, table, th, td and
 * hr had none - so a legal page, which is almost entirely headings,
 * paragraphs and bullet lists, had no rules for two of those three. An
 * unstyled <ul> would have rendered on browser defaults, with an
 * indent and a rhythm that had no relationship to the approved scale.
 *
 * SCOPED UNDER .prose, NOT BARE ELEMENTS, and that is deliberate.
 * page.php already emits <div class="page-content prose">, so the hook
 * was there waiting. Every list component on this site
 * (.pillars__list, .testimonials__list, .expect__compare,
 * .proof__gallery, .primary-nav > ul, .footer-nav ul, .mobile-menu ul,
 * .breadcrumb ol) already resets margin, padding and list-style, so
 * bare rules would have been safe TODAY - but that safety depends on
 * every future component remembering to reset. Scoping removes the
 * class of collision instead of relying on a habit.
 *
 * HEADINGS KEEP THEIR SIZE FROM SECTION 02. The h1-h6 element
 * fallbacks already supply --type-h1..--type-h4 and --leading-heading,
 * so this section only adds the space AROUND them. Same finding that
 * made .section__title margin-only in section 18: 14 rules there were
 * restating a size the element fallback already gave them.
 *
 * SPACING IS FIGMA'S OWN, UNCAPPED. Frame 618:833 draws 32px between
 * sections, 12px between blocks inside a section, and 8px between a
 * sub-heading and its list. All three are existing steps on the
 * approved scale, so for once nothing had to be capped or rounded.
 *
 * !! .prose IS CAPPED AT --measure-wide AS OF 2026-09-01, and the note
 * it replaces is kept below because the reasoning matters.
 *
 * THE ORIGINAL DECISION, Gerek's call on 2026-08-20:
 *
 *     "NO MEASURE CAP ON .prose - DECIDED, NOT DEFAULTED... Figma
 *      draws the text 1088px wide against a 1092px content box, so
 *      following the design means adding nothing here. The cost is
 *      recorded rather than argued: rendered Poppins 400 at 15px
 *      measures 7.36px per character, so a full-width line runs about
 *      148 characters against a 45-75 comfortable band."
 *
 * That was right when it was written, and it did not survive the
 * container. "Following the design means adding nothing" was true only
 * while the content box was 1092 and Figma's 1088 filled it. The box is
 * 1352 now, so adding nothing means running 264px WIDER than Figma ever
 * drew - about 125 characters a line.
 *
 * So the cap RESTORES the original decision rather than reversing it:
 * --measure-wide is 1100px, which is Figma's 1088 to within a rounding
 * step, and it is the same cap the approved homepage uses for its two
 * widest sections. Gerek chose it on 2026-09-01 from a measured list.
 *
 * !! THIS IS THE SECOND DECISION THIS WEEK THAT SILENTLY CHANGED
 * MEANING WHEN THE CONTAINER MOVED - the measure scale in 1.6b was the
 * first. The pattern to watch for: any rule justified by "it matches
 * what Figma fills" is a rule that depends on the container being
 * Figma's, and stops being true the moment it is not.
 * ================================================================== */

/* Body copy is MUTED, not ink.
 *
 * Figma draws every paragraph in frame 618:833 at #6f6866, and every
 * other body-copy component on this site already uses --neutral-muted:
 * .hero__body, .trained__body, .pillars__intro, .expect__body and the
 * whole prenatal-*__body set. Without this, .prose would inherit
 * `body { color: var(--neutral-ink) }` and run darker than the design
 * AND darker than body copy on every other page.
 *
 * Headings are unaffected: the h1-h6 rule in section 02 sets
 * --neutral-ink explicitly, so it wins over this inherited value. Same
 * for .prose th and .prose dt below. */
.prose {
	max-width: var(--measure-wide);
	color: var(--neutral-muted);
}

/* The wrapper collapses its own outer edges so the page's spacing is
 * owned by the template, not by whichever element the editor happens to
 * put first or last. */
.prose > :first-child { margin-top: 0; }
.prose > :last-child  { margin-bottom: 0; }

/* !! FIGMA'S 32 WAS COPIED WITHOUT ITS HEADING, AND THAT IS THE BUG.
 * This rule used to read "Figma: 32 between sections, 12 from a heading
 * to the block beneath it" - both numbers lifted straight off frame
 * 618:833 and correct as far as they went. What did not travel with
 * them is what they were measured AGAINST: Figma draws these section
 * headings at 24px.
 *
 * The build does not. The locked type scale beats Figma's pixel values
 * - the standing rule, applied deliberately - so --type-h2 renders
 * these at 46px, and the scale has been raised twice since (34, then
 * 40, now 46). The heading has very nearly doubled. Both gaps stayed on
 * Figma's originals through all of it.
 *
 * So the space above a section heading had become SMALLER THAN THE
 * HEADING ITSELF - 32 against 46 - where Figma has it comfortably
 * larger. On the Privacy Policy, 22 section headings deep, that reads
 * as one unbroken wall of text, which is what Gerek caught.
 *
 * THE RATIO IS THE THING THAT TRANSFERS, not the pixel count:
 *
 *   Figma   32 above / 24 heading = 1.33     12 below = 0.50
 *   Here    64 above / 46 heading = 1.39     24 below = 0.52
 *
 * 64 is calc(--space-32 * 2), the same doubling idiom the 96 section
 * boundary uses, so it stays tied to the scale rather than minting a
 * literal. 48 was the other candidate and is the nearest single step,
 * but it lands at 1.04 - still under the heading's own size, which is
 * the exact failure being fixed.
 *
 * !! NOT 96. That is the boundary between two page-level SECTIONS, each
 * a separate block. These are headings inside one flowing document, and
 * 96 across 22 of them would space a legal notice like a landing page.
 *
 * GLOBAL ON PURPOSE - the cause is global. Every .prose h2 on the site
 * renders at 46 with 32 above, so every prose page has been tight for
 * the same reason. .prose--faq h2 sets its own margin and is unaffected. */
.prose h2 {
	margin-top: calc(var(--space-32) * 2);
	margin-bottom: var(--space-24);
}
/* !! POPPINS, NOT BALOO. Figma draws prose sub-headings as Poppins
 * SemiBold, where every other heading level on the site is Baloo 2 Bold
 * from the section 02 element rule. That is a FACE decision, not a size
 * one, so the locked type scale does not settle it - and there is
 * precedent: .pillar-card__title and .ready__title both switch to the
 * body face for the same reason, because Figma draws them that way.
 *
 * The SIZE stays --type-h3 from the element fallback. Figma draws 18px
 * here against the ladder's 26, and the standing rule is that the
 * locked scale beats Figma's pixel values - the same call made on
 * Homepage and Prenatal. */
/* THE SUB-HEADING CARRIES THE SAME CORRECTION, for the same reason and
 * by the same arithmetic. Figma draws it at 18 with 24 above and 12
 * below; the ladder renders it at 30.
 *
 *   Figma   24 above / 18 heading = 1.33     12 below = 0.67
 *   Here    40 above / 30 heading = 1.33     20 below = 0.67
 *
 * Both land exactly on Figma's ratio and both are single scale steps,
 * so this one needs no doubling idiom.
 *
 * !! IT HAS TO MOVE WITH THE h2 ABOVE, NOT BE LEFT ALONE. Correcting
 * the section heading to 64 and leaving the sub-heading at 24 would
 * squash the two levels together - the whole point of the pair is that
 * a reader can feel which one outranks the other. 64 / 40 keeps that
 * gap open; 64 / 24 would widen it into something the design never
 * drew. */
.prose h3 {
	margin-top: var(--space-40);
	margin-bottom: var(--space-20);
	font-family: var(--font-body);
	font-weight: var(--weight-semibold);
}

/* ---- 19.1b  .prose--legal: a scoped sub-heading step-down ----
 *
 * ONE PROPERTY, ON ONE PAGE'S SUB-HEADINGS. Applied in
 * page-privacy-policy.php on the .prose wrapper, so it reaches exactly
 * the two <h3> elements that page carries and nothing else. The base
 * .prose h3 above is untouched, because section 19 defines it for every
 * prose page still to come - About, Articles, FAQ, Momma's Village.
 *
 * WHY. Figma frame 618:833 sets section headings at 24 and sub-headings
 * at 18. The locked ladder renders those as 34 and 26, and the RATIO is
 * actually right - 26/34 is 0.76 against Figma's 18/24 at 0.75. What is
 * wrong is the perceived weight: Poppins SemiBold at 26 holds nearly as
 * much of the page as Baloo 2 Bold at 34, so the two levels stop reading
 * as levels. Baloo is a rounded display face and carries its size
 * differently from Poppins at the same nominal px.
 *
 * So this is a WEIGHT problem solved with size, not a ratio correction -
 * worth saying, because the arithmetic looks fine and the page does not.
 *
 * --type-h4 (22px), NOT A NEW TOKEN. It already exists, it already
 * ladders 22/21/20/19/18 alongside --type-h3's 26/24/22/20/18, and a
 * two-instance need does not justify a fifth heading step. That is the
 * same test --type-h4-long had to pass in section 01, and unlike that
 * one this needs no new value at all.
 *
 * The FACE is unchanged - still Poppins SemiBold from the rule above.
 * Only the size moves.
 *
 * !! Named for the page TYPE, not the page. The sitemap locks exactly
 * one Legal page today; a second would want the same treatment, and
 * naming it .prose--privacy would make that a copy-paste instead of a
 * class. It is used immediately, so it is not spare vocabulary. */
.prose--legal h3 {
	font-size: var(--type-h4);
}


/* ---- 19.1d  the top of the Privacy Policy page ----
 *
 * 96 where .section gives 48, the same one-line override Reviews, FAQ,
 * Products and Momma's Village carry. Fifth page, still not
 * consolidated, and for the same reason: .page-article is shared with
 * eighteen templates and several put a hero above it, where 96 is
 * wrong.
 *
 * The hook is on <main>, not <body> - body_class() is default on this
 * build and prints no per-page class, so a rule expecting one matches
 * nothing and fails silently, which looks identical to the bug it was
 * meant to fix. */
.privacy .page-article {
	padding-top: calc(var(--space-48) * 2);
}


/* ---- 19.1c  .prose--faq: the FAQ page's question rhythm ----
 *
 * Frame 623:126 is a FLAT, ALWAYS-OPEN Q&A LIST - not an accordion. The
 * Stage 2.5 review assumed this page would need a disclosure component;
 * reading the design and the copy document showed neither asks for one.
 * Figma draws every answer open, with no chevron, toggle, open/closed
 * variant or prototype wiring, and the document has no interaction
 * instruction of any kind. So there is nothing here but prose with its
 * own rhythm, and .prose already supplies the rest.
 *
 * SPACING IS FIGMA'S OWN: 40 between blocks, 8 from a question to its
 * answer. Both are existing steps, so nothing is capped or rounded. It
 * differs from .prose's default 32/12, which is why the modifier exists.
 *
 * !! SIZE: --type-h4 ON AN <h2>, AND THE LEVEL IS STILL h2. The copy
 * document marks all eleven headings [H2], and the master rule is that
 * heading LEVELS come from the document rather than from Figma's type
 * sizes - so they stay <h2> in the markup and in the outline.
 *
 * What changes is only the rendered size. Figma draws the ten questions
 * at 20px and the closing heading at 22px, against the ladder's 34.
 * Ten headings at 34px would make the page a wall of type, and --type-h4
 * is 22px - within 2px of BOTH Figma values, and already on the central
 * list. Same shape as the .prose--legal exception, and cheaper: it needs
 * no new token at all, where that one at least needed a decision. */
.prose--faq h2 {
	margin-top: var(--space-40);
	margin-bottom: var(--space-8);
	font-size: var(--type-h4);
}

/* The ten questions, and only those.
 *
 * Figma draws them in Poppins SemiBold where the closing "Still Have a
 * Question?" heading stays Baloo 2 Bold - two different faces on two
 * headings the document marks identically. So the face goes on the
 * questions rather than on the modifier: the closing heading keeps Baloo
 * from the base h1-h6 rule by simply not carrying this class.
 *
 * Precedent for a heading switching to the body face: .pillar-card__title,
 * .ready__title and .prose h3 all do it, each because Figma draws them
 * that way. */
.faq__question {
	font-family: var(--font-body);
	font-weight: var(--weight-semibold);
}


/* ---- 19.1d  the FAQ card grid  (added 2026-09-01) ----
 *
 * !! A DEPARTURE FROM FIGMA, AND THE NOTE ABOVE IN 19.1c IS THE ONE IT
 * DEPARTS FROM. That note records - correctly - that frame 623:126 draws
 * a flat, always-open, single-column list, with no accordion, no
 * chevron, no cards and no interaction instruction anywhere in either
 * source. It is still an accurate reading of the design.
 *
 * What the frame could not know is that --container moved to 1400 after
 * it was drawn. Measured on the live page before this change: ten
 * questions ran as one 1100px column of unbroken text inside a 1352px
 * container, leaving 252px of empty page beside a wall of copy. Gerek
 * asked for the page to be "spiced up"; two columns of cards spend that
 * width and give the eye ten edges to scan instead of one.
 *
 * !! WHAT IS NOT TRADED AWAY. Every answer is still in the DOM, still
 * open, with nothing hiding it behind a disclosure widget - which was
 * 19.1c's actual finding and the thing that matters for an answer-engine
 * page. Same eleven <h2> elements at the same level, same copy, same
 * order. The FAQPage JSON-LD is built in functions.php from its own data
 * and never read this markup.
 *
 * .prose prose--faq STAYS ON THE GRID rather than being unpicked, so the
 * h2 size, the muted body colour and the un-underlined links all keep
 * applying inside the cards. Only .prose's 1100px cap is lifted - and
 * only here, on a wrapper whose children re-establish their own measure
 * through the card padding. The lead paragraphs above the grid stay in a
 * plain .prose and keep the ceiling.
 *
 * TWO COLUMNS DIE AT 991, not 767. A 652px card is a comfortable measure;
 * a 345px one at tablet is a column of two-word lines. This is the same
 * breakpoint every other two-up row on the site stacks at.
 *
 * !! CARDS STRETCH TO MATCH THEIR ROW - the grid default - AND THE FIRST
 * VERSION OF THIS RULE SAID align-items: start. That was reasoned from
 * the copy ("a short answer should not stretch to match a long one") and
 * it lost when both were put on screen. Ragged bottoms turn the gap
 * between two rows into a hole with a card edge hanging into it; matched
 * bottoms turn the same slack into padding INSIDE a box, which reads as
 * a designed grid instead of a layout that fell short. Empty space with
 * a border around it is calmer than empty space without one.
 *
 * The default is left implicit rather than written as `stretch`, so it
 * is obvious that nothing here is overriding the grid. */
.faq-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--space-32);
	max-width: none;
	margin-top: var(--space-40);
}

/* .prose--faq h2 carries a 40px top margin for the flat list it used to
 * be. Inside a card that margin is the card's padding's job, so the
 * first heading in each box starts at the top of it. */
.faq__item > :first-child,
.faq__close > :first-child {
	margin-top: 0;
}

/* And the last paragraph does not add its own 16 to the card's bottom
 * padding - same rule .prose already applies to itself. */
.faq__item > :last-child,
.faq__close > :last-child {
	margin-bottom: 0;
}

/* The closing block is one full-width card under the grid: it addresses
 * everyone who did not find their question, so it should not read as an
 * eleventh question. Its own h2 keeps Baloo from the base rule by not
 * carrying .faq__question, exactly as before. */
.faq__close {
	max-width: none;
	margin-top: var(--space-32);
}

/* ~100px from the header to the H1, Gerek's call 2026-09-01: "the
 * desktop feels like it needs a little bit more space at the top, I
 * think 100 pixels. I don't think it's there on the desktop."
 *
 * MEASURED BEFORE: 48px, which is .section's own padding doing the whole
 * boundary alone. 48 + 48 = 96, which is the ask to within four pixels
 * and stays on the approved --space- scale rather than minting a 100 -
 * the same arithmetic the Reviews page and the three service heroes
 * already use for the same request. Held at every width: the service
 * heroes step their version down on a phone because a photograph sits
 * beside the type there, and here there is only a heading.
 *
 * Scoped to .faq. .page-article is shared with Privacy Policy, First
 * Visit and the plain page template, and none of those has been reviewed
 * for this. */
.faq .page-article {
	padding-top: calc(var(--space-48) + var(--space-48));
}

/* The closing card's CTA, centred under its copy. text-align rather than
 * margin-inline: auto, because .cta-button--icon is inline-flex and auto
 * margins do not centre an inline-level box - see the note in the markup
 * and the one section 08b left after the homepage hit the same trap.
 *
 * The 24 separates it from the last paragraph; the card's own bottom
 * padding is what sits under it, so no bottom margin here. */
.faq__close-cta {
	margin-top: var(--space-24);
	text-align: center;
}

/* !! A BUTTON INSIDE .prose WOULD HAVE GROWN AN UNDERLINE ON HOVER, and
 * this is the first time a CTA has been put inside a prose block on this
 * site. .cta-button sets text-decoration: none in section 1, but
 * `.prose a:hover` in 19.1 is both LATER in the file and more specific
 * (0,1,1 against 0,1,0), so it would have won and underlined the label -
 * the one thing that rule exists to do, applied to the one element that
 * must never take it.
 *
 * Caught by reading the cascade rather than by hovering, and fixed at
 * the COMPONENT rather than on this page: any future prose page that
 * closes with a CTA needs the same guard, and none of them should have
 * to rediscover this. */
.prose .cta-button:hover,
.prose .cta-button:focus-visible {
	text-decoration: none;
}

@media (max-width: 991px) {
	.faq-grid {
		grid-template-columns: 1fr;
		gap: var(--space-24);
	}
}

.prose p { margin-bottom: var(--space-12); }


/* ---- lists ----
 *
 * padding-left, not margin-left: the marker then sits inside the
 * column rather than hanging outside it, and a nested list indents from
 * its parent item instead of from the page edge. */
.prose ul,
.prose ol {
	margin: 0 0 var(--space-12);
	padding-left: var(--space-24);
}
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }

.prose li { margin-bottom: var(--space-8); }
.prose li:last-child { margin-bottom: 0; }

/* Figma's 8px sub-heading-to-list gap carries to nesting. The bottom
 * margin is cleared because the parent <li> already supplies it. */
.prose li > ul,
.prose li > ol {
	margin-top: var(--space-8);
	margin-bottom: 0;
}


/* ---- inline ----
 *
 * !! THE PERSISTENT UNDERLINE IS OFF as of 2026-09-01, at Gerek's
 * request - "just the colored text is fine" - and it returns on hover
 * and on keyboard focus.
 *
 * The rule it replaces said: "colour alone is not enough to mark a link
 * inside a paragraph." That is WCAG 1.4.1, and it is still true - a
 * reader who cannot separate the coral from the surrounding grey now has
 * no cue at rest. Recorded as a known trade-off rather than argued: it
 * is a design call, it was asked for twice, and the hover/focus
 * underline keeps the link discoverable for anyone reaching it with a
 * pointer or the keyboard.
 *
 * SITE-WIDE rather than per-page. Gerek named three places on About and
 * they are all the same component; leaving other pages underlined would
 * only surface the same note again on each of them. */
.prose a {
	text-decoration: none;
}
.prose a:hover,
.prose a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* The pull sentence under the About hero is NOT inside .prose, so it was
 * taking the browser's default underline rather than the rule above.
 * Same treatment. */
.about-pull a {
	text-decoration: none;
}
.about-pull a:hover,
.about-pull a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* Two more running-copy components that are NOT .prose and were
 * therefore still taking the browser's default underline. Gerek found
 * one of them on the Pediatric page 2026-09-01 - "'Prenatal
 * Chiropractic Care', that's a link that is underlined, which I don't
 * want underlined" - and the other is the same defect three sections
 * above it on the same page, so both are done together rather than
 * leaving the second to be reported next.
 *
 *   .hero__aeo         the AEO paragraph under a page hero
 *   .photo-panel__body the copy column of an alternating photo panel
 *
 * Same treatment as .prose a and .about-pull a above, character for
 * character: no persistent underline, and it returns on hover and on
 * keyboard focus so the link is still discoverable by anyone probing
 * with a pointer or the keyboard. Colour alone carrying a link in
 * running copy is WCAG 1.4.1 and it is still a real concession; the
 * hover/focus underline is what keeps it from being a bare one, and
 * this is Gerek's call, made consistently across the site.
 *
 * ON THE COMPONENTS, not on the Pediatric page. .hero__aeo runs on
 * every service page and .photo-panel__body on Pediatric and
 * Postpartum; scoping this to one page would only surface the same note
 * again on the others - the same reasoning the .prose rule above
 * records. */
/* !! ONE RULE INSTEAD OF A GROWING LIST. This started as `.prose a`,
 * then gained .about-pull, then .hero__aeo and .photo-panel__body, then
 * .split__body and .ready__body - six names for one decision, each
 * added the day Gerek found another page still underlining. On the
 * Articles page it would have been two more (.prose__lead and
 * .article-cta__text), which is the point at which a list is the wrong
 * shape.
 *
 * `main a:not([class])` is the actual rule the site has been reaching
 * for. Every link that should lose its underline is a bare <a> written
 * inline in a sentence; every link that must KEEP its own styling -
 * .cta-button, .article-card__link, the nav, the footer - carries a
 * class. The attribute selector separates the two cleanly and does not
 * need updating when the next page is built.
 *
 * SCOPED TO main so the header, the footer and the skip link are out of
 * reach. The underline returns on hover and on keyboard focus, which is
 * what keeps colour-alone from being a bare WCAG 1.4.1 failure. */
main a:not([class]) {
	text-decoration: none;
}
main a:not([class]):hover,
main a:not([class]):focus-visible {
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* .split__body and .ready__body joined the list 2026-09-01, found on
 * the Postpartum page by asking the browser which links still computed
 * an underline rather than by reading the markup. Two more running-copy
 * components that are not .prose, same as the two above them, same
 * treatment.
 *
 * .ready__body is the closing CTA panel and runs on the HOME PAGE and
 * the other service pages as well, so this is not a Postpartum-only
 * change - it is the same site-wide answer applied to the last two
 * places that were still holding the browser default. Every internal
 * link in running copy on this site now reads as coloured text, with
 * the underline returning on hover and on keyboard focus. */

.prose strong { font-weight: var(--weight-semibold); }

/* <address> is the right element for a page's own contact block, and the
 * Privacy Policy ends with one. Browsers italicise it by default, which
 * Figma does not draw and which reads as emphasis the copy does not
 * intend - the same reset .site-footer address and .contact__value
 * already apply for the same reason. */
.prose address {
	font-style: normal;
	margin-bottom: var(--space-12);
}


/* ---- elements no page uses YET ----
 *
 * Written now so the next prose page inherits a foundation instead of
 * inventing one. These are element FALLBACKS rather than vocabulary -
 * they do nothing at all until the element appears in content, which is
 * a different thing from an unused token or an unused class, both of
 * which offer themselves to whoever reads the file.
 *
 * Still omitted on purpose: code, pre, figure, figcaption, sup and sub.
 * No page in the locked sitemap needs them.
 *
 * !! NOT VERIFIED AGAINST A REAL RENDER. Nothing in the build uses hr,
 * blockquote, a table or a definition list today, so these are written
 * from the pattern and the scale rather than measured against content.
 * The first page to use one must check it - the table especially, and
 * at 390. */

.prose hr {
	margin-block: var(--space-32);
	border: 0;
	border-top: 1px solid var(--surface-grey);
}

.prose blockquote {
	margin: 0 0 var(--space-12);
	padding-left: var(--space-20);
	border-left: 2px solid var(--brand-pink);
	color: var(--neutral-muted);
}

/* display: block plus overflow-x on the table ITSELF, rather than the
 * usual wrapper div, because the editor cannot be relied on to produce
 * a wrapper. A wide table then scrolls inside the column instead of
 * pushing the page into horizontal overflow - which is the one thing
 * every verification pass on this build checks at every width. */
.prose table {
	display: block;
	width: 100%;
	max-width: 100%;
	margin: 0 0 var(--space-12);
	overflow-x: auto;
	border-collapse: collapse;
	font-size: var(--type-small);
}
.prose th,
.prose td {
	padding: var(--space-12);
	border-bottom: 1px solid var(--surface-grey);
	text-align: left;
	vertical-align: top;
}
.prose th {
	font-weight: var(--weight-semibold);
	color: var(--neutral-ink);
}

.prose dl { margin: 0 0 var(--space-12); }
.prose dt {
	font-weight: var(--weight-semibold);
	color: var(--neutral-ink);
}
.prose dd { margin: 0 0 var(--space-12) var(--space-24); }


/* ---- 19.2  the generic page template ----
 *
 * !! .site-main, .page-article, .page-content and .page-title had NO
 * rules at all until 2026-08-20 - the classes were in page.php from
 * Stage 1 and nothing had ever styled them. The visible consequence was
 * that the generic template's H1 sat flush against the sticky header
 * with no gap above it, which is what /privacy-policy/ renders today.
 *
 * Figma's Title Block insets the H1 by 56 from the top of the content
 * column. 48 is the approved scale's ceiling AND is exactly what
 * .section already supplies, so page.php takes .section on the article
 * rather than this file inventing a second rule that does the same
 * thing. Nothing is added here for it on purpose. */

/* "Last updated: July 2026". Figma draws it as its own line between the
 * H1 and the intro paragraph - neither a heading nor body copy, so it
 * gets the one class it needs rather than borrowing a component's.
 * Applied in the page content, not by the template. */
.page-meta {
	font-size: var(--type-small);
	color: var(--neutral-muted);
}


/* ==================================================================
 * 20  UTILITY-PAGE COMPONENTS  (added 2026-08-20)
 *
 * Three small pieces the Adult Intake Form page needs and no earlier
 * page had: a slot for a third-party embed, a two-column text row, and
 * a name for the 16px lead paragraph.
 *
 * !! NO FORM VOCABULARY IS ADDED HERE, AND THAT IS THE FINDING RATHER
 * THAN AN OMISSION. Frame 620:126 and the copy document both specify a
 * third-party Aloha/Genesis embed and between them define ZERO fields -
 * no labels, no input types, no required markers, no options, no
 * validation, no consent text, no signature. There is nothing on this
 * page to attach input/label/fieldset rules to, so none are written.
 * The shared form system waits until the Pediatric Intake and Contact
 * documents have been read and there are real fields to generalise
 * from. Decided with Gerek 2026-08-20.
 * ================================================================== */

/* ---- 20.1  .embed-slot - where a third-party form will load ----
 *
 * !! THIS IS DELIBERATELY A VISIBLE PLACEHOLDER, NOT A BLANK BOX. The
 * Aloha/Genesis embed code does not exist yet - the copy document names
 * the platform and stops, with no iframe src, script, account id or
 * endpoint anywhere - and inventing one is prohibited. So the slot
 * announces itself in words rather than rendering as empty space a
 * visitor would read as a broken page. See LAUNCH-CHECKLIST.md B7.
 *
 * The dashed border and the 391px height are Figma's own drawing of the
 * same placeholder (node 620:223). The height is declared as a custom
 * property on the component rather than typed inline, the same shape
 * .prenatal-proof uses for --fan-w: it is a box dimension taken from the
 * design, not a step on the spacing scale, and keeping it named means
 * there is one place to change it when the real embed arrives.
 *
 * --radius-sm (14px) against Figma's 10. Nothing on the approved list is
 * closer, and the box is scaffolding rather than final design - the real
 * embed brings its own corners. Not worth a new token.
 *
 * The whole component is expected to be DELETED, not restyled, once the
 * embed code lands. */
.embed-slot {
	--embed-min-h: 391px;   /* Figma 620:223 draws the placeholder at 391 */

	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-8);
	min-height: var(--embed-min-h);
	margin-top: var(--space-40);
	padding: var(--space-32) var(--space-24);
	border: 2px dashed var(--neutral-muted);
	border-radius: var(--radius-sm);
	background: var(--surface-off);
	text-align: center;
}

.embed-slot__label {
	margin: 0;
	color: var(--neutral-ink);
	font-size: var(--type-ui);
	font-weight: var(--weight-semibold);
}

.embed-slot__note {
	max-width: var(--measure-lead);
	margin: 0;
	color: var(--neutral-muted);
}

@media (max-width: 767px) {
	.embed-slot {
		padding: var(--space-24) var(--space-16);
	}
}


/* ---- 20.1b  .intake-embed - where the real form actually loads ----
 *
 * Added 2026-09-01, when the two intake forms were connected. It is
 * NOT a replacement for .embed-slot above: that placeholder is still
 * live on the Booking Calendar, the home page's article CTA and the
 * Products hero, all three still genuinely pending. Only the two intake
 * pages graduated.
 *
 * !! THIS RULE IS DELIBERATELY ALMOST EMPTY, AND THAT IS THE DECISION.
 * The form is served and styled by Review Wave. Its markup is theirs,
 * its class names are theirs, and they can change either without
 * telling us. Anything this theme says about the INSIDE of that form is
 * a guess with a shelf life - a selector that works today and silently
 * stops matching after a vendor deploy, leaving a form that looks wrong
 * with nothing in our code obviously broken. So this wrapper owns the
 * form's position in the page and nothing else, and there are no
 * descendant selectors reaching into it anywhere in this file.
 *
 * If the vendor's own styling turns out to clash with the page, the fix
 * is to ask Review Wave for their theming options first, and only then
 * to write a narrowly scoped override HERE with a note saying which
 * vendor class it depends on.
 *
 * THE 40px IS INHERITED, not invented: it is exactly the margin-top
 * .embed-slot carried in this position on the Adult page, so connecting
 * the form did not move anything around it.
 *
 * !! THE BOX AROUND IT IS .card card--pad card--raised IN THE MARKUP,
 * NOT PADDING SET HERE, and the reason is source order rather than
 * taste. .card--pad lives in section 18 and carries a mobile step down
 * to a 24px inline gutter; this section is appended AFTER 18, so any
 * padding written here would beat that media query and the form would
 * sit at desktop padding on a phone. Letting the component own its own
 * padding is the only version that keeps the step alive. Same trap the
 * note on .card--pad itself describes.
 *
 * WHY A BOX AT ALL, 2026-09-01. Gerek on the connected form: "the very
 * first thing is a bunch of just white space, and there are kind of no
 * visual cues that we're in a container." He is reading something real
 * and it belongs to the vendor: Review Wave sets a min-height on its own
 * slider equal to its TALLEST step, and that step is the full
 * terms-of-consent text hidden behind the consent screen. Measured 4668px
 * reserved against 400px visible, and the client's existing live site
 * reserves 3277px the same way - so it is how the product behaves, not
 * something this build caused, and it cannot be changed without writing
 * CSS into vendor markup. The card cannot shorten that space. What it
 * does is make it read as the inside of a panel rather than as a page
 * that stopped rendering. */
.intake-embed {
	margin-top: var(--space-40);
	/* A flex/grid child of an unknown width cannot shrink below its
	 * content without this, and the form carries wide rows. Cheap
	 * insurance against one long field pushing the page sideways. */
	min-width: 0;
}

/* !! THE HEADER ILLUSTRATION HAS TO BLEND NOW THAT THERE IS A PAGE
 * BEHIND IT - the same defect the Prenatal hero backdrop had on the same
 * day, found the same way, and worth stating because it is the standing
 * cost of adding a tinted layer under an old page.
 *
 * Both header files are FULLY OPAQUE with pure white corners - sampled,
 * not assumed. That was invisible for as long as the page under them was
 * white. Put the parallax there and each becomes a white rectangle
 * stamped on the tint, with a hard edge on all four sides.
 *
 * multiply is the fix and it is the right one rather than a trick: these
 * are dark line drawings on a white ground, and multiplying by white is
 * the identity, so the ground drops out and only the drawing survives
 * over whatever the parallax is showing.
 *
 * Scoped to .intake. .page-head__figure is a shared component and the
 * other pages that use it have no .page-bg under them, so they have
 * nothing to blend with. Degrades safely: a browser that refuses to
 * blend paints the white box, which is visible rather than broken. */
.intake .page-head__figure {
	mix-blend-mode: multiply;
}

/* !! NOTHING IS ADDED FOR THE TWO CLOSING COLUMNS EITHER. They take
 * .card card--pad card--raised in the markup, the same three classes,
 * so the boxes above and below the form are literally the same
 * component and cannot drift apart. .split__col is shared with Contact
 * and Thank You and is deliberately NOT touched - those pages have not
 * been reviewed and their columns are not meant to be cards.
 *
 * The .intake scope on <main> exists for the same reason: if either of
 * these pages ever does need a rule of its own, there is somewhere to
 * put it that cannot reach the shared components. It carries no
 * declarations today. */

/* On the Pediatric page the embed sits inside .embed-block (22.2),
 * whose 16px gap already owns the rhythm between the heading, the form
 * and the two notes - and whose `> * { margin: 0 }` would zero this
 * anyway. Stated rather than left to that side effect, so the
 * relationship is readable from either end. */
.embed-block .intake-embed {
	margin-top: 0;
}


/* ---- 20.2  .split - a two-column text row ----
 *
 * Claiming the name from the Stage 1.5 vocabulary list, where it has sat
 * unused since Stage 1. Two equal columns of heading-plus-paragraph,
 * nothing else - no cards, no borders, no background.
 *
 * GAP IS 48, FIGMA'S IS 56. Capped at the approved scale's ceiling, the
 * same site-wide call recorded as LAUNCH-CHECKLIST.md B5. The 12 inside
 * a column is Figma's own and already a step.
 *
 * COLLAPSES AT 767, not earlier. At 992 each column still runs about
 * 430px, which is a comfortable measure for two short paragraphs; below
 * 768 they would start breaking badly. One breakpoint, from the locked
 * list.
 *
 * DELIBERATELY OUTSIDE .prose. Its headings are <h2> like the prose
 * headings around them, but they need 0 margin-top inside their column
 * and a different size, so putting the row inside .prose would mean
 * writing two overrides to undo rules that had just been applied. */
.split {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--space-48);
	margin-top: var(--space-40);
}

/* The 22px sub-heading that used to live here is now .subhead, section
 * 22.1 - this page's headings were not the only ones that wanted it, and
 * the Pediatric Intake Form has one that is not inside a .split at all.
 * Same declarations, moved rather than copied. */

/* Muted, like every other body paragraph on the site. margin: 0 because
 * the column is a grid item with its own gap - the base p margin would
 * add a second, uncontrolled 16 underneath. */
.split__body {
	margin: 0;
	color: var(--neutral-muted);
}

@media (max-width: 767px) {
	.split {
		grid-template-columns: 1fr;
		gap: var(--space-32);
	}
}


/* ---- 20.3  .prose__lead - the 16px lead paragraph ----
 *
 * Figma sets the paragraphs directly under a page H1 at 16 against the
 * 15 of the body beneath them - a lead-vs-body distinction, and 16 is
 * --type-ui exactly, so it costs no new value.
 *
 * !! THIS REPLACED .faq__intro, WHICH SECTION 19.1c USED TO CARRY. That
 * one was written page-specific before a second page needed the same
 * thing; when the Adult Intake Form page did, the two were the identical
 * declaration under two names. Folded onto this name and the 19.1c rule
 * deleted, so there is one place the lead size lives. page-faq.php's two
 * intro paragraphs now take .prose__lead.
 *
 * Any later page whose Figma lead runs 16 against a 15 body takes this
 * class rather than minting a third name. */
/* Capped 2026-09-01. It is a SHARED component - several pages use it -
 * and it is frequently used OUTSIDE a .prose wrapper, as a standalone
 * lead paragraph directly inside a page section. So .prose's own cap
 * never reached it, and on /pediatric/ it was running the full 1352px
 * container at about 118 characters a line.
 *
 * The cap belongs on the component rather than on each page that uses
 * it, which is the whole point of it being shared. */
.prose__lead {
	max-width: var(--measure-wide);
	font-size: var(--type-ui);
}


/* ==================================================================
 * 21  ADULT INTAKE FORM - APPROVED FIGMA OVERRIDE  (added 2026-08-20)
 *
 * !! EVERYTHING IN THIS SECTION EXISTS BECAUSE OF AN EXPLICIT OVERRIDE,
 * NOT BECAUSE THE COPY DOCUMENT ASKS FOR IT. When the page was built in
 * 105d0e6, three things Figma frame 620:126 draws were deliberately NOT
 * built, because the copy document specifies none of them: a decorative
 * illustration in the title row, and two "Mini" CTA buttons in the
 * two-column row. Gerek approved all three on 2026-08-20 as a direct
 * instruction. Logged as LAUNCH-CHECKLIST.md B8.
 *
 * THE OVERRIDE IS VISUAL ONLY. The page's zero-POV constraint is
 * untouched: no copy changed, no sales language, no new destination, no
 * proof or authority content. One of the two buttons is not even a
 * control - see 21.1b.
 * ================================================================== */

/* ---- 21.1  .cta-button--mini - the 36px button variant ----
 *
 * Figma's "Button / Circle-in-Bar / Coral (Glass) / Mini" is the full
 * .cta-button--icon component scaled to 69.23%. Every one of its values
 * is the full variant's value times 0.6923:
 *
 *     full (Figma)   x0.6923   mini (Figma)   BUILT AS
 *     height 52        ->      36             40   (24 icon + 8 + 8)
 *     arrow  40        ->      27.692         24   --space-24
 *     label  16        ->      11.077         11   --type-micro
 *     pad-l  28        ->      19.385         20   --space-20
 *     pad-r   6        ->       4.154          8   --space-8
 *     gap    10        ->       6.923          8   --space-8
 *
 * WHY THESE VALUES AND NOT FIGMA'S DECIMALS. The standing rule is that
 * sizes come from the central list, so each number here is the nearest
 * step on the approved spacing scale or the type ladder, and not one new
 * token was added. Two are effectively exact: --type-micro is 11px
 * against 11.077, and --space-20 is 20 against 19.385. The two that move
 * furthest are the arrow (24 against 27.7) and the padding (8 against
 * 4.15), and the padding is the reason the pill lands at 40px rather
 * than Figma's 36.
 *
 * !! 40 RATHER THAN 36 IS THE SAME CALL THE FULL VARIANT ALREADY MADE,
 * and that is why it is safe. Section 08's comment records that Figma
 * draws the full button at 52 and the build renders 56, because 6px of
 * padding rounded up to the scale's 8. Mini does exactly the same thing,
 * so the RELATIONSHIP between the two buttons is preserved: 40/56 is
 * 0.714 against Figma's own 36/52 at 0.692. Matching Figma's 36 exactly
 * would need a 4px step, and 4 is not on the approved list.
 *
 * APPLIED ALONGSIDE .cta-button--icon, not instead of it - the gradient,
 * the glass overlay, the shadow and the pill radius all come from there
 * unchanged, and only the six size values below are restated.
 *
 * Section order does the rest: this section is appended last, so these
 * declarations beat .cta-button's and .cta-button--icon's own responsive
 * steps at 991 and 767 without needing extra specificity. Media queries
 * add none, so a later block wins on source order alone. */
.cta-button--mini {
	gap: var(--space-8);
	padding: var(--space-8) var(--space-8) var(--space-8) var(--space-20);
	font-size: var(--type-micro);
}

.cta-button--mini .cta-button__icon {
	width: var(--space-24);
	height: var(--space-24);
}

/* The chevron carries width="40" height="40" as ATTRIBUTES in the
 * markup, shared with the full-size button. CSS beats presentational
 * attributes, so the icon is resized here rather than by writing a
 * second SVG. The two-selector form also beats section 08's own
 * `.cta-button__icon svg { width: 32px }` inside the 991 block. */
.cta-button--mini .cta-button__icon svg {
	width: var(--space-24);
	height: var(--space-24);
}


/* ---- 21.1b  .cta-button--static - looks like a button, is not one ----
 *
 * !! DO NOT COPY THIS ONTO A CONTROL THAT ACTUALLY DOES SOMETHING, AND
 * DO NOT "FIX" IT BY TURNING ITS ELEMENT INTO A BUTTON OR A LINK.
 *
 * Figma draws a "Call the Office" Mini button in the Need Help? column.
 * It was confirmed on 2026-08-20 as DECORATIVE ONLY - no tel: link, no
 * action, nothing to navigate to. The copy document gives it no label
 * and no destination; the phone number is already in the sentence above
 * it, as running text, which is what the document specifies.
 *
 * A control that takes focus and then does nothing is a real
 * accessibility failure: a keyboard user tabs to it, presses Enter, and
 * the page does not respond. An <a> with an empty or "#" href is the
 * same failure wearing a valid attribute. So the element in the markup
 * is a <span>, which has no implicit role and is not focusable, and this
 * class only makes it LOOK like its neighbour.
 *
 * Two things have to be neutralised or the span still advertises itself
 * as interactive:
 *   - cursor, which would otherwise stay the default text/arrow while
 *     the real button next to it shows a pointer. Set explicitly so the
 *     two are visibly different to a mouse user.
 *   - :hover, because .cta-button:hover brightens - a hover response is
 *     an affordance, and this element must not offer one. */
.cta-button--static {
	cursor: default;
}

/* !! IT MUST NEUTRALISE ALL THREE, NOT JUST THE FILTER. When hover was
 * only `filter: brightness()` this needed only `filter: none`. Hover now
 * also lifts and casts a shadow, and a "static" element that rises off
 * the page when you point at it is offering exactly the affordance this
 * rule exists to withhold. */
.cta-button--static:hover,
.cta-button--static:active {
	filter: none;
	transform: none;
	box-shadow: none;
}


/* ---- 21.2  .page-head - a page title beside a figure ----
 *
 * Figma node 799:8389: a row, gap 40, with the H1-and-lead block on the
 * left and the illustration on the right. The text block keeps exactly
 * the markup and classes it had before the override; this only puts a
 * row around it.
 *
 * min-width: 0 on the text column because a flex item defaults to
 * min-width: auto, which refuses to shrink below its longest unbreakable
 * word - the mechanism behind the FAQ H1 overflow in 8c5bd13. Here the
 * figure has a fixed width, so without this the text column could push
 * the row past the container instead of wrapping. */
.page-head {
	display: flex;
	align-items: flex-start;
	gap: var(--space-40);
}

.page-head__text {
	flex: 1 1 auto;
	min-width: 0;
}

/* 216 is Figma's own 215.883 rounded. A box dimension from the design,
 * not a step on the spacing scale - the same standing this file gives
 * .prenatal-changing__illustration's 189 and .embed-slot's 391.
 *
 * height: auto with a width/height pair in the markup, so the aspect
 * ratio is reserved before the image loads and nothing shifts. */
.page-head__figure {
	display: block;
	flex: none;
	width: 216px;
	height: auto;
}

/* Figma draws a 1440 frame only. The collapse follows the precedent set
 * by .prenatal-changing__row in section 3: the row becomes a column and
 * the figure centres under the text, rather than shrinking to a size the
 * design never specifies. Stack order is source order - text first -
 * which is also the document's reading order. */
@media (max-width: 767px) {
	.page-head {
		flex-direction: column;
		gap: var(--space-32);
	}

	.page-head__figure {
		align-self: center;
	}
}


/* ---- 21.3  the two mini buttons inside .split ----
 *
 * Figma gives each column a 12px gap between its three children, and the
 * heading-to-body 12 is already .subhead's margin-bottom. This adds
 * the third one without touching that rule.
 *
 * display: flex, NOT the inherited inline-flex. An inline-level pill
 * sits on a text baseline, so the line box adds a descender's worth of
 * space under it that no margin accounts for. Block-level removes it;
 * width: max-content keeps the pill sized to its label rather than
 * stretching across the column, which is what a plain block would do. */
.split__col .cta-button--mini {
	display: flex;
	width: max-content;
	max-width: 100%;
	margin-top: var(--space-12);
}


/* ==================================================================
 * 22  PEDIATRIC INTAKE FORM  (added 2026-08-20)
 *
 * Sitemap page 18, the named sibling of Adult Intake Form. Almost all of
 * that page's vocabulary is reused unchanged - .page-head, .prose__lead,
 * .embed-slot, .split, .cta-button--mini - so only three things are
 * added here, one of which is a rename rather than a new rule.
 *
 * !! NO DECORATIVE BUTTON ON THIS PAGE. Adult Intake needed
 * .cta-button--static because Figma drew a "Call the Office" button the
 * copy document gave no destination for. Here BOTH Mini buttons carry a
 * real one, and both were already linked inline in the document's own
 * sentences - "use the Adult Intake Form -> instead" and "Haven't
 * scheduled the visit yet? Book Your Appointment ->". So both are plain
 * <a> elements and nothing on this page is a non-control.
 *
 * !! AND NO FORM VOCABULARY, for the same reason as section 20. Both
 * sources specify a third-party Aloha/Genesis embed and define ZERO
 * fields between them. No consent, HIPAA, guardian or signature language
 * exists in either source either - see the note on .embed-block.
 * ================================================================== */

/* ---- 22.1  .subhead - the 22px section sub-heading ----
 *
 * !! THIS IS .split__title RENAMED, NOT A NEW RULE. The declarations are
 * byte for byte what section 20.2 carried, so no computed style changes -
 * proved with the geometry differ at zero tolerance across all five
 * previously built pages.
 *
 * WHY THE RENAME. This page has THREE headings at this size and only two
 * of them sit inside a .split: "Fill Out the Form" heads the embed block
 * instead. Keeping .split__title would have meant either putting a
 * split-named class on a heading that is not in a split, or writing a
 * second rule with identical declarations under a second name. That is
 * exactly the duplication .faq__intro and .prose__lead had before they
 * were folded in aad03e6, caught this time before it shipped rather than
 * after.
 *
 * page-adult-intake-form.php's two headings move onto this class in the
 * same commit, so there is never a moment with two names for one rule.
 *
 * --type-h4 is 22px, which is EXACTLY what Figma draws for all three -
 * the locked ladder and the design agree with nothing to reconcile. The
 * face is Baloo 2 Bold from the base h1-h6 rule, so only the size and
 * the margins are written. Same size the FAQ page's closing heading
 * takes.
 *
 * The LEVEL is still whatever the document marks. Both copy documents
 * mark every one of these [H2]; this class changes their size, never
 * their place in the outline. */
.subhead {
	margin: 0 0 var(--space-12);
	font-size: var(--type-h4);
}


/* ---- 22.2  .embed-block - a heading and notes around the embed ----
 *
 * Figma node 621:223. Adult Intake has no equivalent: there the dashed
 * placeholder is a bare child of the body with nothing above or below
 * it. Here the copy document gives the embed its own [H2] and two
 * paragraphs of instructions underneath, so it needs a wrapper.
 *
 * GAP IS FIGMA'S OWN 16, and the one rule that makes it the only spacing
 * in the block is `> * { margin: 0 }`. Three different inherited margins
 * would otherwise land inside here and none of them is 16:
 *   .subhead        margin-bottom 12   (section 22.1)
 *   p               margin-bottom 16   (section 02 element rule)
 *   .embed-slot     margin-top    40   (section 20.1)
 * Clearing all three in one declaration is cheaper and far easier to
 * reason about than three targeted overrides, and it means a fourth
 * child added later cannot bring a surprise margin with it.
 *
 * FOUR DIRECT CHILDREN - heading, slot, note, note - so the same 16 also
 * falls between the two notes. Figma draws them as one text node with a
 * blank line between; 16 is that blank line.
 *
 * !! THE SLOT KEEPS ITS 391px, WHERE FIGMA DRAWS 398 HERE. Deliberate.
 * --embed-min-h is scaffolding that gets DELETED when the real
 * Aloha/Genesis code arrives - see LAUNCH-CHECKLIST.md B7 - and a 7px
 * difference on a box with a known expiry date does not justify a
 * modifier and a second value to keep in step. Recorded rather than
 * silently absorbed.
 *
 * !! NOTHING HERE IS EVER WIRED UP. No <form>, no mail(), no SMTP, no
 * plugin, no API key, no invented endpoint. Neither source specifies a
 * single field, and neither carries any consent, HIPAA, guardian or
 * signature language - checked term by term across the whole document.
 * If a minor's intake needs any of that it lives inside the third-party
 * form, or it comes from the client. It is not written here. */
.embed-block {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	margin-top: var(--space-40);
}

.embed-block > * {
	margin: 0;
}

.embed-block__note {
	color: var(--neutral-muted);
}


/* ---- 22.3  two paragraphs in one .split column ----
 *
 * .split__body sets margin: 0, because on Adult Intake every column held
 * exactly one paragraph and the base 16 would have hung off the bottom
 * of the grid item. This page's "Need a Hand?" column has TWO, so
 * without this they would run together as one block of text.
 *
 * 12 is the column's own gap, which is what Figma puts between every
 * other pair of children in there.
 *
 * ADJACENT-SIBLING ONLY, so it reaches nothing on Adult Intake - that
 * page has no .split__body followed by another. The geometry differ
 * confirms it: zero elements moved there. */
.split__body + .split__body {
	margin-top: var(--space-12);
}


/* ==================================================================
 * 23  ONLINE BOOKING SYSTEM  (added 2026-08-20)
 *
 * Sitemap page 10, Funnel Step 3 of 4, slug booking-calendar. The
 * scheduler step: reached only after payment on Checkout, in no menu,
 * noindex.
 *
 * !! ZERO POV, AND THIS IS THE PAGE THE CONSTRAINT WAS WRITTEN FOR. The
 * copy document's decision log: "This page is the locked zero-POV
 * 'Booking' Utility page described in 3.2/3.3/3.4/3.5 - frictionless, no
 * sales copy, no positioning. It just arrives one step later in the
 * funnel than originally described." Not to be confused with page 08,
 * /booking/, which IS a sales page and carries persuasive copy - the
 * sitemap warns against applying this constraint there.
 *
 * !! AND NO FORM VOCABULARY, for the third page running. Figma and the
 * document both NAME the fields this time - First Name, Last Name,
 * Email, Mobile Phone, Add a Note, a privacy-policy checkbox and a
 * Continue button - but both place every one of them INSIDE the
 * third-party widget, and the widget itself is undetermined: "Aloha
 * calendar or the current DropFunnels-native tool, TBD". Building them
 * locally would mean inventing a form the vendor is going to replace.
 * See LAUNCH-CHECKLIST.md B9.
 * ================================================================== */

/* ---- 23.1  .steps - the two numbered markers ----
 *
 * Figma node 799:8376. A row of two FIXED 313px columns at gap 12, left
 * aligned - NOT .split, which is two equal fractions filling the
 * container. At 1088px of content the two columns occupy 638 and the
 * rest is empty on purpose, so .split would have stretched them to more
 * than double their drawn width.
 *
 * !! THE BLOCK CARRIES NO TEXT, AND THAT IS THE CONTENT DECISION RATHER
 * THAN AN OMISSION. Figma draws a caption beside each numeral - "You'll
 * first pick your date and time." / "Pay the $99 deposit." - but those
 * are Figma REWRITING the copy document, which puts the same two clauses
 * inline in one sentence: "This is a 2-step process... (1) you'll first
 * pick your date and time, then (2) you'll pay the $99 deposit."
 * Document wins on content, so the sentence is built as written and the
 * numerals stay as the visual device Figma adds around it. Rendering
 * both would have put the same copy on the page twice, once in the
 * document's words and once in Figma's.
 *
 * !! SO THE WHOLE BLOCK IS aria-hidden IN THE MARKUP. With no text of
 * its own it adds nothing a screen reader has not already heard from the
 * sentence above, and a bare "1." "2." announced on their own are noise.
 * The images are decorative for the same reason and carry alt="".
 *
 * flex-wrap, not a media query: the row folds to one column exactly when
 * 313 + 12 + 313 stops fitting, which is the only thing that governs it.
 * max-width: 100% keeps a column inside the container below 313 + the
 * gutters. */
.steps {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-12);
	margin-bottom: var(--space-12);
}

.steps__item {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	width: 313px;      /* Figma's own column width */
	max-width: 100%;
}

/* --type-h1, WHICH IS 46 WHERE FIGMA DRAWS 40, AND THAT IS THE POINT.
 * Figma sets the H1 and these numerals at the SAME 40px. The locked
 * ladder maps a 40px Figma H1 to --type-h1 (46) on every page built so
 * far, so mapping the numerals to the same token preserves the design's
 * own 1:1 relationship instead of inventing a new ratio - and the two
 * then step down together at every breakpoint rather than drifting
 * apart.
 *
 * WEIGHT 700, WHERE FIGMA DRAWS 800. Decided with Gerek 2026-08-20. The
 * theme requests Baloo 2 at wght@700 only (functions.php), and adding
 * 800 would put an extra weight on every page's font payload for two
 * numerals on one noindexed, payment-gated page. --weight-bold is what
 * every other Baloo heading on the site already uses.
 *
 * line-height: 1 because this is a display figure, not running text -
 * --leading-heading's 1.18 would add a half-line of air under a glyph
 * that has nothing beneath it. */
.steps__num {
	margin: 0;
	color: var(--brand-mint);
	font-family: var(--font-heading);
	font-size: var(--type-h1);
	font-weight: var(--weight-bold);
	line-height: 1;
}

/* Figma gives the two numerals different mints: "1." is #beeadc, which
 * is --brand-mint exactly, and "2." is #8fd6c2, which is new.
 *
 * !! NOT A RESTORE OF --brand-mint-deep. That token existed and was
 * deleted in the section 01 cleanup for having zero references, so it
 * was worth checking before adding a second mint - but its value was
 * #85d0bc, and this is #8fd6c2. Different colour, genuinely new. */
.steps__num--two {
	color: var(--steps-two);
}

.steps__figure {
	display: block;
	width: 109px;      /* Figma 108.906 and 108.816, rounded */
	height: auto;
}


/* ---- 23.2  .steps__warning - the payment warning ----
 *
 * Figma draws this at 15px Poppins SemiBold in INK, against the muted
 * body copy around it. 15 is --type-body, which the base body rule
 * already supplies, so only the weight and the colour are written - the
 * same finding that made .section__title margin-only in section 18.
 *
 * It sits inside .prose, so .prose p supplies the 12px rhythm and
 * .prose > :last-child clears the last margin. */
.steps__warning {
	color: var(--neutral-ink);
	font-weight: var(--weight-semibold);
}


/* ---- 23.3  the embed slot on this page ----
 *
 * NOTHING IS ADDED. .embed-slot from section 20.1 is reused exactly as
 * the two intake pages use it, and two of Figma's numbers are
 * deliberately not followed:
 *
 *   height   Figma draws 475 here, 398 on Pediatric, 391 on Adult. All
 *            three reuse --embed-min-h at 391. The box is scaffolding
 *            that gets DELETED when the real widget arrives, so a
 *            per-page height is a value to keep in step for no return.
 *
 *   gap      Figma sets 32 between the title block and the slot on this
 *            page, where the intake pages set 40 and .embed-slot's own
 *            margin-top is 40. Kept at 40 ON PURPOSE: the three pages
 *            that carry a pending embed then share one gap, and a gap
 *            that repeats reads as intentional where a 32 here and a 40
 *            there reads as drift. Both values are on the approved
 *            scale, so this is a consistency choice, not a rounding one.
 *
 * Recorded here rather than absorbed silently. */


/* ==================================================================
 * 24  THANK YOU  (added 2026-08-20)
 *
 * Sitemap page 11, Funnel Step 4 of 4, slug booking-thank-you. The
 * confirmation step: reached only after scheduling completes on Step 3,
 * in no menu, noindex. Zero POV, which the document ties straight to
 * Constitution 5.8's confirmation-page rule.
 *
 * !! THIS SECTION IS SMALL ON PURPOSE. Three things the build brief
 * expected to be new turned out to exist already, and finding that out
 * was most of the work:
 *
 *   .parallax   NOT BUILT - .page-bg from section 08 already is it, and
 *               it is deliberately NOT animated. See 24.3.
 *   the row     NOT BUILT - .split from section 20.2 fits after all.
 *               See 24.4.
 *   .map-embed  A FOLD out of .contact__map, not a new component.
 *               See 24.2.
 *
 * What is genuinely new is one component: .video-thumb.
 * ================================================================== */

/* ---- 24.1  .video-thumb - a video still that does not play ----
 *
 * Figma node 915:2630 draws a finished-looking video card: a 428x223
 * still at radius 24 with a 56px play button centred on it. It is the
 * only embed on this site Figma does NOT draw as a dashed placeholder.
 *
 * !! THERE IS NO VIDEO TO PLAY, AND NOTHING HERE MAY BECOME A CONTROL.
 * The copy document says only "[Embed: existing patient testimonial
 * video]" and, in its decision log, "treat as existing assets to
 * re-embed" - referring to a screenshot of the old DropFunnels page. No
 * YouTube id, no URL, no channel reference exists in either source. The
 * thumbnail is real (media library 160, and it carries a burned-in
 * subtitle, so it is a still from a real recording) but the recording
 * itself is not identified anywhere.
 *
 * So the markup is an <img> plus an aria-hidden <span>. NOT a <button>,
 * NOT an <a href="#">. A play affordance that takes focus and then does
 * nothing is the same accessibility failure .cta-button--static exists
 * to avoid, and this one would be worse: a play triangle promises a
 * specific action. Confirmed with Gerek 2026-08-20; tracked in
 * LAUNCH-CHECKLIST.md B9.
 *
 * When the video id arrives this becomes a real embed and this whole
 * component is deleted rather than restyled.
 *
 * 428px is Figma's own box width, capped by max-width so it shrinks
 * inside a narrow column. aspect-ratio holds the 428:223 crop at every
 * size, so the image never letterboxes and nothing shifts as it loads.
 *
 * overflow: hidden on the wrapper because an <img> will not clip itself
 * to a parent's radius - the same reason .map-embed clips its iframe. */
.video-thumb {
	position: relative;
	width: 428px;
	max-width: 100%;
	aspect-ratio: 428 / 223;
	overflow: hidden;
	border-radius: var(--radius);
}

.video-thumb img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Figma draws the button at 56px, which is not on the approved scale;
 * --space-48 is the nearest step and the scale's ceiling, the same cap
 * recorded as LAUNCH-CHECKLIST.md B5. Centred by Figma's own numbers:
 * it sits at left 186 of 428 and top 84 of 223, both exactly centre. */
.video-thumb__play {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);

	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--space-48);
	height: var(--space-48);
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.9);
	color: var(--brand-coral-deep);
}


/* ---- 24.2  .map-embed - the shared map chrome ----
 *
 * !! THIS IS A FOLD OUT OF .contact__map, NOT A NEW COMPONENT. Section
 * 16 built the homepage Contact map in 2026-08-16 as a live Google Maps
 * iframe - B4b records Gerek approving that in place of a static asset
 * that does not exist in the media library. This page draws the same
 * element at a different size, and the chrome is identical: Figma gives
 * both radius 24 and the same 0 6px 20px rgba(0,0,0,0.12) shadow, which
 * is NOT --shadow-card and never was.
 *
 * So the chrome moves here and the SIZE stays with each user, which is
 * the only thing that differs: Contact is 460x320, this page is 329x230.
 * .contact__map keeps its flex/width/aspect-ratio and its two responsive
 * steps, and gains .map-embed in the markup. No declaration changes
 * hands, so no computed style changes - proved with the geometry differ
 * at zero tolerance against the homepage.
 *
 * Same shape as the .faq__intro -> .prose__lead and
 * .split__title -> .subhead folds: generalise on the second user rather
 * than write a second rule with the same body.
 *
 * The radius has to live on the wrapper: an iframe will not clip its own
 * corners, so it is overflow-hidden from the outside. isolation forms a
 * stacking context so the iframe cannot paint over those corners, which
 * it otherwise will. */
.map-embed {
	overflow: hidden;
	border-radius: var(--radius);
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
	isolation: isolate;
}

.map-embed iframe {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/* This page's box. Figma node 915:2636 draws 329x230 inside a column
 * that is wider than it, so the width is the design's own rather than a
 * fill. max-width keeps it inside the column once the row collapses. */
.thank-you__map {
	width: 329px;
	max-width: 100%;
	aspect-ratio: 329 / 230;
}

/* <address> defaults to italic. Section 19 already clears that for the
 * one inside .prose; this clears it for the one that sits in a .split
 * column instead. Element-plus-class, so it reaches nothing else. */
address.split__body {
	font-style: normal;
}


/* ---- 24.2b  .split__col min-width - a real bug in the shared row ----
 *
 * !! THIS IS A FIX TO SECTION 20.2's COMPONENT, FOUND HERE. A grid item
 * defaults to min-width: auto, which refuses to shrink below its
 * content's min-content width. Both intake pages only ever put text and
 * a shrink-to-fit button in a .split__col, and text has a small
 * min-content width, so the track always resolved to its 1fr share and
 * nothing showed.
 *
 * This page puts FIXED-WIDTH boxes in there - .video-thumb at 428 and
 * .thank-you__map at 329 - and the track opened to fit them instead.
 * Measured before the fix: the page overflowed at 768 (829 against a
 * 753 viewport) and at 390 (452 against 375), with .split__col itself
 * reporting 428px wide inside a 327px container. Their max-width: 100%
 * could not help, because the column it resolves against was the thing
 * being widened.
 *
 * min-width: 0 is the standard release valve and the same one
 * .page-head__text already carries in section 21.2 for exactly this
 * mechanism.
 *
 * FIXED ON THE SHARED COMPONENT, NOT SCOPED TO THIS PAGE, because the
 * bug is generic: any .split__col given a wide child would overflow the
 * same way. The geometry differ confirms it changes nothing on Adult or
 * Pediatric Intake - their columns hold only text, so the track was
 * already at its 1fr share. */
.split__col {
	min-width: 0;
}


/* ---- 24.3  .actions - a row of two full-size buttons ----
 *
 * Figma draws ONE button here, "Patient Intake Form", with no
 * destination. It cannot have one: the copy document's decision log says
 * the site has two intake forms and "this page doesn't know which one
 * applies without knowing what the visitor selected earlier in the
 * funnel", then explicitly declines to resolve it - "Flagging as an
 * operator/build decision, not something this copy resolves."
 *
 * Gerek chose the document's own second option on 2026-08-20 - "point to
 * a single landing spot that lets them pick the right one" - done inline
 * as two buttons rather than by building a new picker page. So this row
 * exists to hold them. Both destinations are real and already live.
 *
 * The gap is 16, matching the Title block's own gap that this row sits
 * inside. flex-wrap so the pair stacks rather than overflowing once the
 * two 52px pills stop fitting side by side. */
.actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-16);
	margin-top: var(--space-16);
}

/* ---- the Reviews page's closing card ----
 *
 * White box, centred, set apart from the video row above it. Fill,
 * radius, shadow and 48px padding all come from .card + .card--roomy +
 * .card--raised in the markup; this rule only centres what is inside and
 * sets the space around it.
 *
 * .actions is a flex row, so centring the text is not enough - the row
 * needs its own justify-content or the button stays hard left inside a
 * centred block, which is the state Gerek was looking at. */
.reviews-cta {
	margin-top: calc(var(--space-48) * 2);
	text-align: center;
}

/* !! margin-inline: auto IS THE FIX FOR THE OFF-CENTRE TEXT, and it is
 * not a text-align problem.
 *
 * Gerek: "the text has too much spacing on the right-hand side... if it
 * were truly centered, 'Ready to book your visit?' would be centered on
 * the button, which it's not." Measured, and he was reading it exactly:
 *
 *     card      44 -> 1396, centre 720
 *     button                centre 720   (dead on)
 *     prose     92 -> 1192, centre 642   (78px LEFT of the card)
 *
 * .prose carries max-width: var(--measure-wide) - the 1100px reading cap
 * added on 2026-09-01 - with no auto margins. A block narrower than its
 * parent and with margin: 0 sits LEFT, so the paragraphs were 1100 wide
 * in a 1256 content box with all 156px of the remainder on the right.
 * text-align: center then centred each LINE inside that off-centre box,
 * which is why it looked almost right and read wrong.
 *
 * Scoped to this card rather than added to .prose globally: left-aligned
 * is CORRECT for a reading column that starts at the container's left
 * edge, which is every other use of .prose on the site. It is only wrong
 * inside something that is itself centred. */
.reviews-cta .reviews-more {
	margin-top: 0;
	margin-inline: auto;
}
.reviews-cta .actions {
	justify-content: center;
	margin-top: var(--space-24);
}

/* The Google link under the disclaimer. Centred with the disclaimer it
 * follows, and set as its own line rather than trailing the sentence -
 * "Results may vary." is a compliance line and should not have a
 * marketing link running on from it. */
.reviews-google-link {
	margin: var(--space-12) 0 0;
	text-align: center;
	font-size: var(--type-small);
}
.reviews-google-link a {
	color: var(--brand-coral-deep);
	font-weight: var(--weight-semibold);
	text-decoration: none;
}
.reviews-google-link a:hover,
.reviews-google-link a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 3px;
}


/* ---- 24.4  what this page reuses, and two things it does NOT add ----
 *
 * !! NO .parallax COMPONENT, AND NO SCROLL ANIMATION. Figma layers this
 * page's background as four ellipses, two polygons and a blend-mode
 * image, several carrying y-axis motion. None of that is built, for two
 * reasons that are both already recorded rather than newly decided:
 *
 *   1. .page-bg IN SECTION 08 ALREADY IS THIS COMPONENT, and it is
 *      already generic - the artwork arrives as --page-bg-image set on
 *      the element, so a second page needs no CSS at all, only its own
 *      URL. Page 08 will reuse it the same way with media library 138.
 *
 *   2. IT IS DELIBERATELY NOT ANIMATED. Section 08 records Gerek's
 *      direction of 2026-08-17 replacing two scroll-driven layers with
 *      one fixed layer: "NO JAVASCRIPT. A layer that never moves needs
 *      none - both parallax blocks are deleted from main.js.
 *      prefers-reduced-motion stops being a concern here too: there is
 *      no motion left to reduce." Building Figma's motion would reverse
 *      that and re-add the JavaScript that was deliberately removed.
 *
 * And the six vector shapes do not need exporting: media library 141
 * (booking-parallax-background-Thank-you) is the SAME artwork already
 * flattened - a mint glow, a pink circle and a periwinkle triangle,
 * which are exactly the three motifs section 08's own comment names.
 * Checked by looking at it, not inferred from the filename.
 *
 * !! AND NO NEW TWO-COLUMN COMPONENT. The row was proposed as new on the
 * reading that Figma's columns carry px-56 of their own. They do, but
 * that is Figma's frame device: .container already supplies the page
 * gutter and no other page reproduces it - .split's columns have no
 * inner padding either. Strip it and this row is two equal columns that
 * collapse, which is .split exactly.
 *
 * Figma's gap here is 40 where .split is 48. Kept at 48 for the same
 * reason .embed-slot keeps its 40px top margin on the Scheduler: a gap
 * that repeats across pages reads as intentional, where 40 here and 48
 * there reads as drift. Both are on the approved scale, so this is a
 * consistency choice rather than a rounding one. */


/* ==================================================================
 * 25  CONTACT, AND THE FORM VOCABULARY  (added 2026-08-20)
 *
 * Sitemap page 16, slug contact, Utility, FOOTER-ONLY navigation. Zero
 * POV - the document's own notes: "The page is the functional action."
 *
 * !! THIS IS THE FIRST GENUINELY NATIVE FORM OF THE BUILD, and the
 * vocabulary below is the one deferred since the Adult Intake read.
 * Every prior "form" page turned out to be a third-party embed with
 * zero fields of our own; this page's four fields (Name*, Email*,
 * Phone, Message*) are specified by the copy document and drawn by
 * Figma, and the Booking sales page (08) draws its own two fields with
 * BYTE-IDENTICAL treatment - 13px SemiBold labels, gap 6, 48px inputs,
 * #d9d9d9 border, radius 8. So this is shared vocabulary with two
 * confirmed users, not a one-page style.
 *
 * !! BUT THE FORM HAS NO DESTINATION, AND NOTHING HERE SUBMITS. The
 * document confirms it twice: "No confirmed general-inquiry email
 * address exists either; the contact form is the email-equivalent
 * path" - an email-equivalent path with no email to be equivalent to.
 * See the long note in page-contact.php and LAUNCH-CHECKLIST.md B10.
 * No <form> element exists in the markup (deliberately - see the
 * template), so none of these classes may ever assume one.
 * ================================================================== */

/* ---- 25.1  .form-grid - the stack inside a form card ----
 *
 * Figma's form card stacks its children at gap 20 (a step on the
 * approved scale, kept exactly). The same > * { margin: 0 } device
 * .embed-block uses: the gap owns the rhythm, so no child can smuggle
 * in an inherited margin - the base p margin-bottom 16 would otherwise
 * land under the pending note. */
.form-grid {
	display: flex;
	flex-direction: column;
	gap: var(--space-20);
}

.form-grid > * {
	margin: 0;
}


/* ---- 25.2  .field - one label-above-input unit ----
 *
 * Figma's own numbers throughout: 6px from label to input, 13px
 * SemiBold labels in ink, 48px inputs. 6 IS NOT ON THE APPROVED SPACING
 * SCALE - the nearest step is 8 - but it is inside a component, not a
 * layout gap, and the label-to-field distance is what visually binds a
 * label to ITS field rather than the one below. Rounding it up weakens
 * exactly that binding, so Figma's 6 is kept and flagged here rather
 * than silently snapped. Same standing as .embed-slot's 391.
 *
 * display: flex + gap rather than a margin on the label, so the label
 * cannot collapse margins with anything and a hint line could be added
 * later without new spacing rules. */
.field {
	display: flex;
	flex-direction: column;
	gap: 6px;   /* Figma's own - see the note above */
}

/* <label>, not a heading and not placeholder text. 13px is --type-xs
 * exactly. Ink, where the body around it is muted - the same
 * label-vs-body contrast Figma draws. */
.field__label {
	color: var(--neutral-ink);
	font-size: var(--type-xs);
	font-weight: var(--weight-semibold);
}

/* !! TWO NEW TOKENS LIVE IN SECTION 01 FOR THIS RULE: --border-field
 * (#d9d9d9, verified nowhere else in this file) and --radius-field
 * (8px). The radius is deliberately NOT snapped to --radius-sm (14):
 * a 14px radius on a 48px box reads visibly rounder than the design,
 * the exact 8px value appears on BOTH pages that draw native inputs
 * (Contact and Booking 08), and unlike the embed-slot's scaffolding
 * border these fields are permanent. Two confirmed users is the same
 * test --type-h4-long passed.
 *
 * font: inherit, because inputs and textareas do NOT inherit font by
 * default - without it the browser serves its own monospace-ish UI
 * font inside an otherwise-Poppins page.
 *
 * The focus ring is the site's coral, 2px and offset so it reads
 * against both the white field and the white card. :focus-visible so
 * mouse clicks do not flash it but keyboards always get it. */
.field__input {
	width: 100%;
	height: 48px;   /* Figma's own box */
	padding-inline: var(--space-16);
	border: 1px solid var(--border-field);
	border-radius: var(--radius-field);
	background: var(--surface-white);
	color: var(--neutral-ink);
	font: inherit;
	font-size: var(--type-body);
}

.field__input:focus-visible {
	outline: 2px solid var(--brand-coral-deep);
	outline-offset: 2px;
}

/* ---- 25.4b  filled inputs on phone and tablet ----
 *
 * Gerek 2026-09-02: the boxes are "white on white with just a small
 * faded gray outline" and want to stand out.
 *
 * He is right, and it is worse on a phone than the stylesheet suggests.
 * A 1px #d9d9d9 hairline is plenty of definition on a desktop monitor
 * at arm's length; on a phone held at reading distance, in daylight,
 * on a white card, it is close to nothing. The field stops announcing
 * itself as somewhere you type.
 *
 * A FILL DOES THAT JOB WHERE A BORDER CANNOT, because it gives the eye
 * an area rather than an edge. hbchiropractic's booking form - the one
 * this page is modelled on - fills its inputs for the same reason.
 *
 * --surface-off, not a new grey. It is the site's existing "not quite
 * white" surface and carries the same warm cast as the ink, so the
 * boxes read as part of the palette rather than as a grey borrowed from
 * a form library.
 *
 * !! THE BORDER STAYS. Fill alone would fail for anyone who cannot
 * separate two near-identical light values - a very light grey on white
 * is exactly the comparison that goes first with age or in bright sun.
 * Two signals, either of which is sufficient.
 *
 * !! AND THE FIELD GOES WHITE WHEN IT HAS FOCUS, which is the half that
 * makes the fill worth having: the box you are typing in becomes the
 * lightest thing on the card and every other box recedes. The coral
 * focus outline is untouched and still does the accessible work - this
 * only adds a second, quieter cue for people who are not watching the
 * outline.
 *
 * Desktop keeps plain white: "Desktop is fine." */
@media (max-width: 991px) {
	.field__input {
		background: var(--surface-off);
	}

	.field__input:focus {
		background: var(--surface-white);
	}
}

/* The message box. min-height rather than height so long messages can
 * grow it; resize: vertical keeps the drag handle but stops sideways
 * resizing from breaking the card. 120 is Figma's own. */
textarea.field__input {
	height: auto;
	min-height: 120px;
	padding-block: var(--space-12);
	resize: vertical;
}


/* ---- 25.3  .field-row - two fields side by side ----
 *
 * Figma pairs Name* and Email* at gap 16. Equal columns that stack at
 * 767, the same breakpoint every two-column row on this site uses.
 * min-width: 0 on the children for the same grid-item reason section
 * 24.2b fixed on .split__col - an input's min-content width could
 * otherwise hold the track open. */
.field-row {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--space-16);
}

.field-row > * {
	min-width: 0;
}

@media (max-width: 767px) {
	.field-row {
		grid-template-columns: 1fr;
	}
}


/* ---- 25.4  the disabled submit, and the pending note ----
 *
 * !! THE SUBMIT IS <button type="submit" disabled> INSIDE NO <form>,
 * AND BOTH HALVES OF THAT ARE DELIBERATE - decided with Gerek
 * 2026-08-20. No form element: a <form> with no action submits to
 * itself, so Enter in any field would reload the page with the
 * visitor's name, email and message in the URL - a real PII leak into
 * server logs. Disabled: a control that is announced as unavailable is
 * telling the truth, where an enabled button that does nothing is the
 * focusable-dead-control failure this build has refused three times.
 * The real <form> and the live button arrive together with an actual
 * destination - see LAUNCH-CHECKLIST.md B10.
 *
 * .cta-button carries no disabled state because nothing disabled ever
 * existed before. The gradient stays (it is still the site's button),
 * dimmed enough to read as inactive; the cursor and the hover
 * brightening are neutralised the same way .cta-button--static does. */
.cta-button:disabled {
	opacity: 0.55;
	cursor: default;
}

/* Same reasoning as .cta-button--static:hover in 21.1 - all three
 * properties, or a disabled button still lifts under the cursor and
 * looks pressable. */
.cta-button:disabled:hover,
.cta-button:disabled:active {
	filter: none;
	transform: none;
	box-shadow: none;
}

/* The visible reason the button is disabled - same register as
 * .embed-slot's label: scaffolding text, not page copy, deleted when
 * the form is wired. The button's aria-describedby points here. */
.form-pending {
	color: var(--neutral-muted);
	font-size: var(--type-small);
}


/* ---- 25.5  this page's odds and ends ----
 *
 * The map box: .map-embed supplies the chrome (section 24.2); this
 * carries only the size. Figma draws it full-width at 280 tall, radius
 * 10 and shadowless - the radius and shadow follow .map-embed's 24 +
 * shadow instead, the deliberate consistency call recorded in the
 * build notes: three maps on the site now share one chrome. */
.contact-page__map {
	width: 100%;
	height: 280px;
}

/* The phone line inside the Call or Text Us card. Figma draws 18px
 * SemiBold ink; 18 is not on the type ladder and the nearest step is
 * --type-ui (16). Snapped, same rule as every off-ladder size so far.
 * A real tel: link - this page's phone is the functional action, so it
 * is live, unlike the Pediatric page's running-text mention. */
.contact-card__phone {
	color: var(--neutral-ink);
	font-size: var(--type-ui);
	font-weight: var(--weight-semibold);
	text-decoration: none;
}

.contact-card__phone:hover {
	color: var(--brand-coral-deep);
}

/* ---- 25.6  .contact-block - this page's vertical rhythm ----
 *
 * The page is a flat run of sibling blocks - info row, map, two prose
 * headers, the form card, the button - and only .split and .actions
 * carry their own top margins. Figma's Content Column spaces the major
 * blocks at gap 40 and puts 16 between the "Send Us a Message" header
 * and its card, so those two values become one class and its modifier
 * rather than a margin invented per block. Same one-sided-rhythm
 * principle .section promoted in 18.1: each boundary is paid for once,
 * by the block below it. */
/* !! 96, NOT FIGMA'S 40, AND THIS RULE HAD THE SAME BUG THE WHOLE PAGE
 * DID. The note above is accurate about where 40 came from - Figma's
 * Content Column spaces its major blocks at gap 40 - and that is exactly
 * the trap. 40 is a FRAME GAP in a design whose headings are drawn at 22
 * and 28; this build renders them off the locked ladder at 46 and 30,
 * and the house boundary between two H2 sections is 96 everywhere else
 * on the site. Products, Reviews, FAQ and Momma's Village all pay it as
 * calc(var(--space-48) * 2) when their blocks sit inside one .section,
 * which is precisely this page's shape.
 *
 * Measured against /products/ in the browser before changing anything,
 * because a stylesheet cannot show a one-sided boundary. */
.contact-block {
	margin-top: calc(var(--space-48) * 2);
}

/* The exception, and it is not an exception to the rhythm - it is a
 * block that has not ended. "Send Us a Message" and its card are one
 * unit: a heading, its one line, and the thing they introduce. Figma's
 * own 16 is right here for the same reason the H1's 16 to the lead is,
 * and pushing the card 96 away from its own heading would read as two
 * sections rather than one. */
.contact-block--tight {
	margin-top: var(--space-16);
}

/* The info row is the page's first block after the intro, and .split
 * carries its own 40 from section 24.2 - shared with pages that have
 * not been reviewed for this, so it is corrected HERE rather than
 * there. Scoped by the page class, which makes it (0,2,0) and settles
 * it against .split's (0,1,0) on specificity instead of source order. */
.contact .split {
	margin-top: calc(var(--space-48) * 2);
}

/* 96 where .section gives 48 - the sixth page to carry this same
 * one-line override, after Reviews, FAQ, Products, Momma's Village and
 * the Privacy Policy. The header contributes no padding, so the first
 * block on a page always faces a one-sided boundary. */
.contact .page-article {
	padding-top: calc(var(--space-48) * 2);
}


/* ---- 25.8  the top of the Booking page ----
 *
 * 96 where .section gives 48. SEVENTH page to carry this identical
 * one-line override, after Reviews, FAQ, Products, Momma's Village, the
 * Privacy Policy and Contact.
 *
 * At seven it has stopped being an exception and become the rule with
 * an awkward implementation. It is still not consolidated for the
 * reason it never was: .page-article is shared with eighteen templates
 * and several put a hero above it, where 96 would be wrong. The right
 * fix is to invert it - give .page-article the 96 and let the
 * hero-bearing templates opt out - and that is a change to make when
 * the last page has been reviewed, not in the middle of reviewing them.
 *
 * The hook is on <main>, not <body>: body_class() is default on this
 * build and prints no per-page class, so a rule expecting one matches
 * nothing and fails silently.
 *
 * !! AND THIS PAGE TAKES 60, NOT 96 - THE ONLY EXCEPTION ON THE SITE.
 * Gerek's call 2026-09-02, made knowingly against his own rule that the
 * pages should all match: "I need to get more information above the
 * fold." It is the funnel entry and the fold is worth more here than
 * the rhythm is.
 *
 * 60 is written as 48 + 12 rather than as a literal, for the same
 * reason 96 is written as 48 x 2 everywhere else: it stays tied to the
 * scale, so it cannot drift into an arbitrary number later. Both
 * addends are real steps.
 *
 * AT EVERY WIDTH, after a few hours as a desktop-only rule. 96 above an
 * H1 costs proportionally more of an 844px phone screen than of a 900px
 * desktop one, so if the fold is worth buying here it is worth buying
 * there first. */
/* !! CHECKOUT IS ON THIS RULE TOO, because Gerek's instruction for it
 * was "clone the same page" - the two are one layout with a different
 * card in the right-hand half, and a different top gap would be the
 * only thing that gave that away. */
.booking .page-article,
.checkout .page-article {
	padding-top: calc(var(--space-48) + var(--space-12));
}

/* The phone glyph above "Call or Text Us" (Figma 1235:2616). Figma
 * draws it at 14.983 x 15 in ink; it takes currentColor from this rule
 * so the card owns the colour rather than the SVG hardcoding it.
 *
 * display:block so the span does not sit on the text baseline and
 * inherit a descender's worth of extra space under it - the icon is a
 * block in the card's column, not a character in a line. */
.contact-card__icon {
	display: block;
	color: var(--neutral-ink);
	line-height: 0;
}

.contact-card__icon svg {
	display: block;
}


/* ---- 25.6b  the form's notice line ----
 *
 * ONE LINE, ABOVE THE FIELDS, IN TWO TONES. It reports a sent message
 * or a refused one, and it is the only thing on this page that changes
 * between requests.
 *
 * role="alert" and tabindex="-1" are in the markup rather than here,
 * because they are behaviour. What CSS owns is that the two tones are
 * distinguishable WITHOUT relying on the colour alone: each carries a
 * left border and its own background tint, so the difference survives
 * a monochrome screen and colour-blindness both.
 *
 * Tinted from the brand tokens rather than the usual green/red, because
 * this site has no error palette and inventing one for a single line
 * would put two unrelated reds on the same page as the coral CTA. Mint
 * for sent, coral for refused - the two colours the brand already uses
 * for "good" and "attention". */
.form-notice {
	margin: 0;
	padding: var(--space-12) var(--space-16);
	border-left: 4px solid currentColor;
	border-radius: var(--radius-sm);
	font-size: var(--type-xs);
}

.form-notice--success {
	color: var(--brand-mint-deep);
	background: rgba(109, 226, 178, 0.12);
}

.form-notice--error {
	color: var(--brand-coral-deep);
	background: rgba(221, 90, 63, 0.08);
}

/* A field the visitor has to come back to. The border is the signal;
 * the notice above names which field and why, so this never has to
 * carry the message on its own. */
.field__input.is-invalid {
	border-color: var(--brand-coral-deep);
}

/* Cloudflare's widget, when this site has keys. It renders nothing at
 * all until then, so this rule simply has nothing to style in the
 * meantime and costs nothing to ship early. */
.itv-turnstile {
	margin: 0;
}


/* ==================================================================
 * 26  CHECKOUT  (added 2026-08-20)
 *
 * Sitemap page 09, slug booking-checkout, FUNNEL STEP 2 OF 4. Figma
 * frame 636:126. NOT a zero-POV Utility page - the document's own
 * decision log puts it with the Booking sales page, so the value stack
 * below is sanctioned copy rather than a rule being bent.
 *
 * !! THIS SECTION ADDS NO FORM VOCABULARY, and that is the headline.
 * Nine of this page's ten fields are label-above-input units that
 * section 25 already defines: .form-grid, .field, .field__label,
 * .field__input, .field-row, .cta-button:disabled and .form-pending are
 * reused unchanged, at Figma's byte-identical treatment - 13px SemiBold
 * labels, gap 6, 48px inputs, #d9d9d9, radius 8. Section 25 predicted
 * this ("shared vocabulary with two confirmed users"); this is the
 * third, and nothing about it needed generalising. Only FOUR genuinely
 * new things exist here: a three-across row, a transparent offer card,
 * a card-details slot with no control in it, and the order summary.
 *
 * !! THE TENTH FIELD IS NOT A FIELD - see .payment-slot in 26.3 and the
 * long note in page-booking-checkout.php. No card input exists in this
 * theme and none may be added.
 * ================================================================== */

/* ---- 26.1  .offer-card - the value stack ----
 *
 * !! TRANSPARENT, AND THIS IS THE ONE RULE MOST LIKELY TO BE "FIXED" BY
 * MISTAKE. Figma node 917:2985 has NO fill at all - only a 24px radius
 * and the pink glow. The soft mint-to-blue wash the card appears to sit
 * on in the render is the .page-bg parallax layer showing THROUGH it.
 * Painting it --surface-white would flatten exactly that effect and is
 * the obvious wrong answer if it ever looks bare on its own. It is
 * deliberately NOT built on .card, whose whole job is to paint white.
 *
 * 540 is Figma's own box width, declared as a custom property rather
 * than typed into the rule - the same shape .embed-slot uses for
 * --embed-min-h and .prenatal-proof for --fan-w: a dimension taken from
 * the design, not a step on the spacing scale, kept in one named place.
 *
 * PADDING 40/48 against Figma's 40/56. The 40 is exact; 56 is off the
 * approved scale and 48 is its ceiling, so the sides snap down. Same
 * call .card--pad records for Figma's 30/56.
 *
 * gap 16 and the outer 40 are both Figma's own and both on the scale -
 * the Content Column spaces its major blocks at 40, so the block below
 * each boundary pays for it, the one-sided rhythm .section set in 18.1.
 *
 * text-align is NOT set here. Figma centres two lines inside this card
 * and left-aligns the rest, so centring belongs on those rules rather
 * than on the container with overrides under it. */
.offer-card {
	--offer-card-w: 540px;   /* Figma 917:2985 draws the box at 540 */

	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	max-width: var(--offer-card-w);
	margin-top: var(--space-40);
	margin-inline: auto;
	padding: var(--space-40) var(--space-48);
	border-radius: var(--radius);
	box-shadow: var(--shadow-offer-glow);
	color: var(--neutral-ink);
}

/* The gap owns the rhythm, so no child may smuggle in an inherited
 * margin - the base p and ul margins would otherwise land on top of it.
 * Same device .form-grid and .embed-block use. */
.offer-card > * {
	margin: 0;
}

/* --type-h3 is 26px, which is EXACTLY what Figma draws. The element
 * rule would otherwise serve --type-h2 (34) because this is an <h2>,
 * and the LEVEL is what the document marks - this class changes its
 * size, never its place in the outline. The face stays Baloo 2 Bold
 * from the section 02 element rule, which is also what Figma draws. */
.offer-card__title {
	font-size: var(--type-h3);
}

/* The seven values. list-style and padding are set here rather than
 * inherited because this card is NOT inside .prose - it is a component,
 * and .prose's list rules would not reach it. Figma types the bullet
 * glyphs into a text node; a real <ul> is the honest markup and the
 * disc is drawn by the browser instead. */
.offer-card__list {
	padding-left: var(--space-24);
	list-style: disc;
	font-size: var(--type-body);
}

.offer-card__list li {
	margin-bottom: var(--space-8);
}

.offer-card__list li:last-child {
	margin-bottom: 0;
}

/* "Total Value = $785". Figma draws 18px Bold centred; 18 is not on the
 * type ladder and --type-ui (16) is the nearest step, so it snaps - the
 * same rule section 25.5 applied to the Contact page's phone line and
 * the same one .prose h3 records. SemiBold rather than Figma's Bold,
 * because --weight-semibold is what every other emphasis on this site
 * uses and the ladder of weights is as locked as the ladder of sizes. */
.offer-card__total {
	font-size: var(--type-ui);
	font-weight: var(--weight-semibold);
	text-align: center;
}

/* The three prices and the deposit line: plain body copy in ink, which
 * is Figma's own treatment for both. They get named classes rather than
 * bare <p> selectors so that a future change to one cannot silently
 * catch the other. */
.offer-card__price,
.offer-card__deposit {
	font-size: var(--type-body);
}

/* The X-ray footnote and the remaining-balance term. 13px is --type-xs
 * exactly, and muted against the ink above it is Figma's own contrast
 * step - the same label-vs-body relationship .field__label draws in the
 * other direction. */
.offer-card__note {
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}


/* ---- 26.2  .field-row--three - City / State / Zip ----
 *
 * The only new layout on the page. Figma draws these three across at
 * gap 16, the same gap .field-row already uses, so this modifier
 * changes the track count and nothing else.
 *
 * !! IT NEEDS ITS OWN MEDIA QUERY AND CANNOT BORROW .field-row's. This
 * section is appended, so the rule above beats section 25's max-width
 * 767 block on source order - media queries add no specificity. Without
 * the query below, three inputs would stay three across on a phone.
 * That is the same trap section 18.3 documents for .prenatal-fit__point
 * and section 3's row note describes.
 *
 * It stacks straight to one column rather than passing through two. A
 * 2+1 arrangement would put Zip alone on a row under City and State,
 * which reads as a separate question rather than the third part of one
 * address. */
.field-row--three {
	grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 767px) {
	.field-row--three {
		grid-template-columns: 1fr;
	}
}


/* ---- 26.3  .payment-slot - where card details will NEVER be typed ----
 *
 * !! READ THIS BEFORE CHANGING ANYTHING IN IT. This is a labelled
 * placeholder with NO CONTROL INSIDE IT, and it stays that way. Card
 * details may only ever be entered into the payment provider's own
 * iframe, which the provider serves and this site never sees. An
 * <input> here - even disabled, even readonly, even type="text" - would
 * put a card number in this site's DOM and in browser autofill while
 * looking trustworthy enough that somebody would type one in. Figma
 * refuses to draw one too: node 636:1763 is a dashed grey box named
 * "[Stripe Elements Embed - card number, expiry, CVC]".
 *
 * When a processor is chosen this whole block is REPLACED by its mount
 * point, not restyled and not filled in. LAUNCH-CHECKLIST.md B12.
 *
 * The dashed border, grey fill and 8px radius are Figma's own drawing
 * of this placeholder. The radius is --radius-field rather than
 * .embed-slot's --radius-sm because this box sits in a run of 48px
 * inputs that all carry 8, and matching its neighbours matters more
 * here than matching the other placeholder on a different page.
 *
 * min-height, not height: the note inside wraps to three lines on a
 * phone, and a fixed 48 would clip it. 48 is Figma's own box and is
 * what it renders at on the desktop width Figma draws. */
.payment-slot {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 48px;
	padding: var(--space-12) var(--space-16);
	border: 1px dashed var(--border-field);
	border-radius: var(--radius-field);
	background: var(--surface-grey);
	text-align: center;
}

/* !! SCAFFOLDING, NOT PAGE COPY - same register as .embed-slot__label
 * and .form-pending. Deleted with the integration. */
.payment-slot__note {
	max-width: var(--measure-lead);
	margin: 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}


/* ---- 26.4  .order-summary - the deposit line, and the method ----
 *
 * Two blocks share this class: Figma's Order Summary (636:1764) and the
 * document's Payment Method block, which Figma drops and which is
 * restored on this page. They are the same shape - a label over one or
 * two lines of detail - so they are one rule rather than two identical
 * ones. That is the duplication .subhead was created to end.
 *
 * ALL THREE SIZES ARE EXACT LADDER STEPS with nothing to reconcile:
 * Figma's 15 is --type-body, its 14 is --type-small, its 16 is
 * --type-ui. Rare on this build and worth recording.
 *
 * The title is a <p>, not a heading - the card already has its H2 and
 * this is a label on a two-line block, not a section of the page.
 * Marking it up as a heading would put a stray level in the outline. */
.order-summary {
	display: flex;
	flex-direction: column;
	gap: var(--space-8);
}

.order-summary > * {
	margin: 0;
}

.order-summary__title {
	color: var(--neutral-ink);
	font-size: var(--type-body);
	font-weight: var(--weight-semibold);
}

.order-summary__line {
	color: var(--neutral-muted);
	font-size: var(--type-small);
}

.order-summary__total {
	color: var(--neutral-ink);
	font-size: var(--type-ui);
	font-weight: var(--weight-semibold);
}


/* ---- 26.5  the payment card, its button row and its privacy line ----
 *
 * PADDING 20/40, both Figma's own and both on the approved scale, so
 * neither is snapped. It is NOT .card--pad (32/48): that modifier is
 * the common case and this card genuinely draws tighter, with a 20px
 * top edge above its own heading.
 *
 * The card itself is .card.card--raised - and --shadow-card is an EXACT
 * match for what Figma draws here, 0 4px 30px -10px at 15% ink. Nothing
 * new is needed for the box.
 *
 * The 40 above it is the Content Column's gap again, the same value
 * .offer-card carries. */
/* !! margin-top IS GONE. The card used to stand alone under the offer
 * card and pay for its own boundary; inside .booking-row the ROW owns
 * the spacing, and a margin here would push it below the offer card
 * beside it. Same override .offer-card--in-row already carries, for the
 * same reason. */
.checkout-form {
	padding: var(--space-20) var(--space-40);
}


/* ---- 26.3  UPSELL PLUGIN'S CHECKOUT, MADE TO LOOK LIKE OUR OWN FORMS ----
 *
 * !! THE TARGET IS THE BOOKING FORM ON /booking/, NOT hbchiropractic's
 * PIXELS. Gerek put the two checkouts side by side and asked for HB's
 * "small and compact". Copying HB's numbers literally would be the
 * wrong reading of that: HB's type scale is far smaller than this site's
 * - its --type-ui is 17px where ours is 23 - so its 17px inputs are
 * normal there and would be tiny here. What he is describing is a form
 * that reads as ONE thing rather than a plugin dropped into a page.
 *
 * So every value below is taken from THIS site's own form components -
 * .field, .field__label, .field__input, .form-grid, .field-row - which
 * the visitor has just used on step 1. Step 1 and step 2 now look like
 * the same form, which is the actual goal.
 *
 * !! EVERY SELECTOR IS SCOPED UNDER .checkout-form. The plugin's classes
 * are generic and half of them are Bootstrap's - .form-group, .row,
 * .form-control, legend. Unscoped, these would reach the intake-form
 * embeds and any other third-party markup this site ever hosts.
 * .checkout-form exists on this page alone. */

.checkout-form .upsell-checkout-form {
	margin-bottom: 0;
}

/* The plugin opens the form with a spacer div carrying no class, no id
 * and nothing but whitespace. :empty does NOT match it - a text node of
 * spaces is still a child - so it is matched on "a div with no class
 * attribute at all", which in this form is only ever that one. */
.checkout-form .upsell-checkout-form > div:not([class]) {
	display: none;
}

/* "BILLING DETAILS" and "PAYMENT INFO".
 *
 * !! background: none IS THE WHOLE FIX FOR THE GREY PLATE. The plugin's
 * bundled bootstrap carries `.upsell legend { background: #e9ebee }` -
 * a pale grey block behind the words which, on a white card, reads as
 * accidentally selected text rather than as a heading. It is the single
 * most obviously wrong thing in the before screenshot.
 *
 * !! AND text-align: left, width: auto. The same bootstrap sets
 * `width: 100%; text-align: center`, which is why these two headings
 * were sitting centred over a card whose title, every label and every
 * field are left-aligned.
 *
 * !! font-weight CARRIES !important AND IT IS NOT OPTIONAL. The plugin
 * prints an inline <style> in the body with
 * `.upsell legend { font-weight: normal !important }`, and the same for
 * label, .form-control and .btn. An author !important can only be
 * answered by another author !important - without it this silently
 * renders at 400 and the rule looks like it worked.
 *
 * Shipped at 32px centred; this is the eyebrow treatment
 * .product-card__slot-label already uses for a small label on this
 * site. */
.checkout-form .upsell-checkout-form legend {
	position: relative;
	width: auto;
	margin: 0 0 var(--space-16);
	padding: 0 0 var(--space-8);
	border: 0;
	background: none;
	color: var(--neutral-muted);
	font-family: var(--font-body);
	font-size: var(--type-eyebrow);
	font-weight: var(--weight-semibold) !important;
	line-height: var(--leading-heading);
	letter-spacing: 0.08em;
	text-align: left;
	text-transform: uppercase;
}

/* The accent rule under it. Coral is this site's action colour and it is
 * used here as a RULE rather than as type - a 3px bar carries the eye
 * without asking coral to hold small text on white, which it does not
 * do well. 3px because 1 is a hairline and vanishes beside 18px
 * uppercase. */
.checkout-form .upsell-checkout-form legend::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0;
	width: var(--space-32);
	height: 3px;
	border-radius: var(--radius-pill);
	background: var(--brand-coral-deep);
}

.checkout-form .upsell-checkout-form fieldset {
	margin-bottom: calc(var(--space-48) * 0.5);
}

/* !! NOT 0. The terms checkbox is the LAST fieldset and the submit
 * button follows it immediately, so zeroing this put a payment button
 * hard against the line the customer has to read and agree to before
 * pressing it - which is both ugly and, on a consent control, the wrong
 * thing to crowd. Gerek flagged it on the rendered page.
 *
 * The gap belongs on the button's own group rather than here, so it
 * holds whichever fieldset happens to come last. */
.checkout-form .upsell-checkout-form fieldset:last-of-type {
	margin-bottom: 0;
}

/* The submit button's wrapper. Bootstrap gives .control-group nothing,
 * and the button is the one control on this page that should not look
 * hurried. */
.checkout-form .upsell-checkout-form .control-group {
	margin-top: var(--space-24);
}

/* The plugin wraps the button in <center>, which is deprecated markup
 * this theme does not otherwise contend with. Left in place - it is the
 * plugin's own view and removing it would mean filtering its HTML - but
 * the button is made full width so the centring has nothing to do and
 * the control reads as the page's primary action rather than as a
 * floating pill. */
.checkout-form .upsell-checkout-form .control-group .btn {
	display: block;
	width: 100%;
}

/* The field gap, and it is the number that does most of the work - it
 * applies nine times in the billing section alone. --space-20 is
 * .form-grid's own gap, so the plugin's fields breathe exactly like the
 * booking form's. */
.checkout-form .upsell-checkout-form .form-group {
	margin-bottom: var(--space-20);
}

/* .field__label, exactly - 19px semibold ink, sitting 6px above its
 * field the way .field's own gap does. NOT uppercase: this site's form
 * labels are sentence case on every other page, and the eyebrow
 * treatment above is what separates a SECTION heading from a field
 * label. !important for the same inline-style reason as the legend. */
.checkout-form .upsell-checkout-form .form-group > label,
.checkout-form .upsell-checkout-form label.control-label {
	display: block;
	margin-bottom: 6px;
	color: var(--neutral-ink);
	font-family: var(--font-body);
	font-size: var(--type-xs);
	font-weight: var(--weight-semibold) !important;
	letter-spacing: normal;
	text-transform: none;
	line-height: var(--leading-heading);
}

/* .field__input, exactly. The plugin ships `height: calc(1.5em + 2px +
 * 0.75rem)` and 15px/1.4rem padding, which produced a 41px box holding
 * 18px text - tighter than our own 48px field and in a different
 * typeface. height:auto lets the padding define the box the way ours
 * does. */
.checkout-form .upsell-checkout-form .form-control,
.checkout-form .upsell-checkout-form select.form-control,
.checkout-form .upsell-checkout-form input[type="text"],
.checkout-form .upsell-checkout-form input[type="email"],
.checkout-form .upsell-checkout-form input[type="tel"],
.checkout-form .upsell-checkout-form input[type="number"],
.checkout-form .upsell-checkout-form input[type="password"] {
	height: auto;
	min-height: 48px;
	padding: var(--space-8) var(--space-16);
	border: 1px solid var(--border-field);
	border-radius: var(--radius-field);
	background-color: var(--surface-white);
	color: var(--neutral-ink);
	font-family: var(--font-body);
	font-size: var(--type-body);
	font-weight: 400 !important;
	line-height: var(--leading-heading);
	box-shadow: none;
}

.checkout-form .upsell-checkout-form .form-control::placeholder {
	color: var(--neutral-muted);
	opacity: 1;
}

/* The coral focus ring every other field on this site uses. Stripe's
 * card box gets it too - .StripeElement--focus is the class their
 * iframe wrapper takes, and without it the one field that matters most
 * would be the only one with no focus state. */
.checkout-form .upsell-checkout-form .form-control:focus,
.checkout-form .upsell-checkout-form .StripeElement--focus {
	border-color: var(--brand-coral-deep);
	background-color: var(--surface-white);
	box-shadow: none;
	outline: 2px solid var(--brand-coral-deep);
	outline-offset: 2px;
}

/* Bootstrap's grid gutter is 15px each side; --space-16 is ours, and it
 * makes the plugin's side-by-side pairs sit on the same rhythm as
 * .field-row's 16. */
.checkout-form .upsell-checkout-form .row {
	margin-right: calc(var(--space-16) / -2);
	margin-left: calc(var(--space-16) / -2);
}

.checkout-form .upsell-checkout-form .row > [class*="col-"] {
	padding-right: calc(var(--space-16) / 2);
	padding-left: calc(var(--space-16) / 2);
}

/* The order table. Bootstrap gives it full-width borders and a lot of
 * air; this brings it to the card's own radius and quietens it, because
 * it is a summary rather than a thing to read closely. */
.checkout-form .upsell-checkout-form .table {
	margin-bottom: var(--space-16);
	border: 1px solid var(--border-field);
	border-radius: var(--radius-field);
	border-collapse: separate;
	border-spacing: 0;
	overflow: hidden;
	background: var(--surface-off);
	font-size: var(--type-xs);
}

.checkout-form .upsell-checkout-form .table td,
.checkout-form .upsell-checkout-form .table th {
	padding: var(--space-8) var(--space-12);
	border-top: 0;
	vertical-align: middle;
}

/* The submit button. .btn is Bootstrap's and the plugin overrides its
 * weight inline, hence the !important twin again. */
.checkout-form .upsell-checkout-form .btn {
	padding: var(--space-12) var(--space-24);
	border: 0;
	border-radius: var(--radius-pill);
	/* --gradient-cta, the token .cta-button itself uses, NOT a
	 * hand-rolled coral pair. --brand-coral is a pale TINT (#fbdbd2) and
	 * starting a button gradient there would have washed it out - the
	 * button's actual light stop is --brand-coral-light. Using the token
	 * means this button cannot drift from every other button on the
	 * site. */
	background: var(--gradient-cta);
	color: var(--surface-white);
	font-family: var(--font-body);
	font-size: var(--type-xs);
	font-weight: var(--weight-semibold) !important;
	line-height: var(--leading-heading);
}

/* The checkout button is not a .cta-button - it comes from the payment
 * plugin's own markup - so it needs the same treatment stated again
 * rather than inherited. Same three states, same reasoning as the base
 * button in section 3; this is the one button on the site where "did my
 * click register?" costs the practice money. */
.checkout-form .upsell-checkout-form .btn {
	transition: filter .15s ease, transform .15s ease, box-shadow .15s ease;
}

.checkout-form .upsell-checkout-form .btn:hover {
	filter: brightness(1.12) saturate(1.05);
	transform: translateY(-1px);
	box-shadow: 0 6px 16px rgb(221 90 63 / 38%);
}

.checkout-form .upsell-checkout-form .btn:active {
	filter: brightness(.92) saturate(1.05);
	transform: translateY(0);
	box-shadow: 0 1px 3px rgb(221 90 63 / 30%);
}

.checkout-form .upsell-checkout-form .btn:focus-visible {
	outline: 3px solid var(--brand-coral-deep);
	outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
	.checkout-form .upsell-checkout-form .btn:hover,
	.checkout-form .upsell-checkout-form .btn:active {
		transform: none;
	}
}

/* Matching .field__input's own mobile treatment from 25.4b - the fill
 * that stops a white box on a white card disappearing on a phone. */
@media (max-width: 991px) {
	.checkout-form .upsell-checkout-form .form-control,
	.checkout-form .upsell-checkout-form select.form-control,
	.checkout-form .upsell-checkout-form input[type="text"],
	.checkout-form .upsell-checkout-form input[type="email"],
	.checkout-form .upsell-checkout-form input[type="tel"],
	.checkout-form .upsell-checkout-form input[type="number"],
	.checkout-form .upsell-checkout-form input[type="password"] {
		background-color: var(--surface-off);
	}

	.checkout-form .upsell-checkout-form .form-control:focus {
		background-color: var(--surface-white);
	}
}

/* Figma centres the button in the card. The wrapper exists because
 * .form-grid is a column flex container: centring the button directly
 * would need align-self, and a row that can later hold a second control
 * is the more durable shape. */
.form-action {
	display: flex;
	justify-content: center;
}

/* 12px is --type-eyebrow exactly - Figma's own, centred and muted. This
 * is PAGE COPY, both sources word for word, unlike the pending note
 * directly beneath it.
 *
 * !! RENAMED FROM .checkout-form__privacy 2026-08-21, WHEN THE BOOKING
 * SALES PAGE (08) TURNED OUT TO END ON THE SAME SENTENCE. The
 * declarations are byte for byte what they were, so no computed style
 * changes; only the name is wider than one page. This is the same call
 * section 22.1 records for .split__title -> .subhead, and it is made for
 * the same reason: the alternative was either a checkout-named class on
 * a page that is not Checkout, or a second rule with identical
 * declarations under a second name. page-booking-checkout.php moves onto
 * the new name in the same commit, so there is never a moment with two
 * names for one rule. */
.form-privacy {
	color: var(--neutral-muted);
	font-size: var(--type-eyebrow);
	text-align: center;
}


/* ---- 26.6  responsive ----
 *
 * Figma draws desktop only, so these are this build's own steps rather
 * than the design's, and both are the smallest that keep the page
 * honest on a phone: the offer card's 40/48 padding is more than a
 * narrow screen can spare beside a box that is now full width, and the
 * payment card's 40 sides likewise.
 *
 * Both step down to values already on the approved scale. No type sizes
 * are touched: the responsive heading ladder in section 1.4b already
 * steps every heading on this page, and every body-level size here is
 * flat by design. */
@media (max-width: 767px) {
	.offer-card {
		padding: var(--space-32) var(--space-24);
	}

	.checkout-form {
		padding: var(--space-20) var(--space-24);
	}
}


/* ==================================================================
 * 27  BOOKING - NEW PATIENT SPECIAL  (added 2026-08-21)
 *
 * Sitemap page 08, slug booking, FUNNEL STEP 1 OF 4. Figma frame
 * 635:126. A SALES page - the document and the sitemap both say so
 * explicitly - so the value stack and the call to action below are
 * sanctioned copy, not the zero-POV constraint being bent. Do not
 * confuse it with page 10, /booking-calendar/, which IS the locked
 * zero-POV Booking Utility page.
 *
 * !! THIS SECTION REUSES TWO WHOLE COMPONENTS AND BUILDS NEITHER OF
 * THEM AGAIN. .offer-card (section 26) and .testimonial-card (section
 * 09) arrive intact; between them they are most of what is on this
 * page. What is genuinely new is FOUR things: the two-column row that
 * stands the offer card beside a form card, one new offer-card
 * sub-element for the coral line, a plain three-up row to replace the
 * homepage's drag scroller, and the Happy Families block's own rhythm.
 *
 * !! TWO CLASSES WERE RENAMED IN THIS COMMIT, NOT DUPLICATED, because
 * this page ends on the same privacy sentence and the same centred
 * button row as Checkout: .checkout-form__privacy -> .form-privacy and
 * .checkout-form__action -> .form-action, both in section 26.5, both
 * byte-identical declarations. page-booking-checkout.php moves onto the
 * new names in the same commit. Same call section 22.1 records for
 * .split__title -> .subhead.
 * ================================================================== */

/* ---- 27.1  .booking-row - the offer card beside the form ----
 *
 * The ONE structural difference between this page and Checkout: Figma
 * node 913:2611 stands the 540px offer card and the form card side by
 * side at gap 40, equal height, where Checkout stacks the same two
 * components. Nothing else about either card changes.
 *
 * !! flex-basis READS .offer-card's OWN --offer-card-w RATHER THAN
 * REPEATING 540. The property is declared on .offer-card in section
 * 26.1, so the element resolves it in its own flex shorthand - one
 * place still owns that number. A grid track here could not have done
 * that: grid-template-columns is declared on the ROW, which never sees
 * a custom property scoped to its child.
 *
 * align-items defaults to stretch, which is what makes the form card
 * match the offer card's height - Figma draws the form card as h-full
 * and pushes its button to the bottom with justify-between. */
/* !! margin-top IS 96, THE HOUSE BOUNDARY - the horizontal `gap` below
 * is NOT and must not be changed with it. They are two different
 * measurements that happened to share a number: the gap is the space
 * BETWEEN the offer card and the form card, which Figma draws at 40 and
 * which is right; margin-top is the boundary between this row and the
 * intro above it, which every other page on this build pays at 96.
 *
 * Measured against /products/ in the browser, like the other five. */
.booking-row {
	display: flex;

	/* !! row-reverse, AND THE FORM IS FIRST IN THE MARKUP. Read those
	 * two together or neither makes sense.
	 *
	 * Gerek's requirement (2026-09-02): on a phone or tablet the form
	 * must come FIRST, straight after the H1 and its sentence - "we need
	 * to get the form in front of them as quickly as possible". On
	 * desktop the offer card still belongs on the left.
	 *
	 * The tempting fix is CSS `order` on the mobile breakpoint, and it
	 * is the wrong one for the same reason the Contact page's map was
	 * moved in the markup rather than reordered visually: `order` moves
	 * what the eye sees and leaves the DOM alone, so a keyboard and a
	 * screen reader would still meet the two in the old sequence. In a
	 * STACKED layout that is a real mismatch - one thing genuinely comes
	 * before the other.
	 *
	 * So the form goes first in the markup, which makes the mobile case
	 * plain `column` with no trickery and puts the form first in the tab
	 * order everywhere - which on a booking page is what you want
	 * anyway. Desktop then needs the visual swap, and row-reverse is the
	 * honest place to take it: side by side, the two cards are peers
	 * seen at once, so there is no "before" for reading order to get
	 * wrong. The mismatch that matters is in the stack, and the stack is
	 * now correct.
	 *
	 * !! gap IS NOT THE HOUSE BOUNDARY - margin-top is. Two different
	 * measurements that happen to share a number: 40 is the space
	 * between the two cards, which Figma draws and which is right; the
	 * margin-top below is the boundary between this row and the intro
	 * above it.
	 *
	 * !! AND THAT BOUNDARY IS 48 HERE, HALF THE HOUSE 96. Gerek's call
	 * 2026-09-02, made explicitly against his own standing rule that
	 * every page should space alike: "I'm going to go against my normal
	 * advice because I need to get more information above the fold."
	 *
	 * This is the one page on the site where the rhythm loses to
	 * something else, and the something else is real - it is the funnel
	 * entry, and the offer and the form both want to be visible without
	 * scrolling. Exactly half, and still a single step on the scale
	 * rather than a minted number, so it reads as a deliberate halving
	 * rather than a value someone nudged.
	 *
	 * AT EVERY WIDTH. This was desktop-only for a few hours; Gerek asked
	 * for the same on phone and tablet the same day and it is better
	 * there too - the fold is smaller on a phone, not less contested. */
	flex-direction: row-reverse;
	gap: var(--space-40);
	margin-top: var(--space-48);

	/* !! flex-start, NOT THE DEFAULT stretch. Gerek's call: the form
	 * card was being pulled to the offer card's full height, leaving a
	 * long empty run under the Submit button with the button stranded at
	 * the bottom. The two cards hold different amounts and do not need
	 * to be the same height - so each is now as tall as its own content
	 * and the button sits directly under the last field. */
	align-items: flex-start;
}

/* !! 50/50, AND max-width HAS TO BE UNSET FOR IT TO MEAN ANYTHING.
 * Gerek's call 2026-09-02: the columns were 540 and 772 against a 1400
 * container, which read as lopsided rather than as a pair.
 *
 * The old rule was `flex: 0 1 var(--offer-card-w)` - the card's own 540
 * from section 26.1, which Figma draws for the STANDALONE card on
 * Checkout. In a two-column row that number is not a width, it is one
 * side of a ratio, and the ratio is 1:1.
 *
 * !! THE BASIS IS 50%, NOT 0, AND `1 1 0` IS THE OBVIOUS ANSWER THAT
 * DOES NOT WORK HERE. `flex: 1 1 0` is the textbook way to say "equal
 * columns", and it measured 664 / 648 against a 1352 row - close enough
 * to look intentional and wrong by 16px. With a zero basis the two
 * cards' own content still influences how the free space lands, and
 * these two hold very different things: a seven-item priced list, and
 * four inputs.
 *
 * `1 1 50%` states the ratio outright and lets flex-shrink take the
 * 40px gap out of both equally. Measured after the change: 656 and 656.
 *
 * `max-width: none` is the half that would have failed SILENTLY:
 * .offer-card carries `max-width: var(--offer-card-w)` - Figma's 540
 * for the STANDALONE card on Checkout - in its base rule. Without
 * clearing it the card caps at 540 while claiming half the row, and the
 * flex maths reads correctly in the stylesheet while the screen shows
 * something else.
 *
 * min-width: 0 lets the pair narrow together between 992 and 1200
 * rather than forcing a horizontal scrollbar - the same protection the
 * old `0 1` shorthand was providing. */
/* !! TARGETS THE ROW'S CHILDREN, NOT TWO NAMED CLASSES. It used to read
 * `.offer-card--in-row, .booking-row .booking-form`, which was fine
 * while /booking/ was the only page with a row. Checkout now clones the
 * same row with a DIFFERENT card in it (.checkout-form), and naming
 * each one would mean this rule quietly missing a page every time the
 * pattern is reused. A two-column row has exactly two children and they
 * are the halves; that is the thing being described. */
.booking-row > * {
	flex: 1 1 50%;
	min-width: 0;
	max-width: none;
}

/* !! THE CARD HAD NO BACKGROUND AT ALL, WHICH IS WHY THE PAGE SHOWED
 * THROUGH IT. Gerek: the left column is "allowing too much color from
 * the background... a little bit distracting."
 *
 * .offer-card sets a radius, a pink glow and a text colour and never
 * set a fill, so the parallax artwork behind the page - a mint mass on
 * exactly that side - was reading at full strength through the copy.
 * Against the form card's solid white opposite it, the pair looked like
 * two different components.
 *
 * Translucent rather than solid white on purpose: the glow and the
 * artwork are meant to be felt behind this card, just not competed
 * with. 0.78 mutes the mint to a tint while leaving the type on a clean
 * ground. One number, and it is the one to turn if it wants to be
 * softer or firmer.
 *
 * SCOPED TO --in-row, so Checkout's standalone copy of the same
 * component is untouched. It sits on the same parallax and probably
 * wants the same treatment; that is Checkout's review, not this one. */
.offer-card--in-row {
	background: rgba(255, 255, 255, 0.78);
}

/* !! THE TWO OVERRIDES THE ROW NEEDS, AND WHY THE BASE CLASS KEEPS
 * BOTH. Standing alone on Checkout the card centres itself and pays for
 * its own top boundary; in a row the ROW owns both, and the card's own
 * margin-top would push it 40px below the form card beside it. The base
 * class is left as it is because Checkout is still its main user and
 * the stacked case is the common one. */
.offer-card--in-row {
	margin-top: 0;
	margin-inline: 0;
}

/* ---- 27.2  .offer-card__cta - the coral line ----
 *
 * !! COPY, NOT A CONTROL - see the note in page-booking.php. It reads
 * like a button and neither source makes it one.
 *
 * --type-h3 (26) against Figma's 24. Off the ladder, nearest step,
 * snapped - the same rule section 25.5 states and section 26.1 applied
 * to "Total Value = $785". It ties with .offer-card__title at 26, which
 * is Figma's own relationship: it draws 26 and 24, two sizes close
 * enough that the ladder cannot separate them.
 *
 * !! THIS LINE DOES NOT EXIST ON CHECKOUT, DELIBERATELY. It is real
 * copy in this page's document; on Checkout it appeared only in Figma,
 * restating a figure the document had already given, and was cut as
 * repetition (LAUNCH-CHECKLIST.md B12). The class lives here rather
 * than in section 26 for that reason - one page uses it.
 *
 * --brand-coral-deep is Figma's #dd5a3f exactly, and it already existed
 * in section 01. SemiBold rather than Figma's Bold, matching every
 * other emphasis on this site and the call .offer-card__total records. */
.offer-card__cta {
	color: var(--brand-coral-deep);
	font-size: var(--type-h3);
	font-weight: var(--weight-semibold);
	text-align: center;
}


/* ---- 27.3  .booking-form - the two-field opt-in card ----
 *
 * Padding 20/40 is Figma's own and both values are on the scale - the
 * same box Checkout's form card uses, and NOT .card--pad (32/48).
 *
 * justify-content: space-between is what pushes the button and the two
 * notes to the bottom edge, which is only visible because the row
 * stretches this card to the offer card's height. Figma draws exactly
 * that. The min-width: 0 is the grid/flex-item guard section 24.2b
 * documents: an input's min-content width would otherwise hold the
 * column open and force the row wider than its container. */
/* !! NO `flex` HERE. THE ROW OWNS THE HALVES - 27.1's `.booking-row > *`
 * - AND THIS RULE FIGHTING IT IS A BUG I SHIPPED.
 *
 * It used to carry `flex: 1 1 0`. That was harmless while the row's own
 * rule read `.booking-row .booking-form`, which is (0,2,0) and won. When
 * that was generalised to `.booking-row > *` so Checkout's card would be
 * covered too, the specificity dropped to (0,1,0) - a TIE with this
 * selector - and this one is 80 lines further down the file, so source
 * order handed it the win.
 *
 * The result was not subtle and was not caught: the form took basis 0
 * while the offer card took 50%, so a row that had measured 656/656 came
 * out 358/954. Gerek described it as "like 75/25".
 *
 * Deleting it rather than out-specifying the row is the fix, because two
 * rules both claiming to size this card is the actual fault. One owner. */
.booking-form {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: var(--space-20);
	min-width: 0;
	padding: var(--space-20) var(--space-40);
}

/* The button, the privacy line and the pending note travel together as
 * the card's bottom half, so space-between has two children to push
 * apart rather than five. */
.booking-form__foot {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
}

.booking-form__foot > * {
	margin: 0;
}


/* ---- 27.5  .form-cue - "Start Here", and the one moving thing on the site ----
 *
 * Gerek 2026-09-02: this page is the whole reason the site exists for a
 * new patient, and it was not telling anyone where to begin. Two cards
 * side by side, both quiet, no instruction anywhere. So the form card
 * gets a heading that points rather than labels, and Checkout's existing
 * heading takes the same treatment - the two steps then read as one
 * sequence.
 *
 * CORAL, AND SPECIFICALLY --brand-coral-deep, because it is the colour
 * that already means "act" on this page - the Continue button and "Get
 * Started - $99 Deposit!" in the card opposite are both it. A new accent
 * would have been a third signal competing with the two that work.
 *
 * THE HEADING FACE, not the body face, so it reads as a signpost rather
 * than as a field label.
 *
 * !! --type-h3, RAISED FROM h4 ON 2026-09-02 at Gerek's request - "+4px"
 * is exactly the h4-to-h3 step on this ladder (26 -> 30), so it took a
 * real token rather than a one-off size. The note here used to argue for
 * h4 on the grounds that h3 competed with the H1; with the step marker
 * beside it the line now reads as a unit rather than as a rival
 * heading, and it does not.
 *
 * THE STEP MARKER SITS A STEP BELOW IT, at h4, same face and colour.
 * "Start Here" is the instruction and "- Step 1 of 3" is the context, so
 * they should not carry equal weight - but the weight is not available
 * to separate them with: --font-heading is Baloo 2 and the kit loads
 * BOLD ONLY, so asking for 400 here would hand the browser a synthetic
 * face. Size is the honest lever. On the smallest breakpoint h3 and h4
 * both land on 18 and the distinction quietly disappears, which is
 * fine - at that width the line is short enough to read as one thing
 * anyway.
 *
 * !! THE ARROW MOVES AND THE WORDS DO NOT. That is the whole restraint.
 * Gerek asked for movement that catches the eye and is not annoying, and
 * those pull in opposite directions - animated TEXT is the thing people
 * find irritating, because the eye keeps re-reading it. A 5px bob on a
 * glyph beside static words is read once as "down there" and then
 * ignored, which is exactly what a signpost should do.
 *
 * 2.4s and ease-in-out: slow enough to read as a nudge rather than a
 * blink, and it rests at both ends of the cycle rather than moving
 * constantly. */
.form-cue {
	display: flex;
	align-items: center;
	gap: var(--space-8);
	margin: 0 0 var(--space-16);
	color: var(--brand-coral-deep);
	font-family: var(--font-heading);
	font-size: var(--type-h3);
	font-weight: 700;
	line-height: var(--leading-heading);
}

/* Context, not instruction - see the note above for why this is
 * separated by size rather than by weight. */
.form-cue__step {
	font-size: var(--type-h4);
}

.form-cue__arrow {
	display: inline-flex;
	animation: mc-cue-bob 2.4s ease-in-out infinite;
}

.form-cue__arrow svg {
	display: block;
}

@keyframes mc-cue-bob {
	0%, 100% { transform: translateY(0); }
	50%      { transform: translateY(5px); }
}

/* !! ANYONE WHO HAS ASKED THEIR SYSTEM FOR LESS MOTION GETS NONE. Not a
 * nicety on a page like this - vestibular disorders are common enough
 * that a looping animation can genuinely make somebody unable to use a
 * booking form, and the cue still does its job in colour and type with
 * the movement switched off. */
@media (prefers-reduced-motion: reduce) {
	.form-cue__arrow {
		animation: none;
	}
}


/* ---- 27.6  the Booking intro, tightened to buy back the fold ----
 *
 * Gerek's call, and it is the trade the "Start Here" line above costs.
 * That heading pushes the Continue button down, and on this page the
 * button being visible without scrolling is worth more than matching the
 * site's default leading.
 *
 * !! ONLY THE LEADING, AND ONLY ON THIS PAGE'S INTRO. --leading-body is
 * 1.6 site-wide and stays there; this is three paragraphs of orientation
 * copy above a form, read once, not an article. 1.35 is still
 * comfortably above the 1.2 floor where lines start to collide, and the
 * measured saving is real: paragraph 2 goes 70 -> 59 and paragraph 3
 * 106 -> 89.
 *
 * The paragraph GAP comes down with it - 12 to 8 - because tightening
 * the lines without tightening the gaps makes the paragraphs read as
 * further apart than they were, which is the opposite of the intent. */
.booking .page-content.prose p {
	line-height: 1.35;
	margin-bottom: var(--space-8);
}

.booking .page-content.prose p:last-child {
	margin-bottom: 0;
}


/* ---- 27.7  the Booking form, tightened for the same reason ----
 *
 * The "Start Here" line costs about 60px and the intro tightening gave
 * back 34, so the Continue button moved DOWN by roughly 26 - the exact
 * thing Gerek was worried about. The rest comes out of the form's own
 * spacing rather than out of the page, because the page-level 60 and 48
 * were his call two changes ago and should not be quietly re-cut.
 *
 * !! SCOPED TO .booking. .form-grid and .field are shared with the
 * Contact form and both intake pages, none of which is fighting for a
 * fold - unscoped, this would tighten four pages to solve one.
 *
 * Nothing here touches the input height. 48px is the site's touch
 * target and shrinking it to win pixels on a phone-shaped problem would
 * be the wrong trade on the page most likely to be filled in on a
 * phone. */
.booking .booking-form {
	gap: var(--space-16);
}

.booking .form-grid {
	gap: var(--space-16);
}

.booking .form-cue {
	margin-bottom: var(--space-12);
}


/* ---- 27.8  step 3 - the scheduler and the receipt on the thank-you page ----
 *
 * Gerek collapsed the funnel from four pages to three on 2026-09-02,
 * mirroring hbchiropractic: the scheduler moves onto the thank-you page
 * so a patient pays and books in one place instead of being handed on
 * again. /booking-calendar/ is what that replaces.
 *
 * The blocks take the house 96 between them, like every other stacked
 * section on the site. This page is NOT on the booking pages' tightened
 * 60/48 - nothing here is competing for the fold, because the visitor
 * has already paid and is not being asked to decide anything. */
.thank-you__schedule,
.ty-receipt,
.thank-you__intake {
	margin-top: calc(var(--space-48) * 2);
}

/* The embed is given a box and nothing else. It brings its own date
 * picker, its own patient fields, its own validation and its own
 * confirm button, and none of that is ours to restyle - the same
 * boundary the two intake pages hold. min-height so the block does not
 * collapse to nothing while the vendor's script is still fetching and
 * shove the page around underneath the reader. */
.scheduler-embed {
	min-height: 560px;   /* Figma 813:230 draws the embed at 1140x560 */
}

/* The "call us instead" line. --type-xs and muted, because it is the
 * fallback rather than the instruction - it should be findable by
 * somebody who has just failed to find a slot, not compete with the
 * scheduler above it. */
.thank-you__schedule-note {
	margin: var(--space-16) 0 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}

/* !! NO padding-bottom OVERRIDE HERE, AND I ADDED ONE BEFORE CHECKING.
 *
 * Gerek asked for "100px from the buttons to the footer" and I reached
 * for the same doubling the TOP of every page needs - and measured 144.
 * The reason is the thing this build keeps relearning from the other
 * direction: .site-footer already carries `margin-top: 48px`. So this
 * boundary was never one-sided. .page-article's own .section gives 48,
 * the footer gives 48, and the house 96 was already there before I
 * touched it.
 *
 * The rule is always "does BOTH sides contribute" - not "is this a
 * boundary". At the top of a page the header contributes nothing, which
 * is why seven pages needed an override there. At the bottom the footer
 * pays its half, so nothing is owed.
 *
 * !! THE TOP OF THIS PAGE IS A DIFFERENT MATTER and did need one - it
 * had been sitting at 48 like every page before it was checked. */
.thank-you .page-article {
	padding-top: calc(var(--space-48) * 2);
}

/* The receipt card. Sized here, contents left to the plugin - the same
 * arrangement .checkout-form uses, and for the same reason: the numbers
 * in it are the order's, not ours to lay out. */
.ty-receipt {
	padding: var(--space-24) var(--space-32);
	border-radius: var(--radius);
	background: var(--surface-off);
}

.ty-receipt .subhead {
	margin-top: 0;
}

.ty-receipt__empty {
	margin: 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}

/* Upsell's order table again, same treatment as Checkout's so a patient
 * who sees both sees one design. Scoped to .ty-receipt for the reason
 * section 26.3 gives: the plugin's classes are generic and half of them
 * are Bootstrap's. */
/* !! display: table AND width: 100%, AND THIS IS THE HALF-WIDTH FIX.
 * Upsell's view puts `table-responsive` on the TABLE itself:
 *
 *     <table class="upsell-order-list-table table table-bordered table-responsive">
 *
 * Bootstrap 4 intends that class for a WRAPPER div and defines it as
 * `display: block; overflow-x: auto`. Applied to a <table>, display:block
 * stops it behaving like a table - it shrinks to its content instead of
 * filling its container, which is why the receipt sat in the left half of
 * a full-width card with dead space beside it. Gerek spotted it on the
 * rendered order.
 *
 * `display: table` puts it back and `width: 100%` fills the card. The
 * horizontal scrolling that table-responsive was there to provide is not
 * lost - the card itself is the scroll container on narrow screens, and
 * four short columns do not need it anyway.
 *
 * The header cells carry an inline `background-color: #ececec` from the
 * plugin's own view, which an author rule cannot beat - so the header
 * band stays grey. Left alone deliberately: it reads as a table header,
 * which is what it is. */
.ty-receipt .upsell-order-list-table,
.ty-receipt .table {
	display: table;
	width: 100%;
	margin-bottom: 0;
	border: 1px solid var(--border-field);
	border-radius: var(--radius-field);
	border-collapse: separate;
	border-spacing: 0;
	overflow: hidden;
	background: var(--surface-white);
	font-size: var(--type-xs);
}

.ty-receipt .table td,
.ty-receipt .table th {
	padding: var(--space-8) var(--space-12);
	border-top: 0;
	vertical-align: middle;
}

@media (max-width: 767px) {
	/* A 560px reservation is most of a phone screen to hold empty while
	 * a script loads. The embed still gets room to land in, just not the
	 * desktop figure. */
	.scheduler-embed {
		min-height: 420px;
	}

	.ty-receipt {
		padding: var(--space-20) var(--space-24);
	}
}


/* ---- 27.4  .testimonial-row - three cards, no scroller ----
 *
 * !! THIS IS NOT THE HOMEPAGE'S CAROUSEL, AND THE DIFFERENCE IS
 * DELIBERATE. Section 09's .testimonials__list is a horizontal drag
 * scroller holding four cards; Figma draws three cards filling the
 * width here, with nothing to scroll. A focusable scroll region that
 * cannot scroll is a keyboard stop for no benefit, so the markup omits
 * tabindex and data-drag-scroll rather than copying them across.
 *
 * !! TWO OF .testimonial-card's OWN DECLARATIONS HAVE TO BE UNDONE, and
 * only one of them would fail loudly. The card carries
 * `flex: 0 0 356px; max-width: 356px` for the carousel. As a GRID item
 * the flex shorthand is simply ignored - but max-width is not, and
 * without the override below every card would cap at 356 and the row
 * would sit short of the container with a gap on the right. That is the
 * quiet kind of breakage, so it is written out explicitly rather than
 * left to be noticed later.
 *
 * scroll-snap-align is inert outside a snap container and is cleared
 * for the same reason: a reader should not have to work out whether it
 * matters here.
 *
 * gap 24 is Figma's own (node 635:1576) and on the scale. */
.testimonial-row {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);
	margin: 0;
	padding: 0;
	list-style: none;
}

.testimonial-row > .testimonial-card {
	flex: initial;
	max-width: none;
	scroll-snap-align: none;
}


/* ---- 27.5  .happy-families - the proof block ----
 *
 * !! FIGMA'S 56px SIDE PADDING IS DELIBERATELY NOT BUILT, and this is
 * the one place this page departs from the frame. Node 635:1573 insets
 * this block by 56 where the same frame's Title block and offer row
 * carry no inset at all, so building it would indent the testimonials
 * against the H1 and the cards directly above them - a misalignment
 * inside one page, caused by an inconsistency between two blocks of one
 * Figma frame. The block stays flush with the rest of the container,
 * which is what every other page on this site does. (The 56 -> 48 cap
 * IS applied where 56 is real: .offer-card's own side padding, section
 * 26.1.) Figma's 64px bottom padding is dropped for the same reason -
 * .section already owns the page's bottom boundary.
 *
 * gap 20 and the outer 40 are Figma's own and both on the scale. */
/* Same correction as .booking-row above, and the same distinction: the
 * 20 is the rhythm INSIDE this block, the top margin is the boundary
 * between it and the row above. */
.happy-families {
	display: flex;
	flex-direction: column;
	gap: var(--space-20);
	margin-top: calc(var(--space-48) * 2);
}

.happy-families > * {
	margin: 0;
}

/* --type-h3 (26) against Figma's 28. Off the ladder, nearest step -
 * --type-h2 (34) is three times further away. The element rule would
 * otherwise serve 34 because this is an <h2>, and the LEVEL is what the
 * document marks: this class changes the size, never the outline. */
.happy-families__title {
	font-size: var(--type-h3);
}

.happy-families__lead {
	color: var(--neutral-muted);
	font-size: var(--type-body);
}

/* !! REQUIRED BY THE COPY DOCUMENT, NOT DRAWN BY FIGMA - the one
 * element on this page that exists because a compliance rule asks for
 * it rather than because a source drew it: "Any condition-referencing
 * testimonial must carry the 'Results may vary' disclaimer per How AI
 * Understands This Business." Two of the three reviews name a condition
 * and an outcome.
 *
 * It is NOT scaffolding and does not come out with the fake reviews -
 * real condition-referencing reviews need it just as much. Given the
 * site's fine-print treatment (13px muted), the same as
 * .offer-card__note, because that is what it is. */
.testimonial-disclaimer {
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}


/* ---- 27.6  responsive ----
 *
 * ONE BREAKPOINT DOES BOTH JOBS, at 991. Above it the offer card and
 * the form stand side by side and the three reviews sit in a row; below
 * it both stack. 991 rather than 767 because the pair genuinely stops
 * fitting there: the offer card alone wants 540, and section 09 already
 * chose 991 for the homepage's testimonial row for the same reason -
 * two blocks that stop fitting at the same width should not stack at
 * two different widths on the same site.
 *
 * The offer card takes its auto side margins back when it stacks: on
 * its own it is a 540px box in a wider container and centres itself,
 * exactly as it does on Checkout. */
@media (max-width: 991px) {
	/* `column`, not column-reverse: the form is already first in the
	 * markup, so the stack needs no reordering at all - which is the
	 * whole reason it was done in the markup. Form, then offer card,
	 * for the eye and the keyboard alike.
	 *
	 * !! THE TIGHTER SPACING RUNS AT EVERY WIDTH - there is no override
	 * here any more. It was written desktop-only on 2026-09-02 and
	 * Gerek looked at the result on a phone the same day: "do the same
	 * spacing issue that I did with the desktop... it looks better that
	 * way."
	 *
	 * He is right, and the reasoning I used to scope it was wrong. I
	 * argued that a stacked page has no second column competing for the
	 * fold, so the exception should stop at the breakpoint. But the
	 * fold is SMALLER on a phone, not less contested - 96 above an H1
	 * costs proportionally more of an 844px screen than of a 900px one.
	 * The 60/48 in the desktop rules above now simply apply throughout. */
	.booking-row {
		flex-direction: column;
	}

	/* !! THE OFFER CARD TAKES A BRAND TINT ON SMALL SCREENS, AND IT IS
	 * THE Products-PAGE PATTERN RATHER THAN A NEW ONE.
	 *
	 * Gerek 2026-09-02: on phone and tablet the page "feels very bland,
	 * there's a lot of white on white." He is describing something real
	 * and specific to the stack: side by side, the offer card reads as
	 * the quiet half of a pair and the parallax mint sits behind it.
	 * Stacked, it becomes a white card following a white card on a
	 * near-white ground, and the only colour left on the screen is the
	 * coral button.
	 *
	 * So the card carries its own tint here, built the way section 31.1
	 * builds .product-card: a brand colour angled across the top-left
	 * and dissolving into near-white before the text gets dense. Six
	 * cards on the Products page already use it, so this is the site's
	 * established way of warming a card rather than a look invented for
	 * one breakpoint.
	 *
	 * MINT, NOT PINK. The pink would sit next to the coral Continue
	 * button and the coral "$99 Deposit!" line and muddy both - they are
	 * neighbours on the wheel. Mint is the complement, it is the mass
	 * the page's own parallax leads with, and it leaves coral as the
	 * only warm thing on the screen, which is exactly where the eye
	 * should go.
	 *
	 * DESKTOP KEEPS THE TRANSLUCENT WHITE. Gerek: "Desktop is fine."
	 * There the card sits ON the parallax mint, so tinting it too would
	 * double the colour in the one place that already had too much. */
	.offer-card--in-row {
		background-image: linear-gradient(
			205deg,
			var(--brand-mint) 8%,
			rgba(255, 255, 255, 0.92) 78%
		);
	}

	/* Back to the card's own 540 and centred, now that it is not half
	 * of a row. max-width returns with `flex: none` reading the base
	 * rule again. */
	.offer-card--in-row {
		flex: none;
		max-width: var(--offer-card-w);
		margin-inline: auto;
	}

	.booking-row > * {
		flex: none;
	}

	.testimonial-row {
		grid-template-columns: 1fr;
	}
}

/* The form card's 40px sides are more than a phone can spare, the same
 * step .checkout-form takes at the same width. */
@media (max-width: 767px) {
	.booking-form {
		padding: var(--space-20) var(--space-24);
	}
}


/* ==================================================================
 * 28  WHAT TO EXPECT  (rebuilt 2026-09-01)
 *
 * Sitemap page 13, Page Name "What to Expect", SLUG /first-visit/ -
 * locked, and the two intentionally differ. Figma frame 645:126.
 *
 * !! THIS SECTION WAS REPLACED WHOLESALE, and the reason is worth
 * recording rather than quietly overwriting. The 2026-08-21 build read
 * this page as "a Utility page and a long prose read: no hero, no side
 * figure, no parallax layer, no cards. Almost all of it is section 19's
 * .prose", and built four things, three of which it called scaffolding:
 * a heading step-down, a dashed icon placeholder row, a line saying the
 * icons were pending, and a full-width photo.
 *
 * Every one of those readings was wrong, and none of them was a
 * judgement call. Figma 645:126 draws a two-column title row with Dr.
 * Aimee's headshot, a white card with a button in it, two four-step
 * icon rows with squiggle connectors, a photo, a three-up amenity row,
 * and a mesh-gradient card. The artwork for all of it was uploaded to
 * the media library and never wired up - fifteen files, every one a
 * clean 2x of its drawn slot. Gerek's description was "it was not built
 * out at all", and against the design that is accurate.
 *
 * The old B15 ("icons that do not exist") and B16 ("mismatched photo")
 * are both cleared by this: the icons exist, and the two photographs
 * the old build guessed at are replaced by the one the design names.
 * ================================================================== */

/* ---- 28.1  the title row (645:220 + 1235:2793) ----
 *
 * Copy at Figma's 526 measure, headshot right at 260 x 365.
 *
 * !! FIGMA POSITIONS THE HEADSHOT ABSOLUTELY, at x=866 y=147 in the
 * 1440 frame, overlapping the content column's right edge. It is a real
 * flex column here instead. An absolutely-placed portrait at one fixed
 * offset has exactly one width at which it does not collide with the
 * copy, and this page has to hold from 320 to 1400. */
.expect-head {
	display: flex;
	align-items: flex-start;
	gap: var(--space-48);
}

/* !! THE COPY IS CAPPED SO THE PORTRAIT HAS SOMEWHERE TO BE. Gerek,
 * pointing at the prototype: "the H1 is kind of off to the left because
 * it's making space for Aimee's image to be on the right."
 *
 * The first build let this column take whatever the photo did not - at
 * 1440 that is 1044px, so the H1 ran most of the way across and the
 * portrait read as an afterthought parked in the corner rather than as
 * the other half of a row.
 *
 * !! TWO WIDTHS, NOT ONE, AND THE H1 IS WHY. Figma sets both the
 * heading and the body in one 526px box. That does not survive the type
 * ladder: this H1 renders at 56px against Figma's 40, so "What to Expect
 * at Your First Visit" needs 822px to stay on one line - measured, not
 * estimated - where the design's own scaled box is 654 (526 x 1352/1088,
 * our inner width against the frame's). A single 654 column wrapped the
 * heading to two lines, which is not what the frame shows.
 *
 * So the COLUMN is 830, wide enough for the heading, and the two lead
 * paragraphs are capped at 654 inside it - the design's measure, kept
 * where it matters. The heading holds its line, the copy holds its
 * width, and the portrait keeps its half of the row: 830 + 48 + 260 is
 * 1138 of 1352, so the two columns still read as a pair rather than as
 * text with a picture parked beside it.
 *
 * flex-basis rather than max-width, so the column SHRINKS on a narrower
 * desktop instead of holding still and squeezing the gap. Below roughly
 * 1140 the heading takes a second line, which is the right thing to
 * give up first. */
.expect-head__copy {
	flex: 0 1 830px;
	min-width: 0;
}

.expect-head__copy .prose__lead {
	max-width: 654px;
}

/* !! align-self: flex-end IS WHAT MAKES THE "ZERO PIXELS" HOLD, and the
 * first attempt got it wrong by relying on the photo being the tallest
 * thing in the row. It is not: the heading plus two paragraphs measure
 * 438 against the portrait's 365, so a top-aligned photo ended 73px
 * short of the row's bottom edge and the card below still had a gap
 * above it - the gap had just moved from the margin into the row.
 *
 * Bottom-aligned, the portrait's lower edge IS the row's lower edge
 * whichever column is taller, so it meets the card at zero at every
 * width and at any copy length. That is the relationship Figma draws -
 * headshot bottom 512, card top 512 - expressed as a rule rather than
 * as two numbers that happen to agree today. */
.expect-head__photo {
	display: block;
	flex: none;
	align-self: flex-end;
	height: auto;
	/* !! multiply ON A PHOTOGRAPH, WHICH IS NOT THE USUAL CALL, AND THE
	 * NUMBERS ARE WHY IT IS SAFE HERE.
	 *
	 * The file is a cut-out portrait on a PURE WHITE ground, fully
	 * opaque - sampled, corners at 255,255,255,255. On Figma's white
	 * page that ground is invisible. The moment a tinted layer went
	 * under it, it became a 260 x 365 white rectangle sitting on the
	 * artwork, which is the same defect the intake headers and the
	 * Prenatal hero backdrop hit, and multiply is the same fix: white
	 * multiplied by anything is that thing, so the ground drops out.
	 *
	 * What is different is that the SUBJECT gets multiplied too, and on
	 * line art that costs nothing while on a face it could. Here it does
	 * not: the portrait sits top-right, where this artwork is a pale
	 * pink wash reading near 250 of 255 after the layer's own 50%
	 * opacity. Multiplying skin by 250/255 darkens it under 2%.
	 *
	 * !! THE THING TO WATCH is where she sits, not the technique. This
	 * artwork's dark motif is the mint circle, and it is bottom-LEFT -
	 * diagonally opposite her. Swap the background for one with a strong
	 * motif in the top right and this rule starts tinting her face.
	 * Check the corner before changing the picture. */
	mix-blend-mode: multiply;
	border-radius: var(--radius-sm);

	/* !! 323, NOT FIGMA'S 260, BECAUSE THE CONTAINER IS WIDER THAN THE
	 * FRAME. Gerek: "Aimee's image needs to be enlarged... it's just way
	 * off." Copying the drawn pixel value made her SMALLER than the
	 * design relative to everything around her: 260 is 23.9% of Figma's
	 * 1088 inner width and only 19.2% of our 1352. 260 x 1352/1088 = 323
	 * puts her back at the design's share of the row. The file is 520 x
	 * 730, so this is still a 1.6x downscale.
	 *
	 * !! AND SHE HANGS BELOW THE ROW ON PURPOSE. This negative margin is
	 * the whole mechanism for two things Gerek asked for that sound
	 * contradictory: "a full spacing between the hero section and the
	 * white box" AND the portrait "set at 0px" to it.
	 *
	 * They are only contradictory if the photo and the copy have to end
	 * on the same line. In Figma they do not - the headshot is absolute,
	 * the Title block ends at y=432, the card starts at 512, and the
	 * portrait's bottom is at 512. The copy gets an 80px gap; the
	 * portrait crosses it and lands on the card.
	 *
	 * The previous build bottom-aligned her to the row, which made the
	 * copy's bottom and hers the SAME edge - so buying clear space for
	 * the text would have pushed her off the card, and keeping her on
	 * the card left the text with none. A negative bottom margin equal
	 * to the gap below breaks the tie: she is pulled 96px out of the
	 * row's height, so the row ends where the COPY ends, the card's own
	 * 96px top margin opens the full spacing after it, and her lower
	 * edge lands exactly on the card's upper edge. One number in two
	 * places, and they must stay equal. */
	width: 323px;
	margin-bottom: -96px;
}

/* ---- 28.2  the two visit sections and their step rows ----
 *
 * !! THE STEP ROW IS ONE IMAGE, NOT FOUR ICONS PLUS A CONNECTOR, and
 * that is the whole reason this row is simple. what-to-expect-step-bar-
 * {prenatal,pediatric}.webp is 2176 x 128 - a clean 2x of the row's own
 * 1088 x 64 - and it carries all four badges AND the squiggle that
 * threads them, flattened.
 *
 * !! THE LABEL GRID USES A PERCENTAGE GAP, AND IT HAS TO. Read out of
 * the bar's own pixels, the four badge centres sit at 127, 405, 683 and
 * 961 of 1088. That is Figma's four-equal-columns-with-a-24px-gap
 * geometry to the pixel: 24 / 1088 = 2.206%, which leaves columns of
 * 23.346% and centres at 11.673%, 37.224%, 62.776% and 88.327%.
 *
 * Written as a percentage, the labels track the badges at EVERY width,
 * because both scale with the container. Written as a literal 24px -
 * which is what it looks like it should be - the gap would hold still
 * while the image scaled, and the labels would sit under their badges
 * at 1088 and nowhere else. Verified by measurement, not by eye. */
/* !! 96 BETWEEN SECTIONS, NOT 48. Gerek: "it needs more space between
 * that section... you're not doing the spacing as you do on all the
 * other pages."
 *
 * He is right and it is measurable. Every other page's boundary is
 * .section's padding-block: 48 on each side of the join, so 96. This
 * page's sections are flex children in one container rather than
 * separate .section elements, so they were carrying a single 48 - half
 * the site's rhythm, on a page whose neighbours are all big blocks.
 * Written as 48 x 2 rather than a typed 96 so it stays tied to the
 * scale step the rest of the site derives it from. */
.expect-visit,
.expect-kids {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-12);   /* Figma 645:226 */
	margin-top: calc(var(--space-48) * 2);
	text-align: center;
}

/* Figma 940:2604 / 941:2604: a pill above the heading, 14/6 padding,
 * Baloo Bold 12 with 0.5 tracking. The size is below the type ladder's
 * floor and is stated literally - it is a label, not running copy, and
 * the ladder's smallest step would make it compete with the H2. */
.expect-eyebrow {
	margin: 0;
	padding: var(--space-8) var(--space-16);
	border-radius: var(--radius-pill);
	font-family: var(--font-display);
	font-weight: var(--weight-bold);
	font-size: 12px;
	letter-spacing: 0.5px;
	text-transform: uppercase;
	color: var(--neutral-ink);
}

.expect-eyebrow--prenatal {
	background: var(--brand-pink);   /* Figma brand/pink, exact */
}

/* Figma names this brand/periwinkle #dee1fa, which is NOT the
 * --accent-periwinkle (#c8ceff) the Products cards use - it is a paler
 * step of the same hue and appears once, on this pill. Inlined with
 * provenance rather than tokenised, the same call section 17 makes for
 * the ICPA navy. */
.expect-eyebrow--pediatric {
	background: #dee1fa;
}

/* !! NO font-size. THE SITE'S H2 IS 46 AND THIS PAGE USES IT, WHICH
 * OVERRULES FIGMA. Gerek: "all the H2s and H3s or whatever need to be
 * the same size. It looks like the Booking Your First Visit H2 is
 * smaller than other pages. Figma is a guide, not the Bible."
 *
 * He is reading a real 20px difference. The first build mapped Figma's
 * 24px section headings onto --type-h4 (26), which is the CARD-title
 * step, while every other page's H2 takes --type-h2 (46) from the base
 * h1-h6 rule. Three headings on this page were two steps down from
 * their equivalents everywhere else.
 *
 * Dropping the declaration is the whole fix - these are real <h2>
 * elements, so the base rule sizes them the moment nothing overrides
 * it. Same for the booking card and the bring-and-wear card below. */
.expect-visit__title {
	margin: 0;
}

.expect-visit__body {
	max-width: var(--measure-wide);
	color: var(--neutral-muted);
}

.expect-visit__body p:last-child {
	margin-bottom: 0;
}

.expect-steps {
	width: 100%;
	margin-block: var(--space-12);
}

/* The squiggle's own colour, read out of each bar image's pixels at a
 * point between two badges: prenatal #fdbcd2, pediatric #c8cdf7. Only
 * the phone's vertical connector uses it - above 768 the line is part
 * of the bar artwork - but it is declared here rather than in the media
 * query so the variant and its colour sit together. */
.expect-steps--prenatal  { --step-line: #fdbcd2; }
.expect-steps--pediatric { --step-line: #c8cdf7; }

/* !! EVERY PIECE OF LINE ART ON THIS PAGE IS OPAQUE ON WHITE, and with
 * a working background under it each one became a white box. Sampled,
 * not assumed: the step bars, the eight badges and the three amenity
 * drawings all report alpha 255 everywhere with pure white corners. On
 * Figma's white page that is invisible; on the parallax the two bars in
 * particular read as white stripes across the full width of the page.
 *
 * multiply, the same fix and the same reason as the intake headers and
 * the Prenatal hero backdrop: white multiplied by anything is that
 * thing, so the ground drops out and the drawing survives.
 *
 * !! ON THE BADGES IT ALSO TOUCHES THE GLYPH, and that is accepted
 * rather than overlooked. Each badge is a filled pink or periwinkle
 * circle with a WHITE glyph inside it, and white inside the image
 * becomes the backdrop too - so the glyph reads as the page's pale tint
 * instead of pure white. Against a circle that saturated the difference
 * is invisible, and the alternative is a white square around every
 * badge. Checked on screen at both breakpoints.
 *
 * THE FAMILY PHOTOGRAPH IS DELIBERATELY NOT IN THIS LIST. Its corners
 * sample white too, but that is a bright wall inside a real photograph
 * filling a rounded frame, not a ground to drop out. Multiplying it
 * would darken the whole picture by the backdrop. */
/* !! THE STEP ART IS NO LONGER IN THIS LIST, AND THAT IS THE FIX RATHER
 * THAN A REGRESSION. The two bars and the eight badges used to blend
 * here. Multiply hid their white squares on a Chrome desktop and left
 * them visible on Gerek's device - "the individual icons have white
 * backgrounds" - for two reasons worth keeping written down:
 *
 *   - it CANNOT clear an antialiased edge. Those pixels are ~90% white,
 *     so they multiply to ~90% of the backdrop and ring every circle
 *     with a pale halo, which on a light page reads as a white box.
 *   - it is a rendering feature. Anything that isolates the element -
 *     anywhere up the stack, on any engine - brings the square back.
 *
 * The ground is cut out of the files themselves now (the -cut variants,
 * see page-first-visit.php), so there is nothing left to blend and
 * blending would only darken the artwork against the page.
 *
 * The three amenity drawings stay. They are dark line art with no fill
 * and no antialiased edge against a shape, which is the case multiply
 * handles cleanly. */
.expect-amenity img {
	mix-blend-mode: multiply;
}

.expect-steps__bar {
	display: block;
	width: 100%;
	height: auto;
}

.expect-steps__labels {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	/* 24 / 1088. See the note above - a literal 24px breaks alignment at
	 * every width except the one the file was drawn at. */
	gap: 2.206%;
	margin: var(--space-8) 0 0;
	padding: 0;
	list-style: none;
	font-family: var(--font-display);
	font-weight: var(--weight-bold);
	font-size: var(--type-xs);   /* Figma 14 */
	color: var(--neutral-ink);
}

/* The per-step badge is the PHONE's copy of what the bar draws above
 * 768. Hidden here, where the bar is already showing it - the two are
 * never visible at once, and a display: none image with loading="lazy"
 * is not fetched until it is shown, so a desktop never pays for the
 * eight files and a phone never pays for the two bars. */
.expect-steps__badge {
	display: none;
}

/* ---- 28.3  Bringing Your Other Kids (645:232) ---- */
.expect-kids__photo {
	display: block;
	width: 420px;   /* Figma 944:2605 */
	max-width: 100%;
	height: auto;
	border-radius: var(--radius-sm);
}

.expect-kids__body {
	max-width: var(--measure-lead);   /* Figma draws this one at 546 */
}

/* Figma 941:2619 is 750 wide, three equal columns, gap 24. Each
 * illustration keeps its OWN drawn width at a shared 100px height
 * rather than being forced square - 121.3, 161.7 and 78.5 in the file,
 * and the three uploads are 244x199, 324x199 and 158x201, every one a
 * clean 2x. */
.expect-amenities {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);
	width: 100%;
	max-width: 750px;
	margin: var(--space-12) 0 0;
	padding: 0;
	list-style: none;
}

.expect-amenity {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-8);
	font-family: var(--font-display);
	font-weight: var(--weight-bold);
	font-size: var(--type-xs);
	color: var(--neutral-ink);
}

.expect-amenity img {
	display: block;
	width: auto;
	height: 100px;   /* Figma: the shared dimension is the height */
	max-width: 100%;
}

/* ---- 28.4  Booking Your First Visit (1235:2785) ----
 *
 * Figma: 854 wide, white, radius 24, Main Card Shadow, contents centred
 * at 570. .card card--raised carries the fill, radius and shadow, so
 * only the two widths and the 20px padding are stated. */
/* !! NO TOP MARGIN, AND THAT IS THE POINT RATHER THAN AN OMISSION.
 * Gerek: the portrait sits "zero pixels from the section below it,
 * which is what the design needs to match."
 *
 * Figma agrees to the pixel, and it takes arithmetic to see. The
 * headshot is absolutely placed at top 147 and is 365 tall, so its
 * bottom edge is at 512. Body Sections starts at y=68 with 120 of top
 * padding, so the Title block runs 188 to 432, and the 80px auto-layout
 * gap puts this card's top at 512 exactly. The portrait does not sit
 * ABOVE the card - it lands on it.
 *
 * Here the portrait is a real flex column rather than an absolute box,
 * so it is the tallest thing in the title row and therefore sets that
 * row's height. Removing this card's top margin is all it takes for the
 * two edges to meet. Every other section on the page keeps its 48. */
/* !! 1061 AND A 96px TOP MARGIN - both changed 2026-09-01 after Gerek
 * saw it beside the prototype. "The white box needs to be full width,
 * or maybe three-quarters width, but it's got to be much wider than it
 * is."
 *
 * Same mistake as the portrait: Figma's 854 was copied as a literal,
 * and 854 is 78.5% of the frame's 1088 inner width but only 63% of our
 * 1352 - so the card came out visibly narrower than the design against
 * everything around it. 854 x 1352/1088 = 1061 restores the share, and
 * lands on his "maybe three-quarters".
 *
 * The 96 is the "full spacing" between the hero copy and this card, and
 * it MUST equal the portrait's negative bottom margin above - that is
 * what puts her lower edge exactly on this card's upper edge. Change
 * one and the other has to move with it. */
.expect-booking {
	width: 100%;
	/* !! 1200 AND A REAL PADDING, 2026-09-01. Gerek: "it needs more space
	 * above and below... make the box a little bit wider and let the text
	 * run wider too, so that way there are fewer lines... I want it wider
	 * and flatter than taller."
	 *
	 * The width is chosen from the COPY rather than from the design.
	 * Measured, the three paragraphs need 823, 1097 and 955px to set on
	 * one line each. 1200 less 40 of inline padding either side leaves
	 * 1120, which clears the longest by 23px - so all three become single
	 * lines and the card loses three lines of height. That is the
	 * "flatter".
	 *
	 * Figma's own 854 is abandoned here rather than scaled. Its card is
	 * built around a 570px column of centred text six lines deep; this
	 * one is built around the same words on four lines, which is what was
	 * asked for and is not a shape the frame draws.
	 *
	 * Block padding 48 against the old 20 is the "more space above and
	 * below". Inline 40 rather than 48 is deliberate: it is what buys the
	 * last 16px of measure the longest line needs. */
	max-width: 1200px;
	margin-top: calc(var(--space-48) * 2);
	margin-inline: auto;
	padding: var(--space-48) var(--space-40);
}

.expect-booking__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-12);
	/* The card's full content box, 1200 less 40 either side. Stated
	 * rather than left to `none` so the number that has to clear the
	 * longest line - 1097px, measured - is written down next to the
	 * thing it was measured for. */
	max-width: 1120px;
	margin-inline: auto;
	text-align: center;
	color: var(--neutral-muted);
}

.expect-booking__title {
	margin: 0;
	color: var(--neutral-ink);
}

.expect-booking__inner p {
	margin: 0;
}

/* ---- 28.5  What to Bring and Wear (1235:2766) ----
 *
 * !! THIS ONE MESH GRADIENT IS REPRODUCIBLE WHERE THE PRENATAL PANEL'S
 * WAS NOT, and the difference is what kind of shader it is. Section 17
 * records that 682:128's fill is a four-point SHADER with no CSS
 * equivalent. This card's shader is a different animal: read from the
 * WGSL the Figma MCP returns, its fragment stage is
 *
 *     top = mix(colorA, colorB, uv.x)
 *     bot = mix(colorC, colorD, uv.x)
 *     col = mix(top, bot, uv.y)
 *
 * - a plain bilinear interpolation of four corners, which is exactly
 * two horizontal gradients cross-faded vertically. CSS does that
 * natively. The four corners, converted from the shader's float
 * parameters:
 *
 *     colorA  top-left      #85d0bc   = --brand-mint-deep
 *     colorB  top-right     #beeadc   = --brand-mint
 *     colorC  bottom-left   #b3e3f8   the footer's light blue
 *     colorD  bottom-right  #e6efff   a pale periwinkle
 *
 * The two untokenised corners appear once each, here. The base layer is
 * the bottom pair; the ::before is the top pair, masked to fade out
 * downwards - the same construction .prenatal-changing__panel uses for
 * its approximation, except this one is exact rather than fitted.
 *
 * opacity 0.6 is the shader's own, applied to the gradient layers
 * rather than to the card, so the text on top keeps full contrast. The
 * shadow is --shadow-offer-glow, which is Figma's 0 0 70 -30 #ffbed6 to
 * the digit. */
.expect-bring {
	position: relative;
	isolation: isolate;
	width: 100%;
	/* !! 540 -> 1200. Gerek: "the What to Bring In section with the
	 * colored box needs to go much wider than it is now, almost like
	 * double. It just looks really, really bad right now."
	 *
	 * It was Figma's literal 540, which is 49.6% of the frame's 1088 and
	 * only 40% of our 1352 - so it read as a small tinted box marooned in
	 * a wide page, directly under a card twice its size. It now matches
	 * the booking card exactly, so the page's two panels are one width
	 * rather than two arbitrary ones. 1200 less 48 either side leaves a
	 * 1104px measure, which is the site's 1100 reading ceiling to within
	 * four pixels. */
	max-width: 1200px;
	margin-top: calc(var(--space-48) * 2);
	margin-inline: auto;
	padding: var(--space-48);
	border-radius: var(--radius);
	box-shadow: var(--shadow-offer-glow);
	color: var(--neutral-muted);
}

.expect-bring::before,
.expect-bring::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	opacity: 0.6;
	pointer-events: none;
}

/* The bottom pair, full height, under everything. */
.expect-bring::after {
	background: linear-gradient(to right, #b3e3f8, #e6efff);
}

/* The top pair, faded out downward so the bottom pair shows through. */
.expect-bring::before {
	background: linear-gradient(to right, var(--brand-mint-deep), var(--brand-mint));
	-webkit-mask-image: linear-gradient(to bottom, #000, transparent);
	        mask-image: linear-gradient(to bottom, #000, transparent);
}

/* ::after must paint UNDER ::before, and source order alone would put it
 * on top. Both sit at z-index -1 inside the card's own stacking
 * context; this drops the base one further. */
.expect-bring::after {
	z-index: -2;
}

.expect-bring__title {
	margin: 0 0 var(--space-16);
	color: var(--neutral-ink);
}

.expect-bring p:last-child {
	margin-bottom: 0;
}

/* ---- 28.6  the closing block (645:238) ---- */
.expect-close {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-16);
	margin-top: calc(var(--space-48) * 2);
	text-align: center;
}

/* ---- 28.7  responsive ---- */

@media (max-width: 991px) {
	/* The headshot stops being a column. A 260px portrait beside a
	 * shrinking measure is what pushes the H1 into four lines. */
	.expect-head {
		flex-direction: column;
		align-items: center;
		gap: var(--space-32);
		text-align: center;
	}

	.expect-head__copy {
		flex: 1 1 auto;
	}

	/* !! STACKED, SHE STILL LANDS ON THE CARD. Gerek: "the part where
	 * Aimee's picture shows is not zero pixels from the white box below
	 * it. There's a gap."
	 *
	 * The first version treated the zero as a desktop-only trick - the
	 * negative margin came off here and the card took a 48px top margin
	 * back, so the phone got a gap the desktop does not have. It is not a
	 * trick, it is the relationship: her lower edge meets the card's
	 * upper edge. Stacked there is no row to hang out of, so the
	 * mechanism is simpler rather than absent - the negative margin goes,
	 * and the card's top margin goes with it. */
	.expect-head__photo {
		margin-bottom: 0;
	}

	.expect-booking {
		margin-top: 0;
	}
}

@media (max-width: 767px) {
	/* !! THE STEP LABELS STACK, AND THE BAR GOES WITH THEM. Four labels
	 * across a 342px card gives each one 78px - "First Adjustment" sets
	 * as three lines of one word. The row becomes a single column and
	 * the bar is hidden rather than shrunk: at 342 wide the badges draw
	 * at 20px and the squiggle disappears into a hairline, which is a
	 * decoration nobody can read taking 64px of a phone screen.
	 *
	 * It is aria-hidden already, so hiding it removes nothing from the
	 * accessible page - the four labels ARE the content and they stay. */
	.expect-steps__bar {
		display: none;
	}

	/* !! A VERTICAL CONNECTED STEPPER, REPLACING A 2x2 GRID OF LOOSE
	 * BADGES. Gerek: "there are no lines between the pink icons and the
	 * green icons... I'm not sure how to fix that or how you do that
	 * with tablet and mobile, but it's not working as it should be."
	 *
	 * He is pointing at something the desktop row has and the phone lost.
	 * The connector is not a separate asset - it is painted INTO the bar
	 * image along with the four badges - so hiding the bar at this width
	 * took the line with it, and there was nothing left to say these four
	 * things happen in order. Four badges in a 2x2 grid read as a menu,
	 * not as a sequence.
	 *
	 * One column, badge left and label right, with a short line dropping
	 * from each badge to the next. That is the standard vertical stepper
	 * and it says "in this order" without needing the artwork. Four rows
	 * at 88px is also shorter than a 2x2 grid of centred badges was.
	 *
	 * !! THE LINE IS DRAWN FROM THE BADGE'S CENTRE, not from a fixed
	 * offset: `top: calc(50% + 32px)` is the bottom of a 64px badge that
	 * the grid has vertically centred. If a label ever wraps to two lines
	 * the row grows, the badge stays centred, and the line still starts
	 * exactly where the badge ends. A hard-coded top would drift. */
	.expect-steps__labels {
		grid-template-columns: 1fr;
		gap: 0;
		text-align: left;
	}

	.expect-steps__labels li {
		position: relative;
		display: grid;
		grid-template-columns: 64px 1fr;
		align-items: center;
		gap: var(--space-16);
		padding-block: var(--space-12);
	}

	.expect-steps__labels li:not(:last-child)::after {
		content: "";
		position: absolute;
		left: 32px;                      /* the badge's own centre */
		top: calc(50% + 32px);           /* its lower edge */
		bottom: calc(-1 * var(--space-12));
		width: 2px;
		transform: translateX(-1px);
		border-radius: 1px;
		background: var(--step-line, #fdbcd2);
	}

	.expect-steps__badge {
		display: block;
		width: 64px;
		height: auto;
	}

	.expect-amenities {
		grid-template-columns: 1fr;
		gap: var(--space-32);
	}

	.expect-bring,
	.expect-booking {
		padding: var(--space-32) var(--space-24);
	}

	/* !! 240 ON A PHONE. "On mobile the image is actually too large. It
	 * needs to be reduced to be more balanced. On desktop only, it is
	 * fine." 323 is the right share of a 1352 row and 94% of a 342px
	 * one - a portrait nearly the full width of the screen with a card
	 * directly under it. 240 is 70%, which leaves her reading as an
	 * element on the page rather than as the page. Tablet keeps 323:
	 * at 786 that is 41%, which is not the complaint. */
	.expect-head__photo {
		width: 240px;
	}
}

/* ==================================================================
 * 29  MOMMA'S VILLAGE  (added 2026-08-21)
 *
 * Sitemap page 15, slug mommas-village, Utility, primary nav Tier 2
 * under Resources. Figma frame 632:126.
 *
 * A DIRECTORY - eleven independently owned referral partners in a card
 * grid, each with an outbound link. It is the first page on this site
 * that links off it, and the first that names other businesses. Read
 * LAUNCH-CHECKLIST.md B17 before changing any of its content: the
 * roster is unconfirmed research about real third parties.
 *
 * The card is close to .offer-card's neighbours but is not one of them:
 * it paints white where .offer-card is transparent, it holds a fixed
 * three-across grid rather than standing beside a form, and it ends in
 * a Mini button. It reuses .cta-button--mini (21.1) and .card's shape
 * language without inheriting .card itself, because .card carries no
 * padding by design and every one of these needs the same 24.
 * ================================================================== */

/* ---- 29.1  .village-grid and .village-card ----
 *
 * Figma lays eleven cards as four rows of 3 / 3 / 3 / 2 at gap 24. A
 * plain three-column grid produces exactly that from eleven items, and
 * the last row's two cards keep their column width instead of
 * stretching - which is what Figma draws (both are 347 wide, the same
 * as every other card, with the third column simply empty).
 *
 * !! min-height, NOT height, AND THAT IS A CONTENT DECISION. Figma
 * fixes the top row at 318 and the rest at 298, sized to its own
 * shortened card copy. The document's descriptions are longer - a full
 * two sentences each rather than a trimmed clause - and the document
 * wins on content, so the cards have to be able to grow. 298 is kept as
 * the floor so a short card still matches its neighbours; the taller
 * ones set the row.
 *
 * --shadow-card against Figma's 0 4px 30px -10px at 18% ink. The token
 * is the same geometry at 15%. Three percent of alpha does not earn a
 * fourth shadow token, and the ladder beats the near-miss - the same
 * call every off-token value on this build has taken.
 *
 * !! THE DOT-GRID TEXTURE IS BUILT NOW, AND THE TILE IS 256px - NOT
 * FIGMA'S REPORTED 50. It was recorded in B18 as unbuildable: the PNG
 * "is not in the media library" and redrawing it in CSS would mean
 * guessing. Neither held. assets/img/texture-dot-grid.png has been in
 * this theme since 2026-09-01 and is byte-identical to what Figma
 * exports for node 774:2552 (md5 1e81f8a9b2b2434dd6f82a2a73923cf3), so
 * nothing was guessed and nothing was re-exported.
 *
 * !! DO NOT COPY THE 50px FROM SECTION 32.1. Figma's dev output reports
 * bg-size-[50px_50px] for this fill, and section 32.1 took that number
 * at face value for the same tile on the Articles cards. Against a
 * 512x512 source whose dots sit 24px apart, 50px would put them 2.3px
 * apart - not a dot grid, a flat grey wash.
 *
 * MEASURED OFF FIGMA'S OWN RENDER instead of trusting the number: a
 * 1:1 export of card 632:257, read pixel by pixel, puts the dot centres
 * exactly 12.0px apart with a ~2px dot. 24px source spacing at 12px on
 * screen is a half-scale tile, so the tile is 512 / 2 = 256px. Figma's
 * fill is top-left anchored, which is background-position 0 0.
 *
 * The Articles page is very likely showing that flat wash today rather
 * than a dot grid. Not touched from here - it is section 32.1's rule
 * and its own page's review. Recorded in LAUNCH-CHECKLIST.md B19. */
.village-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);
	margin: calc(var(--space-48) * 2) 0 0;
	padding: 0;
	list-style: none;
}

.village-card {
	--village-card-min-h: 298px;   /* Figma's own floor (632:241 and its row) */

	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: var(--space-20);
	min-height: var(--village-card-min-h);
	min-width: 0;
	margin: 0;
	padding: var(--space-24);
	border-radius: var(--radius);
	box-shadow: var(--shadow-card);

	/* --village-card-texture is set on the <ul> in the template and
	 * inherits to all eleven. Shorthand `background` is deliberately not
	 * used: the tile has to sit ON the white, so the colour and the
	 * image are set as separate longhands. border-radius clips the tile
	 * to the card's corners on its own, which is Figma's overflow-clip. */
	background-color: var(--surface-white);
	background-image: var(--village-card-texture);
	background-size: 256px 256px;
	background-position: 0 0;
	background-repeat: repeat;
}

/* The logo, name and description travel together as the card's top
 * half, so space-between has two children to push apart rather than
 * four. gap 12 is Figma's own (node 789:8288). */
.village-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
}

.village-card__body > * {
	margin: 0;
}

/* !! THIS IS A REAL IMAGE NOW, NOT A DASHED PLACEHOLDER, AND ONLY FOUR
 * CARDS HAVE ONE. The rule used to draw an empty 72x72 dashed box on
 * all eleven cards, matched to .visit-step__icon's pending treatment,
 * because "none of the eleven is in the media library - checked by
 * looking". Four of them were: mommas-village-partner-logo-{slug}.webp,
 * uploaded 2026-08-22, each 144x144 for a 72 box.
 *
 * That makes this the THIRD placeholder set on this build to turn out
 * to be finished artwork nobody had wired up, after What to Expect's
 * fifteen files and the Articles headshot. The seven cards with no logo
 * are not pending - Figma draws them with no logo slot at all.
 *
 * No border and no radius: the four marks bring their own shape and
 * their own whitespace (Ashley Shapiro's is a soft watercolour wash
 * that a 14px corner would visibly slice). Figma draws the frame
 * square and unstyled, and that is now the right answer rather than
 * scaffolding.
 *
 * object-fit: contain guards the one thing a 1:1 box cannot: all four
 * sources are square today, and a future partner supplying a wide
 * wordmark would otherwise be squashed rather than letterboxed. */
.village-card__logo {
	--village-logo-size: 72px;   /* Figma's own box (node 632:226) */

	width: var(--village-logo-size);
	height: var(--village-logo-size);
	object-fit: contain;
	object-position: left center;
}

/* --type-ui (16) against Figma's 17. Off the ladder, nearest step, and
 * the gap is one pixel.
 *
 * !! --type-h4-long IS EXACTLY 17px AND IS DELIBERATELY NOT USED. Its
 * own note in section 01 says it "exists for exactly one string" - the
 * third pillar card's title - and it is named for a wrapping problem
 * rather than for a size. Pointing eleven card titles at it would turn
 * a documented one-off into a general heading step by the back door. If
 * 17 ever becomes a real level, it gets its own token and its own note.
 *
 * --type-h4 (22) was the other candidate, since these are card titles
 * and that is the card-title step. Five pixels up from Figma, on a
 * three-across card, wraps "South Coast Midwifery & Women's Health
 * Care" to a third line. Nearest step wins.
 *
 * The element rule would otherwise serve --type-h2 (34) because these
 * are <h2> - the document marks every partner [H2] and that level is
 * the document's call, not this rule's. Poppins SemiBold rather than
 * Baloo is Figma's own treatment here, the same face swap .prose h3
 * records. */
.village-card__name {
	color: var(--neutral-ink);
	font-family: var(--font-body);
	font-size: var(--type-ui);
	font-weight: var(--weight-semibold);
}

/* 13px is --type-xs exactly - Figma's own, muted against the ink name
 * above it. */
.village-card__desc {
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}

/* The Mini button sits at the card's bottom edge and must not stretch
 * to the card's full width the way a flex column child otherwise
 * would. align-self keeps it its own size, which is what Figma draws.
 *
 * !! THE LABEL HAS TO BE ALLOWED TO WRAP, AND FIGMA NEVER SOLVED THIS.
 * The button's label is the document's own "Visit <partner name>", and
 * the longest is "Visit South Coast Midwifery & Women's Health Care" -
 * 48 characters. .cta-button and .cta-button__label both carry
 * white-space: nowrap, which is right for every other button on the
 * site because every other button's label is a fixed short phrase.
 *
 * Here it is not: at 11px that label measures wider than the card's
 * 299px content box at EVERY width, including 1440. Figma has the same
 * problem and hides it - its card frames are overflow-clip, so the
 * button simply runs under the card edge and gets cut. Measured at 390
 * before this rule existed, the page scrolled sideways by 5px.
 *
 * So the two nowraps are lifted here and only here. The pill wraps to
 * two lines for the one or two longest partners and stays one line for
 * the rest. Shortening the label was the other option and is not
 * available - it is the document's copy, and the partner names are
 * exactly what this page exists to publish. */
.village-card .cta-button {
	align-self: flex-start;
	max-width: 100%;
	white-space: normal;
}

.village-card .cta-button__label {
	white-space: normal;
}

/* .village-grid__pending is GONE, and so is the markup that used it.
 * It read "Partner logos pending - the empty boxes above are
 * placeholders at the size the design calls for, not finished
 * artwork." The boxes are not empty and the artwork is not pending, so
 * the line was describing a state that no longer exists. Same deletion
 * .visit-steps__pending took on 2026-09-01, for the same reason. */


/* ---- 29.2  the page's two smaller pieces ----
 *
 * The lead sits in ink here where .prose paints muted, because Figma
 * draws it that way (node 632:221, #33302f) - it is the page's one-line
 * definition of what Momma's Village is, and it carries more weight
 * than the paragraph under it. One property, scoped to this page's
 * .prose, the same shape 19.1b and 28.1 use.
 *
 * !! THE CLOSING BLOCK TAKES 96, NOT 40, AND THAT WAS A REAL BUG. The
 * note here used to read "the Content Column's own 40 gap, the value
 * every major block boundary on this build pays". That is Figma's
 * internal frame gap, and it is not what this build pays between H2
 * sections - the house boundary is 96, built as .section's 48 from each
 * side, and every page that stacks blocks inside ONE .section instead
 * pays it as calc(var(--space-48) * 2). Products, Reviews and FAQ all
 * do exactly that.
 *
 * This page had 40 at both of its boundaries and 48 at the top, so all
 * three of its gaps were short. Measured in the browser against
 * /products/ rather than reasoned from the stylesheet, which is the
 * only way this class of bug shows up. See 29.4 for the top. */
.prose--village .prose__lead {
	color: var(--neutral-ink);
}

.village-closing {
	margin-top: calc(var(--space-48) * 2);
}

/* --type-h3 (26) against Figma's 24 for the closing heading, the same
 * snap .offer-card__cta and .happy-families__title take. Scoped to this
 * page's prose so the base .prose h2 is untouched. */
.prose--village h2 {
	font-size: var(--type-h3);
}


/* ---- 29.3  responsive ----
 *
 * TWO STEPS, AND THE FIRST IS EARLIER THAN THIS BUILD'S USUAL 991.
 * Three cards across a 992px container leaves each one about 300px
 * wide holding a two-sentence description and a button whose label is
 * the full business name - "Visit South Coast Midwifery & Women's
 * Health Care" is 46 characters. It stops fitting before the layout
 * stops being three columns, so the grid drops to two at 1199 rather
 * than waiting for 991.
 *
 * One column below 767, where two would leave each card near 170px.
 *
 * !! .village-card__name and .village-card__desc DO NOT STEP DOWN.
 * Both are flat body-level sizes by design - the responsive ladder in
 * 1.4b moves headings, and these are a heading only in the outline. */
@media (max-width: 1199px) {
	.village-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

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


/* ---- 29.4  the top of the page ----
 *
 * 96 where .section gives 48. The header contributes no padding, so the
 * first block on any page faces a one-sided boundary and silently comes
 * out half the house gap - the trap that had all three of this page's
 * gaps short until 2026-09-02.
 *
 * This is the FOURTH page carrying this identical one-line override,
 * after Reviews, FAQ and Products. It still wants to be one rule and is
 * still not made one, because .page-article is shared with eighteen
 * templates and several put a hero above it, where 96 would be wrong.
 *
 * !! THE HOOK IS ON <main>, NOT <body>. body_class() is left at its
 * default on this build and prints only wp-singular / page / page-id-N
 * - there is no per-page class on the body at all, so a rule written as
 * `.mommas-village .page-article` against the body would match nothing
 * and fail silently. .faq and .products put their class on <main> and
 * this page now does the same. */
.mommas-village .page-article {
	padding-top: calc(var(--space-48) * 2);
}


/* ==================================================================
 * 30  REVIEWS / TESTIMONIALS  (added 2026-08-21)
 *
 * Sitemap page 07, slug reviews, Utility, primary nav Tier 2 under
 * About. Figma frame 634:126.
 *
 * !! THE PAGE HAS NO REVIEWS ON IT. The copy document supplies none -
 * it marks the slot and says the content arrives as designer-made
 * graphics that do not exist yet - and Figma's six testimonial cards
 * are all labelled "(Fake) Verified Google Review" and were not built.
 * Read LAUNCH-CHECKLIST.md B19 before adding anything to this page.
 *
 * That is why there is no card CSS in this section. .testimonial-card
 * from section 09 was the obvious reuse and is deliberately unused:
 * there is nothing real to put in it. The review slot is .embed-slot
 * (20.1), the site's existing "pending third-party block" component,
 * which is meant to be deleted rather than restyled when content lands.
 *
 * So this section is two things: a rating badge, and a row of video
 * placeholders.
 *
 * !! TIE-BREAK RULE USED TWICE HERE, STATED ONCE. Figma draws the badge
 * at gap 14 and padding 10/20. Both 14 and 10 sit exactly halfway
 * between two steps on the approved scale, which the nearest-step rule
 * cannot settle. Ties round UP - gap 16, padding 12 - applied the same
 * way to both rather than argued twice.
 * ================================================================== */

/* ---- 30.1  .reviews-head and .reviews-badge ----
 *
 * The title row REUSES .page-head from 21.2 - a title beside a thing -
 * with one property changed. 21.2 aligns to flex-start because its
 * right-hand element is a tall illustration whose top should meet the
 * H1's; this row's right-hand element is a badge Figma centres against
 * the H1 instead (node 799:8360, items-center).
 *
 * .page-head's own 767 rule already turns the row into a column, and
 * that is inherited. What is NOT inherited is its .page-head__figure
 * centring, because the badge is not a __figure - so the badge's own
 * stacked alignment is set below. */
/* ~100px above the H1, Gerek's call - "it's too close to the top."
 *
 * The page's own top spacing is .section's 48. That reads fine when a
 * section follows another section, and thin when it is the FIRST thing
 * under a sticky-free header with nothing above it. 48 + 48 = 96, which
 * is the ask to within four pixels and stays on the approved scale
 * rather than minting a 100.
 *
 * On .reviews-head rather than on .page-head: this is about where THIS
 * page starts, not about the shared head component, and the other pages
 * that use .page-head have not been reviewed yet. */
.reviews-head {
	align-items: center;
	margin-top: var(--space-48);
}

/* 488 is Figma's own box width (node 799:8346), a design dimension
 * rather than a scale step, so it lives in a named property the way
 * .offer-card's 540 and .embed-slot's 391 do. max-width rather than
 * width: below about 1100 the row needs the badge to give ground to the
 * H1 rather than force the pair wider than the container.
 *
 * !! --shadow-card IS AN EXACT MATCH, and an earlier note here said it
 * was not. That note was written from the MCP code generator's output,
 * which reported "0px 4px 30px 0px" - spread 0. Reading the node's real
 * effect data gives DROP_SHADOW, offset (0,4), radius 30, spread -10,
 * colour rgba(0.2, 0.1882, 0.1843, 0.15) - which is rgba(51,48,47,0.15)
 * exactly, and identical to the token in every value. Corrected
 * 2026-08-21. The generator is not a reliable source for fills and
 * effects; the node data is.
 *
 * !! THE FILL IS A FIGMA "SHADER" PAINT AND CSS CANNOT REPRODUCE IT.
 * The code generator emitted no background at all for this node, so the
 * first cut of this rule painted --surface-white and the badge shipped
 * to staging as a flat white box. Reading node 799:8346's real fills
 * shows type: SHADER - a procedural paint - carrying four colours:
 *
 *     #ffbed6   (r 1, g 0.7464, b 0.8403)   - twice
 *     #e6efff   (r 0.902, g 0.937, b 1)
 *     #f7dce6   (r 0.9686, g 0.8627, b 0.9020)  = --brand-pink exactly
 *
 * There is no CSS equivalent of a shader paint, so what ships below is
 * an APPROXIMATION - and it is built from the shader's own declared
 * colours rather than from colours picked by eye.
 *
 * THE DIRECTION WAS MEASURED, NOT GUESSED, by sampling the design's own
 * rendered pixels. The corners come out:
 *
 *     top-left  #fcc5da        top-right #fac8df
 *     bottom-left #f7dae6      bottom-right #e9e9fa
 *
 * Pinkest at top-left, bluest at bottom-right, the two off-corners in
 * between - a diagonal blend. The top edge is nearly flat (#fcc5da ->
 * #fac8df across 450px) while the bottom edge ramps hard (#f7dae6 ->
 * #e9eafa), which is why a first reading of the top band alone looked
 * vertical and was wrong. "to bottom right" rather than a fixed angle,
 * so the endpoints land on the real corners at any box aspect.
 *
 * !! ONE-OFF, NOT TOKENS. #e6efff has exactly one user on this site.
 * #ffbed6 has two - here and inside --shadow-offer-glow (26.1) - so it
 * is a candidate for promotion under the same two-user test
 * --border-field and --radius-field passed. NOT done here: that means
 * editing a token already deployed on Checkout and Booking, which is a
 * refactor rather than this fix. Recorded so it is a decision somebody
 * makes rather than a coincidence nobody spots.
 *
 * GAP AND PADDING ARE SNAPPED, and both were ties. Figma measures gap
 * 14 and padding 10/20. 14 sits exactly between --space-12 and
 * --space-16, and 10 exactly between --space-8 and --space-12; the
 * nearest-step rule cannot settle either. Ties round up, the same rule
 * stated once at the top of this section and applied to both. The 20 is
 * exact. */
.reviews-badge {
	--reviews-badge-w: 488px;        /* Figma 799:8346 */
	--reviews-badge-from: #ffbed6;   /* shader colour 1 - see the note above */
	--reviews-badge-to:   #e6efff;   /* shader colour 2 */

	display: flex;
	flex: 0 1 var(--reviews-badge-w);
	align-items: center;
	justify-content: center;
	gap: var(--space-16);
	max-width: var(--reviews-badge-w);
	margin: 0;
	padding: var(--space-12) var(--space-20);
	border-radius: var(--radius);
	background-image: linear-gradient(to bottom right, var(--reviews-badge-from), var(--reviews-badge-to));
	box-shadow: var(--shadow-card);
}

/* 44x44 is Figma's own. A real asset - icon-star.webp - not a
 * placeholder. Decorative: the caption beside it says "5.0 stars" in
 * words, so the markup carries alt="". */
.reviews-badge__star {
	display: block;
	flex: none;
	width: 44px;    /* Figma 799:8347 */
	height: 44px;
}

/* !! --type-h1, AND THE TIE WAS BROKEN BY FIGMA'S OWN RELATIONSHIP.
 * Figma draws "285+" at 40px - exactly the size it draws the H1 beside
 * it - and 40 sits equidistant between --type-h1 (46) and --type-h2
 * (34). Nearest-step cannot choose. What decides it is that the design
 * makes these two the same size on purpose: the headline and the number
 * carry the row together. Matching tokens keeps that; picking 34 would
 * quietly demote the number.
 *
 * Section 01 describes --type-h3 as covering "stat figures", which is
 * what this is - but at 26 against a 46 H1 it would read as a caption,
 * not a headline figure, and Figma's intent here is unambiguous.
 *
 * Baloo Bold from the heading face, which is what Figma draws. It is a
 * <span> inside a <p>, not a heading - it is a number, not a section
 * title, and it must not enter the outline. */
.reviews-badge__figure {
	color: var(--neutral-ink);
	font-family: var(--font-heading);
	font-size: var(--type-h1);
	font-weight: var(--weight-bold);
	line-height: 1;
}

/* 14px is --type-small exactly - Figma's own. inline-flex so the Google
 * mark sits on the text baseline row between "across" and "reviews."
 * rather than breaking the sentence in two. */
.reviews-badge__caption {
	display: inline-flex;
	align-items: center;
	gap: var(--space-8);
	color: var(--neutral-muted);
	font-size: var(--type-small);
}

/* 28x28 is Figma's own (node 799:8352), and google-g-wiki.webp is the
 * real asset. NOT decorative - it identifies whose reviews these are,
 * which is the entire claim the badge makes, so it carries alt text. */
.reviews-badge__google {
	display: block;
	flex: none;
	width: 28px;    /* Figma 799:8352 */
	height: 28px;
}


/* ---- 30.2  .video-row - three stills that do not exist ----
 *
 * !! ALL THREE BOXES ARE EMPTY. Figma draws frames from real Momma's
 * Chiro YouTube videos (nodes 634:1495 / 1497 / 1499) and none of them
 * is in the media library - checked by looking. The play button and
 * play icon Figma layers over each still are not built either: there is
 * nothing under them to play, and a play control that plays nothing is
 * the dead-control failure this build has refused throughout.
 *
 * The document asks for one thing here - "[YOUTUBE CHANNEL EMBED OR
 * LINK]" - and Figma answers it with three tiles. Both are honoured:
 * the tiles at Figma's exact 182 height, and a real link to the channel
 * underneath. An embed would need a video id and no source gives one.
 *
 * Same dashed treatment as .village-card__logo (29.1). Two pages now
 * ship empty boxes waiting on artwork and they should look like one
 * kind of pending. (.visit-step__icon was the third until section 28
 * was rebuilt on 2026-09-01 - see the note there.)
 *
 * gap 20 is Figma's own and on the scale. --radius (24) is Figma's own
 * exactly. */
.video-row {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-20);
	margin: var(--space-40) 0 0;
	padding: 0;
	list-style: none;
}

.video-row__item {
	margin: 0;
	min-width: 0;
}

/* .video-row__box - the dashed empty placeholder - is DELETED. Three
 * real videos replaced it on 2026-09-01, along with its pending note. */

/* ---- the video facade ----
 *
 * A real <button> wrapping a self-hosted thumbnail. The YouTube iframe
 * does not exist until it is clicked - see the note in page-reviews.php
 * for why that is the choice rather than three live embeds.
 *
 * aspect-ratio rather than Figma's fixed 182px height: the thumbnails
 * are 16:9 and a fixed height would either letterbox them or crop them.
 * At the tile's ~430px width 16:9 gives ~242px, close enough to Figma's
 * 182 that the row still reads the same, and correct at every width
 * instead of one. */
.video-card {
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	padding: 0;
	overflow: hidden;
	border: 0;
	border-radius: var(--radius);
	background: var(--neutral-ink);
	cursor: pointer;
	/* A little lift - Gerek's call, amount left to me. Figma's published
	 * Main Card Shadow, the same token every card on the site uses
	 * rather than a bespoke value, so the tiles sit on the page the way
	 * the cards around them do. It deepens on hover, which is the only
	 * cue that the whole tile is clickable. */
	box-shadow: var(--shadow-card);
	transition: box-shadow .2s ease, transform .2s ease;
}
.video-card:hover,
.video-card:focus-visible {
	box-shadow: 0 10px 34px -12px rgba(51, 48, 47, 0.32);
	transform: translateY(-2px);
}

.video-card__thumb {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .3s ease, opacity .2s ease;
}
.video-card:hover .video-card__thumb { transform: scale(1.04); }

/* YouTube's own play-button geometry, so the control reads as "this is a
 * YouTube video" without a logo or a second asset. */
.video-card__play {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}
.video-card__play-bg {
	fill: var(--neutral-ink);
	fill-opacity: .78;
	transition: fill .2s ease, fill-opacity .2s ease;
}
.video-card:hover .video-card__play-bg,
.video-card:focus-visible .video-card__play-bg {
	fill: #f00;              /* YouTube red, and ONLY on this control */
	fill-opacity: 1;
}

/* The iframe the script swaps in. Same box as the button it replaces, so
 * the row does not move when a video starts. */
.video-card__frame {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	border: 0;
	border-radius: var(--radius);
	overflow: hidden;
	box-shadow: var(--shadow-card);   /* matches the tile it replaces */
}

.video-row__title {
	margin: var(--space-12) 0 0;
	font-size: var(--type-small);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-body);
	color: var(--neutral-ink);
}

/* !! SCAFFOLDING, NOT PAGE COPY - same register as .embed-slot__label
 * and .village-grid__pending.
 *
 * !! NAMED GENERICALLY BECAUSE THIS PAGE USES IT TWICE, for two
 * unrelated pending things: the video stills and the placeholder
 * reviews. A .video-row__pending on a line about testimonials would be
 * the same misnaming .checkout-form__privacy had before it became
 * .form-privacy in 797cd95.
 *
 * .village-grid__pending (29.1) carries byte-identical declarations
 * and is a candidate to fold into this name. NOT done here - that is
 * another template to touch. Recorded so the next person sees two names
 * for one rule and knows it was noticed. (.visit-steps__pending was the
 * third until section 28 was rebuilt on 2026-09-01.) */
/* Capped 2026-09-01. It had no max-width, so it ran the full 1352px
 * container at about 123 characters a line - the widest text left on the
 * site after .prose was capped. It is placeholder copy, which is exactly
 * the kind of thing that never gets looked at. */
.pending-note {
	max-width: var(--measure-wide);
	margin: var(--space-12) 0 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}


/* ---- 30.2b  the placeholder review grid's spacing ----
 *
 * .testimonial-row (27.4) and .testimonial-card (09) are reused whole
 * for this page's six cards - six items fill the three-column grid as
 * two rows, which is exactly what Figma draws, so the grid itself
 * needed no new CSS at all. What it did need is the two boundaries
 * around it, because .testimonial-row carries margin: 0 by design and
 * .testimonial-disclaimer only sets colour and size.
 *
 * 40 above is the Content Column's own block gap. 12 below the grid is
 * Figma's own within-section gap, and the same step .pending-note
 * uses, so the disclaimer and the pending line sit on one rhythm. */
.reviews-grid {
	margin-top: var(--space-40);
}

.reviews-grid + .testimonial-disclaimer {
	margin-top: var(--space-12);
}


/* ---- 30.3  the page's two smaller pieces ----
 *
 * !! THE --type-h3 SNAP IS GONE, 2026-09-01. It set both of this page's
 * section headings to 30px "against Figma's 28", which left them two
 * steps under every other H2 on the site - About's are 46. Gerek caught
 * it: "not the same as the About page. That's a miss right there."
 *
 * It was a defensible reading of one frame and it loses to consistency.
 * Figma's 28 was drawn against Figma's 15px body; the type scale has
 * moved twice since, and a per-page snap to an artboard number is
 * exactly the kind of local decision that drifts. The base h2 element
 * rule applies now, so these render at --type-h2 like everything else.
 *
 * !! .prose--village h2 CARRIES THE SAME SNAP and is deliberately NOT
 * changed here - Momma's Village has not been through the page pass yet.
 * Flagged; it will read as the same miss when we get to it.
 *
 * ---- SPACING, 2026-09-01 ----
 *
 * Every major boundary on this page pays 96px, which is what the home
 * page and About pay between sections. Gerek: "match what the home page
 * is and the about page is."
 *
 * Those two get their 96 from adjacent .section elements, each with
 * padding-block: 48. This page is a flat run of blocks inside one
 * container with no .section wrappers, so there is no pair of paddings
 * to add up - the value has to be stated. calc(48 * 2) rather than a
 * literal 96, so it stays tied to the scale step it is derived from and
 * moves if that ever moves.
 *
 * Measured before: 32px from the Direct Answer to "What Patients Are
 * Saying", 48 from the Google link to "More From Momma's Chiro", and 48
 * from the YouTube line to the closing card. All three read as tight
 * against the rest of the site, and all three were section boundaries
 * being paid at less than section rates. */
.prose--reviews h2 {
	margin-top: calc(var(--space-48) * 2);
}

/* 40 -> 48. Gerek: "more separation after the Hero section and the first
 * H2... and after that section." 48 is what every section boundary on
 * the site pays since .section went to padding-block. */
/* 96, the same boundary value. .prose > :first-child zeroes the H2's
 * own top margin when the heading opens a block, so for those blocks the
 * separation has to come from the block rather than the heading. */
.reviews-more {
	margin-top: calc(var(--space-48) * 2);
}

/* !! THE EXCEPTION TO THE 96. The channel link directly under the video
 * row is PART OF THAT SECTION, not the start of a new one - Gerek:
 * "there should be very little spacing between the three videos and the
 * 'Visit the Momma's Chiro YouTube channel' link, because it's part of
 * the section above."
 *
 * He is right, and 96 there was the boundary rule applied without asking
 * what the boundary was. 24px reads as "this belongs to the thing above
 * it", which is what it is. The 96 still lands where a real section
 * starts - the closing card below keeps it.
 *
 * An adjacent-sibling selector rather than a modifier class in the
 * markup: "the block immediately after the video row" IS the condition,
 * so the CSS can say exactly that and no template has to remember. */
.video-row + .reviews-more {
	margin-top: var(--space-24);
}


/* ---- 30.4  responsive ----
 *
 * The badge stacks under the H1 at 767, which .page-head's own media
 * query already does for the row; what is added here is the badge's
 * behaviour once stacked - full width rather than a 488px box floating
 * left under a wrapped headline.
 *
 * The video row goes straight to ONE column rather than two. Three
 * 182px-tall boxes at two across would be about 170px wide on a phone,
 * which is narrower than they are tall by a wide margin and stops
 * reading as video at all. */
@media (max-width: 767px) {
	/* !! THE CAPTION WAS RUNNING OUT OF THE BADGE, and only here.
	 *
	 * Measured at 390: the badge is 342 wide with 20px of padding, so
	 * 302 of content box. The row needs star 44 + gap 16 + "285+" 77 +
	 * gap 16 + caption 168 = 321. The caption ended at x=356 against a
	 * padding edge of 346 - ten pixels outside its own pink box, which
	 * is the "Google reviews is outside the scope" Gerek saw.
	 *
	 * Nothing could give: .reviews-badge is flex-wrap: nowrap, and
	 * .reviews-badge__caption is an inline-flex whose three items - the
	 * words, the Google G and "reviews." - cannot wrap either. A flex
	 * item will not shrink below its min-content width, so the overflow
	 * had nowhere to go.
	 *
	 * The caption takes a full row of its own instead of trying to fit
	 * beside the figure. "285+" with its star reads as one unit on line
	 * one and the sentence sits under it, which is also the more legible
	 * arrangement on a phone. Desktop and tablet are untouched - Gerek
	 * confirmed both are correct. */
	.reviews-badge {
		flex: none;
		flex-wrap: wrap;
		justify-content: center;
		width: 100%;
		max-width: none;
	}

	.reviews-badge__caption {
		flex-basis: 100%;
		justify-content: center;
		min-width: 0;
		text-align: center;
	}

	.video-row {
		grid-template-columns: 1fr;
	}
}

.custom-margin-top-40 {
	margin-top: 40px;
}


/* ==================================================================
 * 31  PRODUCTS (RETAIL)  (added 2026-08-21)
 *
 * Sitemap page 05, slug products, Branch. Figma frame 627:126,
 * "(unverified) Products (Retail)". Copy document
 * "3.6 Products (Retail) - Momma's Chiro".
 *
 * Six product cards, a five-across credential row, two panels.
 *
 * !! NO CART, NO PRICES, NO STOCK. The document carries no price, no
 * availability and no purchase mechanism, and neither does Figma. Every
 * "Shop X" control is a plain outbound <a> to the vendor's own site.
 * Nothing here posts anywhere. See page-products.php.
 *
 * ---- values that are NOT on the approved scales, and what they became
 *
 * Spacing, snapped to the nearest step of 8/12/16/20/24/32/40/48, ties
 * up (the rule this file has used since section 12):
 *
 *   Figma 10 (promo pill gap)        -> 12   tie, rounds up
 *   Figma 30 (closing panel pad)     -> 32   nearest
 *   Figma 56 (intro panel side pad)  -> 48   nearest, and 48 is the
 *                                            ceiling the scale allows
 *
 * Type, snapped to the locked ladder:
 *
 *   Figma 40 (H1)      -> --type-h1 (46). The ladder's page-H1 step.
 *                         Not a snap so much as a refusal to let one
 *                         page's H1 differ from every other page's.
 *   Figma 24 + 28 (H2) -> --type-h3 (26). Figma draws this page's two
 *                         section H2s at two different sizes; 26 sits
 *                         between them and is the step both round to.
 *   Figma 22 (H3, and the closing panel title) -> --type-h4 (22) exact
 *   Figma 17 (product card title) -> --type-ui (16)
 *
 * !! THE 17px DECISION IS THE SAME ONE SECTION 29 MADE, and it is made
 * the same way. --type-h4-long IS exactly 17px and is again DELIBERATELY
 * NOT USED: its note in section 01 says it "exists for exactly one
 * string" and it is named for a wrapping problem, not for a size.
 * Pointing six more card titles at it would finish converting a
 * documented one-off into a general heading step by the back door -
 * .village-card__name declined to do that for eleven, and this declines
 * for six. --type-ui (16) is one pixel away. --type-h4 (22) is five.
 *
 * ---- colour
 *
 * The three card tints are the design's own brand colours and two of
 * them come BACK from the dead. --brand-mint-deep (#85d0bc) and
 * --accent-periwinkle (#c8ceff) were deleted on 2026-08-20 for having
 * zero references, and that note said the deletion should be reversed by
 * "the next page to want a mint panel". This is that page: Figma tints
 * cards 3 and 6 mint and cards 2 and 5 periwinkle. They are re-added to
 * the central list in section 01, not typed in here.
 *
 * Figma's pink stop is rgb(248,220,230); --brand-pink is #f7dce6. One
 * step of red, below any threshold worth a second token.
 *
 * Figma's closing panel is #f0f0f0. --surface-grey (#f5f5f5) is the
 * nearest surface on the list. Same call as every other near-miss here.
 *
 * ---- shadow
 *
 * !! CORRECTED 2026-08-21 AFTER RE-READING THE NODES' OWN EFFECT DATA.
 * An earlier version of this note claimed Figma draws the product cards
 * with "0 4px 15px rgba(51,48,47,0.18) - tighter, darker, no spread".
 * THAT WAS WRONG, and it was never in the file; it was written from a
 * structure read rather than from the nodes. Re-read through the Plugin
 * API with the file key finally to hand, all six cards - 627:1278,
 * 1284, 1290, 1297, 1303, 1309 - carry one identical drop shadow:
 *
 *   offset 0,4   radius 30   spread -10   rgba(51,48,47, 0.18)
 *
 * --shadow-card is  0 4px 30px -10px rgba(51,48,47,0.15).
 *
 * So the geometry is IDENTICAL and the ONLY difference is the alpha:
 * 0.18 against 0.15. The five credential cards were re-read in the same
 * pass and carry --shadow-card to the digit - 0,4 / 30 / -10 / 0.15 -
 * which the earlier note got right.
 *
 * --shadow-card is what ships on both, and the true numbers make that a
 * better call than the false ones did: three hundredths of alpha on one
 * page's cards is not a distinction anybody chose. One shadow,
 * everywhere. This is a note correction only - no declaration changed.
 *
 * ---- what Figma draws here and this file does not
 *
 * !! A DOT-GRID TEXTURE overlays the top 440px of all six cards
 * (nodes 771:2488 / 2499 / 2505 / 2516 / 2527 / 2533). It is a raster
 * asset that lives only in the design file and is not in the media
 * library. It is not rebuilt as a CSS pattern, because a hand-rolled
 * approximation of an artwork nobody has exported is a guess wearing the
 * artwork's name. The cards ship on their gradient alone.
 * LAUNCH-CHECKLIST.md B21.
 * ================================================================== */

/* ---- 31.1  the six product cards ---- */

/* !! THE WHOLE PAGE WAS RUNNING AT HALF THE SITE'S RHYTHM, and this was
 * found by sweeping the boundaries before handing the page over rather
 * than by Gerek reporting it for the seventh time.
 *
 * MEASURED at 1440, top to bottom: 48 above the H1, then 40 / 40 / 48 /
 * 40 / 40 between every block. The site's boundary is 96 - .section's
 * padding-block on each side of a join - and NOTHING on this page is a
 * .section except the <article> wrapper, so none of those joins had two
 * contributors and none of them got near it.
 *
 * All of them go to 96, written as 48 x 2 so they stay tied to the step
 * the rest of the site derives it from. The page gets airier, which is
 * the point: every block here is a card or a grid with its own heading,
 * and 40px between two cards reads as one thing that wrapped rather
 * than two things in sequence.
 *
 * !! THE H3 DISCLAIMER PANEL GOES WITH THEM, and that was the one call
 * to make rather than copy. "These Products Aren't a Substitute for
 * Care" is an H3, so it is subordinate by level - but it is its own
 * <section> with its own card, sitting between two H2 sections. Left at
 * 40 it would have read as belonging to the proof block above it, which
 * it does not: it qualifies the products. Uniform 96.
 *
 * !! THE TOP OF THE PAGE IS SCOPED, LIKE FAQ'S. .products .page-article
 * below takes 96 where .section gives it 48 - the header contributes
 * nothing, so the first block always faces a one-sided boundary. That
 * is now the THIRD page carrying this same one-line override (Reviews,
 * FAQ, Products). It wants to be one rule; it is not made one yet
 * because .page-article is shared with eighteen templates and several
 * of them put a hero above it, where 96 would be wrong. Consolidate
 * once the last pages are reviewed. */
.products-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);
	margin: calc(var(--space-48) * 2) 0 0;
	padding: 0;
	list-style: none;
}

/* The tint and the angle are the only things that vary between the six.
 * Both are set by the modifiers below, so the gradient itself is written
 * once. Figma's own stop percentages drift by a few tenths from card to
 * card - handles dragged by eye, not a decision - so each tint takes one
 * pair. */
.product-card {
	--product-tint:   var(--brand-pink);
	--product-angle:  205.75deg;
	--product-stop-a: 47%;
	--product-stop-b: 88.5%;
	--product-card-min-h: 553px;   /* Figma's shorter row; the taller row
	                                * is 563 and the grid equalises both */

	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	min-height: var(--product-card-min-h);
	min-width: 0;
	margin: 0;
	border-radius: var(--radius);
	box-shadow: var(--shadow-card);

	/* !! THE DOT TEXTURE IS BUILT NOW - B21, and it lands here on the
	 * same change that built it on Momma's Village, which is exactly
	 * what B21 asked for: "both pages should take it in one change
	 * rather than one page growing a texture the other lacks."
	 *
	 * B21 recorded it as "a raster asset that exists only in the design
	 * file and is not in the media library", so approximating it would
	 * be "a guess wearing the artwork's name". Right instinct, wrong
	 * premise - it is not in the media library and never needed to be.
	 * assets/img/texture-dot-grid.png is Figma's own tile, byte for
	 * byte, and has been in this theme since 2026-09-01.
	 *
	 * TWO LAYERS, TEXTURE OVER GRADIENT, and layer order is why the
	 * shorthand cannot be used: the first background-image paints on
	 * top. 256px is the measured tile - see section 32.1 for how that
	 * number was arrived at and why Figma's reported 50 is wrong.
	 *
	 * !! FIGMA STOPS THE TEXTURE AT 440px AND THIS DOES NOT. Node
	 * 771:2488 is 347x440 against a card that is 553 tall, so the
	 * texture covers the card's top four fifths and stops. This build
	 * already made that same call on the gradient underneath it, which
	 * Figma also draws at 440 and which has run the full card height
	 * since the page shipped. A texture that stopped where the gradient
	 * did not would draw a visible horizontal edge across every card.
	 * Both run full height, together. */
	background-image:
		var(--product-card-texture),
		linear-gradient(
			var(--product-angle),
			var(--product-tint) var(--product-stop-a),
			rgba(255, 255, 255, 0.7) var(--product-stop-b)
		);
	background-size: 256px 256px, auto;
	background-position: 0 0, 0 0;
	background-repeat: repeat, no-repeat;
}

.product-card--periwinkle {
	--product-tint: var(--accent-periwinkle);
}

/* Figma turns the mint cards' gradient almost vertical and pushes the
 * far stop to the bottom edge. Not a variation worth flattening - it is
 * why the mint cards read as paler than the other four. */
.product-card--mint {
	--product-tint:   var(--brand-mint-deep);
	--product-angle:  180.4deg;
	--product-stop-a: 46.4%;
	--product-stop-b: 99.78%;
}

.products .page-article {
	padding-top: calc(var(--space-48) * 2);
}


/* ---- 31.2  the card's image slot ----
 *
 * !! ALL SIX ARE PLACEHOLDERS. None of the six products is photographed
 * in the media library - checked by opening every photo in it, not by
 * reading filenames. What is in there is Momma's Chiro's own
 * photography: the office, the treatment room, Dr. Aimee, and adjustment
 * photos. No product shots at all. LAUNCH-CHECKLIST.md B20.
 *
 * The box is Figma's exact 260px, full card width, square-cornered at
 * the bottom because the card continues underneath it. */
.product-card__media {
	--product-media-h: 260px;   /* Figma 627:1279 and its five siblings */

	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-8);
	height: var(--product-media-h);
	padding: var(--space-16);
	border: 2px dashed var(--neutral-muted);
	border-radius: var(--radius) var(--radius) 0 0;
	background: var(--surface-white);
	text-align: center;
}

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

/* When a real photograph lands, the slot stops being a slot. Adding the
 * image and this modifier is the whole change. */
.product-card__media--filled {
	display: block;
	padding: 0;
	border: 0;
}

.product-card__slot-label {
	color: var(--neutral-muted);
	font-size: var(--type-eyebrow);
	font-weight: var(--weight-semibold);
	text-transform: uppercase;
	letter-spacing: 0.08em;
}

.product-card__slot-note {
	margin: 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}

/* ---- 31.3  the card's text ---- */

.product-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	justify-content: space-between;
	gap: var(--space-32);
	padding: 0 var(--space-24) var(--space-24);
}

.product-card__name {
	margin: 0 0 var(--space-8);
	color: var(--neutral-ink);
	font-family: var(--font-body);   /* Poppins SemiBold is Figma's own
	                                  * treatment here, the same face swap
	                                  * .prose h3 and .village-card__name
	                                  * both record */
	font-size: var(--type-ui);       /* see the 17px note at the top */
	font-weight: var(--weight-semibold);
}

.product-card__desc {
	margin: 0;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
}

.product-card__actions {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-8);
}

/* ---- 31.4  the promo code pill ----
 *
 * Figma draws a copy icon inside this. It is built as a real button that
 * really copies, because the alternative is a control that looks
 * pressable and is not - the same reason the Reviews page declined to
 * draw a play button over a still that plays nothing. It writes to the
 * clipboard and touches nothing else; there is no endpoint behind it. */
.promo-code {
	display: inline-flex;
	align-items: center;
	gap: var(--space-12);   /* Figma 10, tie, rounds up */
	padding: var(--space-8) var(--space-16);
	border: 1.5px solid var(--brand-coral-deep);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--neutral-ink);
	font-family: var(--font-body);
	font-size: var(--type-xs);
	font-weight: var(--weight-semibold);
	line-height: 1.2;
	cursor: pointer;
}

.promo-code__icon {
	width: var(--space-16);
	height: var(--space-16);
	flex-shrink: 0;
}

.promo-code__icon svg {
	display: block;
	width: 100%;
	height: 100%;
}

/* Feedback for the copy, swapped in by main.js and announced politely.
 * Sized and coloured here so the script never sets a style. */
.promo-code__status {
	color: var(--brand-coral-deep);
	font-size: var(--type-xs);
}

/* ---- 31.5  the two panels ---- */

.product-panel {
	margin: calc(var(--space-48) * 2) 0 0;
	padding: var(--space-40) var(--space-48);   /* Figma 40 / 56 */
	border-radius: var(--radius);
	background: var(--surface-white);
}

.product-panel--closing {
	padding: var(--space-32);          /* Figma 30 */
	background: var(--surface-grey);   /* Figma #f0f0f0 */
}

.product-panel__title {
	margin: 0 0 var(--space-20);
	color: var(--neutral-ink);
	font-size: var(--type-h3);
}

.product-panel--closing .product-panel__title {
	margin-bottom: var(--space-16);
	font-size: var(--type-h4);
}

.product-panel p {
	margin: 0 0 var(--space-12);
	color: var(--neutral-muted);
	font-size: var(--type-body);
}

.product-panel p:last-child {
	margin-bottom: 0;
}

/* ---- 31.5b  the Proof section's heading ----
 *
 * !! NOT .subhead, and that is a deliberate refusal of the obvious
 * reuse. .subhead is --type-h4 (22). Figma draws this H2 at 28, and the
 * type note at the top of this section commits BOTH of this page's
 * section H2s to --type-h3 (26) - "Why Dr. Aimee Is Selective" at
 * Figma's 24 and this one at Figma's 28, one step between them. Using
 * .subhead here would have made the page's two H2s different sizes for
 * no reason anybody chose, which is the exact failure the locked ladder
 * exists to prevent. Same size as .product-panel__title; different class
 * because this heading is not in a panel and does not want the panel's
 * padding context. */
.products-proof {
	margin-top: calc(var(--space-48) * 2);
}

.products-proof__title {
	margin: 0;
	color: var(--neutral-ink);
	font-size: var(--type-h3);
}

/* ---- 31.6  the credential row ----
 *
 * !! ALL FIVE LOGOS ARE REAL and all five were matched by opening them
 * beside the Figma renders: media 89 icpa-logo, 90 scuhs-logo, 87
 * dca-shield-logo, 88 huntington-beach-seal, 54 google-logo. Each is
 * exactly twice its Figma box, which is what a retina export looks like.
 * This is the first section on this page with nothing pending in it. */
.proof-grid {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: var(--space-16);
	margin: var(--space-24) 0 0;
	padding: 0;
	list-style: none;
}

.proof-card {
	--proof-card-min-h: 199px;   /* Figma 772:2514 and its row */

	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: var(--space-16);
	min-height: var(--proof-card-min-h);
	min-width: 0;
	margin: 0;
	padding: var(--space-20);
	border-radius: var(--radius);
	background: var(--surface-white);
	box-shadow: var(--shadow-card);
}

/* Figma gives each logo its own box and object-fits inside it, so the
 * five different aspect ratios all sit on one baseline. */
.proof-card__logo {
	--proof-logo-h: 40px;   /* the tallest of the five; Figma's own */

	display: block;
	width: auto;
	max-width: 100%;
	height: var(--proof-logo-h);
	object-fit: contain;
	object-position: left center;
}

.proof-card__text {
	margin: 0;
	color: var(--neutral-ink);
	font-size: var(--type-small);
}

/* ---- 31.7  the pending line under the product grid ----
 *
 * Reuses .pending-note from section 30 rather than adding a class that
 * would do the same thing under a different name. Nothing added here. */

/* ---- 31.8  narrower screens ----
 *
 * Two grids, and they do not step at the same widths. The product cards
 * carry a photograph and two controls, so they need real width and go
 * three -> two -> one. The credential cards carry a logo and one line,
 * so five across only has to survive to 1200, where it thirds. */

@media (max-width: 1200px) {
	.proof-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (max-width: 992px) {
	.products-grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.product-panel {
		padding: var(--space-32);
	}
}

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

	.proof-grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.product-card {
		min-height: 0;   /* one across, nothing left to equalise against */
	}
}

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

	.proof-card {
		min-height: 0;
	}

	.product-panel,
	.product-panel--closing {
		padding: var(--space-24);
	}
}


/* ==================================================================
 * 32  ARTICLES  (added 2026-08-21)
 *
 * The blog index at /articles/, built as an EMPTY INDEX SHELL: the
 * sitemap, the copy document and Figma 624:126 all three agree there
 * are no posts this phase, and there are none on the server either
 * (zero of any status, checked). Nothing here styles a post, an
 * excerpt or a pagination control, because none is rendered.
 *
 * !! ALMOST NOTHING NEW. The page is built out of components that were
 * already here - .pillar-card and its illustration/title/text parts
 * from section 11, .cta-button, .card's radius and shadow tokens,
 * .prose__lead, .section, .page-bg and .embed-slot. This section holds
 * only the deltas between the homepage's pillar row and the one Figma
 * draws here, plus the CTA row's two-column shape.
 *
 * The three cards ARE the homepage's three care pillars - same order,
 * same illustrations, same dot-grid card textures. Verified rather than
 * assumed: Figma's illustration boxes are 99.96/85.259/96.6 x 35.7, and
 * the live media-library files are 238/203/230 x 85. Every one of those
 * three aspect ratios matches to three decimals (2.800, 2.388, 2.706),
 * so they are the same artwork at 42%, not new assets. Nothing was
 * downloaded from Figma and no placeholder was needed for them.
 * ================================================================== */

/* ---- 32.1  the category card row ----
 *
 * .article-cats__list sits on .pillars__list and changes one value:
 * Figma's card gap here is 24, not the homepage's 16. Both are on the
 * approved scale, so this is a straight swap rather than a capped one. */
.article-cats__list {
	gap: var(--space-24);
}

/* .article-cat sits on .pillar-card. Three deltas, all from Figma:
 *
 *   1. left-aligned, not centred. The homepage's pillar row centres
 *      because it has no button and reads as a poster; this one has a
 *      CTA per card and reads as a list.
 *   2. padding 32, not 20. Figma draws 30, which is not on the approved
 *      scale - 32 is the snap, and it is the SAME snap .card--pad
 *      already records for Figma's 30 on the Prenatal cards. Not a new
 *      decision.
 *   3. space-between, so the button sits on the card floor and all
 *      three buttons line up however long the titles run.
 *
 * The 12px inner gap is NOT overridden: .pillar-card's own gap is
 * already var(--space-12) and Figma's card gap here is 12 too. */
.article-cat {
	align-items: flex-start;
	justify-content: space-between;
	padding: var(--space-32);

	/* !! THE HOMEPAGE'S COLOURED CARD ART IS DELIBERATELY NOT REUSED
	 * HERE, and this was caught by rendering it rather than by reading
	 * it. .pillar-card pulls --pillar-card-bg, and the three
	 * pillar-card-*.webp files in the media library carry a COLOURED
	 * GRADIENT with the dot texture baked into it - pink, lavender and
	 * green. Figma draws these three cards WHITE with the dot grid over
	 * the top and no gradient at all (624:227 / 624:230 / 624:233, each
	 * bg-white with a separate "Texture - Dot Grid" child). Inheriting
	 * the homepage art gave three coloured cards on a page whose design
	 * has none, so all four of .pillar-card's background properties are
	 * overridden rather than three of them.
	 *
	 * The texture is Figma's own tile, exported from the file and
	 * committed to the THEME folder - not redrawn as a CSS gradient,
	 * which would have meant inventing a dot size, spacing and colour
	 * that the design already specifies. It lives in assets/img/ for the
	 * same reason the favicons do: the media library lives in the
	 * database and the database does not travel through GitHub.
	 *
	 * !! THE TILE IS 256px. IT WAS 50px, AND 50 WAS A MISREAD. This rule
	 * used to say "50px is Figma's own tile size (bg-size-[50px_50px])
	 * against a 512x512 source", taking the dev-mode number at face
	 * value. That number does not survive arithmetic: the source tile's
	 * dots sit 24px apart within 512, so drawing the tile at 50px puts
	 * them 2.3px apart - which is not a dot grid, it is a flat grey
	 * wash, and that is what this page has been serving since it
	 * shipped.
	 *
	 * MEASURED, NOT INFERRED, off Figma's own renders, read pixel by
	 * pixel: the dot centres are 12.0px apart on the Momma's Village
	 * card (632:257), 12.0px on the Products card (771:2488), and ~12.5
	 * on this page's own frame (624:126, measured through a 0.674
	 * downscale, so the noise is the downscale). One tile, one spacing,
	 * three pages. 24px source spacing rendered at 12px is a half-scale
	 * tile: 512 / 2 = 256px.
	 *
	 * --article-cat-texture is set on the <ul> in home.php and inherits
	 * to all three cards, so the one asset URL on this page stays in the
	 * template - the same arrangement --pillar-card-bg, --page-bg-image
	 * and now --village-card-texture use. */
	background-color: var(--surface-white);
	background-image: var(--article-cat-texture);
	background-size: 256px 256px;
	background-position: 0 0;
	background-repeat: repeat;
}

.article-cat__body {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	width: 100%;
}

/* Figma runs these illustrations at 35.7 tall against the homepage's
 * 85 - the same files, drawn smaller. 36 is a BOX DIMENSION TAKEN FROM
 * THE DESIGN, not a step on the spacing scale, so it is declared as a
 * named custom property rather than typed into the rule - the same
 * shape --embed-min-h and --fan-w already use. */
.article-cat {
	--article-cat-illo-h: 36px;
}

/* !! align-self IS THE FIX AND THE HEIGHT WAS NEVER THE BUG. Gerek:
 * "the icons spread out too wide, meaning they're rendering at the full
 * width, which they shouldn't be."
 *
 * Measured: 371px wide against an intrinsic 101 at this height. The
 * rule below sets `height` and the base rule in section 11 sets
 * `width: auto`, which looks like it should preserve the ratio - and
 * would, anywhere but here. .article-cat__body is a COLUMN flex
 * container with no align-items, so it defaults to `stretch`, and a
 * flex item whose cross size is `auto` stretches to fill. The image was
 * being pulled to the card's full width and squashed to 36px tall.
 *
 * align-self opts this one item out. flex-start rather than centre
 * because Figma left-aligns the whole card body, and the three drawn
 * boxes are 99.96, 85.259 and 96.6 wide - three different widths at one
 * shared height, which is exactly what an intrinsic ratio gives back
 * once nothing is stretching it. */
.article-cat .pillar-card__illustration {
	align-self: flex-start;
	height: var(--article-cat-illo-h);
}

/* ---- 32.2  the card CTA has to wrap ----
 *
 * !! THE LABEL HAS TO BE ALLOWED TO WRAP, AND FIGMA NEVER SOLVED THIS -
 * the same finding, on the same component, for the same reason as
 * .village-card in section 29. That rule is the precedent this one
 * follows rather than a second answer to the same question.
 *
 * The labels are the copy document's own link text: "See Prenatal
 * Chiropractic Care", "See Pediatric Chiropractic" and "See Postpartum
 * Chiropractic Recovery". Both .cta-button (section 04) and
 * .cta-button__label (section 08) carry white-space: nowrap, which is
 * right for every other button on the site because every other label is
 * a fixed short phrase. Here it is not.
 *
 * Measured at the 1200 container: the card's content box is 282.7 wide
 * (1088 body, two 24 gaps, 346.7 a card, less 32 padding each side) and
 * the button's own chrome takes 32 + 8 of padding, a 40 arrow and a 12
 * gap, leaving about 190 for text. The longest label needs about 306 at
 * 16px SemiBold. It does not fit at ANY width, 1440 included, because
 * the cards are equal fractions of the row rather than a fixed size.
 *
 * Figma has exactly the same problem and hides it: 624:227 and its two
 * siblings are overflow-clip, so its own button would simply run under
 * the card edge and be cut. It avoided the collision by shortening the
 * label instead, and that option is not available here - the long form
 * is the document's copy and carrying it is Gerek's decision of
 * 2026-08-21.
 *
 * So the two nowraps are lifted for .article-cat and nowhere else. The
 * pill wraps to two lines for all three labels at most widths and the
 * arrow stays vertically centred against the block, which is what
 * .cta-button--icon's align-items: center already does. .cta-button's
 * base line-height of 1.2 is what keeps the two lines tight.
 *
 * No align-self is needed, unlike .village-card: .article-cat already
 * sets align-items: flex-start, so the button is its own width without
 * being told twice. max-width caps it at the card. */
.article-cat .cta-button {
	max-width: 100%;
	white-space: normal;
}

.article-cat .cta-button__label {
	white-space: normal;
}


/* ---- 32.3  the closing CTA row ----
 *
 * Text column beside the photograph, Figma 789:8230.
 *
 * !! FIGMA'S padding-inline: 150 IS NOT REPRODUCED, and that is
 * deliberate rather than dropped. 150 is not on the approved scale and
 * capping it to 48 would not do the same job - in Figma it is a
 * CENTRING device, not a gutter: the row's contents (385 + 48 + 317 =
 * 750) sit inside a 1088 body, and the 150 either side is simply what
 * is left over. justify-content: center produces the same result out of
 * the numbers the design actually cares about, and survives a change of
 * container width, which a hardcoded 150 would not. */
.article-cta {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-48);
}

/* Figma's own 385. A measure, like --measure-lead, but this column is
 * narrower than that token's 560 because it shares the row with a
 * 317-wide photograph. Named rather than typed, same reason as above. */
.article-cta__text {
	--article-cta-measure: 385px;

	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-20);
	max-width: var(--article-cta-measure);
}

.article-cta__text > * {
	margin: 0;
}

/* The photograph slot. .embed-slot supplies the dashed labelled box;
 * only the two box dimensions are Figma's own (317 x 443), and both are
 * set through the component's existing custom property rather than by
 * adding rules here. --embed-slot's default 40px top margin is zeroed
 * because this one sits in a centred flex row, not stacked under prose. */
/* Was .article-cta__media, an .embed-slot placeholder. The photograph
 * exists - see the note in home.php - so this is now a real image at
 * Figma's own 317. multiply for the same reason the What to Expect
 * portrait takes it: the file is a cut-out on opaque white and this
 * page has a .page-bg, so without it the picture arrives in a white
 * box. The parallax here is pale where the photo sits, so the subject
 * darkens by a couple of percent and the ground disappears. */
.article-cta__photo {
	flex: 0 0 auto;

	/* !! 394, NOT FIGMA'S 317, for the reason every other literal on
	 * this build has moved: 317 is 29% of the frame's 1088 inner width
	 * and 23% of our 1352. 317 x 1352/1088 = 394 puts her back at the
	 * design's share of the row. The file is 520 wide, so this is still
	 * a 1.3x downscale. */
	width: 394px;
	height: auto;
	border-radius: var(--radius-sm);
	mix-blend-mode: multiply;

	/* !! SHE STANDS ON THE FOOTER, AT ZERO. Gerek, from the Figma:
	 * "Aimee being a zero pixel on the footer... you would require you
	 * to minimize the space in the Ready to Get Started section but also
	 * make her bigger."
	 *
	 * Both halves are this one pair of declarations. align-self pins her
	 * lower edge to the row's, and the negative margin then pulls that
	 * edge down past everything between the row and the footer panel.
	 *
	 * !! THE 4 IS FOUR REAL GAPS, NOT A FUDGE, and every one of them is
	 * --space-48. Measured on the live page, top to bottom:
	 *
	 *     .article-cta   padding-bottom   48
	 *     main           padding-bottom   48
	 *     .site-footer   margin-top       48
	 *     .site-footer   padding-top      48
	 *                                    ---
	 *                                    192   to .footer-panel's top
	 *
	 * Written as 48 x -4 rather than -192 so it stays tied to the step
	 * all four are derived from - if the scale ever moves, they move
	 * together and so does this.
	 *
	 * !! AND ONE OF THE FOUR IS NOT 48 ON A PHONE. The 767 block in
	 * section 07 steps .site-footer's padding-block to 32, so the sum
	 * there is 48 + 48 + 48 + 32 = 176. Left at 192 she overshot by 16
	 * and sank BEHIND the footer panel - caught by measuring rather than
	 * by looking, because 16px of a portrait disappearing under an
	 * opaque blue edge reads as a crop, not as a bug. Corrected in that
	 * block below.
	 *
	 * THE REAL LESSON IS THE DEPENDENCY, not the number: this rule
	 * reaches across three other components to land on a fourth. Anyone
	 * changing .article-cta's padding, main's padding, or the footer's
	 * margin or padding has to come back here.
	 *
	 * AND THIS IS WHAT SHORTENS THE SECTION. The photo used to be the
	 * tallest thing in the row and set its height; pulled 192 out of its
	 * own layout height it no longer is, so the row is now the text
	 * column's 380 and the section drops from 541 to 476. The "minimize
	 * the space" is a consequence of the mechanism rather than a second
	 * change.
	 *
	 * !! SHE NEVER OVERLAPS THE FOOTER, she meets it - so nothing has to
	 * be re-stacked. She is in <main> and the footer paints after it, so
	 * an overlap would put her behind the panel with no way to fix it
	 * short of a z-index fight. Landing exactly on the edge avoids the
	 * question entirely. */
	align-self: flex-end;
	margin-bottom: calc(var(--space-48) * -4);
}


/* ---- 32.5  .article-card - the post feed  (added 2026-09-01) ----
 *
 * !! MODELLED ON TWO SITES THAT ALREADY SOLVED THIS, at Gerek's
 * direction: itechvalet.com/learn/ and peninsulacregroup.com/articles/.
 * Both run the same component, down to the class names, and those names
 * are reused here rather than invented a third time - .article-grid
 * holding .article-card, and inside each card __link > __media > __img
 * plus __body > __title / __date / __excerpt / __more.
 *
 * Their geometry, measured off the live pages rather than eyeballed:
 * three columns at a 20px gap, a 16:9 media box, 24px of body padding,
 * and a three-line clamp on both the title and the excerpt so a long
 * headline cannot push one card taller than the two beside it.
 *
 * !! THE STRUCTURE IS COPIED, THE SKIN IS NOT. Both references are
 * square-cornered, borderless, and set in Lora and Karla. This is a
 * .card: --radius, --shadow-card, Baloo and Poppins, coral link. The
 * gap goes to --space-24 rather than their 20 because 20 is not a step
 * on this scale.
 *
 * !! IT RENDERS NOTHING TODAY. Zero posts exist, so have_posts() is
 * false and none of this is reachable yet. It is written now so the
 * first published post lands in a designed component instead of the
 * bare <h2>-and-excerpt fallback this page carried, whose own comment
 * asked for exactly this. */
.article-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);
	margin: var(--space-32) 0 0;
	padding: 0;
	list-style: none;
}

.article-card {
	display: flex;
	border-radius: var(--radius);
	background: var(--surface-white);
	box-shadow: var(--shadow-card);
	overflow: hidden;   /* the media's square corners meet the card's round ones */
}

/* The whole card is the link. flex column so the body can grow and the
 * cards in a row stay the same height without a fixed one. */
.article-card__link {
	display: flex;
	flex-direction: column;
	width: 100%;
	color: inherit;
	text-decoration: none;
}

/* 16:9 whether or not there is a thumbnail - a post published without a
 * featured image leaves a tinted panel rather than collapsing to a
 * different height than the cards beside it. */
.article-card__media {
	display: block;
	aspect-ratio: 16 / 9;
	background: var(--surface-off);
}

.article-card__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.article-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--space-8);
	padding: var(--space-24);
}

/* --type-h4 is the card-title step this site already uses, against the
 * references' 20px. The clamp is theirs and is the load-bearing part:
 * without it one four-line headline sets the height of its whole row. */
.article-card__title {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	margin: 0;
	overflow: hidden;
	font-size: var(--type-h4);
	color: var(--neutral-ink);
}

.article-card__date {
	font-size: var(--type-xs);
	color: var(--neutral-muted);
}

.article-card__excerpt {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	overflow: hidden;
	color: var(--neutral-muted);
}

/* Pushed to the bottom of the body so it sits on one line across a row
 * of cards whose titles and excerpts are different lengths. It is not a
 * link - the card is - so it takes the link colour by hand. */
.article-card__more {
	margin-top: auto;
	padding-top: var(--space-8);
	font-weight: var(--weight-semibold);
	color: var(--brand-coral-deep);
}

.article-card__link:hover .article-card__more,
.article-card__link:focus-visible .article-card__more {
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* ---- 32.4  responsive ----
 *
 * 991 is the same breakpoint .pillars__list already stacks at, and for
 * the same measured reason: three cards plus their illustrations stop
 * fitting before the container does. Inheriting that breakpoint keeps
 * the two rows changing shape together rather than drifting apart. */
@media (max-width: 991px) {
	/* Two across, the same step the reference sites take and the same
	 * width the pillar row stacks at. */
	.article-grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.article-cta {
		flex-direction: column;
		align-items: flex-start;
	}

	.article-cta__text {
		max-width: none;
	}

	/* The photograph stops being a fixed column once it is no longer
	 * beside anything. It does NOT take the whole container the way the
	 * old placeholder box did - a 1352-wide portrait is not the same
	 * design decision as a 1352-wide dashed slot - so it keeps its width
	 * as a ceiling and centres.
	 *
	 * !! AND SHE STILL STANDS ON THE FOOTER. The hang is kept rather
	 * than switched off the way What to Expect's is, because the
	 * relationship here is to the FOOTER and the footer is still
	 * directly below her when the row stacks - unlike that page, where
	 * the thing she landed on moved out from under her. The four gaps
	 * she is pulled through are the same four at every width. */
	.article-cta__photo {
		width: 100%;
		max-width: 394px;
		margin-inline: auto;
	}
}

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

	/* The fourth gap is 32 here, not 48 - section 07's 767 block steps
	 * .site-footer's padding-block down. 48 + 48 + 48 + 32 = 176, and
	 * leaving the 192 put 16px of her behind the footer panel.
	 *
	 * !! AND SHE COMES DOWN TO 240, the same ceiling the What to Expect
	 * portrait takes at this width and for the same reason Gerek gave
	 * there: at 342 she is the full column and reads as the page rather
	 * than as an element on it. Applied here before it was reported -
	 * it is the same photograph, the same screen and the same
	 * complaint. Tablet keeps 394, where she is 47% of the row. */
	.article-cta__photo {
		max-width: 240px;
		margin-bottom: calc((var(--space-48) * -3) - var(--space-32));
	}
}


/* ==================================================================
 * 33  ABOUT DR. AIMEE / PHILOSOPHY  (added 2026-08-21)
 *
 * Page 06, the most biographical page on the site. Every credential,
 * date and figure in the template is the copy document's own string -
 * nothing here computes, rounds or infers anything about Dr. Aimee.
 *
 * MEASURE, DECLARED BEFORE THE BUILD as the master document's "THE
 * MEASURE" section requires. That section names About specifically and
 * suggests capping text near 1000px. Figma does not use one uniform
 * measure, so the cap is taken per block from the file instead:
 *
 *   body sections        padding-inline 56  -> var(--space-48)
 *   centred assumption   padding-inline 150 -> ~788 effective, capped
 *                        at var(--measure-card) (840)
 *   proven grid          1028 wide
 *   is/isn't row         806 wide
 *   closing banner       1140 x 440
 *
 * NO --measure-prose: 1000px WAS MINTED. 840 is the widest cap the
 * ladder already has and lands within ~50px of Figma's own 788, so the
 * intent is met without a new token. Decided with Gerek 2026-08-21.
 *
 * !! MOST OF THIS PAGE IS NOT HERE, and that is the point. The closing
 * banner is .ready__panel from section 13, already carrying THIS EXACT
 * PHOTOGRAPH at THIS EXACT SLOT on Prenatal. The credential grid is
 * .proof-grid / .proof-card from section 31, already carrying five of
 * this page's six credentials from the same files. The breadcrumb is
 * .breadcrumb from 18.5. Buttons, cards, prose, page background and
 * the whole type ladder are all shared. Section 33 holds the deltas
 * and the four shapes that genuinely had no home.
 * ================================================================== */

/* ---- 33.1  the hero card ----
 *
 * Figma 449:2216: a 451-tall rounded card with the family photograph
 * bled across it and the text sitting on top at the left. NOT
 * .page-hero - that component is the Prenatal/Pediatric hero with its
 * own 10px header offset and a different internal grid. This one is a
 * photo card, closer to .ready__panel, but its content is
 * left-aligned and top-stacked rather than centred, so it takes its
 * own small block rather than bending .ready__ out of shape. */
.about-hero {
	position: relative;
	display: flex;
	align-items: center;
	min-height: 451px;   /* Figma 449:2216, a drawn box not a scale step */
	overflow: hidden;
	border-radius: var(--radius);
	background: var(--surface-off);
}

.about-hero__photo {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: right center;   /* Figma anchors the family right */
}

/* !! THERE IS NO VEIL ELEMENT, AND THAT IS THE FINDING RATHER THAN AN
 * OMISSION. hero-family-photo.webp ALREADY HAS THE FADE BAKED INTO THE
 * FILE - open it and the left 55% is white, with the family on the
 * right. It is a pre-flattened export of Figma's whole hero composite:
 * node 449:2216 stacks the photo at 25% opacity, the photo again at
 * full opacity, and a "from-[#f8f8f8] from-[28.331%] to-[rgba(255,255,
 * 255,0)] to-[73.685%]" gradient over the top. The library file is all
 * three already flattened together.
 *
 * Two cuts of this rule got that wrong. The first guessed a gradient
 * and left the family 22% visible; the second copied Figma's real
 * stops and was still wrong, because it was applying a second fade on
 * top of one the asset already carried. Caught by opening the .webp
 * instead of trusting the node tree. The photograph needs no overlay
 * at all - the text sits on the white the file supplies. */

.about-hero__frame {
	position: relative;   /* above photo and veil */
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-16);

	/* A flex child will not shrink below its min-content width without
	 * this, and the CTA's nowrap label gives this one a wide one. See
	 * the 600 block in 33.8 - measured at 320, the frame came out 301
	 * inside a 257 parent before both fixes. */
	min-width: 0;

	/* !! 55%, NOT --measure-head. This is the fix for the text running
	 * over the family, 2026-09-01.
	 *
	 * The photograph is not a decoration behind the text - it is a
	 * PRE-FLATTENED COMPOSITE whose left 55% is already white, so the
	 * copy is meant to sit on white the file itself supplies. See the
	 * note on .about-hero__photo above. That makes 55% a hard geometric
	 * limit, not a taste call: past it, text lands on photograph.
	 *
	 * --measure-head is 1027px. Against the old 1092px card that was 94%
	 * and already over the fade; against today's 1352px card it is 76%,
	 * and the H1 and lead were both running onto the family. Gerek's read
	 * was exactly right - "a full row that is covering the image".
	 *
	 * A PERCENTAGE, not a px value, because the constraint is a
	 * proportion of the asset and has to survive the next container
	 * change. That is what the px version failed to do.
	 *
	 * 55% is Figma's own split: the Page Hero component gives its text
	 * frame 624 of 1140, which is 54.7%. */
	max-width: 55%;
	padding: var(--space-20) var(--space-48);
}

.about-hero__lead {
	margin: 0;
	color: var(--neutral-muted);
}

/* ---- 33.2  the pull line ----
 *
 * Figma sets this one sentence on its own, centred, between the hero
 * and the first content row - smaller than body copy and clearly not
 * part of either block. --type-xs is the step Figma draws (13). */
.about-pull {
	max-width: var(--measure-card);
	margin: 0 auto;
	color: var(--neutral-muted);
	font-size: var(--type-xs);
	text-align: center;
}

/* ---- 33.3  the alternating image/text rows ----
 *
 * Two of them: image left on "Why the Person", image right on the
 * Background section. Figma draws both images at 480 x 340 and pads
 * the row by 48-56.
 *
 * The reverse uses row-reverse rather than a second markup order, so
 * the DOM stays image-then-text in both and the stacked order is
 * consistent - the master document's own rule for zigzag layouts
 * ("must reverse to a single consistent order on small screens"). */
/* A CARD as of 2026-09-01. Figma draws both of these rows inside a
 * white rounded panel and the build had them loose on the page
 * background - Gerek: "the second section... in a white-padded or white
 * box with rounded corners, whereas your file does not."
 *
 * The fill, radius and shadow come from .card + .card--raised in the
 * markup, so this rule keeps only the layout. Padding moves here from
 * padding-inline: it is a card now and needs the block axis too. */
.about-row {
	display: flex;
	align-items: center;
	gap: var(--space-32);
	padding: var(--space-48);
}

.about-row--reverse {
	flex-direction: row-reverse;
}

.about-row__media {
	flex: 0 0 auto;
	width: 480px;   /* Figma's own box, not a scale step */
	max-width: 100%;
}

.about-row__media img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--radius);
}

.about-row__body {
	flex: 1 1 0;
	min-width: 0;
}

/* ---- 33.4  the centred "Industry Assumption" block ----
 *
 * Figma insets this one by 150 either side, which is not on the
 * approved scale and is a centring device rather than a gutter - the
 * same shape the Articles CTA row had. Capped and centred instead, at
 * the measure declared at the top of this section. */
.about-assumption {
	max-width: var(--measure-card);
	margin-inline: auto;
	text-align: center;
}

/* The Care Philosophy block shares the centred block's measure and
 * centring but sets its copy left - Figma centres the Assumption
 * section and left-aligns this one, at the same width. A modifier
 * rather than an inline style so the value stays in the stylesheet. */
.about-assumption--left {
	text-align: left;
}

.about-assumption__icon {
	display: block;
	width: 88px;   /* Figma 658:161, a drawn box */
	height: auto;
	margin: 0 auto var(--space-16);
}

/* Figma draws this sub-heading at 18 Poppins SemiBold. It is a real
 * <h3> under the section's <h2>, so it takes --type-h4, the card-title
 * step, exactly as .pillar-card__title does for the same reason: the
 * heading ladder has no 18, and minting one for a single string is
 * what the type consolidation in section 01 exists to prevent. */
.about-assumption__claim {
	margin: 0 0 var(--space-16);
	font-family: var(--font-body);
	font-size: var(--type-h4);
	font-weight: var(--weight-semibold);
	color: var(--neutral-ink);
}

/* ---- 33.5  the credential grid, two across ----
 *
 * .proof-grid is repeat(5, 1fr) for the Products row of five. Figma
 * lays this page's six out 2 x 3 (714:1884 - three "Card Row"s of
 * 1028), and .proof-card is a VERTICAL card there against a horizontal
 * one here, so both get a modifier and neither base rule is touched.
 * Products is unaffected. */
/* !! NO max-width. It carried Figma's own 1028px until 2026-09-01.
 *
 * 1028 was 94% of the old 1092px content box, so it read as full width
 * and the centre gap sat on the page's centre line. Against today's
 * 1352px box it is 76%, left-aligned - the gap between the two columns
 * lands well left of centre and 324px of dead space opens on the right.
 * Gerek: "should be full width, where they are centered. The gap is in
 * the center, not the left."
 *
 * Dropping the cap restores the intent rather than changing it - the
 * same failure as .prose and --measure-card, and the same fix. */
.proof-grid--pairs {
	grid-template-columns: repeat(2, 1fr);
}

/* The sentence under the credential grid. .proof-grid carries 24px above
 * itself and had nothing below, so the link ran straight into the last
 * row of cards. Gerek: "literally no pixels or 5px of space after that." */
.proof-grid + .prose {
	margin-top: var(--space-32);
}

.proof-card--row {
	--proof-card-min-h: 0;   /* the 199 was for the tall vertical card */

	flex-direction: row;
	align-items: center;
	justify-content: flex-start;
	gap: var(--space-16);   /* Figma 14, nearest step, and the value
	                         * .proof-card already uses */
}

/* ---- 33.6  the is / isn't comparison row ----
 *
 * Figma 597:134, 806 wide, gap 20, two equal columns each with a 26px
 * badge above a line of copy. badge-check.webp and badge-x.webp are
 * real assets and are exactly twice the drawn box.
 *
 * These sit on .card / .card--raised for their white fill, radius and
 * shadow rather than restating any of it. */
.about-compare {
	display: flex;
	gap: var(--space-20);
	max-width: 806px;   /* Figma's own row width */
	/* 32 above. There was none, so the two tiles sat directly under the
	 * last line of the paragraph - .prose zeroes its last child's margin,
	 * so nothing separated the copy from the cards. Gerek: "no space
	 * after the text of Main Street Office to the two white tiles." */
	margin: var(--space-32) auto 0;
}

.about-compare__col {
	display: flex;
	flex: 1 1 0;
	flex-direction: column;
	align-items: center;
	gap: var(--space-12);
	min-width: 0;
	padding: var(--space-20);
	text-align: center;
}

.about-compare__badge {
	display: block;
	width: 26px;   /* Figma 597:137 / 597:143 */
	height: 26px;
}

.about-compare__text {
	margin: 0;
	color: var(--neutral-ink);
	font-size: var(--type-small);
}

/* ---- 33.7  the closing banner ----
 *
 * .ready__panel does all of the work. Only two values differ from the
 * homepage's instance, and BOTH ARE ALREADY DUPLICATED ON PRENATAL -
 * see .prenatal-cta__panel and .prenatal-cta__scrim in section 17,
 * which set the identical 440 and the identical darker scrim over the
 * identical photograph.
 *
 * !! FLAGGED AS A GENERALISATION CANDIDATE RATHER THAN GENERALISED
 * HERE. Two pages now want "the 440 banner with the darker scrim", and
 * a third would make it a rule. Folding it into a shared
 * .ready__panel--tall would mean editing page-prenatal.php in a build
 * meant to add a page, so it is recorded and left. B-list. */
.about-banner {
	min-height: 440px;   /* Figma 433:610 */
	background: #e6e6e6;   /* same fallback .prenatal-cta__panel carries */
}

.about-banner__scrim {
	background: rgba(10, 10, 10, 0.4);
}

.about-banner__title {
	max-width: var(--measure-head);
}

/* ---- 33.8  responsive ----
 *
 * 991 is where .pillars__list and .article-cats__list already stack,
 * and the alternating rows hit the same wall for the same reason: a
 * fixed 480 image beside a text column stops fitting before the
 * container does. Inheriting the breakpoint keeps the page changing
 * shape with the rest of the site rather than on its own schedule. */
@media (max-width: 991px) {
	/* Both rows stack image-then-text. The reverse is dropped rather
	 * than kept, so the stacked order is identical on both - the master
	 * document's zigzag rule. */
	.about-row,
	.about-row--reverse {
		flex-direction: column;
		align-items: stretch;
		/* 32, not the 0 this used to set. Zero was right while the row
		 * was loose on the page; it is a card now, and a card with no
		 * inline padding puts its photo hard against its own edge. */
		padding: var(--space-32);
	}

	.about-row__media {
		width: 100%;
	}

	.proof-grid--pairs {
		grid-template-columns: 1fr;
	}

	/* !! THE HERO STOPS BEING A PHOTO-BEHIND-TEXT COMPOSITE AT 991,
	 * MOVED UP FROM 767 ON 2026-09-01.
	 *
	 * The original note - kept, because the reasoning is right and only
	 * its breakpoint was wrong:
	 *
	 *     "The fade baked into hero-family-photo.webp whitens the LEFT
	 *      of a 2276-wide file, so on a wide card the text sits on white
	 *      by construction. Below 768 the card is narrower than the
	 *      white run, the crop keeps the family, and the text lands on
	 *      it. The card stops being a photo-behind-text composite and
	 *      becomes two stacked blocks - photo, then text - so the copy
	 *      is on the card's own surface and can never be over image
	 *      pixels again."
	 *
	 * All true. 768 was simply not where it starts. MEASURED at 834:
	 * the card is 786 wide, the text column 432, and because 24px body
	 * in 432px is very tall the card grows to roughly 900. object-fit:
	 * cover then scales the asset by HEIGHT, 900/876, giving a 2337-wide
	 * scaled image cropped to 786 from the right - so only the right 34%
	 * of the file is visible, which is all family and none of the white
	 * run. The entire text block rendered on top of the photograph.
	 *
	 * The composite only works while the card is wide enough AND short
	 * enough that the white run still covers the text, and that is a
	 * desktop-only condition. Anything narrower stacks. */
	.about-hero {
		display: block;
		min-height: 0;
	}

	/* !! 7/6, NOT 16/9, AND THE RATIO IS DOING REAL WORK HERE.
	 *
	 * Stacked, the photo is its own block, and object-fit: cover with
	 * object-position: right means the aspect ratio decides HOW MUCH of
	 * the asset you see. The asset's left 55% is the white run - so the
	 * ratio has to be tight enough that the visible window starts AFTER
	 * it, or a slab of blank white renders as the hero image.
	 *
	 * Computed against the real 2276x876 file:
	 *
	 *     ratio   window starts at   result
	 *     16/9        31.6%          a third of it is blank white
	 *     4/3         48.7%          still catches the fade
	 *     7/6         55.1%          all family
	 *
	 * 16/9 was the old value and it was chosen when this rule only ran
	 * below 768, where the card is narrow enough that the maths lands
	 * differently. At 834 it put a 786x150 band of white above the
	 * family. 7/6 starts at 55.1%, which is the fade boundary to within
	 * a tenth of a percent.
	 *
	 * !! IF THE PHOTOGRAPH IS EVER RE-EXPORTED, RE-DERIVE THIS. It is a
	 * function of where the white run ends in that specific file, not a
	 * design preference. */
	.about-hero__photo {
		position: static;
		aspect-ratio: 7 / 6;
		height: auto;
	}

	.about-hero__frame {
		max-width: none;
		padding-inline: var(--space-24);
		padding-block: var(--space-24);
	}
}

@media (max-width: 767px) {
	.about-compare {
		flex-direction: column;
	}
}

/* !! MEASURED, NOT GUESSED. At 320 the hero frame rendered 301 wide
 * inside a 257 parent - a 44px overflow, clipped rather than scrolled
 * because .about-hero is overflow: hidden, which is the worst kind: the
 * button was being cut off with no scrollbar to show it.
 *
 * The cause is .cta-button__label's white-space: nowrap, right for
 * every wide button on the site and wrong in a 257px card: "Book Your
 * Appointment" plus a 40px arrow and its padding has a 253px
 * min-content width, and a flex child will not go below that.
 *
 * Fixed the way .village-card and .article-cat already fix it - the
 * nowrap is lifted for this component and nowhere else - except that
 * here it is lifted only at the width where it actually breaks, since
 * the button fits perfectly well at every other size. .cta-button
 * itself is untouched. */
@media (max-width: 600px) {
	.about-hero__frame {
		padding-inline: var(--space-16);
	}

	.about-hero .cta-button,
	.about-hero .cta-button__label {
		white-space: normal;
	}

	/* !! MEASURED, NOT GUESSED. At 320 .proof-card__text ran to 352
	 * inside a 305 viewport - 47px of real sideways scroll, and only on
	 * the SCU card.
	 *
	 * The cause is one logo's shape, not the grid. scuhs-logo.webp is
	 * 656 x 130, so at the shared 40px logo height it draws 202 wide -
	 * five times the width of the near-square other five. 202 plus the
	 * 16 gap plus the 40 of card padding leaves 6px for a text column
	 * whose min-content is the word "Chiropractic,", and a flex child
	 * will not go below that.
	 *
	 * Capping the logo was rejected: it would shrink SCU off the 40px
	 * baseline the other five sit on, which is the one thing
	 * .proof-card__logo exists to hold. Instead the row modifier stands
	 * down below 600 and .proof-card's own column direction takes over
	 * - the base component, unmodified, which is what the vertical card
	 * already does on Products. Every logo keeps its height, the text
	 * gets the full card width, and nothing is capped. 600 is the step
	 * this block already exists at; 390 and up keep the row. */
	.proof-card--row {
		flex-direction: column;
		align-items: flex-start;
	}
}


/* ==================================================================
 * 34  POSTPARTUM CHIROPRACTIC RECOVERY  (Figma 448:688)
 *
 * "(unverified) Postpartum Chiropractic Recovery — Option B: Accent
 * Only" - the third frame in a row to carry that label. Recorded, not
 * acted on; the Option B question stays deferred per standing
 * instruction until every page is built.
 *
 * !! MOST OF THIS PAGE IS NOT HERE, AND THAT IS THE POINT. The hero is
 * .page-hero from section 18, already parameterised, and every one of
 * its knobs is either a Prenatal default or a one-line override. The
 * comparison row is .expect__compare / .expect-point from section 15,
 * whose 806 max-width, 20 gap and 26px badge are Figma's numbers here
 * to the pixel - nothing to add. The closing banner is .ready__panel
 * from section 13. The two-column trust row is .split from section 20.
 * Section 34 holds four things: one shared panel that had to be lifted
 * out of Prenatal, one shared banner modifier that three pages now
 * want, and two page-scoped shapes.
 * ================================================================== */

/* ---- 34.1  .tint-panel - the tinted content panel ----
 *
 * !! LIFTED OUT OF PRENATAL RATHER THAN COPIED FROM IT. Figma 682:140
 * is the same object as Prenatal's 795-wide changing panel: a tinted,
 * dot-textured, soft-shadowed box with a fixed width ceiling that is
 * allowed to shrink, sitting beside a fixed-aspect illustration. Third
 * time this shape has been drawn, so it gets a name.
 *
 * !! .prenatal-changing__panel IS NOT MIGRATED TO IT IN THIS BUILD, and
 * that is deliberate. Rewriting page-prenatal.php's markup inside a
 * build meant to add a page is exactly the change that makes a geometry
 * diff meaningless. The shared component is defined here and used here
 * only; Prenatal keeps its own rules untouched. MIGRATION IS RECORDED
 * AND OWED - .prenatal-changing__panel should become .tint-panel with
 * --tint-panel-gradient set to its pink. B-list.
 *
 * Knobs:
 *   --tint-panel-max        width ceiling      (Figma  795px)
 *   --tint-panel-gradient   the fill           (Figma mint, below)
 *
 * The gradient is Figma's own, read off node 682:140 rather than
 * guessed: linear-gradient(269.796deg, #85d0bc 0.115%, #beeadc 50.058%).
 * 269.796 is 270 to within a fifth of a degree - a dragged handle, not
 * an intent - so it is written as 270deg, which is `to left` and reads
 * as what it is. BOTH STOPS ARE EXISTING TOKENS AND NEITHER IS NEW:
 * #85d0bc is --brand-mint-deep and #beeadc is --brand-mint. The two
 * came back from the dead on 2026-08-21 for "the next page to want a
 * mint panel" (see the note in section 1.6b). This is a later one. */
.tint-panel {
	position: relative;
	/* isolation, not z-index juggling - same reasoning as Prenatal's
	 * panel: it makes the panel a stacking context so the dot layer can
	 * sit at z-index -1, above the panel's own fill and beneath its
	 * text, without escaping to fight anything else on the page. */
	isolation: isolate;

	display: flex;
	flex: 0 1 var(--tint-panel-max, 795px);
	flex-direction: column;
	gap: var(--space-16);
	min-width: 0;
	max-width: var(--tint-panel-max, 795px);
	padding: var(--space-32) var(--space-48);   /* Figma 30 / 56 */
	border-radius: var(--radius);
	background: var(--tint-panel-gradient, linear-gradient(270deg, var(--brand-mint-deep) 0%, var(--brand-mint) 50%));
	box-shadow: var(--shadow-card);
}

/* The dot matrix, built in CSS per Gerek's direction rather than
 * shipped as the raster tile Figma uses.
 *
 * !! A SUPPLIED COMPOSITE EXISTS FOR THIS PANEL AND IS NOT USED.
 * postpartum-recovery-credential-card-mint.webp bakes the gradient,
 * dots, corners and shadow into one 835 x 299 raster - the same export
 * shape as prenatal-credential-card-pink.webp, which was tried on the
 * Prenatal panel and reverted on 2026-08-18. The reason has not
 * changed: this is a growing text panel, not fixed-aspect artwork, so
 * stretching the raster elongates the dot matrix - and body copy here
 * is locked at 22px against Figma's 15px, so it grows further than
 * Prenatal's did. Gerek's call stands.
 *
 * Every number below is section 16's, unchanged and not re-derived: the
 * tile is the same tile, and the measurement that produced these
 * figures is written out in full against .prenatal-changing__panel
 * ::after. Repeating the arithmetic here would only give it a second
 * chance to disagree with itself. */
.tint-panel::after {
	content: '';
	position: absolute;
	z-index: -1;
	inset: 0;
	border-radius: inherit;
	background-image: radial-gradient(
		circle at center,
		rgba(61, 46, 34, 0.118) 0.9px,
		rgba(61, 46, 34, 0) 1.15px
	);
	background-size: 12px 12px;
	pointer-events: none;
}

/* Figma 448:694 is the same panel with the decoration taken off: a flat
 * fill, centred text, no dots and no shadow. A modifier rather than a
 * second component, because everything else about it - radius, padding,
 * gap, the shrinking width ceiling - is identical.
 *
 * !! THE FILL IS SNAPPED TO --brand-mint AND THE DELTA IS RECORDED.
 * Figma draws #bfe8dd; --brand-mint is #beeadc. That is 1/255 on red,
 * 2/255 on green, 1/255 on blue. The alternative was a third mint token
 * for a difference no screen shows, and section 1.6b already carries a
 * written warning about minting mints. 2/255 is the accuracy bar this
 * file holds itself to elsewhere. */
.tint-panel--flat {
	/* Figma 448:694 sits at x=267 in a 1440 frame and is 906 wide -
	 * 267 of slack on each side, so it is centred, not packed left the
	 * way 682:140 is. The base panel is a flex child in a row; this one
	 * is a lone block in a .section, so it centres on its own margins. */
	margin-inline: auto;

	background: var(--brand-mint);
	box-shadow: none;
	text-align: center;
}

.tint-panel--flat::after {
	content: none;
}

.tint-panel__title {
	margin: 0;
}

.tint-panel__body {
	margin: 0;
}

.tint-panel__body + .tint-panel__body {
	margin-top: var(--space-16);
}

/* ---- 34.2  .ready__panel--tall - the 440 banner ----
 *
 * !! THE THIRD PAGE WANTS IT, WHICH IS WHAT SECTION 33.7 SAID WOULD
 * MAKE IT A RULE. That note - written on the About build - flagged the
 * 440 height and the darker scrim as duplicated between
 * .prenatal-cta__panel and .about-banner, and said folding them into a
 * shared .ready__panel--tall would mean editing page-prenatal.php in a
 * build meant to add a page. The same constraint applies here, so the
 * same half-step is taken: the shared modifier is DEFINED and used by
 * this page, and the two existing instances are left alone.
 *
 * MIGRATION IS RECORDED AND OWED: .prenatal-cta__panel /
 * .prenatal-cta__scrim and .about-banner / .about-banner__scrim should
 * both become .ready__panel--tall. That is a three-file markup change
 * and belongs in its own commit, not this one. B-list.
 *
 * All three values verified against Figma 448:701 / 448:703 for THIS
 * page rather than assumed from the other two: min-height 440, #e6e6e6
 * behind the photo, scrim rgba(10,10,10,0.4). All three match. */
.ready__panel--tall {
	/* The 440 is Postpartum's and stays the DEFAULT; Pediatric draws the
	 * same banner at 390 and sets --ready-panel-h. See section 35.4. */
	min-height: var(--ready-panel-h, 440px);
	background: #e6e6e6;   /* the fallback the other two instances carry */
}

.ready__panel--tall .ready__scrim {
	background: rgba(10, 10, 10, 0.4);
}

.ready__panel--tall .ready__title {
	max-width: var(--measure-head);
}

/* ---- 34.3  page-scoped shapes ----
 *
 * Four values and three small shapes. Everything else on this page is a
 * shared component taking its defaults. */
/* !! --hero-backdrop-w AND --hero-backdrop-w-sm ARE GONE, 2026-09-01,
 * for the same reason they went from .pediatric: they sized an
 * <img class="page-hero__backdrop"> that no longer exists. This page's
 * mint artwork was promoted to a full-width fixed .page-bg layer, so
 * the hero-local copy of it was deleted rather than doubled. Two custom
 * properties nothing reads are worse than none - they read as a working
 * knob. See the note at the top of
 * page-postpartum-chiropractic-recovery.php. */
.postpartum {
	--squiggle-color:     var(--accent-spring);
	/* !! 142, NOT FIGMA'S 249, AND THE FILE IS WHY. 2026-09-01 the hero
	 * watermark was found reading right-justified on a phone. The box was
	 * centred; the ARTWORK was not. postpartum-recovery-watermark-
	 * postpartum.webp is 498 x 184 with its ink running 221..497 - 221px
	 * of empty space down the left, 44% of the file - so a centred box
	 * put the drawing in its right-hand half. Prenatal's and Pediatric's
	 * marks were checked the same way and both run edge to edge; this one
	 * file is the outlier.
	 *
	 * The file is cropped to 283 x 184 (-trimmed, see the note in
	 * page-postpartum-chiropractic-recovery.php) and this width follows
	 * it down: 142 of the trimmed file renders the SAME 139px of ink that
	 * 249 of the padded one did, and it now starts flush with the H1
	 * rather than 110px indented from it.
	 *
	 * !! ON A PHONE IT DOES GET BIGGER, and that is a consequence rather
	 * than a decision - the first version of this note claimed the mark
	 * did not change size anywhere, which is true of desktop only. The
	 * mobile rule in 18.6 caps the BOX at 300, and the box used to be 44%
	 * empty. Same 300px box, no padding inside it: the ink goes from 167
	 * to 294. That happens to be the right answer - Prenatal and
	 * Pediatric draw 300px of ink at this width and this page was the odd
	 * one out at 167 - but it was not aimed at, so it is written down. */
	--hero-watermark-w:   142px;

	/* !! 225, A QUARTER OFF THE SHARED 300, AND IT IS A RESOLUTION LIMIT
	 * RATHER THAN A DESIGN ONE. Gerek 2026-09-01: "that icon is a little
	 * grainy when it's blown up. Reduce the size by 25%, but it did get
	 * centered, which is what I want."
	 *
	 * He is reading the trim. Cropping the file's 221px of dead margin
	 * moved this mark from 167px of ink to 294 inside the same 300px box
	 * - the box never changed, the padding inside it went away - and the
	 * trimmed file is only 283 wide, so 300 was drawing it ABOVE 1:1. On
	 * a 2x phone that is a 283px source stretched over 600 device pixels,
	 * which is exactly the grain he saw. The other two pages get away
	 * with 300 because their files are 512 and 440 wide.
	 *
	 * 225 is his 25%, and it puts this mark back under its own source at
	 * 1.26x device pixels. Still 35% larger than the 167 it drew before
	 * the trim, and still centred, which was the half he wanted kept.
	 *
	 * A 600px-wide re-export of the trimmed artwork is what would let
	 * this page take the shared 300 like the other two. Flagged. */
	--hero-watermark-w-sm: 225px;
}

/* !! 96 BELOW THE HERO, NOT 48. Gerek 2026-09-01: "from the hero
 * section, which is Book Your Appointment, to the next section in the
 * green box feels a little tight."
 *
 * MEASURED: 48px, and the cause is that this page has no AEO paragraph.
 * Prenatal and Pediatric both put one between the hero and the first
 * content section, and that paragraph's wrapper carries .section - so
 * on those pages the hero-to-body distance is 48 + the paragraph + 96.
 * Here the hero hands straight to a section and the single boundary was
 * doing all the work at 48, half of what every other boundary on the
 * site is.
 *
 * 48 of bottom padding on the hero brings it to 96, matching .section's
 * own 48-and-48. Spent on the HERO rather than as extra padding-top on
 * the section below it, because .section's one-sided rhythm is the rule
 * this build follows everywhere - each boundary contributed by exactly
 * one side - and the hero is the side that is missing its half here.
 *
 * Scoped to .postpartum: Prenatal and Pediatric do not need it, they
 * have the AEO paragraph doing the same job. */
.postpartum .page-hero {
	padding-bottom: var(--space-48);
}

/* !! THE PHOTOGRAPH DROPS 24px. Gerek: "for the hero section with the
 * image, drop it down about 20 to 30 pixels because it feels like it's
 * a little high."
 *
 * The row is top-aligned - Figma's counter axis MIN - and that reads
 * correctly on Prenatal and Pediatric, where the text column is close
 * to the photo's own height. THIS page's hero carries the full Direct
 * Answer paragraph in the hero itself rather than below it, so the text
 * column measures 626px against the photo's 408. Top-aligned, a 408px
 * photo against a 626px column sits visibly high in its own half.
 *
 * 24 is the middle of what was asked for and a real step on the scale.
 * NOT centring, which was the other candidate and is wrong: centring
 * would drop it (626 - 408) / 2 = 109px, four times the ask, and would
 * leave the photo floating with no edge agreeing with anything.
 *
 * margin on the column, not align-self: the column keeps its top
 * alignment, so if the copy is ever shortened the photo tracks the top
 * of the row again rather than drifting to a new centre. */
/* MIN-WIDTH 768, because below that the row is a column and there is
 * no "high" left to fix - the photo is simply the next thing down. The
 * 24 measured as 56px of stack gap on a phone (the row's own 32 plus
 * this), which is a hole rather than a correction. */
@media (min-width: 768px) {
	.postpartum .page-hero__photo-col {
		margin-top: var(--space-24);
	}
}

/* Figma 449:2446 packs the panel and its illustration with a 103 gap
 * and centres them on the cross axis. 103 is not a step and the two
 * nearest are 96 and 112. --space-48 doubled is 96, and the row carries
 * 79 of trailing slack in Figma anyway (795 + 103 + 162.5 = 1060.5 in a
 * 1140 row), so the 7px is spent rather than a raw number typed in. */
.postpartum-body__row {
	display: flex;
	align-items: center;
	gap: var(--space-48);
}

.postpartum-body__illustration {
	display: block;
	flex: 0 0 auto;
	width: 162px;   /* Figma 162.525, a drawn box not a scale step */
	height: auto;
}

/* !! 75/25 AT DESKTOP, 2026-09-01 - the third page to take this fix and
 * the worst of the three before it.
 *
 * The two widths were drawn against Figma's 1140 row and the container
 * is now 1400. Measured at 1440: a 795 panel plus a 48 gap plus a 162
 * illustration is 1005 inside a 1352 row, so 347px trailed off the
 * right with nothing in it - against Pediatric's 262 and Prenatal's
 * 320. The panel sat left, the illustration floated in the middle of
 * that emptiness, and the pair read as unbalanced.
 *
 * Both columns now take a share, with the 48px gap split evenly out of
 * the two so 75 + 25 still adds to the full row: 75% - 24 and 25% - 24.
 * At 1440 that is a 990px panel and a 314px column.
 *
 * The panel is set through .tint-panel's OWN --tint-panel-max knob
 * rather than by overriding its flex and max-width separately, exactly
 * as Pediatric's row does - so the shared component is untouched and
 * the two pages cannot drift.
 *
 * !! THE ILLUSTRATION IS CAPPED AT 240 AND CENTRED IN ITS COLUMN
 * INSTEAD OF FILLING IT, AND THAT IS THE ONE PLACE THIS PAGE DEPARTS
 * FROM THE OTHER TWO. postpartum-recovery-pillar-illustration-
 * postpartum.webp is 325 x 397 - the SMALLEST of the three pillar
 * illustrations, against Prenatal's 379 and Pediatric's 493. Filling
 * the 314px column would draw it at 0.97x, which is not a downscale at
 * all: on a 2x screen that is a 325px source stretched across 628
 * device pixels, and Gerek reviews on an iPad and an iPhone.
 *
 * 240 is 1.35x - bigger than Prenatal's accepted 1.21x, crisper than
 * Pediatric's accepted 1.47x - and still a 48% increase on the 162 it
 * was. The 74px left over inside the column splits evenly either side,
 * so the COLUMNS are a true 75/25 and only the artwork sits inset
 * within its own.
 *
 * A 650px-wide re-export of that one file is what would let it fill the
 * column at a clean 2x. Flagged, not invented.
 *
 * MIN-WIDTH, not an unconditional rule: the row already stacks at 991
 * below, and this file is append-only, so an unconditional rule here
 * would beat that media query on source order and follow the stacked
 * column down to a phone. */
@media (min-width: 992px) {
	.postpartum-body__row .tint-panel {
		--tint-panel-max: calc(75% - var(--space-24));
	}

	.postpartum-body__illustration {
		flex: 0 0 calc(25% - var(--space-24));
		width: auto;
		max-width: 240px;
		margin-inline: auto;
	}
}

/* Figma 657:131 centres an 88 x 81.4 line-art mark above the heading,
 * with the whole block centred. No card behind it - the section sits
 * straight on the page background, which is why .trained__inner is not
 * reused here despite the same icon-over-heading shape. */
.postpartum-clearance {
	text-align: center;
}

/* 120, up from Figma's 88 (657:131), 2026-09-01. This is the second
 * half of a promise the Pediatric build wrote down: .pediatric-brings
 * __icon went to 120 that morning and its note said the two sections
 * draw the same mark, now at DIFFERENT numbers, and that this page
 * should match when it was reviewed. It has been. They match.
 *
 * The file is 176 x 163, so the mark draws at 1.47x rather than a clean
 * 2x. Flat line art carries that, and at 88 it read as an afterthought
 * over a 34px heading rather than as its mark. */
.postpartum-clearance__icon {
	display: block;
	width: 120px;
	height: auto;
	margin: 0 auto var(--space-24);
}

/* !! THE TEXT BLOCK WAS NOT CENTRED, AND text-align WAS HIDING IT -
 * the identical defect Pediatric's "What Brings Families" section had,
 * in the identical shape, and Gerek called it on sight: "there are
 * sections that are centered, but there's too much space on the right."
 *
 * Measured at 1440: the section is 1352 and centred, and the mark above
 * is centred inside it - but .prose carries max-width: var(--measure-
 * wide) (1100px) with NO auto margins, so the block sat flush left and
 * left 252px open on the right. Every line inside it is centre-aligned,
 * which is exactly what makes this hard to see: the copy looks centred,
 * just centred on the wrong axis - 594 instead of 720. The mark WAS on
 * 720, so the two disagreed by 126px.
 *
 * The 1100 measure itself is left alone. It is the reading ceiling
 * Gerek set site-wide on 2026-09-01 and the Pediatric fix already
 * learned this lesson the expensive way: narrowing it there changed
 * nothing, because --measure-card is 1100 too. Only the centring was
 * ever wrong. */
.postpartum-clearance .prose {
	margin-inline: auto;
}

/* Figma 448:695 is a 329 photo against a 771 text column, not the even
 * halves .split defaults to. The photograph is a cut-out on
 * transparency, so it gets no radius and no object-fit - it is artwork
 * rather than a cropped photo, and boxing it would clip the shoulder
 * that overhangs its own frame. */
/* !! A WHITE CARD WITH THE PHOTO STANDING ON ITS BOTTOM EDGE, added
 * 2026-09-01. Gerek: "let's put that in a white box - that way the
 * image of the doctor can have 0px to the bottom, and it looks
 * professional."
 *
 * .card in the markup supplies the white and the radius. What has to be
 * said here is the padding, and its whole point is that the BOTTOM IS
 * ZERO: 40 on top and 48 on the sides, nothing underneath. The
 * photograph is a cut-out on transparency with no frame of its own, so
 * standing it on the card's edge is what makes it read as standing IN
 * the card rather than floating in it. Any bottom padding at all and it
 * hovers.
 *
 * overflow: hidden so the radius clips the photo's bottom corners
 * rather than letting the artwork square them off.
 *
 * --shadow-card, which .about__panel does NOT carry, and the difference
 * is the fill: that panel is a gradient and reads on its own, this one
 * is white on a page whose mint parallax is a 50% wash over near-white.
 * Same call the Prenatal "Why One Adjustment" card made on the same
 * day, for the same reason.
 *
 * align-items stays CENTER for the text column - the photo opts out
 * below rather than the row changing its mind. */
.postpartum-trust {
	grid-template-columns: 329px 1fr;
	align-items: center;
	overflow: hidden;
	padding: var(--space-40) var(--space-48) 0;
	box-shadow: var(--shadow-card);
}

/* end, not the row's centre: this is the "0px to the bottom". The photo
 * is the taller of the two columns and sets the row height, so at
 * today's copy length centre and end land in the same place - but the
 * moment the text column grows past 389px they would not, and the photo
 * would lift off the edge without a word of warning. */
.postpartum-trust__photo {
	display: block;
	align-self: end;
	width: 100%;
	height: auto;
}

/* Figma 595:795 centres the heading, the lead, the comparison row and
 * the button in one column. .expect__compare is already margin: 0, so
 * it needs centring in the flow rather than restyling. */
.postpartum-plan {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-24);
	text-align: center;
}

/* ---- 34.4  responsive ----
 *
 * 991 is where .split and the alternating rows across this site already
 * stack, and the two-column rows here hit the same wall for the same
 * reason: a fixed-width image beside a text column stops fitting before
 * the container does. */
@media (max-width: 991px) {
	.postpartum-body__row {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--space-32);
	}

	.tint-panel {
		flex: 1 1 auto;
		max-width: none;
	}

	/* !! THE STACKED ILLUSTRATION CENTRES, the same fix Pediatric's row
	 * took on 2026-09-01 and found the same way - by looking rather than
	 * by being told. Once the row becomes a column the panel goes full
	 * width and a 162px drawing was left ranged against its left edge,
	 * which reads as artwork that failed to load into a wider slot
	 * rather than as a placement.
	 *
	 * align-self on the child, not align-items on the row: flex-start is
	 * still correct for the panel itself, which has to fill the width.
	 * Prenatal and Pediatric both centre their stacked illustration, so
	 * all three service pages now agree. */
	/* !! THE ILLUSTRATION LEADS THE STACK, 2026-09-01. Gerek, on all
	 * three service pages at once: "after the hero, we have the colored
	 * box, then the icon, and then the next section starts with another
	 * icon. On both tablet and mobile the icons are right after each
	 * other. Reverse it, so the icon comes first and then the colored
	 * box follows - that way we are breaking up the icon versus the
	 * content."
	 *
	 * He is describing a collision the desktop layout hides. Side by
	 * side, this illustration sits to the RIGHT of the panel and the next
	 * section's centred mark is a whole section away. Stacked, the
	 * illustration lands at the BOTTOM of this section and that mark at
	 * the TOP of the next one, so the two pieces of line art end up
	 * adjacent with only a section boundary between them - two drawings
	 * in a row and no words. Leading with it puts the panel's copy
	 * between them instead.
	 *
	 * order: -1 rather than reordering the markup. Source order is
	 * panel-then-illustration on all three pages and that is correct for
	 * a screen reader and for desktop: the panel carries the H2 that
	 * names the section, and the illustration is decorative and
	 * aria-hidden. Moving the <img> in the template would put a hidden
	 * decoration ahead of the heading in the document for the sake of one
	 * breakpoint. A visual order property is exactly the tool for a
	 * visual-only reversal.
	 *
	 * 991, not 767: "on both tablet and mobile", and 991 is where all
	 * three of these rows stack, so it is the same boundary the collision
	 * appears at. */
	.postpartum-body__illustration {
		order: -1;
		align-self: center;
	}

	.postpartum-trust {
		grid-template-columns: 1fr;
		/* !! THE BOTTOM PADDING COMES BACK WHEN THE ROW STACKS, and this
		 * is not a stray value - it is the flush-photo trick being
		 * switched off. Stacked, the photo is FIRST and the text column
		 * is what reaches the card's bottom edge, so a 0 there would put
		 * running copy hard against the card border. 40 all round instead,
		 * matching the top. */
		padding: var(--space-40);
	}

	.postpartum-trust__photo {
		max-width: 329px;
		/* Centred rather than ranged left once it is a lone image above a
		 * full-width column - the same call the illustration above it
		 * takes at this width. */
		margin-inline: auto;
	}
}

@media (max-width: 767px) {
	/* 40 all round is right on a 786px card and wrong on a 342px one: it
	 * leaves a 262px measure. The block axis keeps most of the air at 32
	 * and the inline drops to the container's own 24px gutter, which is
	 * the step .card--pad and Pediatric's .photo-panel both take at this
	 * width for the same reason. */
	.postpartum-trust {
		padding: var(--space-32) var(--space-24);
	}
}

@media (max-width: 600px) {
	/* The panel's 48 inline padding is Figma's 56 stepped down once; at
	 * 320 it leaves a 161px measure inside a 257px card. 16 is what
	 * .about-hero__frame steps to at this width for the same reason. */
	.tint-panel {
		padding-inline: var(--space-16);
	}
}


/* ==================================================================
 * 35  PEDIATRIC CHIROPRACTIC  (Figma 448:628)
 *
 * "(unverified) Pediatric Chiropractic — Option B: Accent Only" - the
 * FOURTH frame to carry that label, after Prenatal, Postpartum and
 * this page's own earlier read. Recorded, not acted on. This is the
 * last page in the sitemap, so the Option B question is now fully
 * evidenced and can be decided; it is still deferred here, alongside
 * the other two items parked since the start of the build.
 *
 * !! MOST OF THIS PAGE IS NOT HERE. The hero is .page-hero (18) with
 * four custom properties. The periwinkle panel is .tint-panel from
 * section 34 with ONE property changed - the component built for
 * Postpartum took its second page without a line of new geometry. The
 * comparison row is .expect__compare / .expect-point (15), matching
 * Figma's 806 / 20 / 26px exactly. The closing banner is .ready__panel
 * (13). Section 35 adds one shared component lifted out of Prenatal,
 * one grid modifier, one behaviour-identical knob, and the page's own
 * four values.
 * ================================================================== */

/* ---- 35.1  .tint-panel goes periwinkle ----
 *
 * No new component. Figma 682:134 is section 34's .tint-panel with a
 * different fill: same 795 ceiling, same 24 radius, same 30/56
 * padding, same 16 gap, same dot matrix, and a shadow of
 * 0 4px 30px rgba(51,48,47,0.15) which is --shadow-card to within its
 * -10px spread. The gradient is Figma's own, read off the node:
 * linear-gradient(256.72deg, #c8ceff 25.338%, rgba(255,255,255,0.7)
 * 142.98%).
 *
 * 256.72 is NOT rounded the way Postpartum's 269.796 was. That one sat
 * a fifth of a degree off a cardinal and was plainly meant to be
 * straight; this one is a real diagonal with no round number nearby,
 * so it is carried as drawn.
 *
 * #c8ceff is --accent-periwinkle EXACTLY - the token deleted on
 * 2026-08-20 and restored on 2026-08-21 for "the next page to want a
 * periwinkle panel". This is that page. The 142.98% stop is also
 * Figma's: a handle dragged past the box, which CSS accepts. */
.tint-panel--periwinkle {
	--tint-panel-gradient: linear-gradient(
		256.72deg,
		var(--accent-periwinkle) 25.338%,
		rgba(255, 255, 255, 0.7) 142.98%
	);
}

/* ---- 35.2  .photo-panel - the photo-beside-text card ----
 *
 * !! LIFTED OUT OF PRENATAL RATHER THAN COPIED FROM IT, the same way
 * .tint-panel was on Postpartum. Figma 448:645 is
 * .prenatal-waiting__panel to the pixel - white card, radius 24, a
 * flat 20 padding, a 480 x 340 cover-cropped photo at radius 24, and a
 * text column that fills the remaining width and the card height with
 * 48 inline padding, gap 16, contents centred on the cross axis.
 * Figma 448:635 is the same card with the columns swapped and a
 * 400 x 500 photo.
 *
 * Neither carries a shadow: both are bg-white with no effect, which is
 * .card without .card--raised. That is why .card--raised is opt-in.
 *
 * !! .prenatal-waiting__* IS NOT MIGRATED TO IT IN THIS BUILD. Same
 * reasoning as sections 34.1 and 34.2: rewriting page-prenatal.php's
 * markup inside a build meant to add a page is the change that makes a
 * geometry diff meaningless. MIGRATION IS RECORDED AND OWED. B-list.
 *
 * Knobs:
 *   --photo-panel-w   photo column width   (office 480, tomato 400)
 *   --photo-panel-h   photo height         (office 340, tomato 500)
 *
 * Both are drawn boxes rather than scale steps, which is the same
 * exception .prenatal-waiting__photo already takes for its 480 x 340. */
/* !! THE PADDING IS 40, NOT .card--flush's 20, AS OF 2026-09-01. Gerek,
 * on both of these panels: "the image and the H2 feel a little too
 * close to the top edge. It's only like 20px of space. It needs a
 * little more breathing room. Whatever we do for the top, we need to
 * match the bottom."
 *
 * Measured before: 20px on all four sides, top and bottom already equal
 * - so the ask is not to rebalance them, it is that 20 is too tight for
 * a card this large. 40 is the next step up that is still on the
 * approved scale, and being a single value it keeps top and bottom
 * matched by construction rather than by two numbers agreeing.
 *
 * ALL FOUR SIDES, not padding-block alone, and that was tried first.
 * The photograph is INSET inside this card rather than bled to its
 * edge, so growing only the block axis left it 40 from the top and 20
 * from the side - a visibly uneven margin around one rectangle. Even on
 * all four reads as a deliberate inset; uneven reads as a mistake.
 *
 * IT OVERRIDES .card--flush, which the markup still carries and which
 * is still correct for Prenatal's waiting-room panel - that panel is
 * NOT touched here. Section 35 is appended after section 18, so this
 * wins on source order at equal specificity, which is the whole point
 * of the append-only rule. .photo-panel is used on this page and
 * nowhere else, so nothing else can be caught by it. */
.photo-panel {
	display: flex;
	align-items: flex-start;   /* Figma counter axis MIN */
	padding: var(--space-40);
}

.photo-panel__photo {
	display: block;
	flex: none;
	width: var(--photo-panel-w, 480px);
	height: var(--photo-panel-h, 340px);
	object-fit: cover;
	border-radius: var(--radius);
}

.photo-panel__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;   /* Figma primary axis CENTER */
	gap: var(--space-16);
	flex: 1 1 0;
	min-width: 0;
	align-self: stretch;
	padding-inline: var(--space-48);   /* Figma's own 48 */
}

.photo-panel__title,
.photo-panel__body {
	margin: 0;
}

.photo-panel__body {
	color: var(--neutral-muted);
}

.photo-panel__body + .photo-panel__body {
	margin-top: var(--space-16);
}

/* Figma 448:635 puts the text first and the photo second. Source order
 * already says that, so the modifier exists only to name the case for
 * the responsive block below, where BOTH variants have to stack
 * photo-first to keep the two sections reading the same way down the
 * page - the master document's zigzag rule. */
.photo-panel--reverse .photo-panel__photo {
	order: 1;
}

/* ---- 35.3  the credential trio ----
 *
 * Figma 687:126 is three equal cards, gap 24, each white / radius 24 /
 * padding 24 / gap 16 with a mark above a paragraph. .proof-card is
 * already that component - white, radius, --shadow-card, column, gap
 * 16 - and Figma's shadow here is 0 4px 30px -10px rgba(51,48,47,0.15),
 * which is --shadow-card to the digit. Two things differ and both get
 * a modifier rather than a rewrite.
 *
 * .proof-grid is repeat(5, 1fr) for the Products row of five and
 * --pairs is About's 2 x 3. Three across is this page's. */
.proof-grid--trio {
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-24);   /* Figma's own 24, and a scale step */
}

/* The base card packs its two children apart with space-between across
 * a 199 minimum, which is right for a tall vertical card and wrong
 * here: Figma stacks mark and paragraph at the TOP with a 16 gap and
 * lets the box run to its content. */
.proof-card--top {
	justify-content: flex-start;
	min-height: 0;
	padding: var(--space-24);   /* Figma's own 24; the base is 20 */
}

/* !! THE THREE MARKS ARE THREE DIFFERENT SHAPES AND THAT IS THE POINT.
 * .proof-card__logo pins every logo to one 40px baseline, which is
 * what the Products and About rows need - five and six marks in a line
 * have to agree. Figma does not do that here: the licence wreath is
 * drawn 60 x 50, Google 40 x 40, YouTube 52 x 36. Three marks in three
 * boxes, each sized on its own.
 *
 * So this is a sibling of .proof-card__logo, not an override of it,
 * and the height comes in per card as --proof-mark-h rather than being
 * typed into the component. */
.proof-card__mark {
	display: block;
	/* !! MEASURED, NOT GUESSED. Without this the mark came out 300 wide
	 * against Figma's 60. .proof-card is a COLUMN flex container with no
	 * align-items, so a child stretches across it; .proof-card__logo
	 * never hits this because every card that uses it is
	 * .proof-card--row, where the logo is a row item and shrinks to its
	 * content. object-fit kept the picture looking right at the left
	 * edge, which is exactly how a stretched box hides. */
	align-self: flex-start;

	width: auto;
	max-width: 100%;
	height: var(--proof-mark-h, 40px);
	object-fit: contain;
	object-position: left center;
}

/* ---- 35.4  the closing banner takes a height ----
 *
 * !! BEHAVIOUR-IDENTICAL CHANGE TO AN EXISTING RULE, and the geometry
 * diff is the proof. .ready__panel--tall was written on the Postpartum
 * build with a hard 440. Figma draws this page's banner at 390 with
 * the same #e6e6e6 fallback and the same rgba(10,10,10,0.4) scrim -
 * verified on 448:651 / 448:653 for this page rather than assumed - so
 * the height becomes a custom property whose DEFAULT IS STILL 440.
 * Postpartum sets nothing and renders exactly as before.
 *
 * This also sharpens the migration note in 34.2: four pages now want
 * the deep scrim and three different heights (Prenatal 440, About 440,
 * Postpartum 440, Pediatric 390), so the eventual shared component
 * must take the height as a knob. It now does. */
.pediatric-cta {
	--ready-panel-h: 390px;   /* Figma 448:651 */
}

/* ---- 35.5  page-scoped values ---- */
/* !! --hero-backdrop-w AND --hero-backdrop-w-sm ARE GONE, 2026-09-01.
 * They sized an <img class="page-hero__backdrop"> that no longer
 * exists: this page's periwinkle artwork was promoted to a full-width
 * fixed .page-bg layer at Gerek's request, so the hero-local copy of it
 * was deleted rather than doubled. Two custom properties nothing reads
 * are worse than none - they read as a working knob. See the note at
 * the top of page-pediatric.php. */
.pediatric {
	--squiggle-color:     var(--accent-indigo);
	--hero-watermark-w:   220px;   /* Figma 449:1490 */
}

/* Second instance of the pair .prenatal-aeo__text introduced, and the
 * two declarations are identical. Recorded as a generalisation
 * candidate rather than generalised: folding them into a shared
 * .hero__aeo--wide would mean editing page-prenatal.php, which this
 * build does not do. B-list, and it now sits with three others. */
.pediatric-aeo__text {
	max-width: var(--container-narrow);
	margin-bottom: 0;
}

/* Figma 449:2313 packs the panel and its illustration with a 66 gap.
 * 66 is not a step; 64 would be --space-32 doubled and 48 is the top
 * step. The row has 79 of slack in Figma (795 + 66 + 246.7 = 1107.7 in
 * a 1140 row), so 48 is spent rather than a raw number typed in - the
 * same call the Postpartum row made for its 103. */
.pediatric-diff__row {
	display: flex;
	align-items: center;
	gap: var(--space-48);
}

.pediatric-diff__illustration {
	display: block;
	flex: 0 0 auto;
	width: 247px;   /* Figma 246.732, a drawn box not a scale step */
	height: auto;
}

/* !! 75/25 AT DESKTOP, 2026-09-01 - the same fix Prenatal's row got the
 * same day, for the same reason and with the same arithmetic.
 *
 * The two fixed widths were drawn against Figma's 1140 row and the
 * container is now 1400. Measured on the live page at 1440: a 795 panel
 * plus a 48 gap plus a 247 illustration is 1090 inside a 1352 row, so
 * 262px trailed off the right with nothing in it. The panel sat left,
 * the illustration floated in the middle of that emptiness, and the
 * pair read as unbalanced. That is why the illustration looked small -
 * it was not; the space beside it was large.
 *
 * Both columns now take a share, with the 48px gap split evenly out of
 * the two so 75 + 25 still adds to the full row: 75% - 24 and 25% - 24.
 * At 1440 that is a 990 panel and a 314 illustration against 795 and
 * 247.
 *
 * THE PANEL IS SET THROUGH .tint-panel's OWN KNOB, not by overriding
 * its flex and max-width separately. --tint-panel-max is the ceiling
 * the component already reads for both, so one custom property does
 * what two overrides would - and the shared component is untouched, so
 * Postpartum's row keeps its 795 exactly.
 *
 * !! ASSET RESOLUTION, flagged rather than acted on:
 * pediatric-pillar-illustration-pediatric.webp is 493 x 343. At the old
 * 247 slot it was a clean 2x; at 314 it is 1.57x. Still a downscale at
 * every width - the container caps at 1400, so this column never
 * exceeds 314 - and comfortably crisper than Prenatal's equivalent,
 * which Gerek accepted at 1.21x.
 *
 * MIN-WIDTH, not an unconditional rule: the row already stacks at 991
 * in 35.6 below, and this file is append-only, so an unconditional rule
 * here would beat that media query on source order and follow the
 * stacked column down to a phone. A 992 floor cannot reach it. */
@media (min-width: 992px) {
	.pediatric-diff__row .tint-panel {
		--tint-panel-max: calc(75% - var(--space-24));
	}

	.pediatric-diff__illustration {
		flex: 0 0 calc(25% - var(--space-24));
		width: calc(25% - var(--space-24));
	}
}

/* Figma 656:283 centres an 88 x 81.4 line-art mark above the heading,
 * with the whole block centred and no card behind it. Identical to
 * Postpartum's clearance section, which is why the two rules say the
 * same thing - a third instance would be worth a shared name. */
.pediatric-brings {
	text-align: center;
}

/* 120, up from Figma's 88 (656:283), at Gerek's request 2026-09-01 -
 * "the icon in this next section needs to be bigger." The file is
 * 176 x 163, so the mark now draws at 1.47x rather than a clean 2x.
 * Flat line art carries that comfortably, and at 88 it was reading as
 * an afterthought over a 34px heading rather than as its mark.
 *
 * !! POSTPARTUM'S CLEARANCE SECTION DRAWS THE SAME MARK AT 88 AND IS
 * DELIBERATELY LEFT THERE. The comment two rules down already notes
 * that these two sections say the same thing and a third instance would
 * earn a shared name; that is still true, and it is now also true that
 * they have DIFFERENT numbers. That page has not been reviewed. When it
 * is, the two should be one component with one size. */
.pediatric-brings__icon {
	display: block;
	width: 120px;
	height: auto;
	margin: 0 auto var(--space-24);
}

/* !! THE TEXT BLOCK WAS NOT CENTRED, AND text-align WAS HIDING IT.
 * Gerek 2026-09-01: "that whole section needs to be centered, which
 * it's not. It's caged to the left with the right open."
 *
 * Measured on the live page at 1440: the section is 1352 wide and
 * centred, and the mark above is centred inside it - but .prose carries
 * `max-width: var(--measure-wide)` (1100px) with NO auto margins, so
 * the block sat flush left and left 252px open on the right. Every line
 * inside it is centre-aligned, which is exactly what made this hard to
 * see: the copy looked centred, just centred on the wrong axis - 594
 * instead of 720. The mark WAS on 720, so the two disagreed by 126px.
 *
 * !! THE 1100 MEASURE IS LEFT ALONE, AND THAT REVERSES A NARROWING THIS
 * RULE BRIEFLY CARRIED. The first version also capped this block at
 * --measure-card, on the reasoning that 1100px of centred text is a
 * long way for the eye to travel back and that Prenatal's centred
 * block sets at 840.
 *
 * Both halves of that were out of date. --measure-card IS 1100 as of
 * 2026-09-01 - it was 840 against Figma's 1092 content box, and the
 * x1.4667 body rescale plus the 1100 reading ceiling Gerek set the same
 * day put it exactly where --measure-wide already was. So the rule was
 * a no-op dressed up as a decision, and Prenatal's centred copy is at
 * 1100 too, on a page Gerek has approved. Narrowing this one would have
 * made the two pages disagree rather than agree.
 *
 * The 1100 ceiling is his call and it is applied consistently. Only the
 * centring was wrong here, so only the centring is fixed. */
.pediatric-brings .prose {
	margin-inline: auto;
}

/* Figma 595:128 centres the heading, the lead, the comparison row and
 * the button in one column - the same shape Postpartum's 595:795 draws
 * and .postpartum-plan already describes. Second instance; recorded,
 * and a third would earn a shared name. */
.pediatric-fit {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-24);
	text-align: center;
}

/* ---- 35.6  responsive ----
 *
 * 991 is where .split, the alternating rows and Postpartum's panel row
 * already stack, for the same reason: a fixed-width image beside a
 * text column stops fitting before the container does. */
@media (max-width: 991px) {
	.pediatric-diff__row {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--space-32);
	}

	.tint-panel {
		flex: 1 1 auto;
		max-width: none;
	}

	/* !! THE STACKED ILLUSTRATION CENTRES, found in the 2026-09-01 pass
	 * rather than reported. Once the row becomes a column the panel goes
	 * full width and the 247px drawing was left ranged against its left
	 * edge - measured at 390, it ran 24 to 271 with 119px open beside it,
	 * which reads as a drawing that failed to load into a wider slot
	 * rather than as a deliberate placement.
	 *
	 * align-self on the child, not align-items on the row: flex-start is
	 * still correct for the panel itself, which must fill the width.
	 * Prenatal's row already centres its own illustration when it stacks,
	 * so this is the existing answer applied rather than a new one. */
	/* !! THE ILLUSTRATION LEADS THE STACK, 2026-09-01. Gerek, on all
	 * three service pages at once: "after the hero, we have the colored
	 * box, then the icon, and then the next section starts with another
	 * icon. On both tablet and mobile the icons are right after each
	 * other. Reverse it, so the icon comes first and then the colored
	 * box follows - that way we are breaking up the icon versus the
	 * content."
	 *
	 * He is describing a collision the desktop layout hides. Side by
	 * side, this illustration sits to the RIGHT of the panel and the next
	 * section's centred mark is a whole section away. Stacked, the
	 * illustration lands at the BOTTOM of this section and that mark at
	 * the TOP of the next one, so the two pieces of line art end up
	 * adjacent with only a section boundary between them - two drawings
	 * in a row and no words. Leading with it puts the panel's copy
	 * between them instead.
	 *
	 * order: -1 rather than reordering the markup. Source order is
	 * panel-then-illustration on all three pages and that is correct for
	 * a screen reader and for desktop: the panel carries the H2 that
	 * names the section, and the illustration is decorative and
	 * aria-hidden. Moving the <img> in the template would put a hidden
	 * decoration ahead of the heading in the document for the sake of one
	 * breakpoint. A visual order property is exactly the tool for a
	 * visual-only reversal.
	 *
	 * 991, not 767: "on both tablet and mobile", and 991 is where all
	 * three of these rows stack, so it is the same boundary the collision
	 * appears at. */
	.pediatric-diff__illustration {
		order: -1;
		align-self: center;
	}

	.proof-grid--trio {
		grid-template-columns: 1fr;
	}

	/* Both panels stack photo-then-text, so the reversed one drops its
	 * order swap rather than keeping it - the stacked reading order is
	 * identical on both, which is the zigzag rule the alternating rows
	 * on About already follow. */
	.photo-panel {
		flex-direction: column;
		align-items: stretch;
	}

	/* !! order: -1, NOT 0, AND THIS RULE WAS DEAD FOR AS LONG AS IT HAS
	 * EXISTED. Gerek 2026-09-01: "on tablet and mobile, the Ripe Tomato
	 * Standard - I want the image at the top of the section, not at the
	 * bottom."
	 *
	 * The comment above already claims both panels stack photo-first.
	 * They did not. This block reset the photo to `order: 0` intending
	 * "first", but 0 is the INITIAL value and the text column carries it
	 * too - equal order values fall back to document order, and the
	 * reversed panel's markup is text first, photo second. So the reset
	 * un-did the desktop `order: 1` and then changed nothing, and the
	 * photo stayed at the bottom of the stack.
	 *
	 * -1 is the smallest honest fix: it puts the photo in a lower order
	 * group than the default, so it leads regardless of markup. The
	 * unreversed panel is unaffected - its photo is already first in the
	 * DOM and takes no order rule at all - and both sections now read
	 * photo-then-text down a phone, which is what the zigzag rule in the
	 * master document asks for and what the comment above has been
	 * claiming all along. */
	.photo-panel--reverse .photo-panel__photo {
		order: -1;
	}

	/* The fixed box becomes a ratio so the crop is preserved while the
	 * photo takes the card width. --photo-panel-ratio is a third knob
	 * rather than arithmetic on the other two, because those carry px
	 * units and aspect-ratio needs bare numbers. */
	.photo-panel__photo {
		width: 100%;
		height: auto;
		aspect-ratio: var(--photo-panel-ratio, 480 / 340);
	}

	.photo-panel__content {
		padding-inline: 0;
		padding-block: var(--space-24);
	}
}

@media (max-width: 767px) {
	/* The 40 above is right on a card 1352px wide and wrong on one 342px
	 * wide: 80px of side padding inside 342 leaves a 262px measure,
	 * against the 302 this panel had at 20. The block axis keeps most of
	 * the new air at 32 - Gerek's complaint was about top and bottom, and
	 * that is the axis that keeps it - while the inline drops to the
	 * container's own 24px gutter, which is the step .card--pad already
	 * takes at this width for exactly this reason. */
	.photo-panel {
		padding: var(--space-32) var(--space-24);
	}
}

@media (max-width: 600px) {
	/* Matches the step .tint-panel already takes at this width in 34.4:
	 * a 24 inline padding inside a 257px card leaves too little measure,
	 * and 16 is what .about-hero__frame steps to for the same reason. */
	.proof-card--top {
		padding: var(--space-20) var(--space-16);
	}
}
