/*
 * Site styles. There is no CSS framework and no build step — this file is
 * served exactly as written, so everything the site needs is here or in the
 * site's own additions below.
 *
 * ---------------------------------------------------------------------------
 * FLUID TYPE & SPACE SCALE
 *
 * Every size below is derived from the six anchor numbers in --u-*. Change an
 * anchor and the whole scale moves with it, in proportion. That is the point:
 * retuning a site is editing six numbers, not auditing every value in the file.
 *
 * Each step is a clamp() between its value at the minimum viewport and its
 * value at the maximum, so text and spacing scale smoothly with the viewport
 * instead of jumping at breakpoints. No media queries are needed for sizing.
 *
 * TO RETUNE FOR THIS SITE (see refs/design/DESIGN.md):
 *   --u-min-ratio / --u-max-ratio   how dramatic the jump between sizes is.
 *                                   1.2 (minor third) reads calm and even;
 *                                   1.333 (perfect fourth) reads confident;
 *                                   above ~1.4 gets shouty for a practice site.
 *   --u-min-font / --u-max-font     base body size at each end, in px.
 *   --u-min-vw / --u-max-vw         the viewport range the scale spans.
 *
 * A larger max-ratio with the same min-ratio gives a site that is restrained on
 * a phone and expressive on a desktop, which usually suits a hero-led page.
 */
:root {
    /* --- Anchors: the dial for this site ------------------------------- */
    --u-min-vw: 320;
    --u-max-vw: 1240;
    --u-min-font: 16;
    --u-max-font: 18;
    --u-min-ratio: 1.2;
    --u-max-ratio: 1.25;
    /* Space unit (rem) at each end. Every --space-* step and pair is a fixed
     * multiple of it, so these two knobs move the whole spacing rhythm at once. */
    --u-space-min: 1;
    --u-space-max: 1.25;

    /* --- Derivation. Not usually edited by hand. ----------------------- */
    /* Slope and intercept of the line between the two anchor viewports,
     * expressed so each step can be written as clamp(min, calc(...), max). */
    --u-vw-range: calc(var(--u-max-vw) - var(--u-min-vw));

    /* Type steps, in rem at each anchor. Each step multiplies the previous
     * one by the ratio, so the relationship between sizes is fixed even as the
     * absolute values move. */
    --u-min-0: calc(var(--u-min-font) / 16);
    --u-max-0: calc(var(--u-max-font) / 16);

    --u-min--2: calc(var(--u-min-0) / var(--u-min-ratio) / var(--u-min-ratio));
    --u-max--2: calc(var(--u-max-0) / var(--u-max-ratio) / var(--u-max-ratio));
    --u-min--1: calc(var(--u-min-0) / var(--u-min-ratio));
    --u-max--1: calc(var(--u-max-0) / var(--u-max-ratio));
    --u-min-1: calc(var(--u-min-0) * var(--u-min-ratio));
    --u-max-1: calc(var(--u-max-0) * var(--u-max-ratio));
    --u-min-2: calc(var(--u-min-1) * var(--u-min-ratio));
    --u-max-2: calc(var(--u-max-1) * var(--u-max-ratio));
    --u-min-3: calc(var(--u-min-2) * var(--u-min-ratio));
    --u-max-3: calc(var(--u-max-2) * var(--u-max-ratio));
    --u-min-4: calc(var(--u-min-3) * var(--u-min-ratio));
    --u-max-4: calc(var(--u-max-3) * var(--u-max-ratio));
    --u-min-5: calc(var(--u-min-4) * var(--u-min-ratio));
    --u-max-5: calc(var(--u-max-4) * var(--u-max-ratio));
    /* Display extensions, still the same geometric chain: a site never needs
     * to invent a bigger heading step. */
    --u-min-6: calc(var(--u-min-5) * var(--u-min-ratio));
    --u-max-6: calc(var(--u-max-5) * var(--u-max-ratio));
    --u-min-7: calc(var(--u-min-6) * var(--u-min-ratio));
    --u-max-7: calc(var(--u-max-6) * var(--u-max-ratio));
    --u-min-8: calc(var(--u-min-7) * var(--u-min-ratio));
    --u-max-8: calc(var(--u-max-7) * var(--u-max-ratio));

    /* --- Type scale ---------------------------------------------------- */
    /* step--2 fine print · step-0 body · step-2/3 section headings ·
     * step-4/5 hero display. Use these, not raw px. */
    --step--2: clamp(calc(var(--u-min--2) * 1rem), calc(var(--u-min--2) * 1rem + (var(--u-max--2) - var(--u-min--2)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max--2) * 1rem));
    --step--1: clamp(calc(var(--u-min--1) * 1rem), calc(var(--u-min--1) * 1rem + (var(--u-max--1) - var(--u-min--1)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max--1) * 1rem));
    --step-0: clamp(calc(var(--u-min-0) * 1rem), calc(var(--u-min-0) * 1rem + (var(--u-max-0) - var(--u-min-0)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-0) * 1rem));
    --step-1: clamp(calc(var(--u-min-1) * 1rem), calc(var(--u-min-1) * 1rem + (var(--u-max-1) - var(--u-min-1)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-1) * 1rem));
    --step-2: clamp(calc(var(--u-min-2) * 1rem), calc(var(--u-min-2) * 1rem + (var(--u-max-2) - var(--u-min-2)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-2) * 1rem));
    --step-3: clamp(calc(var(--u-min-3) * 1rem), calc(var(--u-min-3) * 1rem + (var(--u-max-3) - var(--u-min-3)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-3) * 1rem));
    --step-4: clamp(calc(var(--u-min-4) * 1rem), calc(var(--u-min-4) * 1rem + (var(--u-max-4) - var(--u-min-4)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-4) * 1rem));
    --step-5: clamp(calc(var(--u-min-5) * 1rem), calc(var(--u-min-5) * 1rem + (var(--u-max-5) - var(--u-min-5)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-5) * 1rem));
    --step-6: clamp(calc(var(--u-min-6) * 1rem), calc(var(--u-min-6) * 1rem + (var(--u-max-6) - var(--u-min-6)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-6) * 1rem));
    --step-7: clamp(calc(var(--u-min-7) * 1rem), calc(var(--u-min-7) * 1rem + (var(--u-max-7) - var(--u-min-7)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-7) * 1rem));
    --step-8: clamp(calc(var(--u-min-8) * 1rem), calc(var(--u-min-8) * 1rem + (var(--u-max-8) - var(--u-min-8)) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-max-8) * 1rem));

    /* --- Weight -------------------------------------------------------- */
    /* Named by the CSS value, not by a word. "bold" is a claim about how a
     * typeface renders; 700 is what you actually asked for, and two faces
     * disagree about which of their cuts reads as bold.
     *
     * THREE WEIGHTS, DELIBERATELY. Body, emphasis, headline — that is the
     * whole system, and it is expressed by which tokens exist rather than by
     * a rule asking politely. Adding --weight-800 needs a reason: the face
     * has a visibly distinct heavy cut AND 700 alone is not creating enough
     * contrast. If 700 and 800 render identically on screen, that is not one.
     * A flat hierarchy is almost always a spacing or size problem, not a
     * weight problem. */
    --weight-400: 400;  /* body */
    --weight-600: 600;  /* nav, card emphasis, eyebrows */
    --weight-700: 700;  /* headlines, CTAs */

    /* --- Leading ------------------------------------------------------- */
    /* A ladder rather than a derived scale, so it is t-shirt sized like
     * --space-* rather than numbered like --step-*. Tightest to loosest.
     *
     * The relationship worth holding: line-height falls as type size rises.
     * Display type at body leading looks unset; body copy at display leading
     * is unreadable. Pair --leading-xs with --step-4/5 and --leading-l with
     * --step-0. */
    --leading-xs: 1.05;   /* display, very large headlines */
    --leading-s: 1.18;     /* headings */
    --leading-m: 1.3;    /* subheadings, ledes, card copy */
    --leading-l: 1.5;     /* body copy */
    --leading-xl: 1.6;   /* long-form and small print, which need more air */

    /* --- Tracking ------------------------------------------------------ */
    /* Same shape: a ladder, tightest to loosest, in em so it scales with
     * whatever size it is applied to.
     *
     * The relationship: tracking tightens as size grows, and ALL-CAPS always
     * needs opening up — uppercase at 0 tracking reads cramped at any size.
     * Pair --tracking-xs with --step-4/5, --tracking-m with body, and
     * --tracking-l or --tracking-xl with any text-transform: uppercase. */
    --tracking-xs: -0.02em;  /* large display */
    --tracking-s: -0.01em;   /* headings */
    --tracking-m: 0;         /* body — the default, stated so it can be reset to */
    --tracking-l: 0.04em;    /* small caps-ish, buttons */
    --tracking-xl: 0.08em;   /* eyebrows and other all-caps runs */

    /* --- Space scale --------------------------------------------------- */
    /* Multiples of the space unit (--u-space-min / --u-space-max) rather than
     * the type ratio, so spacing stays predictable while type can be as
     * dramatic as the site needs. DO NOT redeclare these per site: tune the
     * --u-* knobs above and every step and pair follows. */
    --space-3xs: clamp(calc(var(--u-space-min) * 0.25rem), calc(var(--u-space-min) * 0.25rem + (var(--u-space-max) * 0.3 - var(--u-space-min) * 0.25) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 0.3rem));
    --space-2xs: clamp(calc(var(--u-space-min) * 0.5rem), calc(var(--u-space-min) * 0.5rem + (var(--u-space-max) * 0.5 - var(--u-space-min) * 0.5) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 0.5rem));
    --space-xs: clamp(calc(var(--u-space-min) * 0.75rem), calc(var(--u-space-min) * 0.75rem + (var(--u-space-max) * 0.75 - var(--u-space-min) * 0.75) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 0.75rem));
    --space-s: clamp(calc(var(--u-space-min) * 1rem), calc(var(--u-space-min) * 1rem + (var(--u-space-max) * 1 - var(--u-space-min) * 1) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 1rem));
    --space-m: clamp(calc(var(--u-space-min) * 1.5rem), calc(var(--u-space-min) * 1.5rem + (var(--u-space-max) * 1.5 - var(--u-space-min) * 1.5) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 1.5rem));
    --space-l: clamp(calc(var(--u-space-min) * 2rem), calc(var(--u-space-min) * 2rem + (var(--u-space-max) * 2 - var(--u-space-min) * 2) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 2rem));
    --space-xl: clamp(calc(var(--u-space-min) * 3rem), calc(var(--u-space-min) * 3rem + (var(--u-space-max) * 3 - var(--u-space-min) * 3) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 3rem));
    --space-2xl: clamp(calc(var(--u-space-min) * 4rem), calc(var(--u-space-min) * 4rem + (var(--u-space-max) * 4 - var(--u-space-min) * 4) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 4rem));
    --space-3xl: clamp(calc(var(--u-space-min) * 6rem), calc(var(--u-space-min) * 6rem + (var(--u-space-max) * 6 - var(--u-space-min) * 6) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 6rem));
    --space-4xl: clamp(calc(var(--u-space-min) * 8rem), calc(var(--u-space-min) * 8rem + (var(--u-space-max) * 8 - var(--u-space-min) * 8) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 8rem));

    /* --- Space pairs --------------------------------------------------- */
    /* A pair moves from the SMALLER step on a phone to the LARGER one on a
     * desktop, so the gap changes step rather than merely scaling. This is what
     * replaces per-breakpoint section padding: --space-l-2xl is roughly the old
     * "40-48px mobile, 80-96px desktop" rule, expressed once. */
    --space-2xs-xs: clamp(calc(var(--u-space-min) * 0.5rem), calc(var(--u-space-min) * 0.5rem + (var(--u-space-max) * 0.75 - var(--u-space-min) * 0.5) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 0.75rem));
    --space-xs-s: clamp(calc(var(--u-space-min) * 0.75rem), calc(var(--u-space-min) * 0.75rem + (var(--u-space-max) * 1 - var(--u-space-min) * 0.75) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 1rem));
    --space-s-m: clamp(calc(var(--u-space-min) * 1rem), calc(var(--u-space-min) * 1rem + (var(--u-space-max) * 1.5 - var(--u-space-min) * 1) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 1.5rem));
    --space-m-l: clamp(calc(var(--u-space-min) * 1.5rem), calc(var(--u-space-min) * 1.5rem + (var(--u-space-max) * 2 - var(--u-space-min) * 1.5) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 2rem));
    --space-l-xl: clamp(calc(var(--u-space-min) * 2rem), calc(var(--u-space-min) * 2rem + (var(--u-space-max) * 3 - var(--u-space-min) * 2) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 3rem));
    --space-xl-2xl: clamp(calc(var(--u-space-min) * 3rem), calc(var(--u-space-min) * 3rem + (var(--u-space-max) * 4 - var(--u-space-min) * 3) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 4rem));
    --space-l-2xl: clamp(calc(var(--u-space-min) * 2rem), calc(var(--u-space-min) * 2rem + (var(--u-space-max) * 4 - var(--u-space-min) * 2) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 4rem));
    --space-s-l: clamp(calc(var(--u-space-min) * 1.125rem), calc(var(--u-space-min) * 1.125rem + (var(--u-space-max) * 2 - var(--u-space-min) * 1.125) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 2rem));  /* skip-a-step: grid gutter */
    --space-2xl-3xl: clamp(calc(var(--u-space-min) * 4rem), calc(var(--u-space-min) * 4rem + (var(--u-space-max) * 6 - var(--u-space-min) * 4) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 6rem));
    --space-3xl-4xl: clamp(calc(var(--u-space-min) * 6rem), calc(var(--u-space-min) * 6rem + (var(--u-space-max) * 8 - var(--u-space-min) * 6) * (100vw - var(--u-min-vw) * 1px) / var(--u-vw-range) * 16), calc(var(--u-space-max) * 8rem));

    /* --- Measure ------------------------------------------------------- */
    --measure: 65ch;        /* body copy */
    --measure-tight: 45ch;  /* card and caption copy */

    /* =====================================================================
     * COLOUR
     *
     * Three anchors drive the whole palette. Change them to match the
     * practice; everything else follows. See refs/design/DESIGN.md.
     *
     * That is the default path, and most sites never go past it. Underneath,
     * the palette is layered so a site can take more control WITHOUT hand-
     * writing hex values, which is what a migrated site needs:
     *
     *   1. Anchors        the three below. Tune these first.
     *   2. Per-role       --chroma-secondary, --l-tertiary and friends. A
     *                     brand's second colour is a designer's pick, not a
     *                     rotation of the first, so each role carries its own
     *                     hue, chroma and lightness, defaulting to the anchors.
     *   3. Neutrals       --hue-neutral, --chroma-ink, --chroma-ground. Greys
     *                     are their own decision -- see the block below.
     *   4. Polarity       --scheme. One number flips light/dark.
     *
     * Derive WITHIN a role, declare ACROSS roles. This is the same split
     * Bootstrap 5.3 makes (mix() generates each scale; the base of each theme
     * colour is declared) and the opposite of deriving everything from one
     * hue, which cannot express a real brand pair.
     *
     *   --palette-hue         the practice's brand hue, 0-360.
     *                         Roughly: 25 terracotta · 60 ochre · 145 green
     *                         · 195 teal · 240 blue · 275 violet · 340 rose
     *   --palette-chroma      how saturated the brand reads. OKLCH chroma is
     *                         NOT 0-1 — it runs about 0-0.37 in sRGB, and
     *                         above ~0.15 a surface colour starts to shout.
     *                         0.04 muted · 0.09 confident · 0.14 vivid
     *   --palette-secondary-shift
     *                         degrees from the primary to the secondary.
     *                         150-180 gives a complement; 30-60 an analogous
     *                         neighbour that reads calmer.
     * ===================================================================== */
    --palette-hue: 195;
    --palette-chroma: 0.09;
    --palette-secondary-shift: 150;

    --hue-primary: var(--palette-hue);
    --hue-secondary: calc(var(--palette-hue) + var(--palette-secondary-shift));

    /* Secondary and tertiary each carry their OWN chroma and lightness, not
     * the primary's. They default to it, so a site built from this template
     * still tunes with the three numbers above and renders identically.
     *
     * The override exists because a brand's second colour is often a designer's
     * choice rather than a systematic partner: on a migrated site the secondary
     * was measured at 3.6x the primary's chroma and 0.26 lighter in OKLCH
     * (deep forest green against warm terracotta). One shared chroma and one
     * shared lightness cannot express that pair, and forcing it produced a
     * washed-out grey-green beside a dusty mauve.
     *
     * This is the same concession the feedback colours below already make --
     * each of those carries its own chroma because "yellows need more to read
     * as yellow, reds less before they turn fluorescent". Brand colours want
     * the same latitude. */
    --chroma-secondary: var(--palette-chroma);
    --l-secondary: var(--l-solid);

    /* Tertiary: a third brand colour, typically doing button/CTA work, that is
     * neither a tint of the primary nor semantic. Practices routinely have one
     * ("Button Brown", "Morning Orange"). Defaults to an analogous neighbour of
     * the primary so it is harmless when unused. */
    --palette-tertiary-shift: 30;
    --hue-tertiary: calc(var(--palette-hue) + var(--palette-tertiary-shift));
    --chroma-tertiary: var(--palette-chroma);
    --l-tertiary: var(--l-solid);

    /* --- Neutrals: their own hue, not the brand's ---------------------
     *
     * --hue-neutral defaults to the brand hue, so a site that sets nothing
     * keeps the "greys feel of a piece with the accent" behaviour. But it is
     * a SEPARATE dial, because tying greys to the brand hue is not what any
     * mature system does and not what real practices choose:
     *
     *   - Tailwind ships five grey families (slate/gray/zinc/neutral/stone)
     *     precisely so the temperature of the greys is a decision.
     *   - Bootstrap keeps $gray independent of $theme-colors.
     *   - daisyUI declares base-100/200/300 per theme.
     *
     * And measured on our own migrated sites: both chose warm grounds at hue
     * 80-94 despite green brands at 145 and 166 -- roughly 80 degrees off the
     * brand. A green practice does not want green text, and at the default
     * hue 195 the old derivation gave --ink-strong an R-to-B spread of 17,
     * i.e. a visibly blue-black rather than a black.
     *
     * Set --hue-neutral to taste: the brand hue for a tinted-grey scheme, a
     * warm 70-90 for a settled off-black, or leave chroma at 0 for a true
     * neutral. --chroma-ink and --chroma-ground are separate because ink
     * carries tint better than a large ground does -- the same chroma that
     * reads as "warm grey" in a heading reads as "the wrong white" across a
     * full-bleed background. */
    /* +80 deg off the brand, which is what the previous code already did for
     * --ink-strong alone, and what both migrated practices independently
     * chose for their grounds (warm 80-94 against green brands at 145/166).
     * It defaults to the brand hue so that adding this dial changes nothing on
     * an existing site; the interesting values are elsewhere:
     *
     *   var(--palette-hue)              greys tinted toward the brand (default)
     *   calc(var(--palette-hue) + 80)   the offset real practices tend to pick
     *   70-90                           a warm, settled off-black
     *   with --chroma-ink: 0            a true neutral, no hue at all
     */
    --hue-neutral: var(--palette-hue);
    --chroma-ink: 0.012;    /* text. 0 = true grey; 0.03 = clearly tinted */
    --chroma-ground: calc(0.004 + 0.008 * (1 - var(--scheme)));  /* page background */
    --chroma-surface: calc(0.000 + 0.014 * (1 - var(--scheme))); /* raised surfaces */
    --chroma-border: calc(0.008 + 0.007 * (1 - var(--scheme)));  /* rules and outlines */
    /* Grounds carry MORE tint when dark and less when light: 0.014 across a
     * near-black reads as a warm dark, the same 0.014 across a near-white
     * reads as a colour cast. The old code expressed this by declaring the
     * light and dark neutrals separately; here it rides on --scheme. */

    /* Feedback hues are FIXED and do not follow the brand. A danger colour
     * that rotates with the practice's hue stops reading as danger — the
     * entire value of a semantic colour is that it means the same thing on
     * every site. Only the brand colours above derive from --palette-hue. */
    --hue-success: 145;
    --hue-warning: 75;
    --hue-danger: 27;
    --hue-info: 240;

    /* Lightness ramp. Dark mode overrides these below rather than declaring a
     * second palette — in OKLCH, dark mode is largely an inversion of these
     * four numbers. */
    --l-solid: 0.55;   /* solid fills: buttons, badges */
    --l-hover: 0.48;   /* one step darker for hover */
    --l-light: 0.66;   /* one step lighter: gradient partner for --primary */
    --l-muted: 0.92;   /* a touch stronger than pale, for wash gradients */
    --l-pale: 0.96;    /* tinted backgrounds */
    --l-on-solid: 0.99;/* text on a solid fill */

    /* --- Polarity -----------------------------------------------------
     *
     * --scheme is the one number that says which way round the site runs:
     * 1 = dark ink on a light ground (the norm), 0 = light ink on a dark
     * ground. Every neutral below is written as "light value, plus the
     * distance to its dark counterpart, times (1 - scheme)", so flipping the
     * one number inverts the whole set consistently.
     *
     * This is why the dark-mode block further down only sets --scheme and the
     * three brand-lightness values, instead of re-declaring eight neutrals
     * with a second set of hand-picked literals. Two hand-maintained sets
     * drift: the old block never declared --ink-strong at all, so headings
     * stayed at lightness 0.18 -- dark text on a dark ground.
     *
     * A site that should ALWAYS render dark sets --scheme: 0 here and deletes
     * the media query. A site that should never flip deletes the query alone. */
    --scheme: 1;
    color-scheme: light;

    /* Neutral lightness, light-mode value first, dark-mode value second.
     * Edit these to shift contrast; they are dials, not literals buried in
     * the colour definitions. */
    --l-ink-strong: calc(0.18 + 0.79 * (1 - var(--scheme)));
    --l-ink:        calc(0.25 + 0.70 * (1 - var(--scheme)));
    --l-ink-mid:    calc(0.45 + 0.35 * (1 - var(--scheme)));
    --l-ink-soft:   calc(0.54 + 0.12 * (1 - var(--scheme)));
    --l-bg:         calc(0.99 - 0.81 * (1 - var(--scheme)));
    --l-surface:    calc(1.00 - 0.78 * (1 - var(--scheme)));
    --l-border:     calc(0.90 - 0.58 * (1 - var(--scheme)));
    --l-border-strong: calc(0.82 - 0.40 * (1 - var(--scheme)));

    /* Brand */
    --primary: oklch(var(--l-primary, var(--l-solid)) var(--palette-chroma) var(--hue-primary));
    --primary-hover: oklch(var(--l-hover) var(--palette-chroma) var(--hue-primary));
    --primary-light: oklch(var(--l-light) var(--palette-chroma) var(--hue-primary));
    --primary-muted: oklch(var(--l-muted) calc(var(--palette-chroma) * 0.35) var(--hue-primary));
    /* Chroma falls as lightness rises — a pale tint at full chroma reads
     * muddy rather than tinted. */
    --primary-pale: oklch(var(--l-pale) calc(var(--palette-chroma) * 0.25) var(--hue-primary));
    --on-primary: oklch(var(--l-on-solid) 0 0);

    --secondary: oklch(var(--l-secondary) var(--chroma-secondary) var(--hue-secondary));
    --secondary-hover: oklch(calc(var(--l-secondary) - 0.07) var(--chroma-secondary) var(--hue-secondary));
    --secondary-light: oklch(calc(var(--l-secondary) + 0.11) var(--chroma-secondary) var(--hue-secondary));
    --secondary-pale: oklch(var(--l-pale) calc(var(--chroma-secondary) * 0.25) var(--hue-secondary));
    --on-secondary: oklch(var(--l-on-solid) 0 0);

    /* Tertiary, same shape as the two above. */
    --tertiary: oklch(var(--l-tertiary) var(--chroma-tertiary) var(--hue-tertiary));
    --tertiary-hover: oklch(calc(var(--l-tertiary) - 0.07) var(--chroma-tertiary) var(--hue-tertiary));
    --tertiary-pale: oklch(var(--l-pale) calc(var(--chroma-tertiary) * 0.25) var(--hue-tertiary));
    --on-tertiary: oklch(var(--l-on-solid) 0 0);

    /* Aliases for the older names in DESIGN.md, so existing markup and any
     * site copied from an earlier template keep working. */
    --accent: var(--primary);
    --accent-dark: var(--primary-hover);
    --accent-pale: var(--primary-pale);

    /* Feedback. Chroma is per-hue: yellows need more to read as yellow, reds
     * less before they turn fluorescent. */
    --success: oklch(calc(var(--l-solid) - 0.03) 0.13 var(--hue-success));
    --success-pale: oklch(var(--l-pale) 0.03 var(--hue-success));
    --warning: oklch(0.75 0.14 var(--hue-warning));
    --warning-pale: oklch(var(--l-pale) 0.04 var(--hue-warning));
    --danger: oklch(var(--l-solid) 0.17 var(--hue-danger));
    --danger-pale: oklch(var(--l-pale) 0.03 var(--hue-danger));
    --info: oklch(var(--l-solid) 0.11 var(--hue-info));
    --info-pale: oklch(var(--l-pale) 0.03 var(--hue-info));

    /* Neutrals. DESIGN.md asked in prose for "a teal practice and a navy
     * practice want different --ink warmth"; --hue-neutral is that, made a
     * dial instead of a fixed derivation from the brand.
     *
     * Hue and chroma come from the --hue-neutral / --chroma-*
     * dials above, lightness from the --l-* dials, so all three are tunable
     * without editing any of these lines. Ink chroma tapers as text gets
     * lighter, and grounds use the lower --chroma-ground throughout. */
    --ink-strong: oklch(var(--l-ink-strong) calc(var(--chroma-ink) * 2.5) calc(var(--hue-neutral) + 80));  /* headings, dark surfaces */
    --ink: oklch(var(--l-ink) calc(var(--chroma-ink) * 1.25) var(--hue-neutral));
    --ink-mid: oklch(var(--l-ink-mid) var(--chroma-ink) var(--hue-neutral));
    --ink-soft: oklch(var(--l-ink-soft) calc(var(--chroma-ink) * 0.833) var(--hue-neutral));
    --bg: oklch(var(--l-bg) var(--chroma-ground) var(--hue-neutral));
    --surface: oklch(var(--l-surface) var(--chroma-surface) var(--hue-neutral));
    --border: oklch(var(--l-border) var(--chroma-border) var(--hue-neutral));
    --border-strong: oklch(var(--l-border-strong) calc(var(--chroma-border) * 1.25) var(--hue-neutral));

    /* Paired foreground for each ground, the way daisyUI's -content and
     * Bootstrap's -emphasis work. Use --on-bg rather than reaching for --ink
     * directly when the ground itself might be flipped. */
    --on-bg: var(--ink);
    --on-surface: var(--ink);

    /* --- Grid ---------------------------------------------------------- */
    /* Deliberately minimal: the container and gutter are fluid, placement is
     * left to each layout. Use .u-container and .u-grid below. */
    --grid-max-width: 77.5rem;
    --grid-gutter: var(--space-s-l);
    --grid-columns: 12;

    /* --- Radius -------------------------------------------------------- */
    /* Named by intent rather than by number, so a change of scale does not
     * mean auditing every --radius-3 in the stylesheet. */
    --radius-xs: 0.25rem;   /* tags, inline chips */
    --radius-s: 0.5rem;     /* buttons, inputs */
    --radius-m: 0.75rem;    /* cards */
    --radius-l: 1.25rem;    /* panels, modals, hero media */
    --radius-round: 50%;    /* avatars */
    --radius-pill: 999px;   /* pills, badges */
    --control-radius: var(--radius-s);   /* buttons AND fields: a site sets this once, both follow */

    /* --- Border widths ------------------------------------------------- */
    --border-thin: 1px;
    --border-medium: 2px;
    --border-thick: 4px;    /* accent rules, active states */

    /* --- Touch targets ------------------------------------------------- */
    /* The smallest box a finger hits reliably: 44px (WCAG 2.5.5, Apple's
     * HIG). A fixed dimension rather than spacing, so it does not scale. */
    --target-min: 2.75rem;

    /* --- Shadows ------------------------------------------------------- */
    /* Two knobs drive every shadow. --shadow-color takes the brand hue so a
     * raised card reads as part of this practice rather than as generic grey;
     * --shadow-strength is what dark mode adjusts, because a shadow on a dark
     * background needs MORE opacity to be seen, not less. */
    --shadow-color: var(--hue-primary, 220) 20% 15%;
    --shadow-strength: 1%;

    --shadow-s:
        0 1px 2px -1px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 9%));
    --shadow-m:
        0 3px 5px -2px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 3%)),
        0 7px 14px -5px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 5%));
    --shadow-l:
        0 -1px 3px 0 hsl(var(--shadow-color) / calc(var(--shadow-strength) + 2%)),
        0 6px 12px -3px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 3%)),
        0 15px 30px -6px hsl(var(--shadow-color) / calc(var(--shadow-strength) + 5%));

    --shadow-inner-s: inset 0 1px 2px 0 hsl(var(--shadow-color) / calc(var(--shadow-strength) + 6%));
    --shadow-inner-m: inset 0 2px 6px 0 hsl(var(--shadow-color) / calc(var(--shadow-strength) + 9%));

    /* --- Layers -------------------------------------------------------- */
    /* Six values, so z-index is never invented at the call site. */
    --layer-behind: -1;    /* decorative washes sitting behind content */
    --layer-base: 1;
    --layer-raised: 2;      /* hover cards, sticky rows */
    --layer-nav: 3;         /* sticky header */
    --layer-overlay: 4;     /* dropdowns, popovers */
    --layer-modal: 5;       /* dialogs and their backdrop */
    --layer-top: 2147483647;/* skip links, live-region alerts */

    /* --- Motion -------------------------------------------------------- */
    --duration-fast: 120ms;   /* hover, focus */
    --duration-base: 220ms;   /* most transitions */
    --duration-slow: 400ms;   /* entrances */
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
    --reveal-distance: 1.5rem;

    /* Typefaces. Set these per site; everything else references them. */
    --font-body: system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-heading: var(--font-body);   /* override for a display face */
}

