/*
 * 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 */

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

    /* --- 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.
 */
/* Terrace Clinic 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 below, 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; }

* { 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%;
}

/* 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-4); }
h2, .h2 { font-size: var(--step-3); }
h3, .h3 { font-size: var(--step-2); }
h4, .h4 { font-size: var(--step-1); }
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 inherit typography rather than using browser chrome. */
:where(input, button, textarea, select) { font: inherit; }   /* zero specificity: "Form controls" below must win */

/* ═══ 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) ═════════════════════════ */

/* This site's field look (owner, 2026-10-05: one shape for every text field,
   search and contact alike): pills like every button here (.btn, the Book
   button; one corner, --control-radius, for every button and field), the contact
   form's brand-mid hairline, and the contact form's height (47px at the
   desktop, as live). The textarea takes the pill's own corner, half the
   control height. The fill is per ground: the contact card re-sets it.
   ONE focus ring on links, buttons and fields: the bright brand blue the
   nav and links hover in, measured by painting at 4.6-5.8:1 on the white,
   grey and sky grounds and 3.3:1 on the brand-mid blue. The navy grounds
   (the footer bar, the emergency panel, the phone menu) re-set it to
   brand-mid: 5.5:1 on the navy, 9.4:1 on the deep navy. */
:root {
    --control-radius: var(--radius-pill);
    --field-h: max(var(--target-min), calc(var(--step-0) * var(--leading-l) + 2 * var(--space-2xs) + 2 * var(--border-thin)));
    --field-pad-block: var(--space-2xs);
    --field-pad-inline: var(--space-s);
    --field-textarea-radius: calc(var(--target-min) / 2);
    --field-bg: var(--surface);
    --field-ink: var(--ink);
    --field-border-w: var(--border-thin);
    --field-border: var(--brand-mid);
    --field-border-hover: var(--brand-mid);
    --field-placeholder: var(--ink-soft);
    --focus-ring: var(--secondary);
    --focus-ring-w: var(--border-medium);
}
.site-footer__bar,
.emergency-panel,
.mobile-nav { --focus-ring: var(--brand-mid); }

/* Buttons size to their content, not to the form-control full width. */
input[type="submit"], input[type="button"] { width: auto; }

/*
 * 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. A bare <button> is
 * reset above, not styled, because most of them are menu and dropdown
 * triggers rather than visual buttons. */
.btn,
.mf-search__submit {
    justify-content: center;
    min-block-size: var(--target-min);   /* the 44px tap target */
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
}

.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.
 *
 * Deliberately written against fallbacks rather than this site's own token
 * 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; }
.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-xs);
    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;
    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);
}

/* ===========================================================================
 * TERRACE CLINIC — site layer
 *
 * Transcribed from https://www.terraceclinic.com.au at 1440 (design width),
 * cross-checked at 1920, 1024 and 390. Every value below was measured, not
 * chosen. Where a measurement is expressed through the scale rather than as a
 * literal, the comment names the measured pixel value it reproduces at 1440.
 *
 * TYPE SCALE — the eight --u-* knobs, nothing else.
 * Live sets a FLUID ROOT, html { font-size: calc(0.74981rem + 0.31266vw) },
 * and then states every size as a plain rem multiple of it (0.75 / 0.875 / 1 /
 * 1.125 / 1.25 / 1.5 / 2 / 2.5 / 3 / 3.5 / 4.5). Reproducing that root
 * *underneath* Utopia's own fluid steps would scale every size twice, so the
 * root stays at 100% and the fluidity is carried by the steps instead.
 *
 * The scale block at the top of this file is the template's own, verbatim
 * (_template 14c51d0): every --step-* and --space-* is derived from the knobs
 * below and NO step is redeclared here (owner decision 2026-10-03).
 *
 * THE RANGE is the fleet's common 390 -> 1440 (owner decision 2026-10-03):
 * sizes hold their 1440 values on larger screens. Body is live's exact 16.5
 * at 1440; on a phone it is lifted from live's 13.2 to 16, a readable floor
 * that also keeps iOS from zooming the form fields (they inherit step-0).
 *
 * TYPE. Desktop: live is a pure rem ladder, and 1.2 is its least-squares
 * ratio at 1440 (sqrt-count weighted, one step per size class; optimum
 * 1.198). Phone: body is lifted, so the phone ratio is fitted to live's
 * HEADING px at 390 (1.5rem and up), keeping each class on its desktop step:
 * 1.145. Text below a heading simply rides the lifted body.
 *
 *     live (rem)  @1440 ->  ours     @1440   err    @390 ours (live)
 *     0.75        12.37 ->  step--2  12.20  -1.4%   12.20  (9.91)  "Powered by"
 *     0.875       14.44 ->  step--1  13.97  -3.2%   13.97 (11.57)  footer legal row
 *     1           16.50 ->  step-0   16.50   0.0%   16.00 (13.22)  body
 *     1.125       18.56 ->  step-1   19.80  +6.7%   18.32 (14.87)  ledes, card copy
 *     1.25        20.62 ->  step-1   19.80  -4.0%   18.32 (16.52)  card titles
 *     1.5         24.75 ->  step-2   23.76  -4.0%   20.98 (19.83)  sub-headings
 *     2           33.00 ->  step-4   34.21  +3.7%   27.50 (26.44)  band title
 *     2.5         41.25 ->  step-5   41.06  -0.5%   31.49 (33.05)  legal headings
 *     3           49.50 ->  step-6   49.27  -0.5%   36.05 (39.66)  legal + policy titles
 *     3.5         57.75 ->  step-7   59.12  +2.4%   41.28 (39.66)  section titles
 *     3.5 (phone 46)    ->  step-7, phone step-8    47.27 (46.27)  why + /contact titles
 *     4.5         74.25 ->  step-8   70.95  -4.4%   47.27 (46.27)  hero display
 * step--1 and step--2 come out LARGER at 390 than at 1440 (the lifted body
 * over a gentler phone ratio), and clamp() then holds them at their phone
 * value at every width: 13.97 and 12.20, both nearer live's 1440 sizes than
 * the 1440 anchors were. The hero is the one size above the ladder's reach
 * at 1.2 (step-8 = 4.3x body); left 4.4% short rather than tuning a step.
 *
 * SPACE — live's desktop rhythm is a 10px ladder (10/20/30/40/60/80/120), which
 * IS the template's ladder on a 20px unit: --u-space-max 1.25 lands every step
 * exactly at 1440 (2xs 10, xs 15, s 20, m 30, l 40, xl 60, 2xl 80, 3xl 120),
 * and --u-space-min 1 (0.8 of it) makes a phone 20% tighter (16px unit).
 * Spacing SCALES (owner decision 2026-10-03) where live's band padding stays
 * 120px flat.
 * =========================================================================== */

:root {
    /* --- The eight knobs (see the note above) ------------------------- */
    --u-min-vw: 390;
    --u-max-vw: 1440;
    --u-min-font: 16;
    --u-max-font: 16.5;
    --u-min-ratio: 1.145;
    --u-max-ratio: 1.2;
    --u-space-min: 1;
    --u-space-max: 1.25;

    /* --- Named compositions: sums of steps, never new steps -----------
     * px at 390 / 1024 / 1440, against live's measurement. */
    --space-inset-m: calc(var(--space-l) + var(--space-2xs));             /* 40 / 46 / 50; live 50 */
    --space-indent: calc(var(--space-l) + var(--space-xs));               /* 44 / 51 / 55; live 55, the about body */
    --space-block: calc(var(--space-xl) + var(--space-2xs));              /* 56 / 64 / 70; live 70 head gap, 60 phone band */
    --space-card-pad: calc(var(--space-xs) + var(--space-2xs));           /* 20 / 23 / 25; live 25 card padding, 24.7 button inline */
    --space-hero-top: calc(var(--space-3xl) + var(--space-3xl) + var(--space-2xs));   /* 250 at 1440; live 250 */
    /* A gap that OPENS with the viewport: a pair minus its own small step is
     * 0 at 390 and 40 at 1440. Live's two big insets are 0 on a phone and
     * 90 / 100 at 1440; two of these plus a step reproduce that line. */
    --space-ramp: calc(var(--space-l-2xl) - var(--space-l));                                   /*  0 / 24 / 40 */
    --space-inset-list: calc(var(--space-ramp) + var(--space-ramp) + var(--space-2xs));      /*  8 / 58 / 90; live 0 / - / 90 */
    --space-inset-head: calc(var(--space-ramp) + var(--space-ramp) + var(--space-s));        /* 16 / 67 / 100; live 0 / - / 100 */
    /* The mirror image: a gap that CLOSES as the viewport widens. A step minus
     * its own pair is positive on a narrow screen and 0 at 1440, so taking it
     * off a value leaves 1440 exactly as measured and eases everything below.
     * Used by the desktop nav (2026-10-03) to keep its six links, the logo
     * and the Call pill on one row down to 1200, as live's fluid root does.
     * px at 390 / 1200 / 1300 / 1440: */
    --space-taper: calc(var(--space-3xl) - var(--space-2xl-3xl));                              /* 32 / 7.3 / 4.3 / 0 */
    --space-taper-s: calc(var(--space-s) - var(--space-xs-s));                                 /*  4 / 0.9 / 0.5 / 0 */

    /* --- Shell -------------------------------------------------------
     * Live's container is 1350px and it is a REAL cap: verified at 1920,
     * where it still measures 1350 (not viewport-minus-padding). The
     * services band alone caps at 1500. Gutter 45px at 1440, 20px at 390. */
    /* Live's INNER content measures 1350px, with a 45px gutter outside it, so
     * the container's max-width is 1350 + 2*45 = 1440. Verified at 1920, where
     * the inner still measures 1350 -- a real cap, not viewport-minus-padding.
     * The services band caps its inner at 1500 (so 1500 + 2*50 = 1600). */
    --shell: 90rem;            /* 1440px -> 1350px of content */
    --shell-wide: 100rem;      /* 1600px -> 1500px of content */
    /* MEASURED gutter: 20px@390, 30px@1024, 45px@1440. As a sum of steps:
     * 22 / 36.5 / 46 (the template's s-l gutter pair plus 3xs), so the
     * inner is 1348 at 1440 against live's 1350. */
    --space-gutter: calc(var(--space-s-l) + var(--space-3xs));
    --grid-max-width: var(--shell);
    --grid-gutter: var(--space-gutter);

    /* --- Colour ------------------------------------------------------
     * Measured off live, expressed as OKLCH dials so the palette is one
     * system rather than scattered hex. The comment names the sRGB value
     * each dial reproduces; the check script strips comments before
     * counting raw hex, so these annotations are documentation, not
     * violations.
     *
     *   navy      rgb(0, 27, 72)      the display headline + footer bar
     *   deep      rgb(2, 69, 122)     buttons, card titles, eyebrows
     *   bright    rgb(2, 138, 190)    links, active nav, accent headings
     *   mid       rgb(151, 202, 219)  hairlines, large decorative icons
     *   sky       rgb(215, 232, 239)  tinted grounds, pills, cards
     *   ground    rgb(242, 244, 246)  the page ground
     *   ink       rgb(54, 59, 64)     body copy
     *   ink-soft  rgb(50, 74, 109)    footer nav links
     */
    /* Every value below is an exact OKLCH conversion of live's measured sRGB;
     * the build's first conversions painted 4-9 units off (navy as 1,31,81
     * against live's 0,27,72), now verified by painting each to a canvas.
     *
     * The core colours ARE the template's roles, set on its dials, so every
     * template component (focus rings, skip link, .btn--*, pale/hover
     * variants) paints in the brand rather than the template's default blue:
     *   primary   = deep    rgb(2, 69, 122)   buttons, card titles, eyebrows
     *   secondary = bright  rgb(2, 107, 159)  links, active nav (a11y-corrected, below)
     *   tertiary  = navy    rgb(0, 27, 72)    display headline, footer bar
     *   --bg = ground, --ink = ink, --surface = white, all on the neutral dials.
     * The --brand-* names that remain are the off-family tints the three
     * roles cannot derive (sky, mid, live's vivid, the bluish ink-soft). */
    --palette-hue: 250.11;
    --palette-chroma: 0.1077;
    --l-primary: 0.3845;
    --palette-tertiary-shift: 8.8;      /* 258.91 */
    --chroma-tertiary: 0.091;
    --l-tertiary: 0.2358;
    --hue-neutral: 248.14;
    --chroma-ink: 0.0088;               /* --ink = 1.25x = 0.011 */
    --l-ink: 0.3495;                      /* rgb(54, 59, 64) */
    --l-bg: 0.9662;                     /* rgb(242, 244, 246) */
    --chroma-ground: 0.0034;
    --l-on-solid: 1;                    /* live's text on fills is #fff */
    /* Live's accent is rgb(2,138,190), which measures 3.10:1 on the sky
     * ground and 3.54:1 on the page ground -- below the 4.5:1 AA floor.
     * Darkened 45% toward this site's own navy to rgb(2,107,159): the same
     * blue family, measured 4.66:1 on the painted ground.
     *
     * The 45% (rather than the 42% a flat sky ground implies) comes from
     * SAMPLING THE PAINTED PIXELS rather than reading a declared ancestor
     * colour: the hero's radial gradients tint the sky pill to rgb(211,230,238),
     * which is darker than the rgb(215,232,239) the element declares, so a
     * ratio computed against the declared value reads 0.1 high and passes a
     * check the rendered page fails. See the site CLAUDE.md. */
    --palette-secondary-shift: -9.8;    /* 240.31; rgb(2, 107, 159), a11y-corrected from live's rgb(2,138,190) */
    --chroma-secondary: 0.1153;
    --l-secondary: 0.5036;
    --brand-bright-live: oklch(59.71% 0.1248 234.17);/* rgb(2, 138, 190), live's own value */
    /* LARGE HEADINGS ONLY. Live's vivid sky blue measures 3.10:1 on the sky band,
     * 3.54:1 on the page ground and 3.90:1 on white: under the 4.5:1 floor for
     * body-size text (which is why --secondary is darkened) but over the 3:1
     * floor WCAG sets for large text (>= 24px, or >= 18.66px bold). So it is
     * safe on the 24.75px/800 strip title and the 33px band title, and NOT on
     * the 16.5px eyebrow pills, links or the nav. */
    --brand-vivid: var(--brand-bright-live);
    --brand-mid:      oklch(80.91% 0.0582 220.41);   /* rgb(151, 202, 219) */
    --brand-sky:      oklch(92.04% 0.0205 224.31);   /* rgb(215, 232, 239) */
    --brand-ink-soft: oklch(40.61% 0.0664 257.73);   /* rgb(50, 74, 109)   */
    --hairline: var(--brand-mid);
    /* Live's hero glow and the soft card shadow use three blues and a teal
     * that belong to no role; named here so no colour is written inline. */
    --glow-blue:   oklch(59.85% 0.1207 232.11);   /* rgb(8, 139, 187)   */
    --glow-mid:    oklch(83.79% 0.0556 226.36);   /* rgb(164, 210, 230) */
    --glow-pale:   oklch(92.74% 0.0233 220.73);   /* rgb(215, 235, 242) */
    --shadow-tint: oklch(74.47% 0.0578 222.66);   /* rgb(132, 181, 199) */

    /* Leading: live's own steps. xs and s are retuned (xs was unused; s moves
     * 1.18 -> 1.2), 2xs and 2xl are extensions for live's 1.0 eyebrows and
     * 2.125 nav. */
    --leading-2xs: 1;
    --leading-xs: 1.1;
    --leading-s: 1.2;
    --leading-2xl: 2.125;

    /* --- Type faces --------------------------------------------------
     * Live loads four: Outfit (body), Lato (headings), Karla (a handful of
     * eyebrows and the "/" separators) and Open Sans (the single "|" glyph
     * in the footer legal row). Karla and Open Sans are carried for
     * fidelity — see the site CLAUDE.md deviations note. */
    --font-body: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-head: "Lato", Georgia, "Times New Roman", serif;
    --font-alt: "Karla", var(--font-body);

    /* --- Weight: one extension -------------------------------------
     * The template ships 400/600/700 deliberately and asks for a reason
     * before a fourth. Live's reason: it uses 500 as a distinct role —
     * the hero's fact rows, the "/" separators, the footer nav links and
     * the two Karla eyebrows are all 500, and none of them is body (400)
     * or nav emphasis (600). Outfit and Karla both ship a visibly
     * separate Medium cut, so this is a real weight, not a rounding. */
    --weight-500: 500;
}

