/* ==========================================================================
   theme.css -- rules that belong to THIS WordPress build, not to the port.

   assets/css/global.css is GENERATED by bin/port-css.py, which rewrites the
   whole file from the reference build. Anything hand-written in there is one
   re-port away from being silently deleted, so theme-specific rules live here
   instead. Enqueued after global.css, so it wins ties without !important.
   ========================================================================== */

/* Pastel #182 -- Dr. Sytek's portrait on /referral-partners.
 *
 * The photo is 1600x2332 (portrait) rendered in a 1:1 .split-section__photo.
 * object-fit: cover with the default 50% 50% shows the middle 68.6% of the
 * frame, i.e. it cuts 366px off the top of a 2332px image -- and the top of her
 * head starts at 6.9% of the height, so the crop takes it off.
 *
 * 12% pulls the window up to start at 3.8%, clearing her head with a little
 * room above it. Scoped to this page: --square is used by 19 photos across 16
 * pages, most of them wide room shots where centre is correct.
 */
.page-referral-partners .split-section__photo--square img {
  object-position: 50% 12%;
}

/* Pastel #164 -- Dr. Sytek's portrait in the .wws-card grid.
 *
 * 1098x1600 (portrait) in a 382x287 landscape frame. cover at 50% 50% shows the
 * middle 51.6% of the frame, i.e. 24.2%..75.8% -- which starts BELOW her eyes
 * (21%) and ends at her lap. Her head is not in the picture at all.
 *
 * 10% starts the window at 4.8%, clearing the top of her head (8%).
 *
 * Keyed to the file rather than a page or an nth-child because the same photo
 * sits in the same component on /new-patients (card 3) and /technology (card 1)
 * with the identical defect, and because a rule that follows the image survives
 * the cards being reordered. If the photo is swapped in wp-admin the rule stops
 * applying, which is the safe direction to fail in.
 */
.wws-card__photo img[src*="dr-lauren-portrait-white-sweater"] {
  object-position: 50% 10%;
}

/* Pastel #177 -- the costs disclaimer under the price cards.
 *
 * The reference centres this one paragraph with an INLINE style:
 *   style="text-align: center; margin: 32px auto 0; max-width: 60ch;"
 * The port drops inline styles, so it came across left-aligned and John read it
 * as the box having changed style. It had.
 *
 * Scoped rather than applied to _section-trailer.php's .lead, because that
 * partial renders the trailing lead for 27 sections and this is the ONLY one
 * the reference centres (checked all 28 reference pages: 1 centred, 26 not).
 * Keyed to the .cost-grid it follows, which is what makes it this paragraph.
 */
.page-costs-and-financing .cost-grid + .lead.reveal {
  text-align: center;
  margin: 32px auto 0;
  max-width: 60ch;
}

/* Pastel #178 -- the button row under the treatments card grid.
 *
 * Another inline style the port dropped: the reference carries
 * style="margin-top: 48px;" on this row on BOTH pages that have it
 * (/who-we-serve/adults and /who-we-serve/teens). Without it the buttons sit
 * flush against the cards, gap measured at 0px against the reference's 48px.
 *
 * 48px is the design system's value for this position -- .treatments__footer,
 * which is the same row in a different wrapper, already hard-codes it.
 */
.treatments-grid + .btn-row {
  margin-top: 48px;
}

/* Pastel #181 -- the note under the referral trust cards.
 *
 * The cards themselves are byte-identical to the reference (32px padding, 24px
 * radius, same shadow, 16px gap, same three columns). The only difference is
 * the note below them: the reference carries style="margin-top: 32px;" and the
 * port dropped it, so it sits flush against the grid.
 *
 * This is the same dropped-inline-style defect as #177 and #178. It affects
 * .flag notes on about a dozen pages with values between 24px and 40px; only
 * this one was raised, so only this one is changed. See the run report.
 */
.page-referral-partners .trust-grid + .flag {
  margin-top: 32px;
}

/* Pastel #68 -- the one-line bio under a team member's role.
 *
 * New to this build: the reference's team cards carry a name and a role only,
 * so there is no ported rule for this and nothing to match. The client sent a
 * line of personality for each of the six ("Loudest laugh, biggest heart"), and
 * her original comment asked for "photos, titles and short bio".
 *
 * Deliberately quieter than .team-card__role, which is the job title and should
 * stay the more prominent of the two.
 */