/*
 * Dark mode. In OKLCH this is mostly an inversion of the lightness ramp
 * rather than a second palette — the hues and chroma above are unchanged.
 *
 * Two things are not simple inversions and are set explicitly:
 *   - the accent gets LIGHTER (0.68, not 0.55), because a mid-lightness
 *     accent loses contrast against a dark background;
 *   - --shadow-strength rises, because a shadow on a dark surface needs more
 *     opacity to be visible at all.
 *
 * Delete this block for a site that should only ever render light.
 */
/* Priority Health renders light only, as live does: the dark-scheme block that
 * lived here is deleted (the template's own instruction for a light-only site)
 * and color-scheme is pinned above, so UA widgets match. */

/* ═══════════════════════════════════════════════════════════════════════
 * RESET
 *
 * Josh Comeau's Custom CSS Reset (joshwcomeau.com/css/custom-css-reset/),
 * lightly adapted. Normalises browser defaults so the base layer below is
 * building on the same ground in every browser. Leave this alone; make
 * design decisions in the base layer and the tokens above it.
 * ═══════════════════════════════════════════════════════════════════════ */

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

/* Skip link. craft.md requires one as the first element in <body>; without a
 * rule here it renders as a visible link above the header on every site. It is
 * off-screen until focused, then a real control with a visible focus ring. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: var(--layer-top, 2147483647);
    padding: var(--space-xs) var(--space-s);
    background: var(--accent);
    color: var(--on-primary);
    text-decoration: none;
    border-radius: 0 0 0.25rem 0;
}
.skip-link:focus, .skip-link:focus-visible {
    left: 0;   /* the ring is the global one, in Form controls & focus */
}

* { margin: 0; }

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

body {
    -webkit-font-smoothing: antialiased;
    line-height: 1.5;
}

/* Media is block-level and never overflows its container. */
img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
}

/* Form controls inherit typography rather than using browser chrome. */
:where(input, button, textarea, select) { font: inherit; }   /* zero specificity: "Form controls" below must win */

/* Long words and URLs wrap instead of forcing a horizontal scrollbar. */
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }

/* A new stacking context on the root, so app-level layers cannot escape. */
#root, #__next { isolation: isolate; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
 * ELEMENT BASE LAYER
 *
 * Bare elements are styled here so a page is correct with no classes at
 * all. Without this, an unclassed <h2> falls back to the browser default
 * and whoever writes the page patches it with an inline style — which is
 * how a site ends up with a different heading treatment per page.
 *
 * Every heading has a matching .h1-.h6 class. Use the ELEMENT for document
 * structure (SEO, screen readers, outline) and the CLASS for appearance,
 * so an <h1> can look like an eyebrow without lying about the hierarchy:
 *
 *     <h1 class="h4">Still the page's real heading, just smaller</h1>
 *
 * Sites override these; they are a floor, not a straitjacket.
 * ═══════════════════════════════════════════════════════════════════════ */

body {
    font-family: var(--font-body);
    font-size: var(--step-0);
    font-weight: var(--weight-400);
    line-height: var(--leading-l);
    color: var(--ink);
    background: var(--bg);
}

/* Headings ─────────────────────────────────────────────────────────────
 * Margins are top-heavy on purpose: a heading belongs to what follows it,
 * so the space above is larger than the space below. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    margin: var(--space-l) 0 var(--space-s);
    font-family: var(--font-heading);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    letter-spacing: var(--tracking-xs);
    color: var(--ink-strong);
    text-wrap: balance;
}

:where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6) + * {
    margin-top: 0;
}

/* A heading opening its container needs no space above it. */
:where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6):first-child {
    margin-top: 0;
}

h1, .h1 { font-size: var(--step-5); }
h2, .h2 { font-size: var(--step-4); }
h3, .h3 { font-size: var(--step-3); }
h4, .h4 { font-size: var(--step-2); }
h5, .h5 { font-size: var(--step-1); line-height: var(--leading-m); }
h6, .h6 {
    font-size: var(--step-0);
    line-height: var(--leading-m);
    letter-spacing: var(--tracking-m);
    text-transform: uppercase;
}

/* A highlighted phrase inside a heading. One mechanism, not three. */
:where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6) em {
    font-style: italic;
    color: var(--primary);
}

/* Text ─────────────────────────────────────────────────────────────── */
p, ul, ol, dl, blockquote, figure, table, pre {
    margin: 0 0 var(--space-m);
}

/*
 * Line length is a container's job, not every paragraph's — capping each <p>
 * leaves text hugging the left of any column already sized for reading.
 * Use .measure (or set a width on the container) where prose needs it.
 */
.measure { max-width: var(--measure); }
.measure-tight { max-width: var(--measure-tight); }

/* Opening paragraph: larger and looser than body copy. */
.lede {
    font-size: var(--step-1);
    line-height: var(--leading-m);
    color: var(--ink-mid);
}