/* Live's root is fluid; ours is not — the steps carry the fluidity instead.
 * Stated explicitly so nobody reintroduces the double-scaling. */
html { font-size: 100%; }

/* The hero badge bleeds past the right edge by design -- live does the same,
 * deliberately, so the icon tile runs off the page. Live sets
 * `overflow-x: clip` on <body>, which does NOT stop the document scrolling:
 * measured on live, scrollWidth is 1445 against a 1440 viewport and 1110
 * against 1060, i.e. live's own page scrolls sideways at 1060-1440.
 *
 * DELIBERATE DIVERGENCE. The bleed is the design and is kept; the sideways
 * scroll is a live defect and is not. Clipping at the ROOT is what actually
 * stops it. `clip` rather than `hidden` so position:sticky still works. */
html { overflow-x: clip; }
body { overflow-x: clip; }

body {
    font-family: var(--font-body);
    font-size: var(--step-0);
    line-height: var(--leading-l);          /* 24.7488 / 16.4992 = 1.5 exactly */
    color: var(--ink);
    background: var(--surface);
}

h1, h2, h3, h4, h5, h6 { font-family: var(--font-head); }

/* The template sets letter-spacing, text-wrap: balance and
 * overflow-wrap: break-word on every heading. Live sets none of them, and
 * they change line counts (a two-line hero title becomes three). Reset,
 * then opt back in per component where live actually tracks. */
h1, h2, h3, h4, h5, h6 {
    letter-spacing: normal;
    text-wrap: wrap;
    overflow-wrap: normal;
}

a { color: var(--secondary); }

/* --- Containers -------------------------------------------------------- */
.u-container {
    width: 100%;
    max-width: var(--shell);
    margin-inline: auto;
    padding-inline: var(--space-gutter);
}
.u-container--wide { max-width: var(--shell-wide); }

/* A band is the top-level rhythm unit. Live pads 120px top and bottom at
 * 1440 on every band except the accreditation strip (which is flush) and
 * the hero (250px top, to clear the 161px header that sits above it).
 * --space-3xl is that 120 at 1440 and scales to 96 at 390 (owner decision
 * 2026-10-03: spacing scales even where live's is flat). */
.band { padding-block: var(--space-3xl); background: var(--bg); }
.band--sky { background: var(--brand-sky); }

/* --- Buttons ----------------------------------------------------------- */
/* Live: rgb(2,69,122) fill, white text, 100px radius (a pill), padding
 * 12.3744px 24.7488px = 0.75rem 1.5rem of live's own root, uppercase,
 * weight 600, font 16.4992px. The header CTA is the same pill at 45px
 * inline padding and 16px block, weight 700, radius 250px. */
.btn,
.mf-search__submit {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3xs);
    padding: var(--space-2xs-xs) var(--space-card-pad);   /* live 12.4 / 24.7 at 1440 -> 15 / 25; 8 / 20 at 390 (live 9.9 / 19.8) */
    border-radius: var(--control-radius);
    background: var(--primary);
    color: var(--surface);
    font-family: var(--font-body);
    font-size: var(--step-0);
    font-weight: var(--weight-600);
    line-height: var(--leading-l);
    text-transform: uppercase;
    text-decoration: none;
    border: 0;
    transition: background var(--duration-base) ease, transform var(--duration-base) ease;
}
/* HOVER, measured off live (2026-09-18): the fill becomes a two-stop gradient,
 * deep navy -> accent, at 249deg on the body buttons and 131deg on the header
 * CTA. A background-image cannot transition, so the gradient is a ::before at
 * opacity 0 that fades in over the flat fill.
 * DIVERGENCE: live's far stop is rgb(2,138,190), on which the white label
 * measures 3.9:1. Ours ends on --brand-bright, the same darkened accent used
 * everywhere else on this site (5.7:1), so the label passes AA across the
 * whole gradient. */
.btn,
.mf-search__submit { position: relative; isolation: isolate; }
.btn::before,
.mf-search__submit::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: var(--layer-behind);
    border-radius: inherit;
    background-image: linear-gradient(249deg, var(--primary) 0%, var(--secondary) 100%);
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-out);
}
.btn--head::before { background-image: linear-gradient(131deg, var(--primary) 0%, var(--secondary) 100%); }
.btn:hover::before, .btn:focus-visible::before,
.mf-search__submit:hover::before, .mf-search__submit:focus-visible::before { opacity: 1; }
/* the template's own hover fills would otherwise paint over the gradient's base */
.btn:hover, .btn:focus-visible, .btn--primary:hover,
.mf-search__submit:hover, .mf-search__submit:focus-visible { background: var(--primary); color: var(--surface); }
.btn svg { width: 1em; height: 1em; fill: currentColor; flex: none; }

/* MOTION, also live's: the header CTA "floats" -- lifts 8px and holds -- and
 * the body buttons "bob": the same 8px lift, then a slow drift between -8 and
 * -4px for as long as the pointer stays. transform only; hover only (a
 * keyboard focus gets the gradient but does not move); off under reduced motion. */
/* The lift is a TRANSITION on the individual `translate` property and the bob
 * is an ANIMATION on `transform`, so they compose: when the pointer leaves, the
 * bob stops and the lift eases back down. (Elementor does both on `transform`,
 * which makes live's button snap 8px on mouse-out.) */
@media (prefers-reduced-motion: no-preference) {
    .btn { transition: translate 0.3s ease-out; }
    .btn:hover { translate: 0 -8px; }
    .btn:not(.btn--head):hover { animation: btn-bob 1.5s ease-in-out 0.3s infinite alternate; }
}
@keyframes btn-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* PADDING: live states the header CTA's padding in FIXED px (16 / 45), so the
 * button grows only with its label. Ours was in em, which matched at 1440 and
 * then swelled with the fluid type -- 282x62 against live's 254x56 on a 2223px
 * screen, which is where the owner saw it as over-padded. On the space scale
 * now: --space-xs and l + 3xs are 15 / 46 at 1440 and hold there, so the
 * pill no longer swells with its label on a larger screen. */
.btn--head { padding: var(--space-xs) calc(var(--space-l) + var(--space-3xs)); font-weight: var(--weight-700); }

/* --- Eyebrow pill ------------------------------------------------------
 * Live: sky ground, 5px radius, 10px 15px padding, 16.4992px, weight 700,
 * uppercase, bright blue. The services band's variant is outlined instead
 * of filled, and the contact band's is Karla at weight 500. */
.eyebrow {
    display: inline-block;
    padding: var(--space-2xs) var(--space-xs);   /* live 10px 15px; the tokens are 10 / 15 at 1440 */
    border-radius: var(--radius-xs);
    background: var(--brand-sky);
    color: var(--secondary);
    font-family: var(--font-head);
    font-size: var(--step-0);
    font-weight: var(--weight-700);
    line-height: var(--leading-l);
    text-transform: uppercase;
    letter-spacing: normal;
    margin: 0;
}
.eyebrow--deep { color: var(--primary); }
.eyebrow--outline {
    /* live: a 30px-tall tag -- 4px 10px padding and a 1.2 leading, not the filled pill's 10px 15px */
    padding: var(--space-3xs) var(--space-2xs);
    line-height: var(--leading-s);
    background: transparent;
    border: var(--border-thin) solid var(--primary);
    color: var(--primary);
    font-family: var(--font-alt);
    font-weight: var(--weight-500);
}
.eyebrow--alt {
    /* live: 27px tall, 4px 10px, 1px deep-navy outline on a clear ground */
    padding: var(--space-3xs) var(--space-2xs);
    background: transparent;
    border: var(--border-thin) solid var(--primary);
    font-family: var(--font-alt);
    font-weight: var(--weight-500);
    line-height: var(--leading-2xs);
    color: var(--primary);
}

/* --- Display headings --------------------------------------------------
 * Live: hero 74.2465px (4.5rem) weight 600; section titles 57.7473px
 * (3.5rem) weight 700; contact title 32.9984px (2rem) weight 700. All Lato
 * on navy, except the contact title which is bright blue. Line-height
 * re-measured 2026-09-18: 1.1 on the hero (81.7 over 74.25) and 1.2 on the
 * section titles (69.3 over 57.75). */
.display {
    font-family: var(--font-head);
    font-size: var(--step-8);
    font-weight: var(--weight-600);
    line-height: var(--leading-xs);
    color: var(--tertiary);
    margin: 0;
}
.section-title {
    font-family: var(--font-head);
    font-size: var(--step-7);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--tertiary);
    margin: 0;
}
/* .display--sm had no rule of its own, so it fell through to .display's 74px
 * where live sets 49.5px / 700 / 1.2 in the mid navy. */
.display--sm {
    font-family: var(--font-head);
    font-size: var(--step-6);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--primary);
    margin: 0;
}
.band-title {
    font-family: var(--font-head);
    font-size: var(--step-4);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--brand-vivid);
    margin: 0;
}

.lede { font-size: var(--step-1); line-height: var(--leading-l); margin: 0; }
.lede + .lede { margin-top: var(--space-xs); }   /* live 0.8em = 14.8px at 1440 */

/* ===========================================================================
 * HEADER — measured 161px tall at 1440. Logo 260x100 at x=45; nav centred on
 * a 655px run at 16.4992px Lato 700 with 1px sky separators between items;
 * CTA pill hard right. Live's header sits ABOVE the hero, not over it — the
 * hero's own 250px top padding is inside its band.
 * ======================================================================== */
/* MEASURED: live's header is transparent and the hero runs UNDER it -- <main>
 * starts at y=1 while the header occupies 0-161. So the header costs the page
 * no height, and the hero's 250px top padding is mostly spent repaying that
 * overlap (live's H1 lands at y=261, i.e. 100px below the header's bottom).
 * The header must therefore sit above the hero in paint order. */
.site-header {
    position: relative;
    z-index: var(--layer-nav);
    background: transparent;
    border-bottom: var(--border-thin) solid var(--hairline);   /* live: 1px rgb(151,202,219) */
}
.site-main { margin-top: calc(-1 * var(--header-h)); }
:root { --header-h: 10.0625rem; }             /* 161px */
.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-s);
    min-height: 10.0625rem;                 /* live 161px */
    padding-block: var(--space-2xs);
}
.site-logo { flex: none; display: block; }
.site-logo img { width: 16.25rem; height: auto; display: block; }   /* 260px */

/* MEASURED: live's nav starts 50px after the logo (x=355) and is left-aligned;
 * the CTA takes the right edge. It is not centred between them. */
.site-nav { margin-inline: var(--space-m) auto; }
.site-nav ul {
    display: flex;
    align-items: center;
    list-style: none;
    margin: 0;
    padding: 0;
}
/* Live's separator is an accent hairline 18.6px tall, centred on the label,
 * with 5px of air either side -- not a full-height border. */
.site-nav li { position: relative; }
.site-nav li + li { margin-left: var(--space-3xs); padding-left: var(--space-3xs); }
.site-nav li + li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    height: 1.125em;
    border-left: var(--border-thin) solid var(--secondary);
    transform: translateY(-50%);
}
.site-nav a {
    display: block;
    /* Both ease toward the collapse and are untouched at 1440 (label 16.5,
     * padding 15; live 16.5 / 14-15). Padding 15 / 9.8 / 6.1 and label
     * 16.5 / 15.9 / 15.5 at 1440 / 1300 / 1200 -- the label follows live's own
     * fluid text (16.5 / 16.1 / 15.7) rather than staying at body size. */
    padding-inline: calc(var(--space-2xs-xs) - var(--space-taper));
    font-family: var(--font-head);
    font-size: calc(var(--step-0) - var(--space-taper-s));
    font-weight: var(--weight-700);
    line-height: var(--leading-l);
    color: var(--ink);
    text-decoration: none;
    /* A label never breaks inside the one-row bar: the row is swept at 1px
     * from the collapse to 1920 and fits everywhere, so a two-line label can
     * only mean the row ran short, which nowrap turns into a measurable
     * overflow rather than a quiet wrap. Centred all the same, so a label that
     * ever did wrap would sit centred between its two separators (the
     * separator air is symmetrical: padding + 3xs either side of the pipe). */
    white-space: nowrap;
    text-align: center;
}
.site-nav a:hover, .site-nav a:focus-visible { color: var(--secondary); }
.site-nav a.active, .site-nav a[aria-current="page"] { color: var(--secondary); }
.site-header .btn { flex: none; }

/* Mobile nav. Live collapses to a hamburger below 1024; we collapse at 1199
 * (see NAV COLLAPSE below) because our nav labels are wider at those widths
 * than live's. Live collapses to a panel of
 * Barlow 600 links at 20.624px. display:none while closed, deliberately: a
 * translateX(100%) drawer still contributes to document scroll width. */
.nav-toggle {
    display: none;
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    align-items: center;
    justify-content: center;
    /* live: a filled deep-navy square with white bars, 4px radius */
    background: var(--primary);
    border: 0;
    border-radius: var(--radius-xs);
    color: var(--surface);
    cursor: pointer;
}
.nav-toggle svg { width: 1.5rem; height: 1.5rem; fill: currentColor; }

/* NAV COLLAPSE at 1199, not live's 1024 (was 1299 until 2026-10-03; the
 * owner wants the full nav on 1280px laptops). MEASURED: the three header
 * blocks (260px logo, six Lato 700 links, the Call pill) leave the nav ~527px
 * at 1200, where the links at body size and 2xs-xs padding needed ~608. Live
 * fits by shrinking its logo and gutters; ours keeps both and instead eases
 * the link padding and label size on --space-taper / --space-taper-s, which
 * are 0 at 1440. Swept at 1px from 1200 to 1920 on every page: one row, no
 * label wrapped or clipped, the pill clear. 1081-1199 keeps the desktop
 * header's 161px bar and 260px logo.
 * OWNER'S CALL (2026-10-03): the menu button sits on the RIGHT beside the Call
 * pill, one group with the header's own scale gap between them (--space-s,
 * --space-xs from 1080 down) -- not centred between logo and pill as live
 * does at 1024. Its auto left margin takes all the free space. */
@media (max-width: 74.9375rem) {
    .site-nav { display: none; }
    .nav-toggle { display: inline-flex; order: 2; }
    .site-header .btn { order: 3; margin-left: 0; }
    .site-logo { order: 1; }
}

/* ===========================================================================
 * HERO — band 0. 1254px tall at 1440.
 * Ground rgb(242,244,246) under two radial gradients (transcribed verbatim
 * from live's computed background-image). Inner padding 250px top / 120px
 * bottom. Two columns: 700px copy, 570px arch photo with a 60px left margin.
 * ======================================================================== */
/* Four of live's six bands carry their own stack of radial gradients over the
 * ground -- a soft blue wash whose centres and radii differ per band. Copied
 * verbatim from live's computed background-image; this is decoration that
 * cannot be eyeballed, so it is transcribed rather than approximated. */
.hero {
    background:
        radial-gradient(1100px 800px at 85% 40%, color-mix(in srgb, var(--glow-blue) 30%, transparent), transparent 48%),
        radial-gradient(1200px 900px at 70% 55%, color-mix(in srgb, var(--glow-mid) 22%, transparent), transparent 72%),
        radial-gradient(1300px 950px at 25% 50%, color-mix(in srgb, var(--glow-pale) 12%, transparent), transparent 78%),
        var(--bg);
    padding-block: 0;
}
.about {
    background:
        radial-gradient(900px 700px at 22% 22%, color-mix(in srgb, var(--glow-mid) 20%, transparent), transparent 50%),
        radial-gradient(1100px 850px at 45% 55%, color-mix(in srgb, var(--glow-blue) 22%, transparent), transparent 50%),
        radial-gradient(1300px 950px at 28% 78%, color-mix(in srgb, var(--glow-pale) 14%, transparent), transparent 70%),
        var(--bg);
}
.why {
    background:
        radial-gradient(1000px 700px at 20% 25%, color-mix(in srgb, var(--glow-blue) 28%, transparent), transparent 48%),
        radial-gradient(1100px 800px at 80% 40%, color-mix(in srgb, var(--glow-mid) 22%, transparent), transparent 72%),
        radial-gradient(1200px 850px at 30% 75%, color-mix(in srgb, var(--glow-pale) 14%, transparent), transparent 78%),
        var(--bg);
}
.services {
    background:
        radial-gradient(900px 700px at 22% 22%, color-mix(in srgb, var(--glow-mid) 20%, transparent), transparent 72%),
        radial-gradient(1100px 850px at 45% 55%, color-mix(in srgb, var(--glow-blue) 22%, transparent), transparent 70%),
        radial-gradient(1200px 900px at 82% 50%, color-mix(in srgb, var(--glow-blue) 28%, transparent), transparent 55%),
        radial-gradient(1300px 950px at 28% 78%, color-mix(in srgb, var(--glow-pale) 14%, transparent), transparent 80%),
        var(--bg);
}
.hero__inner {
    padding-block: var(--hero-pad-top) var(--hero-pad-bottom);
    display: flex;
    flex-direction: column;
    gap: var(--space-s);
}
/* MEASURED: live's hero pads 250px top / 120px bottom above 1024, and
 * 180px / 50px at 1024 and below. It is a breakpoint on live, not a curve,
 * so it is a breakpoint here. */