.team-card__bio {
  font-size: 14px;
  line-height: 1.45;
  color: var(--sytek-ink-soft);
  opacity: 0.85;
  margin: 6px 0 0;
}

/* ==========================================================================
   The rest of the spacing the port dropped (Pastel #177/#178/#181 were three
   of these). The reference carries it in inline style attributes; port-css.py
   does not keep them. Values below are the reference's own, per page, not a
   single guessed default.

   .flag.reveal is the note _section-trailer.php renders at the end of a
   section. The .reveal is what distinguishes it from the .flag inside a cost
   card, which the reference does not give a margin to.
   ========================================================================== */

/* The reference's most common value: 14 of the 22 trailing notes. */
.flag.reveal { margin: 24px auto 0; }

/* Pages the reference gives 32px instead. */
.page-first-visit .flag.reveal,
.page-teens .flag.reveal,
.page-adults .flag.reveal { margin: 32px auto 0; }

/* The FAQ note sits under a long accordion and the reference gives it more. */
.page-faq .flag.reveal { margin: 40px auto 0; }

/* Thank-you centres its text as well as its box. */
.page-thank-you .flag.reveal { margin: 32px auto 0; text-align: center; }

/* about and referral-partners are NOT centred in the reference: margin-top
   only, so the note stays left-aligned under the body copy. */
.page-about .flag.reveal,
.page-referral-partners .flag.reveal { margin: 32px 0 0; }

/* Costs has two trailing leads. The centred one under the price cards is
   handled above; this is the other, which the reference leaves left-aligned. */
.page-costs-and-financing .lead.reveal { margin: 32px 0 0; max-width: 68ch; }
.page-costs-and-financing .trust-grid--cards { margin-top: 32px; }

/* Pastel #186 -- team member roles in brand green.
 *
 * .team-card__role is the job title under the name. It inherited --sytek-ink-soft
 * from the ported rule, which left it competing with .team-card__bio directly
 * beneath it. Green separates the title from the personality line and matches how
 * the eyebrow reads elsewhere on the page.
 *
 * .team-card__bio deliberately stays soft ink, so the two do not both shout.
 */
.team-card__role {
  color: var(--sytek-green);
  font-weight: 500;
}

/* Pastel #183 -- separate the bullet lists from the body copy above them.
 *
 * The reference gets this gap for free: there, the list follows p.lead, whose
 * 32px bottom margin does the work. On this build the list follows a body
 * paragraph instead, and bare paragraphs in a body without .prose have no
 * bottom margin, so the list sat flush against the text.
 *
 * (Removing the empty dl.visit-info that used to sit between them was the other
 * half of this and is fixed in media_prose.php. It was swallowing the space.)
 *
 * 24px rather than the reference's 32: John asked for ~25, it is a value already
 * in the system, and it matches the list's own 24px bottom margin so the block
 * sits evenly between what comes before and after.
 *
 * :not(:first-child) so a list that opens a body does not gain a stray gap.
 */
.split-section__body .creds-list:not(:first-child) {
  margin-top: 24px;
}

/* ==========================================================================
   Pastel batch 2026-09-22
   ========================================================================== */

/* #203, #220, #226, #229, #233, #247 -- whole card clickable.
 *
 * Stretched link, not a wrapping <a>: the card already contains exactly one
 * link (the arrow / the CTA), so giving that link a full-card ::after makes the
 * whole surface a hit target without nesting interactive elements or changing
 * any markup. Hover already keys off the card, so .card-a:hover .card-a__bg
 * keeps working untouched.
 *
 * Both parents already carry position:relative.
 *
 * NO `color: inherit` HERE. The first version set it to kill the UA anchor
 * blue, but `a.card-a` is (0,1,1) and beats `.card-a { color: var(--sytek-cream) }`
 * at (0,1,0), so every card on the site started inheriting the section's dark
 * ink instead of its own cream. Reported as #288, #290, #291, #292, #299.
 *
 * It was never needed: an author rule beats the UA stylesheet whatever the
 * specificity, so .card-a's own colour already wins once the rule is gone.
 */
a.card-a,
a.hero2__slide {
  text-decoration: none;
  cursor: pointer;
}
a.card-a:focus-visible,
a.hero2__slide:focus-visible {
  outline: 3px solid var(--sytek-green);
  outline-offset: 3px;
}