/* Small label above a heading. */
.eyebrow {
    display: block;
    margin-bottom: var(--space-2xs);
    font-size: var(--step--1);
    font-weight: var(--weight-600);
    letter-spacing: var(--tracking-l);
    text-transform: uppercase;
    color: var(--primary);
}

small, .text-small { font-size: var(--step--1); }

strong, b { font-weight: var(--weight-700); }

a {
    color: var(--primary);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
    transition: color var(--duration-fast) var(--ease-out);
}
a:hover { color: var(--primary-hover); }

ul, ol { padding-left: var(--space-m); }

/*
 * List spacing applies to PROSE lists only. Navigation, button groups and
 * card grids are lists too, and flow margins on those knock flex/grid items
 * out of alignment (the site header) or balloon the spacing (the footer).
 *
 * Presence of a class is not the test — a footer nav is a nav whether or not
 * anyone put a class on it — so navigation context opts out explicitly.
 */
:where(ul, ol) > li { margin-bottom: var(--space-2xs); }
:where(ul, ol) > li:last-child { margin-bottom: 0; }

nav ul, nav ol,
header ul, header ol,
footer ul, footer ol,
menu {
    margin: 0;
    padding-left: 0;
    list-style: none;
}

nav li, header li, footer li, menu li {
    margin-bottom: 0;
}

/* A list explicitly used for layout carries no bullets, indent or margin. */
:where(ul, ol)[class] {
    margin: 0;
    padding-left: 0;
    list-style: none;
}

:where(ul, ol)[class] > li { margin-bottom: 0; }

blockquote {
    padding-left: var(--space-m);
    border-left: var(--border-thick) solid var(--primary-pale);
    color: var(--ink-mid);
    font-style: italic;
}

hr {
    height: 0;
    margin: var(--space-l) 0;
    border: 0;
    border-top: var(--border-thin) solid var(--border);
}

code, kbd, samp, pre {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.9em;
}

/* Media ────────────────────────────────────────────────────────────────
 * Images carry NO radius and NO background plate by default.
 *
 * They used to. `img { border-radius: var(--radius-m); background: var(--surface) }`
 * was added to fix one square photo on Wellbrook, and it cost far more than it
 * saved: every migration since has had to undo it, because a faithful clone of a
 * customer's live site decides its own corners, and the background plate fills
 * the transparent areas of every PNG and SVG — a logo on a dark footer renders
 * as a solid block. Ten of the thirteen sites on this box carried an override
 * for it before it was removed.
 *
 * A radius is a design decision belonging to the site's own stylesheet, next to
 * the cards it should match. Use .is-rounded (below) or a site-layer rule; do
 * not put it back here. */
img, picture, video, canvas, svg {
    height: auto;
}

/* Opt in where a photo should match the radius of the cards around it. */
img.is-rounded, .is-rounded img { border-radius: var(--radius-m); }

figure { margin-inline: 0; }

figcaption {
    margin-top: var(--space-2xs);
    font-size: var(--step--1);
    color: var(--ink-soft);
}

/* Tables ───────────────────────────────────────────────────────────── */
table {
    width: 100%;
    border-collapse: collapse;
    text-align: left;
}

th, td {
    padding: var(--space-2xs) var(--space-xs);
    border-bottom: var(--border-thin) solid var(--border);
    vertical-align: top;
}

th {
    font-weight: var(--weight-600);
    color: var(--ink-strong);
}

tbody tr:last-child :where(th, td) { border-bottom: 0; }

/* ═══ Form controls & focus ═══ form-controls:begin ══════════════════════
 * A text field's look, and every focus ring on the site, are set ONCE,
 * here: by the --field-* and --focus-ring* tokens and a few base rules.
 * Every text-like control on the site (contact, search, careers, booking,
 * anything mediflare.js or Connect writes) inherits it, so they match by
 * construction.
 *
 * - This section is copied VERBATIM from _template and never edited in a
 *   site: check-system.sh fails the site if its copy differs by a character.
 *   A site differs only in TOKEN VALUES, set in the site layer's own :root,
 *   below this section (a later :root wins over the defaults here).
 * - Never restyle a control inside a component. A component rule may place
 *   a field (grid, margin, width) but never sets its border, radius,
 *   background or height -- check-system.sh fails the site if one does.
 * - Fields and buttons share ONE corner, --control-radius: set it in the
 *   site layer's :root and both follow. Never set --field-radius
 *   (check-system.sh fails it), except to 0 in underline mode below;
 *   pill buttons mean pill fields.
 * - The base rules are at zero specificity (:where), so any token change
 *   reaches every field and nothing here outranks a deliberate override.
 * - ONE focus ring per site, on links, buttons and fields alike: an outline
 *   in --focus-ring at --focus-ring-w. No other rule sets a focus outline,
 *   colour or box-shadow (check-system.sh fails one); outline-offset may be
 *   adjusted where a ring would be clipped.
 * - Underline-only fields: set --field-border-w: 0,
 *   --field-border-bottom: var(--border-thin) solid <colour> and
 *   --field-radius: 0 (an underline has no corners to share; a rounded one
 *   curves at its ends, and buttons keep --control-radius); optionally
 *   --field-focus-border-bottom: var(--focus-ring-w) solid var(--focus-ring)
 *   so the underline thickens with the ring. No extra rules. Underline
 *   mode is the only time a site sets --field-radius, and check-system.sh
 *   then requires the 0.
 * - Dark panels and bands: the container re-sets the tokens for its
 *   descendants, never the rules. Tokens defined here are resolved on :root,
 *   so re-set the leaf values, not the ones they derive from:
 *     .band--dark {
 *         --focus-ring: var(--surface);
 *         --field-bg: transparent;
 *         --field-ink: var(--on-primary);
 *         --field-border: var(--on-primary);
 *         --field-placeholder: color-mix(in oklab, var(--on-primary) 70%, transparent);
 *     }
 *   --focus-ring must reach >= 3:1 against every ground it is drawn on.
 * The guard finds this section by the begin/end markers; keep both.
 * ═══════════════════════════════════════════════════════════════════════ */

:root {
    --field-h: var(--target-min);                 /* the single-line height: exact, never below the 44px target */
    --field-textarea-h: calc(var(--field-h) * 3); /* the textarea's minimum height */
    --field-pad-block: var(--space-2xs);          /* textarea only; a one-line field centres its text in --field-h */
    --field-pad-inline: var(--space-xs);
    --field-font-size: max(var(--step-0), 1rem);  /* never below 16px, or iOS zooms the page on focus */
    --field-leading: var(--leading-m);
    --field-weight: var(--weight-400);            /* one weight wherever the field sits */
    --field-radius: var(--control-radius);        /* = the .btn radius, by construction; never set it */
    --field-textarea-radius: min(var(--field-radius), var(--radius-l));   /* a pill textarea is a capsule; cap it */
    --field-bg: var(--surface);
    --field-ink: var(--ink);
    --field-border-w: var(--border-thin);
    --field-border: var(--border-strong);
    --field-border-hover: var(--ink-soft);
    --field-placeholder: var(--ink-soft);
    /* Unset by default, so they follow --field-border-w/--field-border:
     * --field-border-bottom          the resting bottom edge (underline mode)
     * --field-focus-border-bottom    the bottom edge while focused
     * --field-focus-outline          the field's ring; leave it to the default */
    --focus-ring: var(--primary);                 /* >= 3:1 against --bg, --field-bg and every ground it lands on */
    --focus-ring-w: var(--border-medium);         /* >= --field-border-w, so the ring covers the border */
}

/* THE focus ring: every interactive element, always. Fields refine the
 * offset and radius below; nothing else sets a focus outline. */
:focus-visible {
    outline: var(--focus-ring-w) solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

label {
    display: block;
    margin-bottom: var(--space-3xs);
    font-size: var(--step--1);
    font-weight: var(--weight-600);
    color: var(--ink-strong);
}

/* THE base rule. Every text-like control: text, email, tel, search, number,
 * date, url, password, select and textarea -- not checkboxes, radios,
 * ranges, files or button-type inputs. */
:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=submit],[type=button],[type=reset],[type=image],[type=hidden]), select, textarea) {
    --_field-radius: var(--field-radius);
    appearance: none;
    box-sizing: border-box;
    width: 100%;
    height: var(--field-h);
    min-height: var(--field-h);
    padding: 0 var(--field-pad-inline);
    font: inherit;
    font-size: var(--field-font-size);
    font-weight: var(--field-weight);
    line-height: var(--field-leading);
    color: var(--field-ink);
    background-color: var(--field-bg);
    border: var(--field-border-w) solid var(--field-border);
    border-bottom: var(--field-border-bottom, var(--field-border-w) solid var(--field-border));
    border-radius: var(--_field-radius);
    box-shadow: none;
    transition: border-color var(--duration-fast) var(--ease-out);
}

:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=submit],[type=button],[type=reset],[type=image],[type=hidden]), select, textarea):hover {
    border-color: var(--field-border-hover);
}

/* One edge on focus. The offset pulls the ring in by the border's width, so
 * it is drawn ON the border (covering it) rather than outside it; the border
 * and shadow are held at rest so nothing draws a second edge. Specificity
 * 0,1,0 and later than the global :focus-visible, so it wins over that one
 * (whose offset and radius suit links, not fields). In underline mode the
 * bottom edge may take --field-focus-border-bottom as well. */
:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=submit],[type=button],[type=reset],[type=image],[type=hidden]), select, textarea):focus {
    border-color: var(--field-border);
    border-bottom: var(--field-border-bottom, var(--field-border-w) solid var(--field-border));
    box-shadow: none;
    outline: none;
}
:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=submit],[type=button],[type=reset],[type=image],[type=hidden]), select, textarea):focus-visible {
    outline: var(--field-focus-outline, var(--focus-ring-w) solid var(--focus-ring));
    outline-offset: calc(var(--field-border-w) * -1);
    border-bottom: var(--field-focus-border-bottom, var(--field-border-bottom, var(--field-border-w) solid var(--field-border)));
    border-radius: var(--_field-radius);
}

:where(textarea) {
    --_field-radius: var(--field-textarea-radius);
    height: auto;
    min-height: var(--field-textarea-h);
    padding-block: var(--field-pad-block);
    resize: vertical;
}

/* The chevron is one conic-gradient wedge in --field-ink: tokens only, no
 * image, so it recolours with the palette and the scheme. (Two half-square
 * linear gradients leave a seam where they meet.) */
:where(select) {
    padding-inline-end: calc(var(--field-pad-inline) * 2 + var(--space-xs));
    background-image: conic-gradient(from -45deg at 50% 100%, var(--field-ink) 90deg, transparent 0);
    background-position: calc(100% - var(--field-pad-inline)) 50%;
    background-size: calc(var(--space-3xs) * 2.5) calc(var(--space-3xs) * 1.25);
    background-repeat: no-repeat;
    cursor: pointer;
}
:where(select[multiple], select[size]) { height: auto; padding-block: var(--field-pad-block); padding-inline-end: var(--field-pad-inline); background-image: none; }

:where(input, textarea)::placeholder { color: var(--field-placeholder); opacity: 1; }

/* The browser's own clear (x) in a search box, redrawn as a mask so it takes
 * --field-ink instead of the browser's grey. */
:where(input[type=search])::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
    inline-size: 1em;
    block-size: 1em;
    cursor: pointer;
    background-color: var(--field-ink);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 4l8 8M12 4l-8 8' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 4l8 8M12 4l-8 8' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }

fieldset { margin: 0 0 var(--space-m); padding: 0; border: 0; }

/* ═══ form-controls:end (Form controls & focus) ═════════════════════════ */

/*
 * A bare <button> is usually a UI affordance — a menu toggle, a dropdown
 * trigger — not a visual button, so it gets a reset rather than a look.
 * Add .btn when you want one that looks like a button.
 */
button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

/* Buttons ──────────────────────────────────────────────────────────────
 * .btn is opt-in — put it on a <button> or an <a> that should look like a
 * button. Variants change colour, sizes change scale; every one is at least
 * --target-min (44px) tall, the tap target. A bare <button> is
 * reset above, not styled, because most of them are menu and dropdown
 * triggers rather than visual buttons. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3xs);
    padding: var(--space-2xs) var(--space-s);
    font-family: inherit;
    font-size: var(--step--1);
    font-weight: var(--weight-600);
    line-height: var(--leading-s);
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    min-block-size: var(--target-min);   /* 44px tap target, every size and variant */
    cursor: pointer;
    border: var(--border-thin) solid transparent;
    border-radius: var(--control-radius);   /* shared with every field; set --control-radius, not this */
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.btn--primary {
    background: var(--primary);
    color: var(--on-primary);
}
.btn--primary:hover { background: var(--primary-hover); color: var(--on-primary); }

.btn--secondary {
    background: transparent;
    border-color: var(--primary);
    color: var(--primary);
}
.btn--secondary:hover { background: var(--primary-pale); color: var(--primary-hover); }

.btn--ghost {
    background: transparent;
    color: var(--ink-mid);
}
.btn--ghost:hover { background: var(--primary-pale); color: var(--primary); }

.btn--sm { padding: var(--space-3xs) var(--space-xs); font-size: var(--step--2); }
.btn--lg { padding: var(--space-xs) var(--space-m); font-size: var(--step-0); }

/*
 * ON A DARK GROUND
 *
 * Variants above assume a light background. On a dark or brand-coloured
 * section they invert: the solid button goes white, the outline picks up a
 * translucent white edge. Put .on-dark on the button or any ancestor.
 */
.on-dark .btn--primary,
.btn--primary.on-dark {
    background: var(--surface);
    color: var(--primary);
}

.on-dark .btn--primary:hover,
.btn--primary.on-dark:hover {
    background: var(--surface);
    color: var(--primary-hover);
}

.on-dark .btn--secondary,
.btn--secondary.on-dark {
    color: var(--on-primary);
    border-color: color-mix(in oklab, var(--on-primary) 40%, transparent);
}

.on-dark .btn--secondary:hover,
.btn--secondary.on-dark:hover {
    background: color-mix(in oklab, var(--on-primary) 12%, transparent);
    color: var(--on-primary);
}

.btn[disabled], .btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Media frames ────────────────────────────────────────────────────────
 * A frame for an embed — map, video or photo — so the child fills a box of
 * known proportion instead of the element's natural size.
 *
 * The ratio, radius and any framing are DEFAULTS. Change them in this file
 * to suit the design; they are here so a frame is never unstyled, not to
 * fix how it should look. */
.media-frame {
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--radius-m);
    background: var(--surface);
}

.media-frame > :where(iframe, img, video) {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    object-fit: cover;
    border-radius: 0;
}

.media-frame--square { aspect-ratio: 1; }
.media-frame--wide { aspect-ratio: 16 / 9; }

/* Cards ────────────────────────────────────────────────────────────────
 * Deliberately minimal — a surface with a radius. Sites compose their own
 * card variants on top; modelling every one here would be guesswork. */
.card {
    padding: var(--space-m);
    background: var(--surface);
    border: var(--border-thin) solid var(--border);
    border-radius: var(--radius-m);
}

/* Utility ──────────────────────────────────────────────────────────── */
.visually-hidden, .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/*
 * Grid utilities. .u-container centres and pads; .u-grid lays out. Placement
 * within the grid is each layout's business — see refs/design/layouts.md.
 */
.u-container {
    max-width: var(--grid-max-width);
    margin-inline: auto;
    padding-inline: var(--grid-gutter);
}

.u-grid {
    display: grid;
    gap: var(--grid-gutter);
}

/*
 * Section rhythm. EVERY band on a page should be a .section — this is what
 * gives a page its vertical breathing room, and it is the single most
 * repeated review comment when it is missed ("this section is lacking top
 * and bottom padding", raised three separate times on one site).
 *
 * It is a class rather than a bare `section` selector because bands are not
 * always <section> elements, and because a band that deliberately runs flush
 * to its neighbour needs to say so out loud via --flush.
 *
 * Do not hand-roll the padding. --space-l-2xl is the site-wide "40px phone /
 * 80px desktop" step, already a clamp; a bespoke value breaks the rhythm.
 *
 * Verify with bandpad.mjs, which measures the real rendered ink gap between
 * adjacent bands and fails under 40px.
 */
.section { padding-block: var(--space-l-2xl); }
.section--tight { padding-block: var(--space-l); }
.section--flush { padding-block: 0; }

/* A centred intro (eyebrow + heading + standfirst) above a band's content. */
.section__head {
    max-width: 46rem;
    margin-inline: auto;
    text-align: center;
}

/*
 * Scroll entrance.
 *
 * Native scroll-driven animation — no library, no IntersectionObserver, no
 * JS. Add .reveal for a plain fade, or one of the directional modifiers.
 *
 * WHY THE WHOLE BLOCK IS INSIDE prefers-reduced-motion: a reveal implemented
 * as `opacity: 0` plus an animation leaves reduced-motion users staring at
 * invisible content when the animation never runs. Scoping the rule means
 * they simply get the content, with no trap to fall into.
 *
 * Safari and Firefox do not support animation-timeline yet, so content there
 * is simply visible. That is the correct failure for a practice site: no
 * entrance effect is always better than content that never arrives.
 */
@media (prefers-reduced-motion: no-preference) {
    .reveal {
        animation: reveal linear both;
        animation-timeline: view();
        animation-range: entry 0% cover 30%;
    }

    .reveal--up { --reveal-from: translateY(var(--reveal-distance)); }
    .reveal--down { --reveal-from: translateY(calc(var(--reveal-distance) * -1)); }
    .reveal--left { --reveal-from: translateX(var(--reveal-distance)); }
    .reveal--right { --reveal-from: translateX(calc(var(--reveal-distance) * -1)); }
}