:root { --hero-pad-top: var(--space-hero-top); --hero-pad-bottom: var(--space-3xl); }   /* 250 / 120 at 1440 */

.hero__cols {
    display: grid;
    grid-template-columns: minmax(0, 700fr) minmax(0, 630fr);
    gap: var(--space-s);                      /* live 20px */
}
.hero__copy { display: flex; flex-direction: column; align-items: flex-start; }
/* stretch to the media column's height so margin-top:auto has room to work */
.hero__cols { align-items: stretch; }
.hero__copy > * + * { margin-top: 0; }
.hero .display { margin-top: var(--space-s); max-width: 100%; }               /* live 19px */
.hero__lede { margin-top: var(--space-2xs); max-width: 39rem; }   /* live 10px below the h1; 623px wide */

/* The two rule rows. Live: 500px wide, 1px top rule on the first and 1px
 * bottom rule on the second (so the pair reads as three hairlines), 10px
 * block padding, items separated by a Karla "/" at 18.1491px. */
.hero__facts { margin-top: var(--space-l); width: min(100%, 31.25rem); }
.hero__fact-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2xs) var(--space-xs);
    padding-block: var(--space-2xs);
}
/* live: row 1 is ruled top AND bottom, row 2 bottom only -- three hairlines */
.hero__fact-row { border-bottom: var(--border-thin) solid var(--hairline); }
.hero__fact-row:first-child { border-top: var(--border-thin) solid var(--hairline); }
.hero__fact {
    font-size: var(--step-0);
    font-weight: var(--weight-500);
    line-height: var(--leading-l);
    color: var(--primary);
}
.hero__sep {
    font-family: var(--font-alt);
    font-size: var(--step-1);
    font-weight: var(--weight-500);
    line-height: var(--leading-2xs);                           /* the larger glyph must not inflate the 46px row */
    color: var(--secondary);
    margin: 0;
}
.hero .btn { margin-top: var(--space-l); }   /* live 40px */

/* --- The arch crops ----------------------------------------------------
 * THE KEY DETAIL OF THIS PAGE. Live draws every arch as a CSS
 * background-image on a div wearing a FIXED-PIXEL border-radius — not a
 * clip-path, not an SVG mask, not a pre-cropped image.
 *
 * Because the radius is fixed px and the box is not, the shape's CHARACTER
 * changes with the viewport, and live lets it:
 *
 *   hero portrait   1920  570x932   r 500px -> radius > half the width, so
 *                                     the top clamps to a full semicircle
 *                   1440  570x884   r 500px -> same, near-semicircular
 *                   1024  964x849   r 500px -> a shallow, wide arc
 *                    390  350x477   r 500px -> clamps to a semicircle again
 *
 *   small cards     1440  165x200   r 100px -> a tall arch
 *                   1024  231x116   r 100px -> radius > half the height, so
 *                                     the whole top rounds off
 *                    390  165x209   r 100px -> back to a tall arch
 *
 * Reproduced by stating the same fixed radii; the browser's own clamping
 * then produces live's shape at each width, because it is the same
 * mechanism. Measured against live at all three widths.
 */
.arch {
    background-repeat: no-repeat;
    background-size: cover;
    background-position: 50% 50%;
    border-radius: 500px 500px 10px 10px;
}
.arch--sm { border-radius: 100px 100px 10px 10px; }

.hero__media {
    position: relative;
    margin-left: var(--space-l-xl);          /* live 60px, inside the 630 track */
    padding-inline: var(--space-inset-m);         /* live 50px */
    /* No min-height above the stack breakpoint: live's arch is height:auto and
     * simply stretches to the copy column (856px at 1440, 932 at 1920). A
     * fixed 884px made the hero 28px taller than live's. */
    background-image: url("/assets/images/hero-doctor-patient.a7fb1fd554.webp");
}
.hero__badge {
    position: absolute;
    top: 1.875rem;                            /* 30px */
    /* Live's badge overhangs the arch but NEVER the viewport: measured, it
     * clears by 95px at 1440 and 30-50px below that. The -100px offset is
     * relative to the arch, so once the shell stops growing the badge starts
     * eating the gutter and then the edge -- it overhung by 11-16px from
     * 1120 to 1440. Clamp the pull to whatever space is actually spare. */
    right: clamp(0px, calc((100vw - var(--shell)) / 2 - var(--space-m)), var(--space-inset-m));
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    gap: var(--space-xs);                     /* live 14px */
    background: var(--brand-sky);
    border-radius: var(--radius-s);
    padding-left: var(--space-s);
    max-width: 100%;
}
.hero__badge-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 4.0625rem;                         /* 65px */
    height: 4.0625rem;
    background: var(--secondary);
    border-radius: 0 20px 20px 0;
    color: var(--surface);
}
.hero__badge-icon svg { width: 1.6875rem; height: 1.6875rem; fill: currentColor; }
.hero__badge-title {
    font-family: var(--font-head);
    font-size: var(--step-2);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--primary);
    margin: 0;
}
.hero__badge > div { text-align: center; }   /* live centres both lines */
.hero__badge p:not(.hero__badge-title) { margin: 0; font-size: var(--step-0); line-height: var(--leading-l); }

/* The row of three sits INSIDE the copy column, not below both columns --
 * measured on live, where the copy column is 700x884 (the same height as the
 * media column) and the cards land on its bottom edge. Building it as a
 * sibling row below both columns made the hero 279px too tall.
 *
 * Within that 700px column live draws 165 + 165 + 330 with two 20px gaps,
 * which is 700 exactly. margin-top:auto pushes the row to the column's
 * bottom, which is what makes the two columns the same height. */
.hero__cards {
    width: 100%;
    margin-top: auto;
    padding-top: calc(var(--space-2xl) + var(--space-s));  /* 100; live 106px between the CTA and the cards */
    display: grid;
    grid-template-columns: minmax(0, 165fr) minmax(0, 165fr) minmax(0, 330fr);
    gap: var(--space-s);                      /* live 20px */
    align-items: stretch;
}
/* Live leaves the right half of this row empty: the two arch cards sit at
 * x=45 and x=230 and the hours card at x=415, ending at 745 within the 1350
 * inner. The fourth track is that empty remainder -- stated so the three
 * blocks keep live's widths instead of stretching across the row. */
.hero__card-photo {
    position: relative;
    height: 12.5rem;                          /* live 200px, fixed */
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    padding: var(--space-2xs);
}
/* Live's own inconsistency, reproduced: only the FIRST arch card is shadowed. */
.hero__card-photo:first-child { box-shadow: 4px 4px 26px -4px rgba(0, 0, 0, 0.18); }
.hero__card-photo .chip {
    display: grid;
    place-items: center;
    width: 2.125rem;                          /* 34px */
    height: 2.125rem;
    border-radius: var(--radius-round);
    background: var(--brand-sky);
    color: var(--secondary);
}
.hero__card-photo .chip svg { width: 1.0625rem; height: 1.0625rem; fill: currentColor; }

.hours-card {
    height: 12.5rem;                          /* live 200px, fixed */
    background: var(--brand-sky);
    border-radius: var(--radius-s);
    padding: var(--space-s) var(--space-s) var(--space-s) var(--space-2xs);   /* live 20px, 10px left */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);                      /* live 17px */
    text-align: center;
}
.hours-card__icon {
    display: grid;
    place-items: center;
    width: 2.75rem;                           /* 44px */
    height: 2.75rem;
    border-radius: 7px;
    background: var(--brand-mid);
    color: var(--surface);
}
.hours-card__icon svg { width: 1.375rem; height: 1.375rem; fill: currentColor; }
.hours-card h2 {
    font-size: var(--step-2);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--ink);
    margin: 0;
}
.hours-card p { margin: 0; font-size: var(--step-0); line-height: var(--leading-l); }

/* ===========================================================================
 * ACCREDITATION STRIP — band 1. 209px tall, sky ground, flush (no band
 * padding); a 1px mid hairline top and bottom. Copy left, AGPAL badge right
 * at its natural 250x94.
 * ======================================================================== */
.accred {
    background: var(--brand-sky);
    border-top: var(--border-thin) solid var(--hairline);
    border-bottom: var(--border-thin) solid var(--hairline);
}
.accred__inner {
    min-height: 8rem;                         /* live content is 128px tall */
}
/* MEASURED: live holds the logo beside the copy to 1080 and folds at 1024.
 * `flex: 1 1 auto` takes its basis from the content, and the paragraph's
 * max-width made that basis 1000px -- so the row needed 1000 + 250 + gap and
 * wrapped at ~1300, growing the band from 210px to 294px. A percentage basis
 * shrinks; the max-width still caps it at live's width. */
.accred h2 {
    font-size: var(--step-2);
    font-weight: 800;
    line-height: var(--leading-s);
    color: var(--brand-vivid);
    margin: 0 0 var(--space-2xs);
}
.accred p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); max-width: 62.5rem; }
.accred img { width: 15.625rem; height: auto; flex: none; }   /* MEASURED: live paints the AGPAL symbol 250x94, at its natural size. The old 19.9375rem/319px was the HEADER logo's width, mis-copied -- 28% too wide at every width >= 1024. */

/* RE-MEASURED 2026-09-18 from captures at 1024 and 768: live keeps the logo
 * BESIDE the copy through the whole tablet range, shrinking it 250 -> 165px,
 * and only folds it underneath on phones. (An earlier note had the fold at
 * 1024; the captures show otherwise.) `flex-wrap: wrap` only PERMITS a fold,
 * so the phone fold is forced by giving the copy the full row. */
/* Live shrinks the logo THROUGH the folded range -- 250px at 1024 down to
 * 165px at 768. One definition, used by both rules below. */
:root { --accred-logo-w: clamp(10.3125rem, 22vw, 15.625rem); }
@media (max-width: 64rem) and (min-width: 48rem) {
    .accred img { width: var(--accred-logo-w); }   /* live 165-250px */
}

/* ===========================================================================
 * ABOUT — band 2. 1141px. Eyebrow, 57.7px title, a 700px copy column indented
 * to x=100 for the button, then a three-up row of feature panels 418px tall:
 * an outlined card, a translucent card holding an inner card, and a
 * dot-grid panel.
 * ======================================================================== */
.about__head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-m); }
.about__body { margin-top: var(--space-inset-m); margin-left: var(--space-indent); max-width: 62rem; }  /* live 50px top, 55px indent, 992px wide */
.about__body p { font-size: var(--step-1); line-height: var(--leading-l); margin: 0; }
.about__body p + p { margin-top: var(--space-xs); }
.about .btn { margin-top: var(--space-m); margin-left: var(--space-indent); }   /* live x=100 within a 45px gutter */

.about__panels {
    margin-top: var(--space-inset-m);                              /* live 50px */
    min-height: 26.125rem;                                     /* live 418px */
    display: grid;
    grid-template-columns: minmax(0, 315fr) minmax(0, 314fr) minmax(0, 681fr);   /* live, of 1350 */
    gap: var(--space-s);
    align-items: stretch;
}
.panel {
    border-radius: var(--radius-l);
    padding: var(--space-m) var(--space-m);   /* live 30px inline */
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-m);
}
.panel--outline { background: var(--bg); border: var(--border-thin) solid var(--secondary); }
.panel--tint { background: color-mix(in srgb, var(--brand-mid) 31%, transparent); justify-content: flex-start; }   /* live: the card hangs 30px from the top, not centred */
/* Live: a 154deg gradient on the element, with the dot-grid texture on a
 * ::before at OPACITY 0.06 -- barely perceptible. Painting the texture
 * directly at full strength turns a whisper into a dominant pattern, which is
 * exactly what the first pass did. */
.panel--grid {
    position: relative;
    isolation: isolate;
    background-image: linear-gradient(154deg, var(--bg) 32%, color-mix(in srgb, var(--brand-mid) 72%, transparent) 100%);
    /* OWNER'S CALL (2026-09-18): brand row and copy sit as ONE block centred in
     * the panel, 80px apart (live's own gap is 75). space-between had pinned
     * them to opposite edges, which opened a hole in the middle -- ~95px on
     * desktop and ~160px on a phone, where the panel is 477px tall. */
    justify-content: center;
    gap: var(--space-2xl);
}
.panel--grid h3 { font-size: var(--step-2); }
.panel--grid .panel__item { gap: var(--space-xs); }
/* /about: the same three panels, 466px tall, holding a Telephone Calls card
 * (406px) and the practice team. */
.about__panels--tall { min-height: 29.125rem; }
.about__panels--tall .panel__card { min-height: 25.375rem; }
/* These panels' copy is 18.56px (--step-1) like every other panel -- MEASURED
 * off live's DOM. A step-0 override sat here briefly, inferred from how the
 * text wrapped in a screenshot rather than measured; the owner spotted it. */
.panel__brand-text { display: flex; flex-direction: column; }
.panel__brand-sub { font-size: var(--step-0); font-weight: var(--weight-400, 400); line-height: var(--leading-l); text-transform: uppercase; color: var(--ink); }   /* live: Outfit 16.5 / 400 */
.panel--grid .panel__team { gap: 0; max-width: 33.75rem; }   /* live 540px, so the nurses line wraps where live's does */
.panel__team .panel__note { max-width: 28rem; }                /* live 447px */
/* on /about the team block follows the brand row ~85px down; it is NOT pinned
 * to the panel's bottom edge the way the homepage's copy is */
.about__panels--tall .panel--grid { justify-content: flex-start; gap: var(--space-2xl); }
.panel__team .panel__note { margin-top: var(--space-xs); }   /* live ~15px between the title and each paragraph */   /* live 24.75px, a step above the outline panel's */
.panel--grid::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: var(--layer-behind);
    opacity: 0.06;
    border-radius: inherit;
    background-image: url("/assets/images/dot-grid.8e4544bf8b.png");
    background-repeat: no-repeat;
    background-size: cover;
    background-position: 50% 100%;
}
.panel__item { display: flex; flex-direction: column; gap: var(--space-2xs); }
.panel__icon { color: var(--brand-mid); }
.panel__icon svg { width: 2.1875rem; height: 2.1875rem; fill: currentColor; }   /* 35px */
.panel h3 {
    font-size: var(--step-1);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--primary);
    margin: 0;
}
.panel p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }

/* The inner card the tint panel holds — live 254x316, ground, 20px radius,
 * 20px padding, tag pinned to the top and the copy to the bottom. The shadow
 * is live's hard-edged "second sheet" (0 15px 0 -6px), not a soft drop. */
.panel__card {
    background: var(--bg);
    border-radius: var(--radius-l);
    padding: var(--space-s);
    min-height: 19.75rem;                     /* live 316px */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2xs);
    box-shadow: 0 15px 0 -6px rgba(0, 0, 0, 0.19);
}
.panel__card .tag { margin-bottom: auto; }
.panel__card h3 { font-size: var(--step-2); }
.tag {
    display: inline-block;
    padding: var(--space-3xs) var(--space-2xs);   /* live 4px 10px */
    border: var(--border-thin) solid var(--primary);
    border-radius: var(--radius-xs);
    font-family: var(--font-alt);
    font-size: var(--step-0);                 /* live 16.5px on a 1.2 leading: 30px tall with the 4px padding */
    font-weight: var(--weight-500);
    line-height: var(--leading-s);
    text-transform: uppercase;
    color: var(--primary);
}
/* The dot-grid panel's brand row and its two ticked labels. */
.panel__brand { display: flex; align-items: center; gap: var(--space-xs); flex-wrap: wrap; }
.panel__brand img { width: 4.375rem; height: 4.375rem; }      /* 70px */
.panel__brand-name {
    font-family: var(--font-head);
    font-size: var(--step-2);
    font-weight: var(--weight-700);
    color: var(--primary);
}
.panel__ticks { display: flex; gap: var(--space-xs); flex-wrap: wrap; margin-left: auto; }
.panel__tick { display: inline-flex; align-items: center; gap: var(--space-3xs); font-size: var(--step-0); }
.panel__tick svg { width: 0.875rem; height: 0.875rem; fill: var(--secondary); flex: none; }