/* #206 -- the wide trust card is the one editorial card in the grid, so it
 * gets the brand mint instead of the white every other card uses. Kept to a
 * background change: the ink stays --sytek-ink, which measures 8.9:1 on
 * #BFEBD6, so nothing here trades contrast for emphasis.
 */
.trust-card--wide {
  background: var(--sytek-green-soft);
}
.trust-card--wide .trust-card__pills .pill {
  background: #FFFFFF;
}

/* #214 -- the hero CTA button goes full-width at mobile, so its meta line
 * ("60-minute visit") reads as orphaned hanging off the left edge. Centred
 * under the button it belongs to. .hero2__intro keeps align-items:flex-start
 * for everything else, hence align-self rather than a parent change.
 */
@media (max-width: 900px) {
  .hero2__meta {
    align-self: center;
    text-align: center;
  }
}

/* #215 -- section rhythm never stepped down below the 900px breakpoint, so a
 * 375px screen carried the same 72px top and bottom: 144px of empty cream
 * between every pair of sections. Halved at phone width.
 */
@media (max-width: 640px) {
  .section { padding: 48px 0; }
  .section--tight { padding: 40px 0; }
}

/* #200 -- an inline accent link for body copy. `.accent` could not do this
 * job: every rule for it is scoped to a heading (.h-section .accent,
 * .hero2__title .accent and so on), so an .accent inside a paragraph renders
 * with no colour at all and looks like plain text.
 * #036E49 on the --sytek-cream-mid these sit on measures 7.4:1.
 */