@keyframes reveal {
    from {
        opacity: 0;
        transform: var(--reveal-from, none);
    }
}

/*
 * Doctor avatar fallback
 * Shows a gradient + person silhouette when no photo exists.
 * The <img> has onerror="this.style.display='none'" which hides
 * the broken image, revealing this CSS background underneath.
 *
 * Derived from the site's own --accent so a missing photo reads as part of
 * this practice rather than as a stray swatch. Define --accent in :root above.
 */
.doctor-avatar {
    position: relative;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    border-radius: 0.5rem;
    background: linear-gradient(135deg,
        color-mix(in oklab, var(--primary) 85%, black),
        var(--primary));
    display: flex;
    align-items: center;
    justify-content: center;
}

.doctor-avatar::after {
    content: "";
    position: absolute;
    width: 40%;
    height: 40%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='white' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
}

.doctor-avatar img {
    position: relative;
    z-index: var(--layer-base);
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/*
 * Mediflare footer logo — dark/light variants.
 * Default: inverted (white text) for dark footers.
 * Add .footer-light to <footer> for light backgrounds.
 */
.mf-logo-light { display: none; }
.mf-logo-dark { display: inline; }
.footer-light .mf-logo-light { display: inline; }
.footer-light .mf-logo-dark { display: none; }

/* Hours: prevent time strings from breaking between number and am/pm */
.hours-table td:last-child,
.footer__hours-row span {
    white-space: nowrap;
}
/* ── 404 ──────────────────────────────────────────────────────────────
 * Served for any unmatched path, so it stands alone: say what happened, then
 * offer the way back rather than leaving a dead end.
 *
 * Written against the template's shared scale (--step-*, --space-*), never a
 * site's own colour names - every site in the fleet has a different
 * vocabulary, and a 404 that silently loses its layout because one variable is
 * spelt differently is worse than one that is slightly plainer. */
.notfound {
    padding-block: var(--space-3xl) var(--space-2xl);
    text-align: center;
}
.notfound__inner { max-width: 46rem; margin-inline: auto; }

/* Decorative, aria-hidden in the markup - the <h1> carries the meaning. Sized
 * large but kept at a legible contrast: a very pale numeral fails WCAG even
 * when it is ornamental. */
.notfound__code {
    font-size: var(--step-8);   /* the site's display step, as the hero headline */
    font-weight: var(--weight-700);
    line-height: var(--leading-2xs);
    margin: 0 0 var(--space-2xs);
    opacity: 0.55;
}
.notfound h1 { margin-top: 0; }
.notfound__lede { font-size: var(--step-1); line-height: var(--leading-l); margin-inline: auto; max-width: 40rem; }

.notfound__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-s);
    justify-content: center;
    margin-top: var(--space-l);
}

.notfound__links {
    margin-top: var(--space-xl);
    padding-top: var(--space-l);
    border-top: var(--border-thin) solid currentColor;
    /* The rule is a hairline of the surrounding ink rather than a named border
     * token, so it works whatever the site's palette is. */
    border-color: color-mix(in oklab, currentColor 22%, transparent);
}
.notfound__links-title {
    font-size: var(--step-0);
    text-transform: uppercase;
    letter-spacing: var(--tracking-xl);
    margin: 0 0 var(--space-s);
}
.notfound__links ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs) var(--space-l);
    justify-content: center;
}
.notfound__links a {
    display: inline-block;
    min-height: 24px;   /* WCAG 2.5.8 target size */
    line-height: var(--leading-l);
    font-weight: var(--weight-600);
}

/* ── Site search ──────────────────────────────────────────────────────
 * mediflare.js writes the search box wherever the markup asks for one, as
 * plain HTML with stable mf-search__* classes and NO styling. This is a
 * neutral base on the tokens above, so a new site inherits its own palette
 * and can restyle any of it freely in the site layer.
 *
 * The default (partials/header.html) is ONE overlay at every width, opened
 * by .search-trigger: a button drawn as a search field (magnifier and
 * "Search…") from 768px up, a 44px magnifier below. From 768px up the
 * overlay is a centred card over a dimmed page; below, it fills the screen.
 * The alternatives (an icon <details> panel, a visible header box with a
 * dropdown) are documented in /srv/sites/CLAUDE.md ("Site search"); the
 * dropdown's base is kept below because mediflare.js writes its hooks.
 *
 * Style the overlay and the dropdown popup ONLY through :not([hidden]).
 * Giving them a display of their own overrides the browser's
 * [hidden] { display: none }, and they then never close. */

/* The box, wherever it sits. LAYOUT ONLY: the field's look is the base rule
 * in "Form controls" above, exactly like every other field on the site. Do
 * not give .mf-search__input a border, radius, background or height here or
 * in the site layer -- change the --field-* tokens instead. */
.mf-search__form {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-2xs);
    align-items: center;
}

.mf-search__label,
.mf-search__status,
.mf-search__results,
.mf-search__popup { grid-column: 1 / -1; }

.mf-search__label { margin-bottom: 0; }

/* Without a Search button the field takes the whole row. */
.mf-search__input { grid-column: 1 / -1; }
[data-mf-search-submit] .mf-search__input { grid-column: auto; }

.mf-search__submit {
    min-block-size: var(--field-h);   /* the field's height, so the row lines up */
    padding: 0 var(--space-s);
    font-weight: var(--weight-600);
    line-height: var(--leading-s);
    color: var(--on-primary);
    background: var(--primary);
    border-radius: var(--control-radius);   /* the field beside it has the same corner */
    transition: background var(--duration-fast) var(--ease-out);
}
.mf-search__submit:hover { background: var(--primary-hover); }

.mf-search__status {
    margin: 0;
    font-size: var(--step--1);
    line-height: var(--leading-m);
    color: var(--ink-mid);
}
.mf-search__status:empty { display: none; }

.mf-search__results:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: var(--space-3xs);
}

.mf-search__link {
    display: block;
    padding: var(--space-2xs) var(--space-xs);
    border-radius: var(--radius-s);
    color: var(--ink);
    text-decoration: none;
    overflow-wrap: anywhere;
    transition: background var(--duration-fast) var(--ease-out);
}
.mf-search__link:hover,
.mf-search__link:focus-visible {
    color: var(--ink);
    background: var(--primary-pale);
}
/* Inside a scrolling list an outset ring is clipped, so it sits inset. */
.mf-search__link:focus-visible { outline-offset: calc(var(--border-medium) * -1); }

.mf-search__title {
    display: block;
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--ink-strong);
}

.mf-search__excerpt {
    display: block;
    margin-top: var(--space-3xs);
    font-size: var(--step--1);
    line-height: var(--leading-m);
    color: var(--ink-mid);
}

.mf-search__excerpt mark {
    color: var(--ink-strong);
    background: color-mix(in oklab, var(--primary) 18%, transparent);
    border-radius: var(--radius-xs);
}

/* In the header and the overlay the label is read out, not shown: the
 * placeholder says what the field is. Hidden visually, never removed. */
.mf-search--dropdown .mf-search__label,
.mf-search--overlay .mf-search__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.mf-search__popup .mf-search__results:not([hidden]) {
    max-block-size: 60dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* The trigger, drawn as a search field: its border, background and corner
 * are the --field-* tokens, so it matches every field on the site. It is a
 * button, so its focus ring is the global one; only the offset moves onto
 * its border, as a field's does. The ellipsis is drawn, not spoken.
 *
 * In a tight header band shorten it rather than let it wrap the nav, the
 * booking button or the logo: drop the ellipsis (content: none) and step
 * the padding down, or use the 44px magnifier below. The header's height
 * must not change. */
.search-trigger {
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: var(--space-2xs);
    min-inline-size: calc(var(--target-min) * 4);
    min-block-size: var(--field-h);
    padding: 0 var(--space-m) 0 var(--field-pad-inline);
    font-size: var(--step--1);
    line-height: var(--leading-s);
    white-space: nowrap;
    color: var(--ink-mid);
    background: var(--field-bg);
    border: var(--field-border-w) solid var(--field-border);
    border-radius: var(--field-radius);
    cursor: pointer;
    transition: color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                background var(--duration-fast) var(--ease-out);
}
.search-trigger svg { flex: none; }
.search-trigger__text::after { content: "…" / ""; }
.search-trigger:hover { border-color: var(--field-border-hover); }
.search-trigger:focus-visible { outline-offset: calc(var(--field-border-w) * -1); }

/* Phones: a 44px magnifier. The word stays as its name, hidden visually. */
@media (max-width: 767px) {
    .search-trigger {
        justify-content: center;
        min-inline-size: var(--target-min);
        min-block-size: var(--target-min);
        padding: 0;
        background: none;
        border-color: transparent;
        border-radius: var(--radius-s);
    }
    .search-trigger:hover {
        color: var(--primary);
        background: var(--primary-pale);
        border-color: transparent;
    }
    .search-trigger__text {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* Alternative: a visible box whose results float beneath it. */
.mf-search--dropdown { position: relative; }

.mf-search__popup:not([hidden]) {
    position: absolute;
    inset-block-start: calc(100% + var(--space-3xs));
    inset-inline-end: 0;
    z-index: var(--layer-overlay);
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    inline-size: max(100%, min(var(--measure-tight), calc(100vw - 2 * var(--grid-gutter))));
    padding: var(--space-xs);
    background: var(--surface);
    border: var(--border-thin) solid var(--border);
    border-radius: var(--radius-m);
    box-shadow: var(--shadow-l);
}

/* The overlay. Phones: full screen. One row holds Close, the field and Search (Close
 * first, matching the focus order); the results scroll below it, within the
 * overlay. The page behind is locked by mediflare.js itself. Subgrid puts
 * the box's own children on the overlay's grid, so the results run the full
 * width rather than only under the field. */
.mf-search-overlay:not([hidden]) {
    position: fixed;
    inset: 0;
    z-index: var(--layer-modal);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: auto auto minmax(0, 1fr);
    gap: var(--space-xs) var(--space-2xs);
    padding-block-start: max(var(--space-xs), env(safe-area-inset-top));
    padding-inline: max(var(--space-s), env(safe-area-inset-left)) max(var(--space-s), env(safe-area-inset-right));
    background: var(--bg);
}

.mf-search-overlay__header { display: contents; }

.mf-search-overlay__close {
    grid-column: 1;
    grid-row: 1;
    align-self: stretch;
    position: relative;
    z-index: var(--layer-base);   /* above the box, which spans this cell too */
    min-inline-size: var(--target-min);
    min-block-size: var(--target-min);
    padding-inline: var(--space-2xs);
    font-weight: var(--weight-600);
    color: var(--ink-mid);
    border-radius: var(--radius-s);
}
.mf-search-overlay__close:hover { color: var(--primary); background: var(--primary-pale); }

.mf-search--overlay,
.mf-search--overlay .mf-search__form {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: subgrid;
    gap: normal;            /* the overlay's own gap; a different subgrid gap pads the edge row and pushes Search 2px past the field */
    align-items: stretch;   /* Close, the field and Search share one height */
}

.mf-search--overlay .mf-search__input { grid-column: 2; grid-row: 1; }
.mf-search--overlay .mf-search__submit { grid-column: 3; grid-row: 1; }
.mf-search--overlay .mf-search__status { grid-column: 1 / -1; grid-row: 2; }

.mf-search--overlay .mf-search__results {
    grid-column: 1 / -1;
    grid-row: 3;
    align-self: stretch;
    min-block-size: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block-end: max(var(--space-s), env(safe-area-inset-bottom));
}

/* From 768px up the same overlay is a centred card (the DocSearch pattern),
 * offset from the top rather than vertically centred, so it grows downward
 * with its results up to max-block-size, past which they scroll inside it.
 * A press on the dimmed page closes it (mediflare.js, #249).
 *
 * Stacking: the backdrop is body::after at --layer-modal, above the sticky
 * header (--layer-nav) and any dropdown (--layer-overlay). The card sits one
 * above it: body::after follows the overlay in tree order, so at an equal
 * z-index it would paint over the card. */
@media (min-width: 768px) {
    .mf-search-overlay:not([hidden]) {
        inset: var(--space-2xl) 0 auto;
        z-index: calc(var(--layer-modal) + 1);
        inline-size: calc(100% - 2 * var(--space-m));
        max-inline-size: calc(var(--grid-max-width) / 2);
        max-block-size: calc(100dvh - 2 * var(--space-2xl));
        margin-inline: auto;
        padding: var(--space-s);
        background: var(--surface);
        border-radius: var(--radius-l);
        box-shadow: var(--shadow-l);
    }

    .mf-search--overlay .mf-search__results { padding-block-end: 0; }

    html[data-mf-search-overlay="open"] body::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: var(--layer-modal);
        background: color-mix(in oklch, var(--ink-strong) 50%, transparent);
        -webkit-backdrop-filter: blur(var(--space-3xs));
        backdrop-filter: blur(var(--space-3xs));
    }
}

/* Opening is a short fade and settle; closing is immediate, and under
 * reduced motion there is no animation at all. */
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
    .mf-search-overlay:not([hidden]) { animation: search-card-in var(--duration-base) var(--ease-out); }
    html[data-mf-search-overlay="open"] body::after { animation: search-backdrop-in var(--duration-base) var(--ease-out); }
}

@keyframes search-card-in { from { opacity: 0; transform: scale(0.98); } }
@keyframes search-backdrop-in { from { opacity: 0; } }

/* SITE LAYER */

/* ===========================================================================
   PRIORITY HEALTH MEDICAL CENTRE — site layer
   Every value here is MEASURED from https://www.priorityhealthmedicalcentre.com.au
   on 2026-09-11, not chosen. See CLAUDE.md for the measurements and the
   deliberate divergences.
   =========================================================================== */

:root {
    /* --- Utopia anchors, fitted to live's own measured ladder --------------
     * Live sets a plain 16px root (NOT fluid) and states every size in fixed
     * px, identical at 390/768/1024/1440/1920. Body pinned to the measurement
     * at each end; the two ratios searched. Mean error 3.4% at 1440 / 2.5% at
     * 390, with body, 40px and 55px exact at 1440 and 35px exact at 390.
     * The steps carry the fluidity; the root stays at 100%. */
    --u-min-vw: 390;
    --u-max-vw: 1440;
    --u-min-font: 15;
    --u-max-font: 16;
    --u-min-ratio: 1.112;
    --u-max-ratio: 1.165;


    /* --- Leading: live's px line-heights, as ratios ------------------------
     * Live states every line-height in px over fixed-px type, so its RATIOS
     * hold at every width. Each rule's px is re-expressed as the ratio it
     * painted at 1440 (at 390 for a phone-only rule), so desktop keeps its
     * measured rhythm and on a phone the leading follows the fluid type down
     * instead of a fixed px drifting looser as the type shrinks.
     * xs and s are retuned (1.05 -> 1.08, 1.18 -> 1.2); 2xs and 2xl-5xl are
     * extensions for live's tight display lines and its very open copy. */
    --leading-2xs: 1;       /* buttons and one-line display: 45 on 46.6, 18 on 18.6, 22 on 21.7 */
    --leading-xs: 1.08;     /* page titles, band headings: 50 on 46.6, 32 on 29.5, 37 on 34.3 */
    --leading-s: 1.2;       /* 65 on 54.3; the practitioner label */
    --leading-2xl: 1.7;     /* 27px on body type; 23.8 on the footer blurb */
    --leading-3xl: 1.82;    /* footer link rows: 25 on 13.7 */
    --leading-4xl: 1.93;    /* appointment copy: 36 on 18.6 */
    --leading-5xl: 2.125;   /* welcome copy: 34 on 16 */

    /* Live's own weights and tracking: 500 is its heading and link weight and
     * 800 its fee figures; 0.5px on 14-16px button labels is 0.035em. */
    --weight-500: 500;
    --weight-800: 800;
    --tracking-l: 0.035em;
    /* Live's corners: 3px on its Book buttons, chips and cards. Every button
     * and field shares the Book button's 3px (owner, 2026-10-06), so live's
     * 4px CTAs and the footer's 2px button take it too. */
    --radius-2xs: 3px;
    --control-radius: var(--radius-2xs);

    /* Fields, as live's enquiry forms draw them: a 1px #54595F box with the
     * field's name as its placeholder. Everything else is the section's. */
    --field-border: var(--ink-muted);
    --field-border-hover: var(--ink-mid);
    --field-placeholder: var(--ink-muted);
    --field-textarea-h: 6rem;

    /* --- Space knobs ------------------------------------------------------
     * Live does not scale its spacing: 10/20/30/40/60/80/120px at every width.
     * At 1440 the ladder lands on it exactly (unit 1.25rem = 20px: 2xs 10,
     * s 20, m 30, l 40, xl 60, 2xl 80, 3xl 120). On a phone it shrinks on the
     * template's own 1 : 1.25 ratio (unit 16px: s 16, m 24, l 32, xl 48) --
     * the owner's call (2026-10-03): spacing scales with the viewport even
     * though live's is flat; close to live is enough. A content band's
     * 40/60/80 block and 20/25/30 gutter on live are --space-l-2xl and
     * --space-s-m: 32/57/80 and 16/23/30 here at 390/900/1440. */
    --u-space-min: 1;
    --u-space-max: 1.25;

    /* --- Palette. Measured off live's rendered pixels. --------------------
     * Every value is an exact OKLCH conversion of the measured sRGB in its
     * comment, verified by painting each to a canvas.
     *
     * The brand colours ARE the template's roles, set on its dials, so every
     * template component (focus rings, skip link, links, .btn--*, form focus)
     * paints in the brand rather than the template's default teal:
     *   primary   = crimson  rgb(159, 1, 1)    #9F0101  CTAs, accents, social
     *   secondary = blue     rgb(0, 153, 255)  #0099FF  navbar, strips, team band
     *   tertiary  = navy     rgb(2, 0, 167)    #0200A7  sub-nav text, footer heads
     * and the true greys on the neutral dials: --ink black, --ink-mid #444444,
     * --ink-soft #767676, --bg and --surface white. */
    --palette-hue: 29.16;
    --palette-chroma: 0.1806;
    --l-primary: 0.4415;
    --palette-secondary-shift: 219.65;    /* 248.81 */
    --chroma-secondary: 0.1837;
    --l-secondary: 0.669;
    --palette-tertiary-shift: 235.23;     /* 264.39 */
    --chroma-tertiary: 0.2277;
    --l-tertiary: 0.3298;

    /* Live's greys are true greys, so ink carries no chroma. */
    --chroma-ink: 0;
    --l-ink-strong: 0;                    /* headings are black on live, as body ink */
    --l-ink: 0;                           /* rgb(0, 0, 0) */
    --l-ink-mid: 0.3867;                  /* rgb(68, 68, 68)    #444444  icon-card descriptions */
    /* Live's #7A7A7A body grey measures 4.29:1 on white, just under the floor;
     * #767676 is the nearest neutral that passes at 4.54:1. */
    --l-ink-soft: 0.5658;                 /* rgb(118, 118, 118) #767676  body prose (live #7A7A7A) */
    --l-bg: 1;                            /* the page ground is white */
    --chroma-ground: 0;
    --l-on-solid: 1;                      /* live's text on fills is #fff */
    /* --border is the one neutral left with chroma. Live's rule colour was never
     * measured, so it keeps the tint it has always painted (the template's hue
     * 195) instead of following --palette-hue round to crimson. */
    --hue-neutral: 195;

    /* Colours no role can derive, named once here. */
    --ground-mist: oklch(92.21% 0.0104 247.94);   /* rgb(224, 230, 236) #E0E6EC  band 3 ground, social discs */
    --fee-stripe:  oklch(94.31% 0 0);             /* rgb(236, 236, 236) #ECECEC  fee-table alternating row */
    --ink-fee:     oklch(52.78% 0 0);             /* rgb(107, 107, 107) #6B6B6B  4.51:1 on the stripe, 5.33 on white */
    --ink-muted:   oklch(46.17% 0.0116 252.91);   /* rgb(84, 89, 95)    #54595F  footer + contact prose */
    /* Live paints small white text on #0099FF at 3.00:1, below the 4.5:1 floor.
     * Same hue at 80% lightness reaches 4.51:1 — used for blue TEXT on white;
     * the large band grounds keep live's exact blue (--secondary), where white
     * display type passes on the 3:1 large-text floor. */
    --brand-blue-text: oklch(56.7% 0.1546 248.52);     /* rgb(0, 122, 204) #007ACC  4.51:1 on white */
    --brand-blue-surface: oklch(62% 0.1694 248.6);     /* rgb(0, 138, 230) #008AE6  3.63:1 under white text — owner's choice, 2026-10-02 */
    --brand-yellow: oklch(96.91% 0.2005 109.62);       /* rgb(255, 255, 51) #FFFF33  hero wedge on registrars + audiologist */
    --hero-veil: color-mix(in srgb, var(--surface) 93%, transparent);   /* measured alpha on the panel */
    --card-veil: color-mix(in srgb, var(--surface) 82%, transparent);   /* the about intro's card at tablet */
    --subnav-tint: color-mix(in srgb, var(--surface) 12%, transparent); /* open sub-menu on the mobile nav */

    /* The site's earlier names, kept as aliases of the roles because the
     * divider and icon markup colours its fills with them in inline styles. */
    --brand-crimson: var(--primary);
    --brand-blue: var(--secondary);
    --brand-navy: var(--tertiary);
    --white: var(--surface);

    /* --- Structure, measured ---------------------------------------------- */
    /* 1140 is a REAL cap: it holds at 1920, not viewport-minus-padding. */
    --shell: 1140px;
    /* Band padding is 5vw on live (19.5@390, 51.19@1024, 72@1440, 96@1920).
     * Composed from pairs and steps: 16@390, 48.7@1024, 70@1440. Band 7's
     * 10vw (144@1440) is 3xl + s, 140. */
    --band-pad: calc(var(--space-l-2xl) + var(--space-s-m) - var(--space-s) - var(--space-s));
    --header-rule: 5px;      /* crimson top rule + navy bottom rule */
    --hero-vh: 60vh;         /* 540@900, 648@1080, 499@700 — 60vh with a content floor */

    --font-plex: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-roboto: Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
    /* Live's headings fall back to the browser UI stack (its declared face never
     * loads for them). We name a real stack rather than reproduce the fallback. */
    --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

body {
    font-family: var(--font-ui);
    font-size: var(--step-0);
    line-height: var(--leading-l);
    color: var(--ink);
    background: var(--bg);
}

/* Containers ------------------------------------------------------------- */
.ph-shell { width: 100%; max-width: var(--shell); margin-inline: auto; }
.band { padding-block: var(--band-pad); padding-inline: var(--band-pad); }
.band--flush { padding: 0; }
.band--wide-pad { padding-block: calc(var(--space-3xl) + var(--space-s)); }
.band--mist { background: var(--ground-mist); }
.band--blue { background: var(--secondary); }

/* Shape dividers ---------------------------------------------------------
 * Live uses ONE path for all seven dividers (M0,6V0h1000v100L0,6z), varying
 * only fill, height and mirror state. The svg is 1441 wide (100% + 1px) and
 * centred; a horizontal mirror is rotateY(180deg) on the SVG itself, and a
 * vertical flip is rotate(180deg) on the wrapper. Transcribed per divider. */
.shape { position: absolute; left: 0; width: 100%; line-height: 0; direction: ltr; overflow: hidden; pointer-events: none; }
.shape--top { top: 0; }
.shape--bottom { bottom: 0; transform: rotate(180deg); }
.shape svg { display: block; width: calc(100% + 1px); position: relative; left: 50%; transform: translateX(-50%); }
.shape--mirror svg { transform: translateX(-50%) rotateY(180deg); }
.shape path { fill: currentColor; }

/* Header ------------------------------------------------------------------ */
.site-header { position: relative; z-index: var(--layer-nav); }
.site-header__bar {
    background: var(--surface);
    border-top: var(--header-rule) solid var(--primary);
    padding-inline: var(--band-pad);
}
/* Live's header row measures 180px inside a 185px bar (the 5px is the rule).
 * The row is taller than its content on purpose — the contact stack is 109px
 * and the logo 88px, both vertically centred in the row. */
.site-header__bar-inner {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-m); flex-wrap: wrap;
    min-height: 180px;
}
.site-logo img { width: 350px; max-width: 100%; height: auto; }
.site-header__contact { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-s); }
.site-header__phone {
    display: inline-flex; align-items: center; gap: var(--space-2xs);
    font-family: var(--font-plex); font-size: var(--step-1); font-weight: var(--weight-700);
    color: var(--tertiary); text-decoration: none;
}
.site-header__phone svg { width: 1em; height: 1em; fill: currentColor; flex: none; }
/* Search sits beside Book, on the row Book already had, so the contact stack
 * keeps its height: the search field from 768px up, a 44px magnifier on phones.
 * Never in the menu. */