/* ===========================================================================
 * WHY — band 3. 1246px. Left: a 540x637 SKY ARCH with a 540x675 cut-out PNG
 * standing in front of it and overhanging the arch top by 38px. Right: three
 * 700x226 rows separated by 1px hairlines.
 *
 * The photo is a transparent-background cut-out, so it is an <img> over the
 * arch rather than a background on it. Live hides the arch below 1024
 * (elementor-hidden-tablet) and keeps the photo.
 * ======================================================================== */
.why__head {
    display: grid;
    grid-template-columns: minmax(0, 639fr) minmax(0, 691fr);   /* live, of 1350 */
    gap: var(--space-s);
    align-items: start;
    padding-bottom: var(--space-inset-m);         /* live 50px */
    min-height: 16.75rem;                     /* live head block is 268px */
}
.why__head-copy { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-m); }
.why__head-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-m);
    text-align: right;
    padding: var(--space-inset-m) 0 0 var(--space-inset-head);   /* live 50px top, 100px inset */
}
.why__head-side p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }

.why__cols {
    margin-top: 0;
    display: grid;
    grid-template-columns: minmax(0, 540fr) minmax(0, 790fr);   /* live, of 1350 */
    gap: var(--space-s);
    align-items: start;
}
.why__media { position: relative; display: flex; align-items: flex-end; justify-content: center; }
/* MEASURED on live: the arch is 637px behind a 675px photo, both bottom-
 * aligned -- so its top sits 38px BELOW the image top and the person's head
 * overhangs it. That 38px is the whole point of the composition.
 *
 * It was stated as a fixed 637px while the photo scales with the column, so
 * the relationship only held at 1440: by 1120 the photo was 521px and the
 * arch stood 116px ABOVE it, poking into the row above. Express the ratio
 * instead (637/675 = 94.4%), so the head-room scales with the image. */
.why__arch {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 94.4%;
    background: var(--brand-sky);
    border-radius: 500px 500px 10px 10px;
}
.why__photo { position: relative; display: block; width: 100%; height: auto; border-radius: var(--radius-l); }

.why__list { display: flex; flex-direction: column; gap: var(--space-s); padding-left: var(--space-inset-list); }   /* live 90px inset, 20px rows */
.why__item {
    min-height: 14.125rem;                    /* live 226px per row at 1440 */
    justify-content: center;
    padding-block: var(--space-m);
    /* live rules every row top AND bottom, so the 20px gap reads as a pair of hairlines */
    border-block: var(--border-thin) solid var(--hairline);
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
}
.why__item-icon { color: var(--brand-mid); }
.why__item-icon svg { width: 2.1875rem; height: 2.1875rem; fill: currentColor; }
.why__item h3 {
    font-size: var(--step-2);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--primary);
    margin: 0;
}
.why__item p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }

/* ===========================================================================
 * SERVICES — band 4. 1355px. A single 1340x1185 sky card with a 10px radius
 * inside a 1500px shell, holding a centred head and then a four-column row:
 * a ticked services list in a ground card, and three photo+copy columns.
 * ======================================================================== */
/* Live: the band pads 50px top and 120px bottom with a 50px inline gutter,
 * and holds one 1340x1185 sky card whose inner pads 80px on every side. */
.services { padding-block: var(--space-inset-m) var(--space-3xl); }
.services .u-container { padding-inline: var(--space-inset-m); }  /* live 50px */
.services__card {
    background: var(--brand-sky);
    border-radius: var(--radius-s);
    padding: var(--space-2xl);                                 /* live 80px */
}
/* RE-MEASURED 2026-09-18: live stacks tag / title / lede 20px apart and leaves
 * 45px between the lede and the cards. --space-s is the 20px token at 1440
 * (--space-m is 30, the value that once made this head 20px too tall). */
.services__head { text-align: center; display: flex; flex-direction: column; align-items: center;
    gap: var(--space-s); margin-block-end: var(--space-l); }
/* Scoped to :not(.section-title) so this cannot outrank the title's own size
 * -- `.services__head p` and `.section-title` differ by one element in
 * specificity, and this rule is later, so an unscoped version silently wins. */
/* OWNER'S CALL (2026-09-18): two lines -- "Everything You Need / for Better
 * Health" -- as in the reference. Live's box is 43% of the card, so live itself
 * flips between two lines (1700, 2223) and three (1440, 1920) as the font and
 * the box scale at different rates. A cap in em tracks the title's own size, so
 * the break holds at every width: line one is ~9.3em, and adding "for" is ~11. */
.services__head .section-title { max-width: 10em; }
.services__head p:not([class]), .services__head p.services__lede {
    margin: 0;
    font-size: var(--step-1);
    line-height: var(--leading-l);
    max-width: 42.0625rem;                                     /* live 673px */
}

.services__grid {
    margin-top: var(--space-s);                                /* live 20px */
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-m);                                       /* live 30px */
    align-items: start;
}
.services__list {
    background: var(--bg);
    border-radius: var(--radius-s);
    padding: var(--space-l);                                  /* live 40px */
    list-style: none;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space-3xs) * 1.5);                         /* live 7-8px: a 35.5px pitch on 27.8px lines */
}
.services__list li { display: flex; gap: var(--space-2xs); font-size: var(--step-1); line-height: var(--leading-l); }
.services__list svg { width: 0.9375rem; height: 0.9375rem; fill: var(--secondary); flex: none; margin-top: var(--space-2xs); }   /* live 0.45em, 8.4px at 1440 */

/* live: 40px from the photo to the title, then the copy tucked 5px under it */
.service { display: flex; flex-direction: column; gap: var(--space-3xs); text-align: center; }
.service img {
    width: 100%;
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius-l);           /* live 20px... */
    display: block;
}
.service:first-of-type img { border-radius: 10px; }   /* ...except the first photo, which live leaves at 10px */
.service h3 {
    font-size: var(--step-1);
    font-weight: var(--weight-700);
    line-height: var(--leading-m);
    color: var(--primary);
    margin: var(--space-l) 0 0;              /* live 40px under the photo */
}
.service p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }

/* ===========================================================================
 * CONTACT — band 5. 387px, 80px block padding (not 120 — measured). Left:
 * eyebrow, 33px bright-blue title, pill. Right: two 308x226 sky cards.
 * ======================================================================== */
.contact {
    padding-block: var(--space-2xl);          /* live 80px */
    background: var(--bg);
    border-top: var(--border-thin) solid var(--hairline);
}
.contact__inner {
    display: grid;
    /* live: the first card starts flush at x=720, the second 60px after it
     * (720-1028, 1088-1396). The 60fr track IS that gap. */
    grid-template-columns: minmax(0, 675fr) minmax(0, 308fr) minmax(0, 60fr) minmax(0, 308fr);
    gap: 0;
    align-items: center;
}
.contact__inner > .contact-card:last-child { grid-column: 4; }
.contact__copy { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-xs); }
.contact__copy .band-title { max-width: 42.1875rem; }          /* live 675px, one line */
.contact-card {
    min-height: 14.125rem;                    /* live 226px */
    background: var(--brand-sky);
    border-radius: var(--radius-s);
    padding: var(--space-l);                 /* live 40px */
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    height: 100%;
    justify-content: center;
}
.contact-card__icon { color: var(--brand-mid); }
.contact-card__icon svg { width: 2.1875rem; height: 2.1875rem; fill: currentColor; }
.contact-card h2 {
    font-size: var(--step-1);
    font-weight: var(--weight-700);
    line-height: var(--leading-s);
    color: var(--primary);
    margin: 0;
}
.contact-card p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }

/* ===========================================================================
 * FOOTER — 527px: a 462px ground panel in four columns (logo+blurb+social,
 * Quick Links, Working Hours, map) over a 65px navy bar.
 * ======================================================================== */
.site-footer { background: var(--bg); border-top: var(--border-thin) solid var(--secondary); }
.site-footer__inner {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0;                                   /* live: four flush 338px columns (45 / 383 / 720 / 1058) */
    padding-block: var(--space-2xl);          /* live 80px */
    align-items: start;
}
/* Live centres the Quick Links block inside its column (heading at x=498 of a
 * column starting at 383); the other three columns are left-aligned. */
.site-footer__inner > :nth-child(2) { justify-self: center; }
/* Live's columns are a fixed 301px tall, which is what holds the panel at
 * 462px whatever the column contents come to. */
.site-footer__inner > * { min-height: 18.8125rem; }
/* OWNER'S CALL (2026-09-18): the logo's visible top lines up with the column
 * headings and the map. The PNG carries 12px of transparent padding above the
 * mark (alpha bbox 12..88 of 101), which is 11.6px at this 338px width, so the
 * box was level with the map but the MARK sat visibly lower. Pulled up by
 * exactly that padding -- tied to the asset, so a literal rather than a token. */
.site-footer__logo { display: block; }
.site-footer__logo img { width: 21.125rem; max-width: 100%; height: auto; display: block; margin-top: calc(-1 * var(--space-2xs)); }  /* 338px wide; live pulls it up 11.6px */
.site-footer__blurb { margin: var(--space-m) 0 0; font-size: var(--step-0); line-height: var(--leading-l); }
.site-footer__social {
    display: inline-flex;
    margin-top: var(--space-m);
    color: var(--primary);
}
.site-footer__social svg { width: 1.875rem; height: 1.875rem; fill: currentColor; }  /* 30px */
.site-footer h2 {
    font-family: var(--font-head);
    font-size: var(--step-1);
    font-weight: var(--weight-700);
    /* Leading 1 with the cap-height offset taken off the top, so the LETTERS
     * start on the column's top edge with the logo mark and the map. The bottom
     * margin gives back what the 1.5 leading used to occupy, so the links and
     * hours below have not moved. The trim (3xs) and the give-back (s + xs)
     * are on the space scale; live's were -0.14em and 20px + 0.64em. */
    line-height: var(--leading-2xs);
    color: var(--primary);
    margin: calc(-1 * var(--space-3xs)) 0 calc(var(--space-s) + var(--space-xs));
}
.site-footer__links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; }   /* the 35px pitch is the line-height alone */
.site-footer__links a {
    font-size: var(--step-0);
    font-weight: var(--weight-500);
    line-height: var(--leading-2xl);                        /* live rows are 35px apart at 16.5px */
    color: var(--brand-ink-soft);
    text-decoration: none;
}
.site-footer__links a:hover, .site-footer__links a:focus-visible { color: var(--secondary); text-decoration: underline; }
.site-footer__links a.active, .site-footer__links a[aria-current="page"] { color: var(--secondary); }
.site-footer__hours p { margin: 0; font-size: var(--step-0); line-height: var(--leading-l); }
.site-footer__hours em { display: block; margin-top: var(--space-xs); }
/* The hours come from Rails' opening_hours_html tag: a two-column table whose
 * day groupings (Mon–Fri, "Sat, Sun") Rails computes from the data. Laid out as
 * the two text lines it replaced -- bold label and colon, then the times -- with
 * none of the template's cell padding or row rules. The hero hours card centres
 * the block as it centred the old paragraph. */
.site-footer__hours .hours-table,
.hours-card .hours-table { width: auto; margin-block: 0; font-size: var(--step-0); line-height: var(--leading-l); }
.site-footer__hours .hours-table td,
.hours-card .hours-table td { padding: 0; border: 0; }
.site-footer__hours .hours-table td:first-child,
.hours-card .hours-table td:first-child { padding-inline-end: var(--space-3xs); }
.site-footer__hours .hours-table td:first-child::after,
.hours-card .hours-table td:first-child::after { content: ":"; }
.site-footer__hours .hours-table td:first-child { font-weight: var(--weight-700); }
.hours-card .hours-table { margin-inline: auto; }
.site-footer__map {
    width: 100%;
    max-width: 21.125rem;                     /* live 338px */
    aspect-ratio: 338 / 245;
    border: 0;
    border-radius: var(--radius-xs);
    display: block;
}

/* OWNER'S CALL (2026-09-18): more air than live's 20px bar. One 30px unit
 * (--space-l) is used for the bar's padding AND either side of the rule, so
 * copyright | credit reads as evenly spaced instead of three different gaps. */
.site-footer__bar { background: var(--tertiary); color: var(--surface); padding-block: var(--space-m); }
.site-footer__bar-inner {
    display: flex;
    align-items: center;
    justify-content: flex-start;              /* copyright | credit on the left; legal links pushed right by their own auto margin */
    gap: var(--space-xs) var(--space-m);       /* rows (when it wraps) / columns */
    flex-wrap: wrap;
}
.site-footer__bar p, .site-footer__bar a { font-size: var(--step--1); line-height: var(--leading-l); margin: 0; color: var(--surface); }
/* Specificity: `.site-footer__bar p { margin: 0 }` is (0,1,1) and was beating a
 * bare `.site-footer__legal` (0,1,0), so the auto margin never applied and the
 * links sat on the LEFT after the credit. Two classes puts them back right. */
.site-footer__bar .site-footer__legal { display: flex; align-items: center; gap: var(--space-xs); margin-left: auto; }   /* 17px either side of the divider; live has 24, ours had 10 */
.site-footer__legal a { text-decoration: none; transition: color var(--duration-fast) var(--ease-out); }
/* hover: the light brand blue (9.6:1 on the navy), with the underline kept so
 * the state never rests on colour alone */
.site-footer__legal a:hover, .site-footer__legal a:focus-visible { color: var(--brand-mid); text-decoration: underline; text-underline-offset: 0.2em; }
.site-footer__legal span { color: var(--brand-mid); font-family: "Open Sans", var(--font-body); font-weight: var(--weight-500); }
/* Live runs the credit straight after the copyright, behind a 1px white rule --
 * not centred in the bar. */
.powered-by { display: inline-flex; align-items: center; gap: var(--space-xs); padding-left: var(--space-m); border-left: var(--border-thin) solid color-mix(in srgb, var(--surface) 45%, transparent); }
.powered-by span { font-size: var(--step--2); color: var(--brand-sky); }
.powered-by a { display: block; transition: opacity var(--duration-fast) var(--ease-out); }
.powered-by a:hover, .powered-by a:focus-visible { opacity: 0.8; }
.powered-by img { width: 4.6875rem; height: auto; display: block; }   /* 75px */

/* ===========================================================================
 * ENTRANCE REVEALS — one-way, IntersectionObserver, short directional travel.
 * NOT the template's scroll-linked .reveal, which plays backwards on scroll-up.
 * 26px vertical / 18px horizontal, 760ms ease-out.
 * ======================================================================== */
[data-reveal] {
    opacity: 0;
    transition: opacity 760ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 760ms cubic-bezier(0.22, 1, 0.36, 1);
    transition-delay: var(--reveal-delay, 0ms);
    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="fade"]  { transform: none; }
[data-reveal][data-revealed] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ===========================================================================
 * RESPONSIVE
 *
 * Live switches to an Elementor tablet layout at 1024 and a mobile one at 767.
 * Transcribed from measurements at those widths — NOT scaled desktop.
 *
 * Every track above is stated in `fr`, never px, so nothing can refuse to
 * shrink between the breakpoints. The band-scoped collapses are restated at
 * the END of the stylesheet so they win on source order as well as
 * specificity.
 * ======================================================================== */
@media (min-width: 768px) and (max-width: 1080px) {
    /* Tablet-only floors: measured at 1024 and NOT true at 390, so they are
     * bounded rather than left to leak down through the mobile block. */
    .about__panels .panel--grid { min-height: 27.25rem; }        /* live 436px */
    .about__panels .panel--outline,
    .about__panels .panel--tint { min-height: 26.125rem; }       /* live 418px */
}