.link-accent {
  color: var(--sytek-green);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.link-accent:hover,
.link-accent:focus-visible { color: var(--sytek-green-deep); }

/* #225 -- eyebrow labels sitewide, 12px -> 13.5px with slightly tighter
 * tracking so the wider glyphs do not stretch the line.
 *
 * FOUR selectors, not one. Three rules re-declare font-size at 12px and each
 * outranks a bare .eyebrow: .doc .eyebrow, .local__copy .eyebrow and
 * .prose p.eyebrow -- the last of which covers every split-section body, i.e.
 * most inner-page sections.
 *
 * Do not look for these with a line-based grep. `.prose p.eyebrow` puts its
 * selector and its font-size on different lines, so `grep eyebrow | grep
 * font-size` finds nothing and the bump silently applies to about half the
 * site. Ask a browser which rules match instead:
 *   [...document.styleSheets] -> cssRules -> filter(/eyebrow/.test(selectorText))
 */
.eyebrow,
.doc .eyebrow,
.prose p.eyebrow,
.local__copy .eyebrow {
  font-size: 13.5px;
  letter-spacing: 0.07em;
}

/* #277 -- a section whose container holds nothing but a section-head was
 * still getting the full 140px top and bottom. On Costs & Financing that put
 * 194px of content ("Working with your insurance", an eyebrow, a heading and a
 * lead) inside 280px of padding, so the section read as mostly empty.
 *
 * Scoped with :has() rather than a page class so it cannot drift. `.container`
 * must be the section's ONLY child: the first draft checked only that the
 * container held nothing but a head, which also matched every testimonials
 * section, because its 372px marquee is a SIBLING of .container rather than
 * inside it. Those sections have plenty of content and must keep full padding.
 *
 * Browsers without :has() simply keep the old padding, so the fallback is the
 * current design rather than a broken one.
 */
.section:has(> .container:only-child > .section-head:only-child) {
  padding-top: 88px;
  padding-bottom: 88px;
}
@media (max-width: 640px) {
  .section:has(> .container:only-child > .section-head:only-child) {
    padding-top: 36px;
    padding-bottom: 36px;
  }
}

/* #257, #232, #228 -- Dr. Sytek's portrait is 1098x1600, a tall portrait, and
 * every pull-quote renders it into a roughly square box with object-fit:cover.
 * Cover therefore scales to WIDTH and crops vertically, and the inherited
 * object-position of 50% 50% took the crop off the top and bottom equally:
 * about 63px off the top of her head on a 253px box.
 *
 * 25% matches the value .page-hero__photo already uses for the same portrait,
 * so this is the existing convention applied where it was missed rather than a
 * new number. It is on the two containers that actually hold portraits; a
 * landscape image in these boxes crops horizontally and is unaffected.
 */
.pull-quote__photo img,
.split-section__photo img {
  object-position: 50% 25%;
}

/* #238, #270 -- "add space here", both pinned on the top edge of a heading
 * that sits directly under an eyebrow in a split section. The gap was the
 * eyebrow's own 18px bottom margin, which did not grow when the label did.
 *
 * Scoped to the split sections both comments point at, NOT to .eyebrow
 * globally: seven different rules set their own eyebrow margin (16px on
 * section heads, 8px in .visit__intro, 20px in the page hero), so a blanket
 * value lands only on the few eyebrows nothing else claims and makes the
 * spacing less consistent rather than more.
 *
 * The `p` in the selector is load-bearing. `.prose p.eyebrow` is (0,2,1) and
 * a split-section body IS a .prose, so `.split-section__body .eyebrow` at
 * (0,2,0) loses to it and the margin silently stays 18px. Matching the
 * specificity and relying on source order is what actually applies.
 */
.split-section__body p.eyebrow,
.split-section__intro p.eyebrow {
  margin-bottom: 26px;
}

/* #249 -- the one-line summary of Dr. Sytek's story reads as body copy in a
 * column of body copy. Given a green rule and slightly larger type it becomes
 * the pull-out it was written to be, without touching .lead everywhere else.
 */
.about-story .split-section__intro .lead {
  border-left: 3px solid var(--sytek-green);
  padding-left: 20px;
  font-size: 21px;
  line-height: 1.5;
  color: var(--sytek-ink);
}
@media (max-width: 640px) {
  .about-story .split-section__intro .lead {
    padding-left: 16px;
    font-size: 19px;
  }
}

/* #192 -- the desktop nav switched to the drawer at 1100px, but the link row
 * stops fitting well before that. Measured on fresh loads (a resize without a
 * reload reports the OLD layout and gave three wrong readings before this was
 * caught): 1200, 1230 and 1250 all wrap the links onto two rows and push the
 * bar from 101px to 118px; 1270 and up stay on one row.
 *
 * So the drawer now takes over below 1280, the standard breakpoint just above
 * the measured threshold. This restates the whole 1100px block from global.css
 * rather than only the display toggles, so padding and logo size step down at
 * the same moment the links disappear.
 */
@media (max-width: 1279px) {
  .nav { padding: 12px 15px; }
  .nav.is-scrolled { padding: 10px 15px; }
  .nav__links,
  .nav__phone,
  .nav .nav__cta { display: none; }
  .nav .nav__toggle { display: inline-flex; }
  .brand-logo { height: 42px; }
}

/* #224 -- the photo section and .kids-phase are both section--cream, so the
 * 140px+140px between them read as one large empty band and the masked brand
 * mark in .kids-phase::before (inset:0, so its extent IS the section box)
 * floated away from the photo above it.
 *
 * Closing the gap pulls the pattern up to meet the photo.
 *
 * SCOPED TO .page-kids, and that matters. `.kids-phase` reads like a kids-page
 * class and is not: it is on the pull-quote section of FOURTEEN pages. The
 * first version of this rule zeroed padding-top on all of them, which is what
 * #287, #293, #294, #295, #296, #298, #300 and #303 were reporting. Check
 * where a section_class is actually used before keying a rule to it.
 */
.page-kids .section:has(+ .kids-phase) {
  padding-bottom: 32px;
}
.page-kids .kids-phase {
  padding-top: 0;
}

/* #250 -- the optional portrait in a split_prose intro column. Sits under the
 * lead, so the photo lands beside the prose it illustrates rather than above
 * it. Only the About story uses it; every other split_prose leaves the field
 * empty and renders nothing at all.
 */
.split-section__portrait {
  margin: 28px 0 0;
  max-width: 420px;
}
.split-section__portrait img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-card);
}
@media (max-width: 1000px) {
  .split-section__portrait { max-width: 100%; }
}

/* Legal documents (Notice of Privacy Practices, and the privacy policy once
 * it is approved). copy_panel builds its wrapper classes from a field value,
 * so naming the wrapper `legal-doc` INVENTED .legal-doc__layout and
 * .legal-doc__content, and nothing styled them. The result was a full-bleed
 * 1440px column of text running edge to edge with no padding, which is what
 * #304 reported as missing content.
 *
 * A class with no rule is the trap here: the markup looked right in the DOM
 * and the page read as broken. If a wrapper name is ever changed, this block
 * has to change with it.
 */