.site-header__actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-xs); }
/* 768-859 the bar is tight beside the logo: the trigger drops its ellipsis and
 * its spare width rather than push the contact stack under the logo. */
@media (min-width: 768px) and (max-width: 859px) {
    .search-trigger { min-inline-size: 0; padding-inline-end: var(--field-pad-inline); }
    .search-trigger__text::after { content: none; }
}
.site-header__phone:hover, .site-header__phone:focus-visible { text-decoration: underline; }

.btn-book {
    background: var(--brand-blue-surface); color: var(--surface);
    font-size: var(--step--1); font-weight: var(--weight-500); letter-spacing: var(--tracking-l);
    text-transform: uppercase; text-decoration: none;
    padding: var(--space-2xs) var(--space-s); line-height: var(--leading-2xs);
}
.btn-book:hover, .btn-book:focus-visible { background: var(--tertiary); color: var(--surface); }

/* Navbar ------------------------------------------------------------------ */
/* The navbar carries small white link text, so it takes the 4.51:1 blue rather
 * than live's #0099FF (which puts every nav item at 2.99:1). Same hue; the
 * large blue band grounds elsewhere keep live's exact value. */
.site-nav {
    background: var(--brand-blue-surface);
    border-bottom: var(--header-rule) solid var(--tertiary);
    padding-inline: var(--band-pad);
}
.site-nav__inner { display: flex; align-items: stretch; justify-content: space-between; }
.site-nav__list { display: flex; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.site-nav__list > li { position: relative; }
.site-nav__link {
    display: inline-flex; align-items: center; gap: var(--space-3xs);
    padding: var(--space-2xs) var(--space-s);
    min-height: 56px;
    color: var(--surface); text-decoration: none; text-transform: uppercase;
    font-size: var(--step--1); font-weight: var(--weight-400); line-height: var(--leading-m);
}
.site-nav__link[aria-current="page"], .site-nav__link:hover, .site-nav__link:focus-visible { background: var(--tertiary); color: var(--surface); }
.site-nav__sub a:hover, .site-nav__sub a:focus-visible { color: var(--tertiary); }
.site-nav__caret { width: 9px; height: 15px; fill: currentColor; flex: none; }
.site-nav__sub {
    list-style: none; margin: 0; padding: 0;
    position: absolute; top: 100%; left: 0; min-width: 260px;
    background: var(--surface); box-shadow: var(--shadow-m);
    display: none; z-index: var(--layer-overlay);
}
.site-nav__list > li:hover > .site-nav__sub,
.site-nav__list > li:focus-within > .site-nav__sub { display: block; }
.site-nav__sub a {
    display: block; padding: var(--space-2xs) var(--space-s);
    color: var(--tertiary); text-decoration: none;
    font-size: var(--step--2); text-transform: uppercase; line-height: var(--leading-m);
}
.site-nav__sub a:hover, .site-nav__sub a:focus-visible { background: var(--ground-mist); }
/* Crimson is under 3:1 on the blues, so the ring changes there: navy on
 * live's #0099FF bands (5:1; white is 2.99:1), white on the darker #008AE6
 * nav, call bar and tiles (3.6:1), and crimson again on the white dropdown.
 * A nav link's ring is drawn inside it, on the navy it turns when focused,
 * since outside it would cross the white header bar above. */
.band--blue, .strip { --focus-ring: var(--tertiary); }
.site-nav, .site-footer__call, .svc-grid li.svc-tile { --focus-ring: var(--surface); }
.site-nav__sub { --focus-ring: var(--primary); }
.site-nav__link:focus-visible { outline-offset: calc(var(--focus-ring-w) * -1); }

/* Mobile nav -------------------------------------------------------------- */
.nav-toggle {
    display: none; background: none; border: 0; color: var(--surface);
    padding: var(--space-2xs) var(--space-xs); cursor: pointer; align-items: center; gap: var(--space-2xs);
    font-size: var(--step--1); text-transform: uppercase; min-height: 44px;
}
.nav-toggle__bars { display: block; width: 22px; height: 2px; background: currentColor; position: relative; }
.nav-toggle__bars::before, .nav-toggle__bars::after { content: ""; position: absolute; left: 0; width: 22px; height: 2px; background: currentColor; }
.nav-toggle__bars::before { top: -7px; } .nav-toggle__bars::after { top: 7px; }

/* Hero -------------------------------------------------------------------- */
.hero { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-height: var(--hero-vh); }
.hero__copy {
    background: var(--hero-veil);
    display: flex; flex-direction: column; justify-content: center;
    padding-block: var(--space-l); padding-inline: var(--band-pad);
}
.hero__copy-inner { width: 100%; max-width: calc(var(--shell) / 2); margin-left: auto; }
.hero__media { background-size: cover; background-position: 50% 50%; }
.hero__title { font-size: var(--step-8); font-weight: var(--weight-700); line-height: var(--leading-xs); color: var(--ink); margin: 0; letter-spacing: normal; }
.hero__title em { font-style: normal; color: var(--primary); }
.hero__sub { font-family: var(--font-plex); font-size: var(--step-2); font-weight: var(--weight-600); color: var(--tertiary); margin: var(--space-2xs) 0 0; line-height: var(--leading-m); letter-spacing: normal; }
.hero__lede { font-family: var(--font-plex); font-size: var(--step-1); color: var(--ink); margin: var(--space-s) 0 0; line-height: var(--leading-l); }
.hero__lede + .hero__lede { margin-top: var(--space-xs); }
.hero__cta { margin-top: var(--space-m); }

/* Buttons — measured off live, per variant -------------------------------- */
.btn-hero {
    background: var(--primary); color: var(--surface);
    font-size: var(--step-1); font-weight: var(--weight-400); letter-spacing: var(--tracking-xl);
    padding: var(--space-s) var(--space-m); text-decoration: none; line-height: var(--leading-2xs);
}
.btn-solid {
    background: var(--primary); color: var(--surface);
    font-size: var(--step-0); letter-spacing: var(--tracking-l); text-transform: uppercase;
    padding: var(--space-xs) calc(var(--space-m) + var(--space-xs)); text-decoration: none; line-height: var(--leading-2xs);
}
.btn-ghost {
    gap: var(--space-2xs);
    background: transparent; color: var(--primary);
    border: var(--border-medium) solid var(--primary);
    font-size: var(--step-0); letter-spacing: var(--tracking-l); text-transform: uppercase;
    padding: var(--space-xs) var(--space-m); text-decoration: none; line-height: var(--leading-2xs);
    white-space: normal;
}
.btn-hero:hover, .btn-hero:focus-visible, .btn-solid:hover, .btn-solid:focus-visible { background: var(--tertiary); color: var(--surface); }
.btn-ghost:hover, .btn-ghost:focus-visible { background: var(--primary); color: var(--surface); }
.btn-ghost svg, .btn-link svg { width: 0.7em; height: 1em; fill: currentColor; flex: none; }

/* Every site button: the 44px tap target and the shared corner (owner,
 * 2026-10-06). Flex, so a button grown to the floor keeps its label centred. */
.btn-book, .btn-hero, .btn-solid, .btn-ghost, .btn-link {
    display: inline-flex; align-items: center; justify-content: center; text-align: center;
    min-block-size: var(--target-min);
    border-radius: var(--control-radius);
}

/* Small white text on the blue band: underlined and given a darker plate so it
 * clears 4.5:1 without changing the band's colour. */
.btn-link {
    gap: var(--space-2xs);
    background: var(--brand-blue-surface); color: var(--surface);
    text-transform: uppercase; text-decoration: none;
    font-size: var(--step--1); letter-spacing: var(--tracking-l); line-height: var(--leading-2xs);
    padding: var(--space-2xs) var(--space-xs);
}
.btn-link:hover, .btn-link:focus-visible { text-decoration: underline; }

/* Strip bands (1 and 6) — thin blue rules carrying a divider --------------- */
.strip { position: relative; background: var(--secondary); }
/* Under a page hero: live's 8px navy rule, then 66px of blue. */
.strip--tall { height: 74px; box-sizing: border-box; border-top: 8px solid var(--tertiary); }
.strip--short { height: 52px; }
.strip__rule { position: absolute; left: 0; width: 100%; }

/* Band 2 — Welcome -------------------------------------------------------- */
/* Live: 80px block padding, fixed at every width (not the 5vw band rhythm). */
.welcome { padding-block: var(--space-2xl); padding-inline: var(--band-pad); }
.welcome__inner { padding-right: 10%; }
.welcome__title { font-size: var(--step-7); font-weight: var(--weight-600); color: var(--ink); margin: 0 0 var(--space-s); line-height: var(--leading-m); letter-spacing: normal; }
.welcome p { color: var(--ink-soft); font-size: var(--step-0); line-height: var(--leading-5xl); margin: 0 0 var(--space-xs); }
.welcome p:last-child { margin-bottom: var(--space-m); }

/* Band 3 — Whole-Person Care --------------------------------------------- */
.care { position: relative; }
.care__inner { display: grid; grid-template-columns: minmax(0, 437fr) minmax(0, 703fr); align-items: center; }
.care__photo { background-size: cover; background-position: 50% 50%; border-radius: var(--radius-2xs); min-height: 750px; }
.care__body { padding-left: var(--space-2xl); }
.care__title { font-size: var(--step-6); font-weight: var(--weight-500); color: var(--ink); margin: 0 0 var(--space-l); line-height: var(--leading-s); letter-spacing: normal; text-transform: capitalize; }
.care__title em { font-style: normal; color: var(--primary); display: block; }
.care__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-xl) var(--space-l); }
.icard__glyph { width: 35px; height: 35px; fill: var(--secondary); display: block; margin-bottom: var(--space-xs); }
.icard__title { font-size: var(--step-1); font-weight: var(--weight-600); color: var(--ink); margin: 0 0 var(--space-2xs); line-height: var(--leading-m); letter-spacing: normal; }
.icard p { font-size: var(--step-0); color: var(--ink-mid); margin: 0; line-height: var(--leading-l); }
.care__cta { margin-top: var(--space-l); }