@media (max-width: 1080px) {
    /* live: header 143px, hero pads 180/50 */
    :root { --hero-pad-top: calc(var(--space-3xl) + var(--space-xl)); --hero-pad-bottom: var(--space-inset-m); --header-h: 8.9375rem; }

    .accred { padding-block: var(--space-inset-m); }   /* live @1024 is 226px tall */

    /* live @1024: logo left, the menu button CENTRED in the bar, CTA right.
     * Ours puts the menu button beside the CTA on the right (owner, 2026-10-03;
     * the nav collapse itself is the 1199 block above) */
    .site-header__inner { min-height: 8.9375rem; gap: var(--space-xs); }   /* live 143px */
    .site-logo img { width: 20.4375rem; }     /* live 327px */

    /* Live stacks the hero at 1024: copy above, photo below at full width
     * (measured 964px wide, 849 tall — a wide shallow arch). */
    .hero__cols { grid-template-columns: minmax(0, 1fr); }
    .hero__media { margin-left: 0; min-height: 53rem; }
    .hero__badge { right: 0; }   /* see also the 1400px guard on the base rule */
    /* Live's small cards go 231x116 here — squat, so the 100px radius rounds
     * the whole top. Reproduced by letting the row keep three columns while
     * the height drops. */
    /* MEASURED at 1024: live's small cards are 231x116 -- squat enough that
     * the fixed 100px radius exceeds half the height, so the whole top rounds
     * off rather than reading as an arch. The shape change is live's own, and
     * it falls out of stating the same fixed radius on the same box size.
     * height, not min-height: the base rule sets a fixed 200px. */
    /* Live holds ~193px here and only goes squat at 1024 -- see the 1024
     * block below. Dropping to 116 at 1080 made them squat a breakpoint
     * early, which is what the owner saw as "very short". */
    .hero__card-photo { height: 12.0625rem; }   /* live 193px at 1080 */

    /* live @1024: a 2-up with the dot-grid panel full width beneath it at
     * 964x436, and the copy column no longer indented. */
    .about__panels { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-height: 0; row-gap: var(--space-s); }
    .about__panels .panel--grid { grid-column: 1 / -1; }
    .about__body { margin-left: 0; }
    .about .btn { margin-left: 0; }

    .why__head, .why__cols { grid-template-columns: minmax(0, 1fr); }
    .why__head-side { align-items: flex-start; text-align: left; padding: 0; }
    /* MEASURED: live drops the whole media column below 1440 -- the sky arch
     * carries elementor-hidden-tablet, and the cut-out photo itself measures
     * 0x0 at both 1024 and 390. Keeping it made this band 672px too tall. */
    .why__media { display: none; }
    .why__list { padding-left: 0; }

    /* MEASURED: live hides the Open Hours card at 1024 (0x0) and brings it
     * back at 390 (350x181). */
    .hours-card { display: none; }
    /* RE-MEASURED 2026-09-18 at 1024: the two photo cards stay 231px wide at the
     * left; they do not stretch across the row. And the hero copy is CENTRED
     * from the tablet layout down, not only on phones. */
    .hero__cards { grid-template-columns: repeat(2, minmax(0, 14.4375rem)); justify-content: start; }
    /* the tall heroes only: live keeps the SHORT heroes' copy left-aligned on tablet */
    .hero:not(.hero--short) .hero__copy { align-items: center; text-align: center; }
    .hero__lede { max-width: 100%; }
    .hero__facts { width: 100%; }
    .hero__fact-row { justify-content: center; }

    .services__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .services__card { padding-inline: var(--space-m); }

    .contact__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-s); }
    .contact__inner > .contact-card:last-child { grid-column: auto; }
    .contact__copy { grid-column: 1 / -1; }

    /* live @1024: a 2x2 of 447px columns, 80px block padding, bar 96px */
    .site-footer__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-inset-m) var(--space-s); }
    .site-footer__inner > :nth-child(2) { justify-self: start; }
    .site-footer__inner > * { min-height: 0; }
    .site-footer__bar { padding-block: var(--space-l); }   /* live's 96px bar */
}

@media (max-width: 767px) {
    /* live: header 210px, STACKED -- a full-width logo row above, then the
     * hamburger and the CTA side by side below. One row here put the CTA
     * 107px off the right edge at 390. */
    /* --hero-pad-top: live keeps 180px here against a 210px header, which slides
     * the hero's pill under the header's hairline. Cleared by 30px instead. */
    :root { --header-h: 13.125rem; --hero-pad-top: calc(13.125rem + var(--space-s-l)); }   /* live header 210px */
    .site-header__inner {
        flex-wrap: wrap;
        min-height: 13.125rem;
        align-content: center;
        row-gap: var(--space-s);
    }
    /* OWNER'S CALLS, not live's: once the header stacks, the logo is CENTRED on
     * its own row and the menu button and the Call button sit CENTRED TOGETHER
     * beneath it at one height (44px, the touch-target floor; 2026-09-18,
     * re-confirmed 2026-10-03). Right-aligning the pair applies only to the
     * one-row bar above 767. Live parks the menu left and the CTA right, with
     * the CTA ~9px shorter than the menu button. */
    .site-header__inner { justify-content: center; column-gap: var(--space-xs); }
    .site-logo { flex: 1 0 100%; }
    .site-logo img { width: 100%; max-width: 21.9375rem; margin-inline: auto; }   /* live 351px */
    .nav-toggle { margin-inline: 0; }
    .site-header .btn { margin-left: 0; }
    .btn--head { min-height: 2.75rem; padding: 0 calc(var(--space-s) + var(--space-3xs)); }   /* live 1.6em, 21px at 390 */

    /* MEASURED at 390: live centres the hero block and the accreditation copy
     * (text-align: center on the display, both ledes and both fact rows). */
    .hero__copy { align-items: center; text-align: center; }
    .hero__lede { max-width: 100%; }
    .hero__facts { width: 100%; }
    .hero__fact-row { justify-content: center; }
    .accred__inner { text-align: center; }
    .accred__copy { text-align: center; }

    .hero__cols { grid-template-columns: minmax(0, 1fr); }
    .hero__cards { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    /* live 350x181 at 390 -- hidden at 1024, back here */
    .hours-card { display: flex; grid-column: 1 / -1; height: 11.3125rem; }
    .hero__card-photo { height: 13.0625rem; }    /* live 209px at 390 */
    .hero__media { min-height: 29.8125rem; }         /* live 477px at 390 */
    /* MEASURED at 390: live's badge is 225px wide, hugging its text, and sits at
     * the arch's top RIGHT, 20px in from the edge and 20px down. Ours had no
     * width, so as a block it stretched across the arch's whole content box
     * (270px) and sat centred. */
    .hero__badge { position: static; width: fit-content; max-width: 100%; margin: var(--space-s-l) 0 0 auto; padding-left: var(--space-2xs); }
    .hero__media { padding-inline: var(--space-s-l); }   /* 20px at 390: the badge's inset from the arch edge */

    /* live @390: 346px tall, 40px block padding, badge stacked under the copy.
     * The desktop min-height and the 32rem copy basis together made ours 734. */
    /* live @390: 40px block padding; a centred 242px copy block with 50px
     * beneath it; two 177px cards at 25px padding, 50px apart. */
    .contact { padding-block: var(--space-l); }
    .contact__inner { row-gap: var(--space-inset-m); }
    .contact__copy { align-items: center; text-align: center; padding-bottom: var(--space-inset-m); }
    .contact__copy .band-title { max-width: 100%; }
    .contact-card { min-height: 11.0625rem; padding: var(--space-card-pad); }   /* live 177px, 25px */
    .accred__inner { flex-direction: column; align-items: flex-start; min-height: 0; }
    .accred__copy { flex: 1 1 auto; }
    .accred img { width: 15.625rem; }

    /* live @390: the three panels stack at 337 / 312 / 477 tall */
    .about__panels { grid-template-columns: minmax(0, 1fr); min-height: 0; row-gap: var(--space-s); }
    .panel { padding: var(--space-s-l); }
    .panel--outline { min-height: 21.0625rem; }   /* live 337px */
    .panel--tint    { min-height: 19.5rem; }      /* live 312px */
    .panel--grid    { min-height: 29.8125rem; }   /* live 477px */

    .services__grid { grid-template-columns: minmax(0, 1fr); }
    .contact__inner { grid-template-columns: minmax(0, 1fr); }
    /* live @390: the why rows grow to 251px as the copy wraps to more lines */
    .why__item { min-height: 15.6875rem; }

    /* live @390: four stacked columns sized by their own content
     * (222/212/109/245) with 50px between them, 50px block padding, bar 141px */
    .site-footer__inner { grid-template-columns: minmax(0, 1fr); padding-block: var(--space-inset-m); row-gap: var(--space-inset-m); }
    .site-footer__inner > * { min-height: 0; }
    .site-footer__bar-inner { flex-direction: column; align-items: flex-start; gap: var(--space-2xs); }
    .site-footer__bar .site-footer__legal { margin-left: 0; }   /* both classes, to match the base rule */
    .powered-by { padding-left: 0; border-left: 0; }
    .site-footer__bar { padding-block: var(--space-l); }
    .site-footer__bar .site-footer__bar-inner { gap: var(--space-s); }
    .site-footer__bar .site-footer__legal { width: 100%; justify-content: center; padding-top: var(--space-s); border-top: var(--border-thin) solid color-mix(in srgb, var(--surface) 20%, transparent); }
    .site-footer__bar .site-footer__bar-inner { align-items: center; text-align: center; }   /* live centres the whole bar on phones */
}

/* --- Late collapse restatements ----------------------------------------
 * Restated here, at the end of the stylesheet, so a band-scoped rule above
 * cannot beat them on source order. This is the fix for the class of bug
 * where a mobile collapse exists but loses to a later, more specific rule. */
@media (max-width: 767px) {
    .hero .hero__cols,
    .about .about__panels,
    .why .why__head,
    .why .why__cols,
    .services .services__grid,
    .contact .contact__inner,
    .site-footer .site-footer__inner { grid-template-columns: minmax(0, 1fr); }
}


/* ==========================================================================
   PHASE 2 -- /services, /about, /practice-policy, /contact, /health-info
   ========================================================================== */

/* Live paints three band grounds plus transparent. --ground already IS
 * rgb(242,244,246), so the default .band needs no modifier. */
:root {
    --brand-ash: oklch(97.02% 0 0);   /* rgb(245, 245, 245) */
    --weight-800: 800;               /* live uses 800 on the accred + note titles */
}
.band--ash  { background: var(--brand-ash); }
.band--bare { background: transparent; }

/* --- Accreditation strip -------------------------------------------------
 * MEASURED: 40px block padding, not the 120px band rhythm. The paragraph is
 * 1011px wide on /about and /health-info but 819px on /practice-policy --
 * live's own per-page override, reproduced by .accred--narrow rather than
 * averaged away. */
.accred { padding-block: var(--space-l); }   /* MEASURED 40px, both pages */
.accred__inner {
    display: flex; align-items: center; gap: var(--space-m); flex-wrap: wrap;
    justify-content: space-between;
}
/* Live puts the logo on the RIGHT (measured x=1145 of 1350). Reordered
 * visually only -- the DOM keeps heading -> prose -> logo, which is the
 * sensible reading order. */
.accred__inner img { order: 2; }
.accred__copy { order: 1; }
@media (max-width: 29.9375rem) {
    /* live keeps the logo at 250px even at 390 and lets it sit above */
    .accred__inner { gap: var(--space-xs); }
}
.accred__inner img { flex: none; width: 15.625rem; height: auto; }   /* live 250px */
/* Live shrinks the logo through the folded range (250px at 1024 -> 165px at
 * 768). This layer is appended after the base one, so the size has to be
 * restated here or the flat 250px above wins at equal specificity. */
@media (max-width: 64rem) and (min-width: 48rem) {
    .accred__inner img { width: var(--accred-logo-w); }
}
/* `flex: 1 1 30rem` sets the basis on the MAIN axis, so once .accred__inner
 * wraps to a column at narrow widths the 30rem became a 480px HEIGHT and the
 * band ran 676px against live's 368. Live stays row-direction all the way to
 * 390 with the logo at its natural 250x94, so keep the row and let the copy
 * take the remaining width. */
.accred__copy { flex: 1 1 60%; min-width: 0; max-width: 63.1875rem; }   /* live 1011px; % basis so it shrinks before wrapping */
/* The inner pages' strip title is the homepage's <h2> in a <span>: live sets
 * both at Lato 24.75 / 800. It had drifted to Karla at 20.9px. */
.accred__title {
    display: block; font-family: var(--font-head); font-size: var(--step-2);
    font-weight: var(--weight-800); line-height: var(--leading-s);
    margin-block-end: var(--space-2xs);
}
.accred--narrow .accred__copy { max-width: 51.1875rem; }   /* live 819px */

/* RE-MEASURED 2026-09-18 from captures at 1024 and 768: live keeps the logo
 * BESIDE the copy through the whole tablet range, shrinking it 250 -> 165px,
 * and only folds it underneath on phones. (An earlier note had the fold at
 * 1024; the captures show otherwise.) `flex-wrap: wrap` only PERMITS a fold,
 * so the phone fold is forced by giving the copy the full row. */
@media (min-width: 48rem) {
    .accred__inner { flex-wrap: nowrap; }
}
@media (max-width: 47.9375rem) {
    .accred__inner { flex-wrap: wrap; }
    .accred__copy { flex: 1 1 100%; max-width: none; }
}

.accred--narrow .accred__title { margin-block-end: var(--space-xs); }
.accred--narrow .accred__inner { align-items: flex-start; }

/* --- Hero variants -------------------------------------------------------
 * /about and /practice-policy use a shorter hero with a plain (un-arched)
 * photo, unlike the homepage and /services which carry the arch crop. */
/* MEASURED: /about's hero content starts at y=251 under a 161px header, so
 * the band carries ~250px of top padding and ~119 bottom, and its height is
 * set by the 670px photo rather than by the copy. */
/* MEASURED: live's short hero pads 150px at 1440 and 60px at 390 -- it DOES
 * scale, unlike the band rhythm. Holding 250px to 390 cost band0 +241px. */
.hero--short .hero__inner { padding-block: var(--space-hero-top) var(--space-3xl); }   /* /about: live 250 / 119 */
/* RE-MEASURED 2026-09-18: the arch starts at y=271 and runs to the band's
 * bottom edge (271 + 680 = 951), so the band pads 270px top and NOTHING at the
 * bottom.
 * The ground carries the same three radial washes as /health-info's hero. */
.band--ash.hero--short .hero__inner { padding-block: calc(var(--space-hero-top) + var(--space-s)) 0; }   /* live 270 */
/* The copy centres on the arch LESS a 120px foot -- which is why it rides high
 * on /practice-policy (tag 32px below the arch top) and lower on /health-info,
 * where there is no CTA and the block is shorter. One rule gives both. */
.band--ash.hero--short .hero__cols--about { align-items: center; }
.band--ash.hero--short .hero__copy { padding-bottom: var(--space-3xl); }
.hero__cols--about .section-title.section-title--narrow { max-width: 31.625rem; }   /* /health-info: live wraps at 506px */
.band--ash.hero--short { background-image:
    radial-gradient(1100px 800px at 85% 40%, color-mix(in srgb, var(--glow-blue) 30%, transparent), transparent 48%),
    radial-gradient(1200px 900px at 70% 55%, color-mix(in srgb, var(--glow-mid) 22%, transparent), transparent 72%),
    radial-gradient(1300px 950px at 25% 50%, color-mix(in srgb, var(--glow-pale) 12%, transparent), transparent 78%); }
@media (max-width: 47.9375rem) {
    /* Both selectors restated: a media query does NOT raise specificity, so a
     * bare .hero--short (0,1,0) loses to .band--ash.hero--short (0,2,0) above
     * and practice-policy kept its desktop padding at 390. */
    /* DELIBERATE DIVERGENCE (2026-09-18). Live pads these heroes 60px at 390
     * while its transparent header is 210px tall, so on live's own phone
     * layout the tag and the first line of the title sit UNDERNEATH the logo
     * and the Call button -- unreadable. Reproducing that faithfully shipped
     * the same collision here. The copy now starts a clear 30px below the
     * header instead; the 60px bottom is live's. */
    .hero--short .hero__inner,
    .band--ash.hero--short .hero__inner { padding-block: calc(var(--header-h) + var(--space-s-l)) var(--space-block); }   /* bottom: live 60 at 390 */
    .band--ash.hero--short .hero__copy { padding-bottom: 0; }
}
/* MEASURED at 1440: /about and /practice-policy split their hero 595|595
 * inside a 1290px inner -- NOT the homepage's 700|570. The copy column drives
 * how the title and lede wrap, so getting it wrong shortens the whole band. */
.hero__cols--about {
    align-items: center;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-inset-head);         /* live 100px between the two 595px columns */
    /* width:100% is load-bearing: this is a flex item with auto margins, so
     * without it the grid shrink-wraps its (now capped) copy and the whole
     * hero narrows. */
    width: 100%; max-width: 80.625rem; margin-inline: auto;   /* live 1290px inner */
}
.hero__cols--about .hero__lede { max-width: 33.125rem; margin-top: var(--space-m); }   /* live 530px, 30px under the title */
.hero__cols--about .section-title { margin-top: var(--space-l); max-width: 36.5625rem; }   /* live 40px under the tag, 585px */
.hero__cols--about .hero__cta { margin-block-start: var(--space-2xs); }   /* + the button's own 40px = live's ~50px */
.hero__media-plain { display: flex; justify-content: flex-end; }
.hero__media-plain img { width: 100%; height: auto; max-width: 37.1875rem;
    border-radius: 500px 500px 0 0; }   /* live 595px wide, arched top */