.legal-doc__layout {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 40px;
}
.legal-doc__content {
  max-width: 72ch;
}
.legal-doc__content h2 {
  font-size: 26px;
  line-height: 1.2;
  margin: 48px 0 16px;
}
.legal-doc__content h2:first-child { margin-top: 0; }
.legal-doc__content p,
.legal-doc__content li {
  font-size: 17px;
  line-height: 1.65;
  color: var(--sytek-ink);
}
.legal-doc__content p { margin: 0 0 18px; }
.legal-doc__content ul { margin: 0 0 18px; padding-left: 22px; }
.legal-doc__content li { margin: 0 0 10px; }
.legal-doc__content address {
  font-style: normal;
  margin: 0 0 18px;
  padding: 20px 24px;
  background: var(--sytek-cream-mid);
  border-radius: var(--r-card);
  line-height: 1.7;
}
@media (max-width: 640px) {
  .legal-doc__layout { padding: 0 20px; }
  .legal-doc__content h2 { font-size: 22px; margin-top: 36px; }
}

/* #286 -- the story line reads as a caption to the childhood photo rather
 * than as a lead above it, so it sits in a green box directly under the
 * portrait. Cream on --sytek-green measures 8.6:1.
 */
.split-section__portrait-caption {
  margin: 0;
  padding: 16px 20px;
  background: var(--sytek-green);
  color: var(--sytek-cream);
  border-radius: 0 0 var(--r-card) var(--r-card);
  font-size: 16px;
  line-height: 1.5;
}
.split-section__portrait img { border-radius: var(--r-card) var(--r-card) 0 0; }

/* #301 -- the FAQ jump links are centred, which on a phone leaves every
 * wrapped row ragged against a left-aligned page. Left-justified below the
 * mobile breakpoint; the desktop centring is unchanged.
 */
@media (max-width: 640px) {
  .faq-filters { justify-content: flex-start; }
}

/* #217 -- the closing-CTA cutout is clipped in the asset itself: row 0 of
 * sytek-ortho-cta-image-cutout.webp is 44.6% opaque, so the crown of her head
 * is missing from the file and no crop can recover it. On desktop the image
 * bleeds off a tall band and reads as intentional; at phone width it sits in a
 * short box where the flat top is obvious.
 *
 * Per Ellen on the thread, hidden below the mobile breakpoint rather than
 * waiting on replacement art. aria-hidden already, so nothing is lost to a
 * screen reader. Delete this block if a cutout with headroom ever lands.
 */
@media (max-width: 640px) {
  .consult__media { display: none; }
  .consult__content { padding-bottom: 56px; }
}

/* #302 -- the same interaction on every service-style card.
 *
 * The homepage carries two card components that look like the same thing and
 * behaved differently: `.card-a` (the treatment cards) zoomed its photo and
 * inverted its arrow on hover, while `.hero2__slide` (the hero carousel cards)
 * did nothing at all. Both are photo cards with an eyebrow, a title, a
 * description and an arrow, and since #203/#218 both are a single link, so
 * there was no reason for one to respond and the other not to.
 *
 * The slide now uses .card-a's exact values: the same 1.06 scale, the same
 * 600ms cubic-bezier(0.16, 1, 0.3, 1), the same arrow inversion and the same
 * 2px nudge. Copied deliberately rather than approximated, so the two read as
 * one component.
 *
 * NOT applied to .aud-card or .trust-card. Neither is a link (.aud-card only
 * contains one), and giving a non-clickable card a hover state advertises an
 * interaction that is not there.
 */
.hero2__slide-photo img {
  transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}
a.hero2__slide:hover .hero2__slide-photo img,
a.hero2__slide:focus-visible .hero2__slide-photo img {
  transform: scale(1.06);
}
a.hero2__slide:hover .hero2__cta-arrow,
a.hero2__slide:focus-visible .hero2__cta-arrow {
  background: var(--sytek-ink);
  color: var(--sytek-cream);
  transform: translate(2px, -2px);
}