/* Band 4 — Make An Appointment ------------------------------------------- */
.appt { text-align: center; }
.appt__inner { max-width: 800px; margin-inline: auto; }
.appt__title { font-size: var(--step-7); font-weight: var(--weight-600); color: var(--ink); margin: 0 0 var(--space-s); line-height: var(--leading-2xs); letter-spacing: normal; }
.appt__sub { font-size: var(--step-2); font-weight: var(--weight-500); color: var(--tertiary); margin: 0 0 var(--space-s); line-height: var(--leading-xs); letter-spacing: normal; }
.appt p { font-family: var(--font-plex); font-size: var(--step-1); color: var(--ink-soft); margin: 0 0 var(--space-s); line-height: var(--leading-4xl); padding-bottom: var(--space-xs); }
.appt p:has(> .btn-solid) { padding-bottom: 0; line-height: 0; }   /* collapses the line box round the inline-block button: geometry, not leading */
/* Live's rule widget is 32px tall (15px padding each side of a 1px line). */
.appt__rule { border: 0; border-top: var(--border-thin) solid var(--secondary); width: 50%; margin: var(--space-xs) auto var(--space-l); box-sizing: content-box; }

/* Band 5 — Meet The Team -------------------------------------------------- */
.team { position: relative; color: var(--surface); }
.team__inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 0; min-height: 300px; }
.team__copy { padding-left: var(--space-xl); padding-right: calc(var(--space-3xl) + var(--space-m)); }
/* Five avatars in a staggered honeycomb, as live draws them: two above,
 * three below, rows touching (150px pitch, no vertical gap) and the lower row
 * offset left by half a face. ONE list from the app (the first five doctors in
 * the account's order), so the stagger is a six-track grid of half-faces: each
 * face spans two tracks, the first starts at track 2, and the third wraps.
 * Every doctor after the fifth is in the markup and never shown. */
.team__faces { display: flex; flex-direction: column; align-items: center; }
.team__grid { display: grid; grid-template-columns: repeat(6, calc((150px + var(--space-2xs)) / 2)); justify-content: center; list-style: none; margin: 0; padding: 0; }
.team__grid > li { grid-column: span 2; justify-self: center; }
.team__grid > li:first-child { grid-column: 2 / span 2; }
.team__grid > li:nth-child(n+6) { display: none; }
/* The circles are a FIXED 150px on live at every width. The fallback avatar
 * (.doctor-avatar, for a doctor with no photo) takes the circle too. */
.team__face.doctor-avatar { width: 150px; height: 150px; aspect-ratio: auto; border-radius: var(--radius-round); }
.team__faces img { width: 150px; height: 150px; border-radius: var(--radius-round); object-fit: cover; display: block; }
/* Live wraps 'Meet The Team' to two lines in a 365px column (130px tall). */
/* White display type on live's blue lands at exactly 3.00:1 — the large-text
 * floor to three decimals. A 700 weight lifts it clear without changing the
 * colour live uses. */
.team__title { font-size: var(--step-8); font-weight: var(--weight-700); color: var(--surface); margin: 0 0 var(--space-s); line-height: var(--leading-s); letter-spacing: normal; max-width: 365px; }
.team__rule { border: 0; border-top: var(--border-thin) solid var(--primary); width: 60px; margin: var(--space-s) 0 0; }

/* Band 7 — Contact -------------------------------------------------------- */
.contact__inner { display: grid; grid-template-columns: minmax(0, 475fr) minmax(0, 665fr); gap: 0; align-items: stretch; }
.contact__col-map { padding-left: calc(var(--space-l) + var(--space-2xs)); }
.contact__title { font-family: var(--font-roboto); font-size: var(--step-5); font-weight: var(--weight-500); color: var(--ink); margin: 0 0 var(--space-s); line-height: var(--leading-l); letter-spacing: normal; text-transform: capitalize; }
.contact__phone { font-family: var(--font-roboto); font-size: var(--step-5); font-weight: var(--weight-500); color: var(--primary); text-decoration: none; display: block; margin: calc(-1 * var(--space-3xs)) 0 var(--space-s); line-height: var(--leading-xs); }
.contact__phone:hover, .contact__phone:focus-visible { text-decoration: underline; }
.contact__social { margin-top: var(--space-s); }
.contact__social a { min-height: 50px; }
.contact p { font-family: var(--font-roboto); font-size: var(--step-1); color: var(--ink); margin: 0; line-height: var(--leading-l); padding-bottom: var(--space-xs); }
.contact p:last-of-type { padding-bottom: var(--space-l); }
.contact__social { display: flex; gap: var(--space-2xs); list-style: none; margin: var(--space-s) 0 0; padding: 0; }
.pband .contact__social { margin-bottom: var(--space-s); }
/* /contact: live shows bare #54595F glyphs, no disc; the homepage band keeps its discs. */
.pband .contact__social a { color: var(--ink-muted); background: none; width: 32px; height: 44px; min-height: 44px; }
.pband .contact__social a:hover, .pband .contact__social a:focus-visible { background: none; color: var(--primary); }
.pband .contact__social svg { width: 20px; height: 20px; }
.contact__social a {
    display: grid; place-items: center; width: 50px; height: 50px;
    background: var(--ground-mist); border-radius: 10%; color: var(--ink-mid);
}
.contact__social svg { width: 25px; height: 25px; fill: currentColor; }
.contact__social a:hover, .contact__social a:focus-visible { background: var(--primary); color: var(--surface); }
/* The map fills its column, so it ends level with the copy beside it. */
.contact__map { display: block; width: 100%; height: 100%; min-height: 300px; border: 0; }

/* Footer ------------------------------------------------------------------ */
.site-footer__call { position: relative; background: var(--brand-blue-surface); padding-block: calc(var(--space-s) + var(--space-3xs)); padding-inline: var(--band-pad); text-align: center; }
.site-footer__call .ph-shell { padding: var(--space-2xs); }
.site-footer__call p { margin: 0; font-size: var(--step-2); font-weight: var(--weight-500); color: var(--surface); line-height: var(--leading-2xs); }
.site-footer__call a { color: var(--surface); text-decoration: none; }
.site-footer__call a:hover, .site-footer__call a:focus-visible { text-decoration: underline; }

/* Live: 20px top padding on the section, then a 65px inset above the columns,
 * which are 241px tall; the section measures 326px total. */
.site-footer__main { position: relative; padding: var(--space-2xl) var(--band-pad) var(--space-s); }
.site-footer__cols { display: grid; grid-template-columns: minmax(0, 408fr) minmax(0, 182fr) minmax(0, 182fr) minmax(0, 185fr); gap: 0; }
.site-footer__brand { padding-right: var(--space-2xl); }
.site-footer__brand p { max-width: 317px; }
.site-footer__brand img { width: 328px; max-width: 100%; height: auto; }
.site-footer__brand p { font-size: var(--step--1); color: var(--ink-muted); margin: var(--space-s) 0 0; line-height: var(--leading-2xl); }
.site-footer h2 { font-size: var(--step-1); font-weight: var(--weight-500); color: var(--tertiary); margin: 0 0 var(--space-s); line-height: var(--leading-2xs); letter-spacing: normal; }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li + li { margin-top: 0; }
/* Live's footer list rows measure 203/151/99px for 8/6/4 items = ~25.3px each.
 * Keep a 24px target height so the links stay tappable. */
.site-footer li a { font-size: var(--step--1); font-weight: var(--weight-500); color: var(--ink-muted); text-decoration: none; line-height: var(--leading-3xl); display: inline-block; min-height: 24px; }
.site-footer li a:hover, .site-footer li a:focus-visible { color: var(--primary); text-decoration: underline; }
.btn-book--footer { font-size: var(--step--2); background: var(--primary); padding: var(--space-2xs) var(--space-s); }
.site-footer__cols p { margin: var(--space-s) 0 0; }

/* Live: 20px block padding over a 66px row — a 31px divider (15px of padding
 * around a 1px rule, 20px below it) then a 35px row pulled back up 20px. */
/* padding-block was 20px; the top is now spent on the divider gap below, so
   the legal block — and the 524px footer — keep their measured height while
   the social icons stop touching the rule. */
.site-footer__legal { padding-block: 0 var(--space-s); padding-inline: var(--band-pad); }
/* The row used to carry margin-top:-20px, which exactly cancelled this rule's
   20px bottom margin and left the social icons sitting hard against the
   divider. The negative margin is gone; this margin now IS the gap. */
.site-footer__legal-rule { border: 0; border-top: var(--border-thin) solid var(--border); margin: var(--space-xs) 0 var(--space-s); }
.site-footer__legal-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-m); flex-wrap: wrap; min-height: 35px; }
.site-footer__legal p { margin: 0; font-size: var(--step--1); font-weight: var(--weight-500); color: var(--ink-muted); }
.site-footer__legal ul { display: flex; gap: var(--space-s); list-style: none; margin: 0; padding: 0; }
.site-footer__legal a { color: var(--ink-muted); display: grid; place-items: center; min-width: 24px; min-height: 24px; }
.site-footer__legal svg { width: 15px; height: 15px; fill: currentColor; }

/* Entrance reveals — one-way IntersectionObserver, never the scroll-linked
 * template .reveal. Travel 26px vertical / 18px horizontal, 760ms ease-out. */
[data-reveal] { opacity: 0; transition: opacity 760ms ease-out, transform 760ms ease-out; will-change: opacity, transform; }
[data-reveal="up"]    { transform: translateY(26px); }
[data-reveal="down"]  { transform: translateY(-26px); }
[data-reveal="left"]  { transform: translateX(-18px); }
[data-reveal="right"] { transform: translateX(18px); }
[data-reveal][data-revealed] { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
    [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* Responsive --------------------------------------------------------------
 * Live is an Elementor site and switches at ITS breakpoints, measured:
 *   >=1025  desktop: inline nav, two-column bands, footer 524
 *   1024-768 tablet: burger (45x45), header 179, columns STAY side by side,
 *            footer 695 (brand full width above three link columns)
 *   <=767   mobile: header 243, hero photo hidden, everything one column,
 *            footer 1201-1260
 * Reproducing the tablet tier is what the last migration got wrong by simply
 * scaling everything in rem. */

/* ---- Tablet: 1024 down to 768 ---- */
@media (max-width: 1024px) {
    .nav-toggle { display: inline-flex; }
    .site-nav__list { display: none; width: 100%; flex-direction: column; padding-bottom: var(--space-xs); }
    .site-nav__list.is-open { display: flex; }
    .site-nav__list > li { width: 100%; }
    .site-nav__link { width: 100%; justify-content: space-between; min-height: 44px; }
    .site-nav__sub { position: static; display: none; box-shadow: none; background: var(--subnav-tint); min-width: 0; }
    .site-nav__sub a { color: var(--surface); }
    .site-nav__sub { --focus-ring: var(--surface); }   /* the drawer's sub-list sits on the blue */
    .site-nav__list > li.is-open > .site-nav__sub { display: block; }
    .site-nav__list > li:hover > .site-nav__sub,
    .site-nav__list > li:focus-within > .site-nav__sub { display: none; }
    .site-nav__list > li.is-open:hover > .site-nav__sub { display: block; }
    /* Live's tablet header is 179 = a 129px bar over a 50px nav. */
    .site-header__bar-inner { min-height: 124px; }
    .site-nav__link { min-height: 44px; }
    .site-nav { padding-inline: var(--space-s); }
    .site-header__bar { padding-inline: var(--space-s); }
    .site-nav__inner { flex-wrap: wrap; align-items: center; }

    /* Live PINS its tablet paddings rather than scaling them: the welcome and
     * appointment bands take 100px/25px and contact 60px/30px, where the
     * desktop rhythm is 5vw/10vw. Still restated per tier, now on the scale:
     * at 900, 2xl + s is 90px, s-m 23px, xl/m 55/27px. */
    .welcome { padding: calc(var(--space-2xl) + var(--space-s)) var(--space-s-m); }
    .appt { padding: calc(var(--space-2xl) + var(--space-s)) var(--space-s-m); }
    .contact { padding: var(--space-xl) var(--space-m); }
    .contact__inner { align-items: stretch; }
    .contact__col-map { padding-left: var(--space-s-m); }
    /* Live drops its body type to 16px/25.6px at tablet. Ours holds the fluid
     * step (17.9px) — see CLAUDE.md, "type stays on the scale". Matching live's
     * BAND height rather than its font size keeps the composition true. */
    .contact p { line-height: var(--leading-l); padding-bottom: 0; }
    .contact p:last-of-type { padding-bottom: 0; }
    .contact__title { line-height: var(--leading-l); margin-bottom: var(--space-s); }
    .contact__phone { line-height: var(--leading-xs); margin-bottom: var(--space-s); }
    .contact__social { margin-top: var(--space-s); }
    .contact__social a { width: 40px; height: 40px; min-height: 40px; }
    .contact__social svg { width: 20px; height: 20px; }
    .band { padding-inline: var(--space-s-m); }
    .appt__inner { max-width: none; }
    /* The map holds its 365px height on live's tablet. */

    /* A 3-up row of fixed 150px circles needs 474px; below ~940 that no longer
     * fits beside the copy column, so the team band stacks rather than
     * squashing the faces. Found by the 20px width sweep at 840. */
    @media (max-width: 940px) {
        .team__inner { grid-template-columns: minmax(0, 1fr); min-height: 0; row-gap: var(--space-l); }
        .team__copy { padding-left: 0; padding-right: 0; }
    }

    /* Columns stay side by side on live's tablet. */
    /* Live's tablet care band is 749 with both columns 646 tall (353/568). */
    .care { padding: var(--band-pad); }   /* live's 5vw, pinned at its 1024 value */
    .care__inner { align-items: stretch; gap: 0; }
    .care__body { padding-left: var(--space-s-m); display: flex; flex-direction: column; justify-content: center; }
    .care__photo { min-height: 646px; }
    .care__grid { gap: var(--space-m) var(--space-s); }
    .welcome__inner { padding-right: 0; }
    .team__copy { padding-left: var(--space-l); padding-right: 0; }
    .contact__col-map { padding-left: var(--space-l); }
    .contact__inner { gap: var(--space-s); }

    /* Live's tablet footer is 695 = 105 call + 474 main + 116 legal, and it
     * pins 20px side padding rather than following the band rhythm. */
    .site-footer__call { padding: var(--space-s-m) var(--space-s); }
    .site-footer__main { padding: var(--space-s) var(--space-s) 0; }
    /* Live's tablet footer columns are 229px under a 200px brand block, inside
     * a 454px inner with 25px top padding. */
    .site-footer__cols { row-gap: var(--space-s-m); }
    .site-footer__brand { padding-bottom: 0; min-height: 200px; }
    .site-footer__cols > div:not(.site-footer__brand) { min-height: 229px; }
    .site-footer__legal { min-height: 116px; }
    .site-footer__legal { padding: var(--space-m) var(--space-s) 0; }
    .site-footer__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .site-footer__brand { grid-column: 1 / -1; padding-right: 0; padding-bottom: var(--space-l); }
    .site-footer__brand p { max-width: none; }
}

/* ---- Mobile: 767 and below ---- */
@media (max-width: 767px) {
    /* Live hides the hero photo column entirely and runs the copy full width
     * (measured: photo column 0x0 at 767, 600 and 390). */
    .hero { grid-template-columns: minmax(0, 1fr); }
    .hero__media { display: none; }
    .hero__copy-inner { max-width: none; margin-left: 0; }

    /* Header: live is 243 = a 173px bar over a 70px nav, logo 200x39. */
    .site-header__bar-inner { min-height: 173px; }
    .site-logo img { width: 200px; }
    .site-nav__inner { min-height: 70px; }

    /* Live pins 50px/20px on the welcome and appointment bands and keeps the
     * 5vw rhythm on the care and team bands. */
    .welcome { padding: var(--space-xl) var(--space-s); }   /* live 50/20: 48/16 */
    .appt { padding: var(--space-xl) var(--space-s); }
    .contact { padding: var(--space-xl) var(--space-m); }
    .care, .band--blue { padding: var(--band-pad); }

    .care__inner, .team__inner, .contact__inner { grid-template-columns: minmax(0, 1fr); gap: 0; min-height: 0; }
    .care__body, .team__copy, .contact__col-map { padding-left: 0; padding-right: 0; }
    /* ⚠️ Live COLLAPSES the care photo to a 110px strip on mobile — it is an
     * empty div whose 30px padding is its only height, so the photo becomes a
     * band-wide sliver rather than a portrait. Reproduced, not "fixed". */
    .care__photo { min-height: 110px; height: 110px; padding: var(--space-m); }
    .care__grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-l); }
    .care__title { margin-top: var(--space-l); }
    /* Live's mobile team band is 649: a 470px face block (2+2+1 rows of 150px
     * faces in 176px cells, full-bleed across the column) over a 140px copy
     * block. It does NOT centre the copy. */
    .team__inner { text-align: left; }
    .team__faces { margin-bottom: 0; align-items: stretch; }
    .team__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-content: stretch; }
    .team__grid > li, .team__grid > li:first-child { grid-column: auto; }
    .team__grid > li:nth-child(5) { grid-column: 1 / -1; }
    .team__title { line-height: var(--leading-m); margin-bottom: var(--space-s); max-width: none; }
    .team__rule { margin-inline: 0; }
    .site-footer__cols { grid-template-columns: minmax(0, 1fr); }
    .site-footer__cols > div:not(.site-footer__brand) { min-height: 0; }
    .site-footer__brand { min-height: 0; }
    /* Live's mobile footer is 1260 = 130 call + 952 main + 178 legal, its
     * columns stacked at 224/259/210/214 and the legal row stacking too. */
    .site-footer__call { padding: var(--space-s); min-height: 130px; display: grid; place-items: center; }
    .site-footer__main { padding: var(--space-s) var(--space-s) 0; }
    .site-footer__legal { padding: 0 var(--space-s); min-height: 178px; }
    .site-footer__legal-inner { flex-direction: column; align-items: flex-start; gap: var(--space-xs); }
    .site-footer__cols { row-gap: 0; }
    .site-footer__brand { min-height: 224px; }
    .site-footer__cols > div:not(.site-footer__brand) { min-height: 210px; }
    .appt__rule { width: 80%; }
    .appt__inner { max-width: none; }
    /* Live stacks contact copy (436) above the map (415) at 390. */
    .contact__inner { min-height: 0; row-gap: 0; }
    .contact__col-map { padding-top: var(--space-xl); }   /* live 50: 48 */
    .contact p { line-height: var(--leading-2xl); padding-bottom: 0; }
    /* the tablet tier's 45px and 33px lines, as ratios of the phone's smaller type */
    .contact__title { line-height: var(--leading-3xl); }
    .contact__phone { line-height: var(--leading-m); }
    .contact p:last-of-type { padding-bottom: var(--space-s); }
    .contact__map { height: 365px; aspect-ratio: auto; }
}