/* --- /about hero ---------------------------------------------------------
 * MEASURED at 1440: 595px copy | 595px photo inside a 1290px inner, with a
 * 250x340 portrait overlapping the photo's right edge by 209px. The band
 * carries a radial gradient, not a flat ground. The portrait's radius is
 * asymmetric (flat on the left edge where it meets the photo). */
.about-hero { background: radial-gradient(at 100% 50%,
    var(--brand-mid) 0%, color-mix(in srgb, var(--brand-mid) 20%, transparent) 40%); }
.about-hero__media { position: relative; }
/* ONE rule: an earlier `height: 41.875rem` and a later `height: auto` sat at
 * equal specificity, so auto won and the photo rendered 714px against live's
 * 670 -- which is what closed up the space under the hero. */
.about-hero__photo {
    /* RE-MEASURED 2026-09-18: the arch is 500x670 and sits hard LEFT in its
     * 595px column (x=770) -- it does not fill the column. */
    width: 84%; height: auto; aspect-ratio: 500 / 670;
    max-height: 41.875rem; object-fit: cover;             /* live 670px at 1440 */
    border-radius: 500px 500px 10px 10px;
}
/* MEASURED across 390-1440: live keeps this flush to the CONTENT edge, never
 * hanging off it -- it clears the viewport by 1px at every width below 1440,
 * not by the 60px a negative offset gives. A -3.75rem `right` ran it 36px
 * off-screen from 1040 to 1400, which the geometry sweep caught. Live also
 * drops it 250px -> 150px below 1024. */
/* RE-MEASURED 2026-09-18 at 1440: the 250x300 portrait sits 330px down the
 * 670px column and hangs 41px off its right edge (x=1156-1406), overlapping the
 * arch by ~115px. The overhang is taken out of the gutter, less 5px, so it can
 * never reach the viewport edge however narrow the gutter gets. */
.about-hero__portrait {
    position: absolute; top: 49.25%;             /* 330 of 670 */
    right: calc(-1 * (var(--space-gutter) - var(--space-3xs)));
    width: 42%; max-width: 15.625rem; height: auto;   /* live 250px of 595 */
    border-radius: 20px 20px 20px 0;
}
/* Below the two-column breakpoint the overlap has nothing to overlap, so the
 * portrait returns to the flow rather than hanging off the photo. */
@media (max-width: 64rem) {
    .about-hero__portrait { width: 9.375rem; }    /* live drops to 150px */
}
.hero__sep--wide { display: inline; }
.hero__cta { margin-block-start: var(--space-m); }

/* --- /services -----------------------------------------------------------
 * MEASURED at 1440: one four-track grid, 273px columns inside a 1180px inner.
 * Column 1 is the checklist, columns 2-4 the tiles. Tracks are fr-based, not
 * the literal 273px -- a px track is what broke eight pages between 1040 and
 * 1380 before, and the playbook bans them in the site layer. */
/* MEASURED: live's band carries 50px top + 120px bottom on the outer con AND
 * 80px block on the inner -- 130 top / 200 bottom in total, not the 120/120
 * band rhythm. The gap this closed was -372px. */
.svc-all { padding-block: calc(var(--space-3xl) + var(--space-2xs)) calc(var(--space-3xl) + var(--space-2xl)); }   /* 130 / 200 */
/* MEASURED: live caps the title at 507px so it wraps to THREE lines (198px
 * tall), and the aside at 673px. Left unconstrained they render one line and
 * the band comes up ~200px short -- the same trap as the homepage's services
 * title. Both are left-aligned, not centred. */
.svc-grid {
    display: grid; gap: var(--space-m); margin-block-start: var(--space-l);
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
}
/* MEASURED: live sets the checklist items and the aside prose at 18.56px
 * (--step-1), not the 16.5px body default. Across a four-track grid that one
 * step was most of a 264px band shortfall. The phone link stays at 16.5. */
/* MEASURED: the checklist column is a CARD -- rgb(242,244,246) ground, 10px
 * radius, 40px padding. Missing it left the column 213px short and the band
 * visibly wrong, not merely mismeasured. */
.check-list { list-style: none; padding: 0; margin: 0;
    display: grid; gap: var(--space-2xs); font-size: var(--step-1); }
.check-list li { display: flex; gap: var(--space-2xs); align-items: flex-start; }
.check-list svg { flex: none; width: 0.9375rem; height: 0.9375rem;
    fill: var(--brand-mid); margin-block-start: var(--space-3xs); }   /* live 15x15, 0.35em down */
.svc-tile img { width: 100%; height: auto; aspect-ratio: 1; object-fit: cover;
    border-radius: var(--radius-m); }
.svc-tile h3 { margin-block: var(--space-xs) var(--space-3xs); font-size: var(--step-1); }
.svc-tile p  { font-size: var(--step-1); }   /* live 18.56px, not the 16.5 body default */

/* Live's own breakpoints: 4 -> 2 -> 1. Tracks collapse, they never squeeze. */
@media (max-width: 64rem) {
    .svc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 39.9375rem) {
    .svc-grid { grid-template-columns: minmax(0, 1fr); }
}

/* --- /about --------------------------------------------------------------*/
.contact-note__title {
    display: block; font-weight: var(--weight-700); color: var(--primary);
    margin-block-end: var(--space-3xs);
}

.team-card {
    background: var(--surface);
    padding: var(--space-m); display: flex; flex-direction: column;
    gap: var(--space-2xs); align-items: flex-start;
}
.team-card img { width: 100%; max-width: 15.625rem; height: auto; border-radius: var(--radius-m); }

/* MEASURED: the head block carries a 70px bottom gap before the card grid,
 * and the lede is 1122px wide (not the 960px a default max-width gives). */
/* live: the band's inner is 1290px (x=75), and the lede and CTA sit a further
 * 90px in -- the same inset the homepage's about body uses. */
.doctors > .u-container { max-width: calc(80.625rem + 2 * var(--space-gutter)); }
.doctors .section-title { margin-block-start: var(--space-m); }
.doctors__lede { max-width: 70.125rem; margin-block-start: var(--space-m); margin-left: var(--space-inset-list); }
.doctors__lede p { font-size: var(--step-1); }
.doctors .hero__cta { margin-block: var(--space-m) var(--space-block); margin-left: var(--space-inset-list); }   /* live's 70px head gap */
/* MEASURED at 1440: live lays the five doctors out 3-up (410px tracks) then
 * 2-up (630px), each a WHITE card with 50px side padding -- not one uniform
 * auto-fit run. auto-fit would put 4 on the first row and orphan the fifth.
 * The 3/2 split is expressed in fr so no px track reaches the site layer. */
.doctors__grid {
    display: grid; gap: var(--space-m); margin-block-start: var(--space-l);
    grid-template-columns: repeat(6, minmax(0, 1fr));
}
.doctor {
    background: var(--surface); border-radius: 10px;       /* live 10px */
    padding: var(--space-inset-m) var(--space-inset-m) var(--space-l);   /* live 50/50/40 */
    grid-column: span 2;
    text-align: center;                          /* live centres every line */
}
.doctor:nth-child(4), .doctor:nth-child(5) { grid-column: span 3; }

@media (max-width: 64rem) {
    .doctors__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .doctor, .doctor:nth-child(4), .doctor:nth-child(5) { grid-column: auto; }
}
@media (max-width: 39.9375rem) {
    .doctors__grid { grid-template-columns: minmax(0, 1fr); }
}
/* Five doctors in two columns (640-1024) left the fifth beside an empty cell.
 * An odd last card spans the row and is centred at one column's width. */
@media (min-width: 40rem) and (max-width: 64rem) {
    .doctor:last-child:nth-child(odd) { grid-column: 1 / -1; justify-self: center; width: calc((100% - var(--space-m)) / 2); }
}
.doctor h3 { font-size: var(--step-1); color: var(--primary); }
/* live sets the quals and the provider number alike: body ink, one size */
.doctor__quals, .doctor__provider { margin-block: 0; font-size: var(--step-1); line-height: var(--leading-m); color: var(--ink); }
.doctor h3 { margin-block-end: var(--space-3xs); }

/* --- /practice-policy ----------------------------------------------------*/
/* MEASURED: 80px top / 120px bottom, a 70px gap under the head, and three
 * stacked radial gradients over the mist ground. */
.tips { padding-block: var(--space-2xl) var(--space-3xl);   /* live 80/120 at 1440 */ background-color: var(--bg);
    background-image:
        radial-gradient(900px 700px at 22% 22%, color-mix(in srgb, var(--glow-mid) 20%, transparent), transparent 50%),
        radial-gradient(1100px 850px at 45% 55%, color-mix(in srgb, var(--glow-blue) 22%, transparent), transparent 50%),
        radial-gradient(1300px 950px at 28% 78%, color-mix(in srgb, var(--glow-pale) 14%, transparent), transparent 70%); }
/* live: title (810px) and lede (540px, from x=855) side by side over a 1px
 * accent rule, 64px under the title. */
.tips__head {
    display: grid; grid-template-columns: minmax(0, 810fr) minmax(0, 540fr); align-items: start;
    padding-block-end: var(--space-inset-m);   /* live 50 at 1440 */
    border-bottom: var(--border-thin) solid var(--secondary);
}
.tips__lede { margin: var(--space-m) 0 0; font-size: var(--step-1); }  /* live sits 28px below the title's top */
.tips__grid {
    /* live 100px between the 383px tracks; the row gap is its own token because
     * --space-inset-head falls to 0 at phone width, where rows still need air */
    display: grid; gap: var(--space-2xl) var(--space-inset-head); margin-block-start: var(--space-block);   /* live 70 */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
}
.policy__title { font-size: var(--step-1); line-height: var(--leading-l); color: var(--primary); margin-block-end: var(--space-xs); }   /* live 20.6px, 14px above the copy */
.policy p { font-size: var(--step-1); }
.tips__photo {
    width: 100%; aspect-ratio: 383 / 232; border-radius: var(--radius-m);
    background-size: cover; background-position: 50% 50%; background-repeat: no-repeat;
}
@media (max-width: 64rem) {
    .tips__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .tips__head { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 47.9375rem) {
    .tips  { padding-block: var(--space-block) var(--space-3xl); }    /* live 60/120 at 390; the band rhythm scales now */
    .rights { padding-block: var(--space-block); }  /* live 60/60 at 390 */
}
@media (max-width: 39.9375rem) { .tips__grid { grid-template-columns: minmax(0, 1fr); } }
/* MEASURED at 1440: 425px arch photo | 850px holding TWO 400px text columns,
 * inside the 1350px shell. The photo's radius is 350/350/15/15 -- the same
 * arch mechanism as the hero crops, at a third size. */
.rights__cols {
    display: grid; gap: calc(var(--space-xl) + var(--space-xs));      /* live 75px: photo ends 470, text starts 545 */
    grid-template-columns: minmax(0, 425fr) minmax(0, 850fr);
    align-items: center;                                   /* live centres the text block on the photo */
}
.rights__photo {
    width: 100%; height: 43.1875rem; object-fit: cover;    /* live 691px */
    border-radius: 350px 350px 15px 15px;
}
.rights__text {
    display: grid; gap: var(--space-inset-m);                /* live 50px between the columns, ~60 under the title */
    grid-template-columns: repeat(2, minmax(0, 1fr));   /* live 400 + 400 */
    align-content: start;
}
.rights__head { grid-column: 1 / -1; margin-block-end: 0; }

@media (max-width: 64rem) {
    .rights__cols { grid-template-columns: minmax(0, 1fr); }
    .rights__text { grid-template-columns: minmax(0, 1fr); }
    .rights__head { grid-column: 1; }
    .rights__photo { height: auto; aspect-ratio: 425 / 691; }
}
.rights__title { font-size: var(--step-1); line-height: var(--leading-l); color: var(--primary); margin-block-end: var(--space-s); }
.rights__col p { font-size: var(--step-0); }              /* live sets this copy at body size */
.rights__col p + p { margin-block-start: var(--space-s); }

/* --- /contact ------------------------------------------------------------*/
/* RE-BUILT 2026-09-18 from live at 1440. See the comment in contact.html. */
.contact-page {
    background:
        radial-gradient(1100px 800px at 85% 40%, color-mix(in srgb, var(--glow-blue) 30%, transparent), transparent 48%),
        radial-gradient(1200px 900px at 70% 55%, color-mix(in srgb, var(--glow-mid) 22%, transparent), transparent 72%),
        radial-gradient(1300px 950px at 25% 50%, color-mix(in srgb, var(--glow-pale) 12%, transparent), transparent 78%),
        var(--bg);
    padding-block: var(--hero-pad-top) var(--space-3xl);      /* live 250 / 120: it runs under the header like the hero */
}
.contact-page__cols {
    display: grid; align-items: start;
    grid-template-columns: minmax(0, 592fr) minmax(0, 90fr) minmax(0, 668fr);   /* the 90fr track IS live's gutter */
}
.contact-page__cols > .contact-form-wrap { grid-column: 3; }
.contact-page__copy { display: flex; flex-direction: column; align-items: flex-start; }
.contact-page__copy .section-title { margin-top: var(--space-m); }
.contact-page__lede { max-width: 32.625rem; margin-block-start: var(--space-s); }   /* live 522px */
.contact-page__lede p { margin: 0; font-size: var(--step-0); line-height: var(--leading-l); }
.contact-page__lede p + p { margin-top: var(--space-xs); }

/* sky panel > white sheet (live's hard 0 15px 0 -6px shadow) > two gradient cards */
.contact-cluster {
    width: 100%; margin-block-start: var(--space-inset-m);
    padding: var(--space-xs) var(--space-card-pad) var(--space-card-pad);      /* live 17 / 25 / 25 */
    background: var(--brand-sky); border-radius: var(--radius-l);
}
.contact-cluster__sheet {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-s);
    padding: var(--space-s);
    background: var(--surface); border-radius: var(--radius-l);
    box-shadow: 0 15px 0 -6px rgba(0, 0, 0, 0.19);
}
.contact-mini {
    min-height: 11rem;                                            /* live 176px */
    padding: var(--space-s) var(--space-xs);
    display: flex; flex-direction: column; justify-content: center; gap: var(--space-3xs);
    border-radius: 10px;
    background-image: linear-gradient(var(--surface) 0%, var(--brand-sky) 100%);
}
.contact-mini .contact-card__icon { margin-bottom: var(--space-xs); }
.contact-mini h2 { margin: 0; font-size: var(--step-1); font-weight: var(--weight-700); line-height: var(--leading-l); color: var(--primary); }
.contact-mini p { margin: 0; font-size: var(--step-1); line-height: var(--leading-l); }
.contact-mini a { font-size: var(--step-0); }

/* the form card: white, 20px radius, 40px padding, live's soft blue drop */
.contact-form-wrap {
    --field-bg: color-mix(in srgb, var(--brand-sky) 38%, transparent);   /* the fields' wells on the white card */
    padding: var(--space-l);
    background: var(--surface); border-radius: var(--radius-l);
    box-shadow: 7px 21px 34px 0 color-mix(in srgb, var(--shadow-tint) 33%, transparent);
}
.contact-form { display: grid; gap: var(--space-2xs); }
.field { display: grid; gap: var(--space-3xs); }
/* colour stated: the base `label` rule uses --ink-strong, whose hue is rotated
 * +80deg off the brand and lands on a plum rgb(23,13,26) -- it read as pink on
 * the white card. Live's labels are body ink, rgb(54,59,64). */