@media (prefers-reduced-motion: reduce) {
  .hero2__slide-photo img { transition: none; }
  a.hero2__slide:hover .hero2__slide-photo img,
  a.hero2__slide:focus-visible .hero2__slide-photo img { transform: none; }
  a.hero2__slide:hover .hero2__cta-arrow,
  a.hero2__slide:focus-visible .hero2__cta-arrow { transform: none; }
}

/* #289 -- the hero trust line. Two problems, both only visible on a phone.
 *
 * It is a wrapping flex row of three claims separated by dot spans. At 375px
 * every claim takes a full line, which leaves each separator stranded on its
 * own at the end of a line with nothing after it: two floating dots pointing
 * at nothing. A separator only means anything between two things on the same
 * line, so below the breakpoint the row becomes a stack and the dots go.
 *
 * The weight goes to 500 everywhere. At 400 against --sytek-ink-soft the line
 * reads as disclaimer text rather than as the credentials it carries. The
 * colour is unchanged, it already measures 6.4:1 on white; this is about
 * presence, not contrast.
 */
.hero2__trust { font-weight: 500; }

@media (max-width: 640px) {
  .hero2__trust {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
  .hero2__trust .dot { display: none; }
}

/* #307 -- the referral micro-copy is capped at 40ch, which was fine for the
 * one short line it used to carry. The copy is longer now that it also tells
 * referring dentists not to send clinical detail (#285), so 40ch wrapped it to
 * three lines and left roughly 230px of the row empty to its right.
 *
 * flex:1 lets it take what the row actually has instead of a fixed measure.
 * The cap stays as a max so it can never run to a silly line length on a wide
 * form. Below 800px .ref-form__actions is already a column, so this only
 * affects the side-by-side case.
 */
.ref-form__micro {
  flex: 1 1 auto;
  max-width: 58ch;
}

/* ── 404 heading type ──────────────────────────────────────────────────────
 * The 404 heading rendered at BODY size -- 17px/400, indistinguishable from
 * the paragraph beneath it.
 *
 * The source keeps the entire .nf block in an inline <style> inside 404.html,
 * not in a shared stylesheet, and port-css.py only ports the shared sheets. So
 * `.nf h1 { font-size: clamp(40px, 6vw, 72px); line-height: 1.05 }` never came
 * across; global.css received `.nf__inner h1 { margin: 0 0 16px }` and nothing
 * about type.
 *
 * On the source that omission was invisible, because a bare <h1> still had the
 * user-agent default to fall back on. This theme loads Tailwind preflight,
 * which sets `h1 { font-size: inherit; font-weight: inherit }` -- so there was
 * no default left and the heading silently collapsed into body copy. Exactly
 * the documented shape: a rule keyed to a tag, and a reset that removed the
 * fallback holding it up.
 *
 * font-size and line-height are the source's own values. font-weight is stated
 * explicitly BECAUSE of preflight: the source relied on the UA's bold, which
 * `font-weight: inherit` zeroes out, so porting the font-size alone would have
 * fixed the scale and left the heading at 400.
 *
 * Selector matches global.css's own `.nf__inner h1` (0,1,1) rather than the
 * source's `.nf h1`, so this overrides on load order rather than on weight.
 *
 * The eyebrow is deliberately NOT matched to the source. On the reference,
 * `.nf p` (0,1,1) outranks `.nf__code` (0,1,0), so the source's own 14px
 * eyebrow renders at 18px -- a specificity accident, not a design intent. The
 * theme renders it at the intended 14px and keeps it that way.
 */
.nf__inner h1 {
  font-size: clamp(40px, 6vw, 72px);
  line-height: 1.05;
  font-weight: 700;
  margin: 0 0 20px;
}

/* ── Photo grid ────────────────────────────────────────────────────────────
 * For the /about/ community band: client-supplied event photos under the copy.
 *
 * The section above it carries the heading, so this one has none and sits
 * tight to it -- hence the reduced top padding. It is NOT scoped with :has(),
 * deliberately. A `.section:has(+ .photo-grid-section)` rule would be a sitewide
 * selector written to solve one page's spacing, which is how a pull-quote rule
 * once landed on 14 pages. The padding lives on this section's own class, so it
 * can only ever affect a photo grid.
 *
 * The supplied photos are a mix of landscape and portrait, so every tile is
 * cropped to one ratio. Without that the rows stagger and the block reads as
 * broken rather than as a grid.
 */
.photo-grid-section { padding-top: 0; }

.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.photo-grid__item {
  margin: 0;
  border-radius: 16px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.04);
}