@media (max-width: 520px) {
    .site-header__bar-inner { flex-direction: column; align-items: flex-start; gap: var(--space-xs); }
    .site-header__contact { align-items: flex-start; }
    .site-logo img { width: 280px; }
    .contact__social a { width: 44px; height: 44px; min-height: 44px; }
}

/* ===========================================================================
   INNER PAGES — phase 2. Every value measured off live on 2026-09-11.
   =========================================================================== */

/* Shared page hero — 18 of 19 inner pages use this exact component: a 250px
 * band with one photo, `cover` at `100% 50%` (the family sits right, sky left,
 * which is what keeps the black H1 legible at 7.47:1 on its darkest pixel),
 * no overlay, and a 50px/700 H1 vertically centred at the 1140 shell. */
.phero {
    position: relative;
    min-height: 250px;
    display: grid;
    align-items: center;
    padding-inline: var(--band-pad);
    background: url('/assets/images/page-hero-family.81036c486e.webp') 100% 50% / cover no-repeat;
}
/* The /about/ hero is 658px on live and carries the subtitle and intro. The
 * copy sits in a 627px column so the family photo stays visible to its right. */
.phero--tall { align-items: center; padding-block: var(--space-xl); }
.phero--tall .ph-shell { max-width: var(--shell); }
.phero__sub { font-family: var(--font-plex); font-size: var(--step-2); font-weight: var(--weight-600); color: var(--tertiary); line-height: var(--leading-m); letter-spacing: normal; margin: var(--space-2xs) 0 var(--space-s); max-width: 627px; }
.phero__intro { max-width: 627px; }
.phero__lede { font-size: var(--step-1); color: var(--ink); line-height: var(--leading-xl); margin: 0 0 var(--space-xs); }
.phero__lede:last-child { margin-bottom: 0; }
.phero__title {
    font-size: var(--step-7);
    font-weight: var(--weight-700);
    line-height: var(--leading-xs);
    /* Set every property explicitly: this class can sit on more than one tag,
     * and an inherited value breaks silently the moment the tag changes. */
    font-family: var(--font-ui);
    color: var(--ink);
    letter-spacing: normal;
    text-transform: none;
    margin: 0;
}

/* Content bands. Live pads every inner content band 80px/30px at 1440,
 * 60/25 on its tablet and 40/20 on a phone: the --space-l-2xl and --space-s-m
 * pairs carry all three tiers (57/23 at 900, 32/16 at 390), so the
 * per-breakpoint overrides are gone. */
.pband { position: relative; padding: var(--space-l-2xl) var(--space-s-m); }
.pband--mist { background: var(--ground-mist); }
.pband--white { background: var(--surface); }
.pband__inner { width: 100%; max-width: var(--shell); margin-inline: auto; }

/* Section heading, measured: 32px/500 navy, 20px below. */
/* Band defaults sit at :where() specificity (0,0,1) so any component class
 * (.person__role, .fee-note, .lede …) overrides them. At .pband p (0,1,1)
 * they silently beat every single-class rule on the site. */
:where(.pband) h2 {
    font-size: var(--step-4);
    font-weight: var(--weight-500);
    color: var(--tertiary);
    line-height: var(--leading-xs);
    letter-spacing: normal;
    margin: 0 0 var(--space-s);
}
/* Live's "spacer" under every band heading is an Elementor divider: a 60px
 * blue rule 10px below the heading. Same 34px total as before, now visible. */
:where(.pband) h2::after { content: ""; display: block; width: 60px; height: 2px; margin-top: var(--space-2xs); background: var(--secondary); }
:where(.pband) h2:is(.pcard__name, .visually-hidden, .contact-form-title, .join-lead)::after,
:where(.pband) .touch h2::after, :where(.pband) .article-body h2::after,
:where(.pband) .media-split--contact h2::after { content: none; }
:where(.pband) h3 {
    font-size: var(--step-2);
    font-weight: var(--weight-500);
    color: var(--tertiary);
    line-height: var(--leading-m);
    letter-spacing: normal;
    margin: 0 0 var(--space-xs);
}
:where(.pband) p { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-l); margin: 0 0 calc(var(--step-0) * var(--leading-l)); }   /* one body line, as live's 24px */
:where(.pband) p:where(:last-child) { margin-bottom: 0; }
:where(.pband) strong, :where(.pband) b { font-weight: var(--weight-700); color: var(--ink-muted); }
:where(.pband) ul, :where(.pband) ol { margin: 0 0 var(--space-xs); padding-left: var(--space-s); }
:where(.pband) li { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-l); margin-bottom: var(--space-3xs); }
/* ⚠️ Body links only — NEVER a button. `.pband a` is (0,1,1) and beats
 * `.btn-solid` (0,1,0), so without the :not() every button inside a content
 * band rendered crimson-on-crimson: 1:1, completely invisible. A class does
 * not beat a descendant selector — see the same trap on ayr-medical's eyebrow. */
/* Now at :where() (0,0,1), like the band defaults above, so a link component's
 * own colour (.contact-details a, .rcard__title a, .contact__social a) wins.
 * The hover rule is limited to classless body links: it has to outrank the
 * template's a:hover (0,1,1), and anything carrying a class owns its hover. */
:where(.pband) a:where(:not([class*="btn"])) { color: var(--primary); }
:where(.pband) a:not([class]):hover,
:where(.pband) a:not([class]):focus-visible { color: var(--primary); text-decoration: none; }

/* Practitioner card — ONE component, 25 instances across 8 pages. Measured
 * 342 + 798 columns inside the 1140 shell, an 180px circular portrait, and
 * alternating white / mist grounds down the page. */
.pcard__inner {
    display: grid;
    grid-template-columns: minmax(0, 342fr) minmax(0, 798fr);
    align-items: start;
}
/* Column 0 carries the portrait, the name and the quals — measured on live at
 * 180 photo (+10), 42 name (+10), 16 quals (+10), then a 14px spacer. */
.pcard__photo {
    width: 180px; height: 180px;
    border-radius: var(--radius-round);
    object-fit: cover; display: block; margin-bottom: var(--space-2xs);
}
.pcard__photo.is-lg { width: 250px; height: 250px; }
.pcard__photo.is-md { width: 200px; height: 200px; }
.pcard h2.pcard__name { font-size: var(--step-4); font-weight: var(--weight-500); color: var(--tertiary); line-height: var(--leading-l); letter-spacing: normal; margin: 0; }
/* `.pcard` doubles the specificity so `.pband p` and `.pcard__body p` (both
 * 0,1,1) cannot repaint the label and credentials as grey body copy. */
.pcard p.pcard__quals { font-size: var(--step-0); font-weight: var(--weight-600); color: var(--primary); line-height: var(--leading-m); letter-spacing: normal; margin: 0 0 calc(var(--step-0) * var(--leading-l)); }
.pcard p.pcard__quals::after { content: ""; display: block; width: 40px; height: 2px; margin-top: var(--space-xs); background: var(--secondary); }
.pcard p.pcard__role { font-size: var(--step-0); font-weight: var(--weight-600); color: var(--primary); line-height: var(--leading-m); margin: 0 0 var(--space-xs); }
.pcard p.pcard__note { font-size: var(--step--1); font-weight: var(--weight-600); color: var(--tertiary); line-height: var(--leading-m); margin: 0; }
/* Special-interest lists: two columns where the body column is wide enough,
 * so a long list reads as a scan rather than a wall. */
/* Columns clip outside list markers in Chromium, so the bullet is drawn. */
.pcard .pcard__interests { columns: 2 16rem; column-gap: var(--space-l); margin: 0 0 var(--space-s); padding: 0; list-style: none; }
.pcard .pcard__interests li { break-inside: avoid; position: relative; padding-left: var(--space-s); margin-bottom: var(--space-3xs); }
.pcard .pcard__interests li::before, .pcard p.pcard__interests::before { content: ""; position: absolute; left: 0; top: 0.55em; width: 6px; height: 6px; border-radius: var(--radius-round); background: var(--secondary); }
/* Column 1 carries the label, the prose and the CTA. */
.pcard p.pcard__label, .pcard .pcard__body h3 { font-size: var(--step-2); font-weight: var(--weight-600); color: var(--tertiary); line-height: var(--leading-s); letter-spacing: normal; margin: 0 0 var(--space-s); }
.pcard__body p { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-l); margin: 0 0 var(--space-xs); }
.pcard__body > :last-child { margin-bottom: 0; }
.pcard p.pcard__cta { margin: var(--space-s) 0 0; line-height: 0; }   /* geometry, as .appt p above */
.pcard__cta .btn-solid { padding: var(--space-2xs) var(--space-s); font-size: var(--step--1); line-height: var(--leading-xs); }
/* The looped cards (the practitioners loop on /doctors and /our-registrars).
 * Rails holds special interests as ONE comma-separated string, so it is a line
 * of text in the interests element, drawn with the list's own bullet; empty
 * (the practitioner has none) it takes no space, leaving the label over the
 * bio as before. Bios are paragraphs separated by blank lines: pre-line keeps
 * them. The loop cannot alternate a class, so the ground alternates by
 * position among the looped siblings, from the ground the markup names. */
.pcard p.pcard__interests { columns: auto; position: relative; padding-left: var(--space-s); }
.pcard p.pcard__interests:empty { display: none; }
.pcard p.pcard__bio { white-space: pre-line; }
.pcard--stripe.pband--white:nth-child(even of .pcard--stripe) { background: var(--ground-mist); }
.pcard--stripe.pband--mist:nth-child(even of .pcard--stripe) { background: var(--surface); }
/* /our-registrars' yellow wedge: it sat inside the first card, which is now a
 * loop body, so it hangs from a zero-height anchor above the loop instead. */
.pcard-shape { position: relative; height: 0; z-index: 1; }

/* Fee schedules — live builds these as Elementor rows; a <table> is the correct
 * semantic for a billing structure and reads the same. Every value measured off
 * live on 2026-09-12:
 *   header row  navy rgb(2,0,167), white 16px/700 uppercase centred, 102px tall
 *   body rows   alternating white / rgb(236,236,236), 78px tall
 *   body cells  rgb(122,122,122) 16px/800, CENTRED (not right-aligned)
 * The first column carries the long label and is left-aligned so it stays
 * readable when it wraps. */
.fee-table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 var(--space-l);
    background: var(--surface);
}
.fee-table caption {
    text-align: left;
    font-weight: var(--weight-700);
    color: var(--ink-muted);
    padding-bottom: var(--space-xs);
}
.fee-table thead th {
    background: var(--tertiary);
    color: var(--surface);
    font-size: var(--step-0);
    font-weight: var(--weight-700);
    text-transform: uppercase;
    text-align: center;
    line-height: var(--leading-m);
    padding: var(--space-s) var(--space-xs);
    vertical-align: middle;
}
.fee-table tbody tr { background: var(--surface); }
.fee-table tbody tr:nth-child(even) { background: var(--fee-stripe); }
.fee-table tbody th,
.fee-table tbody td {
    font-size: var(--step-0);
    font-weight: var(--weight-800);
    /* Live's #7A7A7A measures 3.19:1 on its own #ECECEC stripe. #6B6B6B is the
     * darkest grey that clears 4.5:1 on BOTH grounds, so every row reads the
     * same rather than alternate rows being harder to read than their
     * neighbours. Applied to the whole table, not just the stripes. */
    color: var(--ink-fee);
    text-align: center;
    line-height: var(--leading-m);
    padding: var(--space-s) var(--space-xs);
    vertical-align: middle;
    border: 0;
}
/* The label column carries the long text: left-aligned, and the one allowed to
 * wrap. Prices never break — "$85.05" split across lines is worse than a
 * scrollbar, and the wrapper scrolls for exactly that reason. */
.fee-table th[scope="row"] {
    text-align: center;           /* live centres every column, labels included */
    font-weight: var(--weight-700);
    white-space: normal;
    overflow-wrap: break-word;
    min-width: 9rem;
}
/* nowrap protects a PRICE from splitting ("$85.05" -> "$8 5.0 5"); it must
 * never reach prose, which then cannot wrap and stretches the table. Applied
 * only to cells narrow enough to be values. */
.fee-table td { white-space: nowrap; }
.fee-table td.is-prose { white-space: normal; overflow-wrap: break-word; text-align: left; }
.fee-table th, .fee-table td { overflow-wrap: normal; }
.fee-table td[colspan] { white-space: normal; }
/* Full-width sub-heading rows inside the independent-doctors schedule. */
/* Section rows would throw the zebra off by one; live keeps price rows white
 * and shades only the full-width sub-headings. */
.fee-table--wide tbody tr, .fee-table--wide tbody tr:nth-child(even) { background: var(--surface); border-bottom: var(--border-thin) solid var(--fee-stripe); }
.fee-table tbody tr.fee-table__section { background: var(--fee-stripe); }
.fee-table tbody tr.fee-table__section th { font-weight: var(--weight-700); text-align: center; padding-block: var(--space-2xs); }
.fee-th-sub { display: block; text-transform: none; font-size: var(--step--1); }
/* The cancellation-fee warning: live sets it crimson, bold, italic. */
.fees p.fee-warning { color: var(--primary); font-weight: var(--weight-700); font-style: italic; }
.fees p.fee-lead { font-weight: var(--weight-700); color: var(--ink-muted); margin-bottom: var(--space-s); }
.fee-em { color: var(--primary); }
.fees h2:not(:first-child) { margin-top: var(--space-xl); }

/* Services list — live bullets each row with the practice's own mark. */
/* Services tiles — live's 3-across grid of blue cards with the yellow cross.
 * Ground is --brand-blue-surface (#008AE6), the owner's choice for small
 * white text, not live's #0099FF (3.0:1). */
.svc-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-m); }
.svc-grid li.svc-tile { margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: var(--space-xs);
    background: var(--brand-blue-surface); color: var(--surface); text-align: center; padding: var(--space-m) var(--space-s) var(--space-l);
    font-size: var(--step-0); font-weight: var(--weight-700); line-height: var(--leading-m); min-height: 10rem; }
.svc-grid li.svc-tile:last-child:nth-child(3n+1) { grid-column: 2; }
.svc-grid img { width: 40px; height: auto; }
@media (max-width: 767px) { .svc-grid { grid-template-columns: minmax(0, 1fr); } .svc-grid li.svc-tile:last-child:nth-child(3n+1) { grid-column: auto; } .svc-grid li.svc-tile { min-height: 0; } }
@media (min-width: 768px) and (max-width: 1024px) { .svc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .svc-grid li.svc-tile:last-child:nth-child(3n+1) { grid-column: auto; } }
.svc-corp h3.svc-kicker { font-size: var(--step-0); font-weight: var(--weight-700); color: var(--primary); margin: var(--space-m) 0 var(--space-2xs); }
.svc-corp h2 + h3.svc-kicker { margin-top: 0; }
.svc-corp p.svc-lead { font-weight: var(--weight-700); color: var(--tertiary); }
.svc-corp .svc-checks { list-style: none; padding: 0; margin: 0 0 var(--space-s); }
.svc-corp .svc-checks li { display: flex; gap: var(--space-2xs); align-items: baseline; margin-bottom: var(--space-3xs); }
.svc-corp .svc-checks svg { width: 14px; height: 14px; fill: var(--primary); flex: none; transform: translateY(2px); }
.svc-corp p.svc-mail { display: flex; gap: var(--space-2xs); align-items: center; }
.svc-corp .svc-mail svg { width: 16px; height: 16px; fill: var(--primary); flex: none; }
.svc-list { list-style: none; margin: 0; padding: 0; }
.svc-list li {
    display: grid; grid-template-columns: 40px minmax(0, 1fr);
    gap: var(--space-xs); align-items: start;
    margin-bottom: var(--space-s);
    font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-2xl);
}
.svc-list img { width: 40px; height: auto; }

/* Article cards — five, each with a photo it draws as a CSS background. */
.articles { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-l); }
.article-card { display: flex; flex-direction: column; background: var(--surface); border-radius: var(--radius-s); overflow: hidden; box-shadow: var(--shadow-s); }
.article-card__media { aspect-ratio: 3 / 2; background-size: cover; background-position: 50% 50%; }
.article-card__body { padding: var(--space-m); display: flex; flex-direction: column; gap: var(--space-2xs); flex: 1; }
.article-card h2 { margin: 0; font-size: var(--step-2); font-weight: var(--weight-500); color: var(--tertiary); line-height: var(--leading-m); letter-spacing: normal; }
.article-card p { margin: 0; font-size: var(--step--1); color: var(--ink-soft); line-height: var(--leading-l); }