.field label { font-size: var(--step-0); font-weight: var(--weight-400, 400); line-height: var(--leading-l); color: var(--ink); }
/* Send is the site's standard .btn pill (owner, 2026-10-05; live's was a
 * full-width 3px-radius bar). Only its place in the form is stated here. */
.contact-form__send { justify-self: start; margin-top: var(--space-3xs); border: 0; cursor: pointer; }
.contact-note {
    margin-block-start: var(--space-s); padding: var(--space-xs);
    background: var(--brand-sky); border-left: 5px solid var(--brand-mid);
}
.contact-note p { margin: 0; font-size: var(--step--1); line-height: var(--leading-l); }
.contact-note .contact-note__title { font-family: var(--font-head); font-size: var(--step-0); font-weight: var(--weight-800); color: var(--ink); }

@media (max-width: 64rem) {
    .contact-page { padding-block: var(--hero-pad-top) var(--space-2xl); }   /* --hero-pad-top is already 180px here */
    .contact-page__cols { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-inset-m); }
    .contact-page__cols > .contact-form-wrap { grid-column: auto; }
}
@media (max-width: 29.9375rem) {
    .contact-cluster__sheet { grid-template-columns: minmax(0, 1fr); }
    .contact-form-wrap { padding: var(--space-s-l); }
}

/* --- Closing CTA ---------------------------------------------------------
 * MEASURED: 80px block padding (not the 120px band rhythm), and two equal
 * columns -- 675px copy, 675px cards, inside the 1350px shell. */
.closing { padding-block: var(--space-l); }

/* --- Narrow widths -------------------------------------------------------
 * MEASURED: live drops the hero card row's hours card below 1024 on
 * /services, exactly as it does on the homepage. */
@media (max-width: 64rem) {
    .hero__media-plain { justify-content: center; }
}
@media (max-width: 47.9375rem) {
    .hero__sep--wide { display: none; }
}

/* The template sets `overflow-wrap: break-word` globally, which splits a long
 * word mid-glyph rather than pushing it to the next line. Live never does
 * this -- its headings wrap on word boundaries at every width. At 390 that
 * rendered "Comprehens / ive". Restore normal wrapping on display type; the
 * rest of the page keeps the template default. */
.display, .section-title, h1, h2, h3 { overflow-wrap: normal; }

/* --- /health-info: curated resources + advice ---------------------------
 * Six national resources plus the practice's own after-hours service: static
 * health information in place of the Your Health hub, which only subscribers
 * get (Terrace is not one). Cards are link-led: the heading IS the link, so the whole card needs
 * no redundant "read more" and screen readers get one meaningful target. */
.resources__grid {
    display: grid; gap: var(--space-m); margin-block-start: var(--space-l);
    /* Track count is stated explicitly below, not left to auto-fit: six cards
     * must read as 3x2, and auto-fit gave 4+2 at some widths. */
}
.resource-card {
    background: var(--surface); border-radius: var(--radius-l); padding: var(--space-m);
}
.resource-card h3 { font-size: var(--step-1); margin-block-end: var(--space-2xs); }
.resource-card h3 a { color: var(--primary); text-decoration: underline;
    text-underline-offset: 0.15em; }
.resource-card h3 a:hover { color: var(--brand-mid); }

.advice__inner {
    display: grid; gap: var(--space-l); align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}
.advice__actions { display: flex; gap: var(--space-xs); flex-wrap: wrap; }
.emergency-panel {
    background: var(--tertiary); color: var(--on-tertiary);
    border-radius: var(--radius-l); padding: var(--space-m);
}
.emergency-panel h3 { color: var(--on-tertiary); font-size: var(--step-1); margin-block-end: var(--space-2xs); }
.emergency-panel p + p { margin-block-start: var(--space-2xs); }
.emergency-panel a { color: var(--on-tertiary); text-decoration: underline; }

/* The resources and advice bands share one ground, so stacked they met with
 * 120 + 120px of nothing between them; the advice band gives its half back. */
.resources + .advice { padding-block-start: 0; }
.resource-card p:last-child { margin-block-end: 0; }
.emergency-panel__block:last-child p:last-child { margin-block-end: 0; }
.advice__copy .section-title + p { margin-block-start: var(--space-s); }
.advice__copy .advice__actions { margin-block-start: var(--space-m); }

/* --- Corrections after owner review (2026-09-10) -------------------------
 * The hero lede rendered rgb(79,86,91) against live's rgb(54,59,64) -- the
 * template's muted ink rather than live's body colour. It passed AA at
 * 6.84:1, which is exactly why the contrast gate did not catch it: the gate
 * asks "is this legible", not "is this live's colour". */
.hero__lede .lede,
.hero__lede p { color: var(--ink); }

/* The team card's backdrop is a linear gradient on live, not a flat white
 * panel. Measured: linear-gradient(154deg, rgb(242,244,246) 32%,
 * rgba(151,202,219,0.72) 100%) on a 681x466 box with a 20px radius. */
.team-card {
    background: linear-gradient(154deg, var(--bg) 32%, color-mix(in srgb, var(--brand-mid) 72%, transparent) 100%);
    border-radius: 20px;
}

/* The trusted band carries the same three-stop radial wash as the tips band
 * on /practice-policy -- measured on live, and previously left flat. */
.trusted {
    background-color: var(--bg);
    background-image:
        radial-gradient(900px 700px at 22% 22%, color-mix(in srgb, var(--glow-mid) 20%, transparent), transparent 50%),
        radial-gradient(1100px 850px at 45% 55%, color-mix(in srgb, var(--glow-blue) 22%, transparent), transparent 50%),
        radial-gradient(1300px 950px at 28% 78%, color-mix(in srgb, var(--glow-pale) 14%, transparent), transparent 70%);
}

/* Two colour bugs found by diffing every text colour against live (2026-09-10).
 * The other differences on those pages are the DOCUMENTED a11y divergence --
 * live's accent rgb(2,138,190) fails AA on pale grounds, so --brand-bright is
 * deliberately rgb(2,107,159). These two were not that:
 *
 *  - .svc-tile h3 set no colour at all and inherited rgb(23,13,26), a near
 *    black that is in no palette here. Live uses its mid navy.
 *  - The closing band's title rendered deep navy where live uses the accent. */
.svc-tile h3 { color: var(--primary); }
.closing .section-title { color: var(--secondary); }

/* Six curated resources, held at 3x2 so the rows balance -- auto-fit reflowed
 * them to 4+2 and 2+2+2 at intermediate widths, which reads as a mistake. */
.resources__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 64rem) { .resources__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 39.9375rem) { .resources__grid { grid-template-columns: minmax(0, 1fr); } }

/* The panel now carries two headed blocks: emergency, and after hours. They
 * are different escalation paths and were reading as one paragraph. */
.emergency-panel__block + .emergency-panel__block {
    margin-block-start: var(--space-s);
    padding-block-start: var(--space-s);
    border-top: var(--border-thin) solid color-mix(in srgb, var(--surface) 22%, transparent);
}
.emergency-panel__block h3 { margin-block-end: var(--space-3xs); }

/* Three more colour mismatches from the full text-colour diff (2026-09-10).
 * All three were MY choice of token, not live's colour:
 *  - the accred strip's title is live's accent, not deep navy (it sits on the
 *    sky band, where the accent still clears AA);
 *  - the team-card tag pills carry body ink on live, not brand colour;
 *  - "Patient Rights & Privacy" is the mid navy, not the display navy. */
.accred__title { color: var(--brand-vivid); }
.rights__head { color: var(--primary); }

/* ==========================================================================
 * PHONE ALIGNMENT — measured off live at 390 (2026-09-18).
 * Live's mobile layout CENTRES nearly every band's copy, and drops the section
 * titles from 3.5rem to 3rem (39.65px at 390). Ours had carried the desktop's
 * left alignment and the 46px title all the way down, so titles ran to three
 * lines where live's take two. At the end of the file so it wins on order.
 * The title SIZES need no rule here any more: the phone ratio (1.145) puts
 * step-7 at 41.3px and step-8 at 47.3 on a 390 screen.
 * ======================================================================== */
@media (max-width: 47.9375rem) {

    .about__head { align-items: center; text-align: center; }
    .about__body { margin-left: 0; text-align: center; }
    .about .btn { display: flex; width: fit-content; margin-inline: auto; }
    .panel--grid .panel__item { text-align: center; }

    .doctors .eyebrow { display: block; width: fit-content; margin-inline: auto; }
    .doctors .section-title, .doctors__lede { text-align: center; margin-left: 0; }
    .doctors .hero__cta { margin-left: 0; display: flex; justify-content: center; }

    .why__head-copy, .why__head-side { align-items: center; text-align: center; }
    .why__item { align-items: center; text-align: center; }

    .contact__copy { align-items: center; text-align: center; }
    .contact-card { align-items: center; text-align: center; }

    .contact-page__copy { align-items: center; text-align: center; }
    .contact-mini { align-items: center; text-align: center; }

    /* the privacy / telephone card is compact on a phone: tag, ~50px, copy --
     * the desktop's 316px floor left a hole in the middle of it */
    .panel__card, .about__panels--tall .panel__card { min-height: 0; }
    .panel__card .tag { margin-bottom: var(--space-inset-m); }
    .panel__ticks { margin-inline: auto; }
    .panel__brand { justify-content: center; }   /* logo + name centred over the centred ticks and copy */

    /* live's services card runs gutter to gutter at 390 (20px), not 40px in */
    .services .u-container { padding-inline: var(--space-gutter); }
}

/* tablet: the /about panels drop their desktop floors, as live's do */
@media (max-width: 1080px) {
    .about__panels--tall, .about__panels--tall .panel__card { min-height: 0; }
    .about__panels--tall .panel__card .tag { margin-bottom: var(--space-inset-m); }
}

/* live's tablet layout (768-1024): the two hero photo cards go squat, 231x116,
 * so the fixed 100px radius rounds the whole top. A comment in the 1080 block
 * pointed at "the 1024 block below" for this, but the rule was never written.
 * Bounded below so it cannot reach the phone layout's 209px cards. */
@media (min-width: 768px) and (max-width: 1024px) {
    .hero__card-photo { height: 7.25rem; }     /* live 116px */
}

/* Live's tablet layout takes effect AT 1024 (Elementor's breakpoint is <=1024),
 * and 1024 is an iPad in landscape. The inner pages' collapses were written as
 * max-width: 63.9375rem and so still showed the desktop layout at exactly 1024;
 * they are now max-width: 64rem throughout. */

/* /about on a phone: the team panel is sized by its content -- the homepage's
 * 477px floor left ~100px of empty gradient under the confidentiality line. */
@media (max-width: 767px) {
    .about__panels--tall .panel--grid { min-height: 0; gap: var(--space-l); }
}

/* ==========================================================================
 * LEGAL PAGES (/privacy-policy, /terms-and-conditions) -- shell measured off
 * live 2026-09-18. Live sizes the photo in VIEWPORT HEIGHT (its section is
 * 100vh, the card starts at 70vh: 1000/701 in a 1000px-tall window, 1128/790
 * in the owner's), so the overlap is stated as 30% of the photo's height and
 * holds at any window size. Clamped so a very short or very tall window cannot
 * crush the photo or push the policy a screen and a half down.
 * ======================================================================== */
.legal { --legal-photo-h: clamp(30rem, 100svh, 70rem); }
.legal__photo {
    display: block; width: 100%; height: var(--legal-photo-h);
    /* --legal-focus is set per page on the <img>: both photos are landscape, so
     * a tall phone crop would otherwise show the blank wall between the people */
    object-fit: cover; object-position: var(--legal-focus, 50%) 0;
}
@media (max-width: 767px) {
    .legal { --legal-photo-h: clamp(24rem, 75svh, 36rem); }
}
.legal__band {
    padding: 0 var(--space-gutter) var(--space-3xl);       /* live 120px under the card */
}
.legal__card {
    position: relative;
    max-width: 80.625rem; margin-inline: auto;                  /* live 1290px */
    margin-top: calc(var(--legal-photo-h) * -0.3);
    padding: max(var(--space-m), var(--space-inset-head));      /* live 100px; never less than 20-30 on a phone */
    background: var(--bg); border-radius: 10px;
}
/* THE BAND MUST START AT THE PHOTO'S FOOT, NOT AT THE CARD'S TOP. The card's
 * negative margin-top is the first thing inside the band, so without a new
 * formatting context it COLLAPSES THROUGH the band: the whole gradient rode up
 * 30% with the card and sliced the photo off flat at the card's top edge -- no
 * straddle at all. flow-root stops the collapse, so only the card moves.
 * (First shipped broken: the check printed the card's position, which was
 * right, and never the band's, which was not.) */
.legal__band { position: relative; display: flow-root; }
.legal__card h1 {
    margin: 0 0 var(--space-m);
    font-family: var(--font-body); font-size: var(--step-6); font-weight: var(--weight-600);
    line-height: var(--leading-s); color: var(--primary);
}
.legal__card h2 {
    margin: var(--space-m) 0 var(--space-xs);
    font-family: var(--font-head); font-size: var(--step-5); font-weight: var(--weight-700);
    line-height: var(--leading-s); color: var(--ink);
}
.legal__card h3 {
    margin: var(--space-s) 0 var(--space-xs);
    font-family: var(--font-head); font-size: var(--step-2); font-weight: var(--weight-700);
    line-height: var(--leading-m); color: var(--ink);
}
.legal__card p, .legal__card li { font-size: var(--step-0); line-height: var(--leading-l); color: var(--ink); }
.legal__card p { margin: 0 0 var(--space-xs); }
.legal__card .lede { font-size: var(--step-1); }
.legal__card ul, .legal__card ol { margin: 0 0 var(--space-xs); padding-left: var(--space-l); }
.legal__card li + li { margin-top: var(--space-3xs); }
.legal__card a { color: var(--secondary); text-decoration: underline; text-underline-offset: 0.15em; }
.legal__card > :last-child { margin-bottom: 0; }

/* A line break that only exists on phones. OWNER'S CALL (2026-09-18): the band
 * title reads "Get in Touch / with Terrace Clinic" at phone width, rather than
 * breaking wherever the column happens to run out ("...with Terrace / Clinic"). */
.br-mobile { display: none; }
@media (max-width: 767px) { .br-mobile { display: inline; } }

/* ==========================================================================
 * MOBILE MENU PANEL. This had NO rules at all -- it rendered as a bare list of
 * blue underlined links in the page flow, pushing the hero down, with no close
 * icon. Built from live's (measured at 390): a full-width deep-navy panel hung
 * from the header's foot, OVER the page; centred white links in 51px rows
 * (14px 20px padding), weight 600; the menu button swaps to an X.
 * One departure: live leaves its inactive links dark grey on the navy, which
 * is close to unreadable. Every link here is white; the current page is marked
 * in the light brand blue with a bar, so the state is not colour alone.
 * No `display` on .mobile-nav itself -- that would defeat the [hidden] attribute
 * the script toggles.
 * ======================================================================== */