.photo-grid__item img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

@media (max-width: 900px) {
  .photo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}

@media (max-width: 560px) {
  .photo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .photo-grid__item { border-radius: 12px; }
}

/* Pull the grid up to the copy it belongs to.
 *
 * The section above keeps the standard 140px bottom padding, which left the
 * photos reading as a separate band rather than as part of the community
 * section. This IS a `:has(+ ...)` selector, which burned this build once
 * before -- but the difference is what it is keyed to. That regression keyed
 * on .kids-phase, a class on 14 unrelated pages. .photo-grid-section exists
 * only where photo_grid.php renders, so this rule cannot match a section that
 * is not immediately followed by a photo grid. Verified as matching exactly one
 * element across all 28 pages.
 */
.section:has(+ .photo-grid-section) { padding-bottom: 48px; }

/* ── /book/ hero: the one page hero that never got its mobile padding (312) ──
 * global.css reduces hero padding at <=900px with TWO rules:
 *     .page-hero                 { padding-top: 110px }   (0,1,0)
 *     .section--green.page-hero  { padding-top: 110px }   (0,2,0)
 * and sets the desktop value with
 *     .section--soft.page-hero   { padding-top: 180px }   (0,2,0, NO media query)
 *
 * /book/ is the only `section--soft` hero on the site. At phone width the
 * (0,1,0) mobile rule loses to the (0,2,0) desktop one, so /book/ kept 180px
 * while every other hero dropped to 110px. A mobile override exists for the
 * green variant and simply was never written for the soft variant.
 *
 * So the honest answer to "is this consistent?" is no, and it is a specificity
 * bug rather than a design choice. Matching the green rule exactly.
 */
@media (max-width: 900px) {
  .section--soft.page-hero { padding-top: 110px; padding-bottom: 64px; }
}

/* ── /book/ "why come in now" photo: hide at phone width (314) ───────────────
 * Same decision, same reason, as .consult__media in the CTA band: this is the
 * cutout portrait, and at phone width the subject is cropped out of the frame
 * entirely, leaving a ~281px block of flat green. It reads as a layout fault
 * rather than a photo, which is also what made the section feel over-spaced
 * (315) -- the space was the empty image, not the section padding, which is
 * already the same 48px as every band below it.
 */
@media (max-width: 640px) {
  .book-why__media { display: none; }
}

/* ── Nav logo at phone width (316) ───────────────────────────────────────────
 * global.css steps the logo down 56px -> 42px at <=1279px and holds 42px all
 * the way to 375px, so the smallest screen gets the smallest mark on the
 * widest relative gutter. 50px still clears the 69px nav.
 */
@media (max-width: 640px) {
  .brand-logo { height: 50px; }
}

/* ── Home hero trust line: green checks, slightly larger (317) ───────────────
 * Reuses the site's existing check idiom (.creds-list__icon: green disc, cream
 * tick, same 24-box path) at a smaller size, rather than inventing a second
 * check mark. The dot separators are redundant once every item is bulleted by
 * its own check, so they are hidden at all widths -- global.css already hid
 * them below 640px, this just extends that upward.
 */
.hero2__trust { font-size: 15px; }
.hero2__trust .dot { display: none; }

.hero2__trust-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.hero2__trust-check {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--sytek-green);
  color: var(--sytek-cream);
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
}