/* Resource links — /patient-resources/ is a list of outbound health links. */
.res-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-xs); }
.res-list li { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-2xl); }
.res-list a { color: var(--primary); word-break: break-word; }

/* Two-column media band (join-the-team, contact policy). */
.media-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-xl); align-items: center; }
.media-split img { width: 100%; height: auto; border-radius: var(--radius-s); display: block; }

/* Contact page --------------------------------------------------------- */
.contact-details { list-style: none; margin: 0 0 var(--space-l); padding: 0; display: grid; gap: var(--space-2xs); }
.contact-details li { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: var(--space-xs); align-items: start;
    font-family: var(--font-plex); font-size: var(--step-0); color: var(--ink-muted); line-height: var(--leading-l); }
.contact-details svg { width: 20px; height: 20px; fill: var(--tertiary); margin-top: calc(var(--space-3xs) / 2); }   /* optical: centres the glyph on the first line */
.contact-intro { font-size: var(--step-1); color: var(--tertiary); line-height: var(--leading-m); margin: 0 0 var(--space-m); }
.contact-intro::after { content: ""; display: block; width: 60px; height: 2px; margin-top: var(--space-s); background: var(--secondary); }
.contact-follow { font-size: var(--step-1); color: var(--tertiary); margin: var(--space-m) 0 var(--space-2xs); }
.contact-details a { color: var(--ink-muted); text-decoration: none; display: inline-block; min-height: 24px; padding-block: calc(var(--space-3xs) / 3); word-break: break-word; overflow-wrap: anywhere; max-width: 100%; }
.contact-details a:hover, .contact-details a:focus-visible { color: var(--primary); text-decoration: underline; }

/* Enquiry form. Live's is an Elementor Pro form; this posts to Mediflare. */
.mf-form { display: grid; gap: var(--space-s); }
.mf-form button { justify-self: start; }
.form-note { font-size: var(--step--1); color: var(--ink-soft); line-height: var(--leading-l); }
.form-note strong { color: var(--primary); }

/* Inner-page responsive ------------------------------------------------- */
@media (max-width: 1024px) {
    .phero { padding-inline: var(--space-s-m); }
    .media-split { gap: var(--space-l); }
}

@media (max-width: 767px) {
    .phero { min-height: 180px; padding-inline: var(--space-s); }
    /* The portrait goes above the copy, as live does. */
    .pcard__inner { grid-template-columns: minmax(0, 1fr); gap: var(--space-m); }
    .pcard__media img, .pcard__media img.is-lg, .pcard__media img.is-md { width: 180px; height: 180px; }
    .media-split { grid-template-columns: minmax(0, 1fr); }
    .articles { grid-template-columns: minmax(0, 1fr); }
    .fee-table th, .fee-table td { padding: var(--space-3xs) var(--space-2xs); font-size: var(--step--1); }
}

/* People grid (/staff/) — live puts the heading and intro in the card's 342
 * column and the grid in its 798: reception 2-across (349px cells), nursing
 * 3-across (233px cells), every cell 298px tall. Three people share one avatar
 * placeholder on live, reproduced rather than substituted. */
.staff__head h2 { margin-bottom: var(--space-xs); }
.staff__head p { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-2xl); margin: 0; }
.staff__head p + p { margin-top: var(--space-xs); }
.people { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-l) 0; justify-items: center; text-align: center; }
.people--2up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.people--3up { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.people img { width: 180px; height: 180px; border-radius: var(--radius-round); object-fit: cover; display: block; margin-bottom: var(--space-xs); }
.person__name { font-size: var(--step-2); font-weight: var(--weight-500); color: var(--tertiary); line-height: var(--leading-s); letter-spacing: normal; margin: 0 0 var(--space-3xs); }
.person__role { font-size: var(--step--1); font-weight: var(--weight-500); color: var(--primary); line-height: var(--leading-m); margin: 0; }
@media (max-width: 767px) {
    .people--2up, .people--3up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) { .people--2up, .people--3up { grid-template-columns: minmax(0, 1fr); } }

/* The template's website terms have no live equivalent, so nothing in live's
 * own footer links them. Published unlinked they would be an orphan, so the
 * legal row carries both. */
.site-footer__legal-links { display: flex; gap: var(--space-s); list-style: none; margin: 0; padding: 0; }
.site-footer__legal-links a { font-size: var(--step--1); font-weight: var(--weight-500); color: var(--ink-muted); text-decoration: none; display: inline-block; min-height: 24px; }
.site-footer__legal-links a:hover, .site-footer__legal-links a:focus-visible { color: var(--primary); text-decoration: underline; }
@media (max-width: 767px) { .site-footer__legal-inner { gap: var(--space-2xs); } }

/* Article pages — live keeps these inside a lightbox; ours are real pages. */
.article-body h2 { font-size: var(--step-2); font-weight: var(--weight-500); color: var(--tertiary); line-height: var(--leading-m); letter-spacing: normal; margin: var(--space-l) 0 var(--space-xs); }
.article-body h2:first-child { margin-top: 0; }
.article-body p { max-width: var(--measure); }
.article-body .lede { color: var(--ink-muted); }   /* live's #54595F, not the template's .lede default (--ink-mid) */
.article-body ul,
.article-body ol { max-width: var(--measure); }
.article-back { margin-top: var(--space-xl); }
.article-more { display: inline-block; font-weight: var(--weight-600); color: var(--primary); text-decoration: none; }
.article-more:hover, .article-more:focus-visible { text-decoration: underline; }

/* Fee schedule extras — the gap figure sits under its price, and the wide
 * independent-doctors table scrolls rather than crushing its five columns. */
.fee-gap { display: block; font-size: var(--step--2); color: var(--ink-soft); }
.fee-sub { display: block; font-weight: var(--weight-400); font-size: var(--step--1); color: var(--ink-soft); }
.fee-caption { font-size: var(--step-0); color: var(--ink-muted); margin: 0 0 var(--space-xs); }
.fee-table--wide { min-width: 44rem; }
.fee-scroll { overflow-x: auto; max-width: 100%; margin-bottom: var(--space-l); }
.fee-scroll .fee-table { width: 100%; min-width: min(30rem, 100%); margin-bottom: 0; }

/* 404 page — the template's own colours are not this site's. The "404"
 * numeral sat at opacity .55 (reading as washed-out grey) and the eyebrow
 * inherited a teal that appears nowhere else here. */
.notfound .eyebrow { color: var(--primary); }
.notfound__code { color: var(--brand-blue-text); opacity: 1; }
.notfound h1 { color: var(--ink); }
.notfound__lede { color: var(--ink-mid); }
.notfound__links-title { color: var(--tertiary); }
.notfound__links a { color: var(--primary); }


/* Buttons state their own colour at a specificity no band-scoped link rule can
 * outrank. A button's text colour must never be inherited or overridable. */
a.btn-solid, a.btn-hero, a.btn-book, a.btn-link { color: var(--surface); }
a.btn-ghost { color: var(--primary); }
a.btn-solid:hover, a.btn-solid:focus-visible,
a.btn-hero:hover, a.btn-hero:focus-visible,
a.btn-book:hover, a.btn-book:focus-visible,
a.btn-link:hover, a.btn-link:focus-visible { color: var(--surface); }
a.btn-ghost:hover, a.btn-ghost:focus-visible { color: var(--surface); }

.fee-note { font-size: var(--step--1); color: var(--ink-muted); line-height: var(--leading-l); margin: 0 0 var(--space-xs); max-width: var(--measure); }
.fee-note:last-of-type { margin-bottom: var(--space-l); }
/* Notes under a schedule: one block, body size, full width, even rhythm. */
.fee-notes { margin: var(--space-s) 0 var(--space-xl); }
.fee-notes p { font-size: var(--step-0); color: var(--ink-soft); line-height: var(--leading-l); margin: 0 0 var(--space-2xs); }
.fee-notes p:last-child { margin-bottom: 0; }

/* Health Resources ------------------------------------------------------- */
/* Curated external links. Own grid rather than reusing .care__grid, which is
   bound to the homepage two-column band. */
.rgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: var(--space-l); margin-top: var(--space-m); }
.rcard { background: var(--surface); border: var(--border-thin) solid var(--ground-mist); border-radius: var(--radius-2xs); padding: var(--space-m); }
.pband--white .rcard { background: var(--ground-mist); border-color: transparent; }
.rcard__title { font-size: var(--step-1); font-weight: var(--weight-600); margin: 0 0 var(--space-2xs); line-height: var(--leading-m); letter-spacing: normal; }
.rcard__title a { color: var(--brand-blue-text); text-decoration: none; }
.rcard__title a:hover, .rcard__title a:focus-visible { color: var(--primary); text-decoration: underline; }
.rcard p { font-size: var(--step-0); color: var(--ink-mid); margin: 0; line-height: var(--leading-l); }

/* Footer legal row: the three items each sit in a 24px-tall box, but they
   filled it differently — the privacy links are inline-block, so their text
   hugged the TOP of the box, while the social icons are grid+place-items:center,
   so their 15px glyph sits in the MIDDLE. Centring the link text in its own box
   makes all three share one optical centre line. A padding nudge on the icons
   only masked it, and broke again at other widths. */
.site-footer__legal-links a { display: grid; place-items: center; }
.site-footer__legal-inner > p { display: grid; place-items: center; min-height: 24px; }
/* MEASURED at 1440: the social <li> rendered 31px tall while its <a> is 24px,
   because a list-item is an inline formatting context and leaves ~7px of
   descender space below the link. The icon therefore centred in the LINK box,
   8px above the text's centre line (svg mid 4493.4 vs text mid 4501.4), even
   though the <ul> and the text boxes themselves aligned exactly. Making the
   item a flex box collapses that leftover space so the glyph centres on the
   row. Do not chase this with padding — the offset is the descender gap. */
.site-footer__social li { display: flex; }
/* The link carried min-height:24px but no height, so as a flex item it shrank
   to its 15px glyph and centred THAT — leaving the icon 4.5px above the text's
   centre line. An explicit height gives it the full 24px box to centre within.
   MEASURED at 1440: svg mid 4496.9 -> 4501.4, exactly matching the copyright
   and privacy links at 4501.4. Verified by injecting the rule into the live
   DOM and re-measuring, not by eye. */
.site-footer__social li a { height: 24px; display: flex; align-items: center; justify-content: center; }
/* NOTE the `li` in that selector: `.site-footer li a` (line ~1473) sets
   display:inline-block at specificity (0,1,2) and beat `.site-footer__social a`
   at (0,1,1) no matter where it sat in the file — height applied, display did
   not, and the flex centring silently never ran. Two rules must AGREE on
   specificity, not just order. */

/* Patient Resources — label/link rows ------------------------------------ */
/* Live renders these as one "Label: url" line each. The original build turned
   them into broken <ul> fragments with URLs split across two <a> tags; this is
   a <dl> so the label/value relationship is real rather than visual. */
.reslist { margin: var(--space-s) 0 0; display: grid; gap: var(--space-2xs); }
.reslist__row { display: flex; flex-wrap: wrap; gap: var(--space-3xs) var(--space-2xs); align-items: baseline; }
.reslist dt { font-weight: var(--weight-600); color: var(--ink); margin: 0; }
.reslist dd { margin: 0; color: var(--ink-soft); }
.reslist a { color: var(--brand-blue-text); word-break: break-word; }
.reslist a:hover, .reslist a:focus-visible { color: var(--primary); }
.reslist__alert { margin: var(--space-s) 0; }
.reslist__links { list-style: none; margin: var(--space-s) 0 0; padding: 0; display: grid; gap: var(--space-2xs); }
.reslist__links a { color: var(--brand-blue-text); }
.reslist__links a:hover, .reslist__links a:focus-visible { color: var(--primary); }


/* About — Our Mission beside live's pull quote ---------------------------- */
.mission { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-xl); align-items: center; }
.mission-quote { margin: 0; text-align: center; max-width: 26rem; justify-self: center; }
.mission-quote__mark { width: 40px; height: 40px; fill: var(--primary); display: block; margin: 0 auto var(--space-2xs); }
.mission-quote blockquote { margin: 0; padding: 0; border: 0; }
.mission-quote blockquote p { font-size: var(--step-2); font-style: italic; color: var(--tertiary); line-height: var(--leading-m); margin: 0 0 var(--space-s); }
.mission-quote figcaption { font-size: var(--step--1); color: var(--ink-soft); line-height: var(--leading-l); }
@media (max-width: 767px) { .mission { grid-template-columns: minmax(0, 1fr); gap: var(--space-l); } }

/* Join the Team ------------------------------------------------------------ */
.pband h2.join-lead { font-size: var(--step-4); font-weight: var(--weight-700); line-height: var(--leading-s); margin-bottom: var(--space-m); }
.pband ul.dot-list { list-style: none; padding-left: var(--space-s); }
.pband ul.dot-list li { position: relative; padding-left: var(--space-s); margin-bottom: var(--space-2xs); }
.pband ul.dot-list li::before { content: ""; position: absolute; left: 0; top: 0.55em; width: 7px; height: 7px; border-radius: var(--radius-round); background: var(--primary); }
.touch { text-align: center; }
.pband .touch h2 { color: var(--primary); font-weight: var(--weight-700); }
.pband .touch p { margin-bottom: var(--space-3xs); }
.touch .mf-form { max-width: 38rem; margin: var(--space-m) auto 0; text-align: left; }

/* Compact forms, as live: the field name sits in the box (the label stays in
 * the accessibility tree), and SEND runs the full width of the form. */
.mf-form--compact { gap: var(--space-2xs); }
.mf-form--compact p:has(> button) { margin: var(--space-3xs) 0 0; }
.mf-form--compact button.btn-solid { width: 100%; justify-content: center; }

/* Contact ------------------------------------------------------------------ */
.media-split--contact { align-items: start; }
.pband p.contact-appt { margin-bottom: var(--space-m); }
.pband h2.contact-form-title { font-size: var(--step-1); line-height: var(--leading-2xl); margin: 0 0 var(--space-xs); }
.pband.contact-map-band { padding-top: 0; padding-bottom: var(--space-2xl); }
.media-split--policy { align-items: start; }
.media-split--policy img { aspect-ratio: 53 / 50; object-fit: cover; object-position: 50% 30%; }
.pband p.policy-warning { color: var(--primary); font-weight: var(--weight-700); margin-bottom: var(--space-xs); }

/* Mobile header + footer, centred (owner's request 2026-10-02). Appended last
 * so it outranks the live-measured min-heights above, which pinned the button
 * flush against the navbar and let footer columns run into each other. */
@media (max-width: 767px) {
    .site-header__bar-inner { flex-direction: column; align-items: center; justify-content: center; text-align: center;
        gap: var(--space-xs); min-height: 0; padding-block: var(--space-s) var(--space-m); }
    .site-header__contact { align-items: center; gap: var(--space-xs); }

    .site-footer__main { text-align: center; }
    .site-footer__cols { row-gap: var(--space-l); }
    .site-footer__cols > div, .site-footer__cols > div:not(.site-footer__brand), .site-footer__brand { min-height: 0; padding: 0; }
    .site-footer__brand img { margin-inline: auto; display: block; }
    .site-footer__brand p { margin-inline: auto; max-width: 32rem; }
    .site-footer h2 { margin-bottom: var(--space-xs); }
    .site-footer__legal { min-height: 0; padding-block: 0 var(--space-m); }
    .site-footer__legal-inner { align-items: center; text-align: center; gap: var(--space-s); }
    .site-footer__legal-links, .site-footer__social { justify-content: center; }
    .site-footer__main { padding-top: var(--space-xl); padding-bottom: var(--space-m); }
    .site-footer__call a { white-space: nowrap; }
}
/* Bigger tap area for resource links without changing the line box. */
.rcard__title a, .reslist a { padding-block: calc(var(--space-3xs) / 2); }

/* Sub-labels split out of run-on paragraphs ("Areas of Support", "Services include:"). */
.pband p.sublabel { margin: var(--space-xs) 0 var(--space-3xs); }
.pband p.sublabel strong { color: var(--ink-muted); }

@media (max-width: 1024px) {
    /* Tablet footer: the live-measured min-heights left a gap under the brand block. */
    .site-footer__brand, .site-footer__cols > div:not(.site-footer__brand) { min-height: 0; }
    /* Homepage contact copy: the tablet tier zeroed the paragraph gap. */
    .contact p { padding-bottom: var(--space-2xs); }
    .site-footer__main { padding-top: var(--space-l); }
    /* About hero: the intro sits over the family photo once the columns
     * narrow; a light card keeps it readable without hiding the photo. */
    .phero--tall .ph-shell { background: var(--card-veil); padding: var(--space-s) var(--space-m); border-radius: var(--radius-s); max-width: 40rem; margin-inline: 0; }
}
@media (max-width: 767px) {
    .phero--tall .ph-shell { padding: var(--space-s); max-width: none; }
}

/* Doctor photos come from the app (photo_url, #301), inside a .doctor-avatar box
 * that shows the fallback for a doctor without one. The box keeps the photo's own
 * size and shape, which .doctor-avatar would otherwise set. */
.pcard__photo.doctor-avatar { aspect-ratio: auto; border-radius: var(--radius-round); display: flex; }
/* The fallback's gradient is for a doctor with no photo: under a photo it shows as
 * a hairline ring at the anti-aliased edge, so a box holding a photo has none. */
.doctor-avatar:has(img) { background: none; }