.mobile-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: var(--layer-nav);
    background: var(--primary);
    box-shadow: 0 18px 30px -12px color-mix(in srgb, var(--tertiary) 35%, transparent);
}
.mobile-nav ul { list-style: none; margin: 0; padding: var(--space-2xs) 0; }
.mobile-nav a {
    display: block;
    padding: var(--space-xs) var(--space-s);           /* live 14px 20px -> a 51px row */
    font-family: var(--font-head);
    font-size: var(--step-1);
    font-weight: var(--weight-700);
    line-height: var(--leading-l);
    text-align: center;
    text-decoration: none;
    color: var(--surface);
}
.mobile-nav li + li { border-top: var(--border-thin) solid color-mix(in srgb, var(--surface) 12%, transparent); }
.mobile-nav a:hover, .mobile-nav a:focus-visible { background: var(--tertiary); }
.mobile-nav a:focus-visible { outline-offset: -4px; }   /* inside the row: the menu clips an outset ring */
.mobile-nav a.active, .mobile-nav a[aria-current="page"] {
    color: var(--brand-mid);
    box-shadow: inset 4px 0 0 var(--brand-mid);
}
/* the button shows bars when closed, an X when open */
.nav-toggle__close { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__open { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__close { display: block; }
/* never in the desktop header, whatever state the script left it in */
@media (min-width: 75rem) { .mobile-nav { display: none; } }   /* 1200: the nav collapse, and partials/scripts.html */
/* a short drop-in; transform + opacity only, and not under reduced motion */
@media (prefers-reduced-motion: no-preference) {
    .mobile-nav:not([hidden]) { animation: mobile-nav-in 0.22s var(--ease-out) both; transform-origin: top; }
}
@keyframes mobile-nav-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ==========================================================================
 * SEAMLESS BANDS -- OWNER'S CALL (2026-09-18): "no hard cuts on the gradient
 * backgrounds". Each tinted band carries its own stack of radial washes (live's,
 * verbatim), and a wash that is still a few levels off the ground where the band
 * ends meets the next band's different wash as a faint straight line. Measured
 * before this block: 5-11 levels either side of /about's doctors band, 16 into
 * and 14 out of /practice-policy's WHITE rights band, 31 where the legal pages'
 * gradient met the footer, and a raw photo edge above it.
 *
 * Fix, once, for every band: a ::before that fades the band's top and bottom
 * edge to exactly --ground, so any two neighbours meet on the same colour. The
 * fade is one band-padding tall (120px), so it lives entirely in the padding
 * and never sits behind copy. z-index -1 inside an isolated band = above the
 * band's own washes, below its content.
 * The sky accreditation strip and the footer's accent rule are deliberate solid
 * edges (live's) and are left alone.
 * ======================================================================== */
.hero, .about, .why, .services, .tips, .contact-page { position: relative; isolation: isolate; }
.hero::before, .about::before, .why::before, .services::before, .tips::before, .contact-page::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: var(--layer-behind);
    pointer-events: none;
    background:
        linear-gradient(to bottom, var(--bg) 0, transparent var(--space-3xl)),
        linear-gradient(to top, var(--bg) 0, transparent var(--space-3xl));
}
/* heroes and the contact band run UNDER the transparent header: their top wash
 * is the glow behind the nav, so only the foot is faded */
.hero::before, .contact-page::before {
    background: linear-gradient(to top, var(--bg) 0, transparent var(--hero-pad-bottom, var(--space-3xl)));
}
/* the short heroes end on a photo that sits flush on the accreditation strip --
 * nothing to fade, and a fade there would wash out the arch's foot */
.hero--short::before { content: none; }

/* flat bands that sat a few levels off the ground now share it exactly:
 * /about's doctors band was rgb(245,245,245) and /practice-policy's rights
 * band was white -- a 16-level step in and a 14-level step out */
.doctors, .rights { background: var(--bg); }

/* legal pages: the photo's foot melts into the band's first colour instead of
 * ending on a raw edge, and the band's gradient lands on the ground so it runs
 * into the footer without a step */
.legal { position: relative; }
.legal::before {
    content: "";
    position: absolute;
    left: 0; right: 0;
    top: calc(var(--legal-photo-h) * 0.62);
    height: calc(var(--legal-photo-h) * 0.38);
    pointer-events: none;
    background: linear-gradient(to bottom, transparent, var(--brand-mid));
}
.legal__band { background-image: linear-gradient(var(--brand-mid) 0%, var(--brand-sky) 55%, var(--bg) 100%); }

/* ==========================================================================
 * PHONE SPACING PASS -- measured against live at 390 (2026-09-18).
 *   accreditation strip: live runs title -> 22 -> copy -> 36 -> logo inside 40px
 *     of padding; ours was 8 / 19 / 32 -- visibly cramped beside the airy hero.
 *   about band: live is title -> ~22 -> body -> 34 -> CTA; ours 42 / 21 (the
 *     desktop's 50px body offset carried down, and a tight CTA).
 *   tint panel: its 312px floor left ~60px of empty panel under the now-compact
 *     privacy card; it hugs the card instead.
 * ======================================================================== */
@media (max-width: 767px) {
    .accred { padding-block: var(--space-inset-m); }
    .accred .accred__inner { gap: var(--space-l); }
    .accred h2, .accred .accred__title { margin-bottom: var(--space-s); }

    .about__body { margin-top: var(--space-s-l); }
    .about .btn { margin-top: var(--space-l); }

    .panel--tint, .about__panels .panel--tint { min-height: 0; }

    .hero__badge { gap: var(--space-3xs); }

    /* live keeps 3.5rem (46.3px) on the why title and the contact hero at 390;
     * the other section titles drop to 3rem, which plain step-7 already is. */
    .why .section-title, .contact-page .section-title { font-size: var(--step-8); }
    /* live's doctor cards stay ~168px tall on phones; --space-xl is 48 here (a halved step once made ours 133) */
    .doctor { padding-block: var(--space-xl); }

    /* /practice-policy on phones: live drops the tips photo, closes the items
     * up (147px pitch; ours ran 170) and puts the arch AFTER the rights copy.
     * Visual order only -- the photo is decorative to the reading order. */
    /* Get in Touch band: live runs tag -> 30 -> title -> 30 -> button -> 50 ->
     * card -> 50 -> card. Ours had 17 / 17 / 80 / 40: the copy block's bottom
     * padding AND the row gap were both being spent under the button. */
    .contact .contact__inner { row-gap: var(--space-xl); }
    .contact .contact__copy { padding-bottom: 0; gap: var(--space-l); }

    /* Dot-grid panel: live's 477px floor left ~67px of empty panel above and
     * below the centred block on phones (owner: too much space). It hugs its
     * content now, on the same 30px the cards beside it use. */
    .about__panels .panel--grid, .panel--grid { min-height: 0; padding-block: var(--space-l); }

    /* Stacked service tiles: live leaves ~60px from one tile's copy to the next
     * photo; the desktop column gutter (20px here) read as one run-on list. */
    .services .services__grid { row-gap: var(--space-xl); }

    .tips__photo { display: none; }
    /* with the photo gone, five tips in two columns (640-767) left the fifth
     * beside an empty cell: an odd last tip takes the whole row */
    .tips__grid > .policy:nth-child(odd):nth-last-child(2) { grid-column: 1 / -1; }
    .tips__grid { row-gap: var(--space-l); }
    .rights__media { order: 2; }
}

/* ===========================================================================
 * SITE SEARCH
 * The platform component (mediflare.js) writes the box unstyled, with
 * mf-search__* class hooks; every rule here is this site's, on its tokens.
 * Contract and the three placements: "Site search" in /srv/sites/CLAUDE.md.
 *
 * ONE search window at every width, the overlay mediflare.js builds: from
 * 768 up a centred card over a dimmed page, below 768 full screen. Two
 * buttons open it, one per width, swapping where the nav collapses
 * (1199/1200), so no width shows both and none shows neither:
 *   1200 and up    .site-search__trigger: the magnifier in the site's search
 *                  field (the --field-* tokens: white, the brand-mid edge,
 *                  the pill), 44px, beside the Call pill. Never wider: the
 *                  nav already needs the bar's whole middle (the links were
 *                  eased to fit at 1200), and measured 1200-1920 the bar has
 *                  at most ~30px beyond the last link's 32px clearance, so
 *                  there is no room for a word at any desktop width.
 *   1199 and down  .site-search__open: the magnifier beside the menu
 *                  button, on the menu button's navy square. On phones that
 *                  is the second row, with the pair; at 371 and below the
 *                  Call pill reads "Call" so the three still fit there.
 *
 * Shape: pills, like every button here (.btn, the Call pill), except the
 * phone and tablet trigger, which is the menu button's square. Search and
 * Close ARE the site's .btn: the .btn rules above list them. Focus is the
 * site's one ring (see the site :root under Form controls).
 *
 * The overlay and the popup are styled ONLY through :not([hidden]): a
 * display of their own would override [hidden] and they would never close.
 * ======================================================================== */
:root {
    --target-min: 2.75rem;   /* 44px, the touch-target floor; the menu button's own size */
    --shadow-card-soft: 7px 21px 34px 0 color-mix(in srgb, var(--shadow-tint) 33%, transparent);   /* the contact form card's drop, and the search card's */
}

/* The desktop trigger, drawn as the field it opens: fill, edge, corner and
 * height are the --field-* tokens, with the glyph in the deep navy like a
 * nav label; the bright blue the nav hovers in, on a sky ground, when
 * hovered or open. The tablet and phone trigger is the menu button's twin
 * instead (see below). */
.site-search__trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: var(--target-min);
    block-size: var(--target-min);
    padding: 0;
    color: var(--primary);
    background: var(--field-bg);
    border: var(--field-border-w) solid var(--field-border);
    border-radius: var(--field-radius);
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}
/* Beside the menu button it matches it (owner, 2026-10-06): the same 44px
 * deep-navy square, 4px radius, white glyph, and like it no hover change. */
.site-search__open {
    display: none;
    flex: none;
    align-items: center;
    justify-content: center;
    inline-size: var(--target-min);
    block-size: var(--target-min);
    padding: 0;
    color: var(--surface);
    background: var(--primary);
    border: 0;
    border-radius: var(--radius-xs);
    cursor: pointer;
}
.site-search__trigger svg,
.site-search__open svg { width: 1.375rem; height: 1.375rem; fill: currentColor; }
.site-search__trigger:hover,
.site-search__trigger[aria-expanded="true"] { color: var(--secondary); background: var(--brand-sky); }

/* Which button shows where. */
@media (max-width: 74.9375rem) {
    .site-search__trigger { display: none; }
    /* Beside the menu button, in its slot: the button gives up its auto
     * margin (it took the bar's free space) to the magnifier before it. */
    .site-search__open { display: inline-flex; order: 2; margin-inline: auto 0; }
    .nav-toggle { margin-inline: 0; }
}
@media (max-width: 767px) {
    .site-search__open { margin-inline: 0; }
}
/* 371 and below the magnifier, the menu button and the full Call pill no
 * longer fit one row, so the pill reads "Call" and keeps the number in its
 * aria-label; the three stay together under the logo (owner, 2026-10-06). */
@media (max-width: 23.1875rem) {
    .btn--head__number { display: none; }
}

/* Making room on the desktop bar. The magnifier is 44 plus the header's gap,
 * and at 1200 the bar had 27px to spare. Both the logo and the Call pill
 * ease on the site's existing --space-taper (0 at 1440 and up, so the wide
 * desktop is untouched): the logo to 227 at 1200, which is live's own logo
 * there (226), and the pill's inline padding by 18 each side. Measured from
 * 1200 to 1440 in 4px steps: the last link stays at least 32px clear of the
 * magnifier, and the pill stays flush with the container. */
@media (min-width: 75rem) {
    .site-logo img { width: calc(16.25rem - 4.5 * var(--space-taper)); }
    .btn--head { padding-inline: calc(var(--space-l) + var(--space-3xs) - 2.5 * var(--space-taper)); }
}
/* 768 to 775: the tablet bar is 4px short with the magnifier in it; the
 * logo gives up exactly what is needed, and nothing elsewhere. */
@media (min-width: 768px) and (max-width: 74.9375rem) {
    .site-logo { flex-shrink: 1; min-width: 0; }
    .site-logo img { max-width: 100%; }
}

/* The box, wherever it sits. */
.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 { grid-column: 1 / -1; }
.mf-search__input { grid-column: 1 / -1; }
[data-mf-search-submit] .mf-search__input { grid-column: auto; }
/* Read out, never shown: the magnifier and the placeholder say it. */
.mf-search__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* TEXT CONTROLS: the shape, hairline and focus of every text field, the
 * search boxes and the contact form alike, are the --field-* tokens (Form
 * controls). */

/* Search and Close are the site's .btn (listed in its rules above); here only
 * the 44px floor. */
.mf-search__submit,
.mf-search-overlay__close {
    min-block-size: var(--target-min);
    white-space: nowrap;
    cursor: pointer;
}

.mf-search__status {
    margin: 0;
    padding-inline: var(--space-xs);
    font-size: var(--step--1);
    line-height: var(--leading-l);
    color: var(--ink);
}
.mf-search__status:empty { display: none; }

.mf-search__results { margin: 0; padding: 0; list-style: none; }
.mf-search__results:not([hidden]) { display: flex; flex-direction: column; gap: var(--space-3xs); }
.mf-search__result { margin: 0; }
.mf-search__link {
    display: block;
    padding: var(--space-2xs) var(--space-xs);
    border-radius: var(--radius-m);
    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(--bg); }
.mf-search__link:focus-visible {
    outline-offset: calc(var(--focus-ring-w) * -1);   /* inset: a scrolling list clips an outset ring */
    border-radius: var(--radius-m);
}
/* Titles are the site's card titles: Lato 700 in the deep navy, turning the
 * bright blue the nav and links hover in. */
.mf-search__title {
    display: block;
    font-family: var(--font-head);
    font-size: var(--step-0);
    font-weight: var(--weight-700);
    line-height: var(--leading-m);
    color: var(--primary);
    transition: color var(--duration-fast) var(--ease-out);
}
.mf-search__link:hover .mf-search__title,
.mf-search__link:focus-visible .mf-search__title { color: var(--secondary); }
.mf-search__excerpt {
    display: block;
    margin-top: var(--space-3xs);
    font-size: var(--step--1);
    line-height: var(--leading-l);
    color: var(--ink);
}
.mf-search__excerpt mark {
    color: var(--ink);
    background: color-mix(in oklch, var(--brand-mid) 60%, transparent);
    border-radius: var(--radius-xs);
}

/* Phones: full screen, on the page's ground. Close sits alone on the
 * top row, at the right and above the box, so what is seen matches the
 * focus order (Close, the box, Search); the box and Search share the next
 * row at full width, and the results scroll below them, inside the overlay.
 * The page behind is locked by mediflare.js. Subgrid puts the box's own
 * children on the overlay's grid. */
.mf-search-overlay:not([hidden]) {
    position: fixed;
    inset: 0;
    z-index: var(--layer-modal);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;   /* Close · box · Search on one row */
    grid-template-rows: auto auto minmax(0, 1fr);
    gap: var(--space-xs) var(--space-2xs);
    padding-block-start: max(var(--space-s), env(safe-area-inset-top));
    padding-inline: max(var(--space-gutter), env(safe-area-inset-left)) max(var(--space-gutter), env(safe-area-inset-right));
    background: var(--bg);
}
.mf-search-overlay__header { display: contents; }
/* Close is an icon, not the site's pill: a 44px × in the navy, left of the
 * box, as on the other sites' search windows (owner, 2026-10-06). */
.mf-search-overlay__close {
    grid-column: 1;
    grid-row: 1;
    z-index: var(--layer-base);   /* above the box, which spans this row too */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: var(--target-min);
    block-size: var(--target-min);
    padding: 0;
    font-size: 0;
    color: var(--primary);
    background: transparent;
    border: 0;
    border-radius: var(--radius-pill);
    transition: background var(--duration-fast) var(--ease-out);
}
.mf-search-overlay__close::before {
    content: "";
    inline-size: 1.5rem;
    block-size: 1.5rem;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M6 18 18 6M6 6l12 12'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M6 18 18 6M6 6l12 12'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mf-search-overlay__close:hover { background: color-mix(in oklch, var(--brand-mid) 45%, transparent); }
.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;
    align-items: center;   /* the field is --field-h; Search is the .btn, up to 1.3px taller mid-range, so they centre on one line */
}
.mf-search--overlay .mf-search__input { grid-column: 2; grid-row: 1; }
.mf-search--overlay .mf-search__submit { grid-column: 3; grid-row: 1; block-size: var(--field-h); padding-block: 0; }   /* level with the box beside it */
.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));
}
.mf-search--overlay .mf-search__link:hover,
.mf-search--overlay .mf-search__link:focus-visible { background: var(--surface); }

/* From 768 up the same overlay is a centred card (the DocSearch pattern),
 * rounded like the contact form's card and on its soft blue drop, offset
 * from the top rather than vertically centred, so it grows downward with its
 * results up to the viewport, past which they scroll inside it. A press on
 * the dimmed page, Close or Esc closes it (mediflare.js, #249). It refines
 * the full-screen rules above at equal specificity, so it must stay after
 * them. Stacking: the backdrop is body::after at --layer-modal; the card
 * sits one above it, since body::after follows the overlay in tree order and
 * would paint over it at an equal z-index. */
@media (min-width: 768px) {
    .mf-search-overlay:not([hidden]) {
        inset: var(--space-3xl) 0 auto;
        z-index: calc(var(--layer-modal) + 1);
        inline-size: calc(100% - 2 * var(--space-gutter));
        max-inline-size: calc(var(--measure-tight) + var(--space-3xl));
        max-block-size: calc(100dvh - 2 * var(--space-3xl));
        margin-inline: auto;
        padding: var(--space-s);
        border-radius: var(--radius-l);
        box-shadow: var(--shadow-card-soft);
    }
    .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));
    }
}