.hero2__trust-check svg {
  width: 11px;
  height: 11px;
  stroke: currentColor;
  stroke-width: 2.6;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The hero is a green band on some breakpoints; invert there so the tick
   reads, exactly as .section--green .creds-list__icon does. */
.section--green .hero2__trust-check {
  background: var(--sytek-cream);
  color: var(--sytek-green);
}

/* ── Hueston footer credit ───────────────────────────────────────────────────
 * Standing SOP requirement on every site (hueston-wp-launch-audit.md §9.3),
 * and it was absent here. Worth recording WHY it was absent: every guard in
 * bin/ compares the build against the static source, and this credit is in no
 * static build, so no source diff could ever surface it. bin/README.md already
 * named this exact blind spot under "What these cannot cover". The rule existed
 * and nothing mechanical checked it, so check-credit.py now does.
 *
 * Sits inline after the copyright rather than as a third flex child, because
 * .footer__base is justify-content: space-between with flex-wrap: nowrap and a
 * third child squashes the row at phone width.
 */
.footer__credit {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.footer__credit img {
  display: inline-block;
  height: 16px;
  width: auto;
  vertical-align: middle;
}

@media (max-width: 640px) {
  .footer__base { flex-wrap: wrap; }
}

/* ── /book/ "why" band on wide screens (320) ─────────────────────────────────
 * "as the screen gets bigger this text box gets too small".
 *
 * The cause is not the image, it is arithmetic in global.css:
 *
 *     .book-why__content {
 *       padding-left: max(40px, calc((100vw - 1280px) / 2 + 40px));
 *       padding-right: 20px;
 *       max-width: 640px;          <- FIXED
 *     }
 *
 * The left padding grows with the viewport, to keep the copy aligned with the
 * 1280px container gutter. The max-width that contains it does not. So the
 * padding eats the cap: text width is 640 - padding - 20, which is 580px at
 * 1280px wide and only 216px at 2008px. At that point the heading wraps onto
 * four lines and the paragraph is a ribbon. The band gets WORSE the bigger the
 * monitor, which is why it looked fine in every earlier review.
 *
 * Fix: let the cap grow by exactly what the padding grows by, so the TEXT
 * stays ~600px at every width rather than the BOX staying 640px.
 *
 * Then cap the image column, which is what was asked for. The photo is
 * height-driven (height:100%, width:auto) so it widens as the band grows and
 * has no natural limit; object-fit keeps it cropping rather than squashing
 * once a width cap applies.
 */
@media (min-width: 901px) {
  .book-why__content {
    max-width: calc(620px + max(40px, calc((100vw - 1280px) / 2 + 40px)));
  }

  .book-why__media img {
    object-fit: cover;
    object-position: center;
  }
}

@media (min-width: 1600px) {
  .book-why__layout { grid-template-columns: minmax(0, 1fr) minmax(0, 880px); }
  .book-why__media img { max-width: 880px; }
}

/* ── Tertiary CTA buttons stretched full width on mobile (321, 322) ──────────
 * global.css has, both inside @media (max-width: 900px):
 *
 *     .btn--tertiary        { width: auto; }    (0,1,0)
 *     .section-cta .btn     { width: 100%; }    (0,2,0)   <- wins
 *
 * The full-width rule is right for a PRIMARY button: a solid pill spanning the
 * column is the usual mobile pattern. It is wrong for .btn--tertiary, which is
 * an underlined text link, so it rendered as a 335px underline across the whole
 * screen. global.css already tries to say so and simply loses on specificity.
 *
 * Matching (0,2,0) and relying on load order rather than inflating the
 * selector, same as every other override in this file.
 *
 * 31 buttons across 17 pages, not just /book/.
 */
@media (max-width: 900px) {
  .section-cta .btn--tertiary { width: auto; }
}

/* ── Timeline: cream mask overhanging the card (323) ─────────────────────────
 * The "small white line" below the last step is a MASK, not a stray border:
 *
 *     .timeline__step:last-child::before {
 *       top: 48px; bottom: -40px; width: 4px;
 *       background: var(--sytek-cream);
 *     }
 *
 * It paints over .timeline::before, the grey connector, so the line stops at
 * the final marker instead of running on. The trick is sound; the -40px is not.
 * Every other step has padding-bottom: 40px and :last-child drops it to 0, so
 * on the last step that -40px reaches 8px past the card's bottom edge. On a
 * cream page that is invisible. Inside .timeline--card, which is white, it
 * shows as a pale stub poking out under the rounded corner.
 *
 * Fixing the cause rather than clipping it: the connector it masks already
 * stops 24px short (.timeline::before has bottom: 24px), so bottom: 0 still
 * covers it completely and stops at the card edge. overflow: hidden on the card
 * would have hidden the symptom and left a 40px mask hanging in the layout.
 *
 * Scoped to .timeline--card because outside a card the overhang is cream on
 * cream and there is nothing to fix. 14 pages carry this section.
 */
.timeline--card .timeline__step:last-child::before { bottom: 0; }
