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

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

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

    /* --- 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 base size rather than the type ratio, so spacing stays
     * predictable while type can be as dramatic as the site needs. */
    --space-3xs: clamp(0.25rem, calc(0.25rem + 0.1vw), 0.375rem);
    --space-2xs: clamp(0.5rem, calc(0.45rem + 0.2vw), 0.625rem);
    --space-xs: clamp(0.75rem, calc(0.68rem + 0.3vw), 0.9375rem);
    --space-s: clamp(1rem, calc(0.91rem + 0.43vw), 1.25rem);
    /* The gap between a primary button and the text link beside it. */
    --action-gap: var(--space-s);
    --space-m: clamp(1.5rem, calc(1.37rem + 0.65vw), 1.875rem);
    --space-l: clamp(2rem, calc(1.82rem + 0.87vw), 2.5rem);
    --space-xl: clamp(3rem, calc(2.73rem + 1.3vw), 3.75rem);
    --space-2xl: clamp(4rem, calc(3.64rem + 1.74vw), 5rem);
    --space-3xl: clamp(6rem, calc(5.46rem + 2.61vw), 7.5rem);

    /* --- 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(0.5rem, calc(0.35rem + 0.76vw), 0.9375rem);
    --space-xs-s: clamp(0.75rem, calc(0.57rem + 0.87vw), 1.25rem);
    --space-s-m: clamp(1rem, calc(0.7rem + 1.52vw), 1.875rem);
    --space-m-l: clamp(1.5rem, calc(1.13rem + 1.85vw), 2.5rem);
    --space-l-xl: clamp(2rem, calc(1.36rem + 3.15vw), 3.75rem);
    --space-xl-2xl: clamp(3rem, calc(2.27rem + 3.62vw), 5rem);
    --space-l-2xl: clamp(2rem, calc(0.91rem + 5.43vw), 5rem);
    --space-s-l: clamp(1.125rem, calc(0.72rem + 2.04vw), 2.5rem);  /* skip-a-step: grid gutter */
    --space-2xl-3xl: clamp(4rem, calc(2.55rem + 7.24vw), 7.5rem);

    /* --- 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.
     * ===================================================================== */
    /* High Street Family Practice — measured from the live site, not chosen.
     * Primary teal  rgb(14,116,132)  -> oklch(0.514 0.086 212)
     * Deep blue H3  rgb(1,88,138)    -> oklch(0.443 0.108 244), i.e. +32 deg
     * Body charcoal rgb(54,59,64)    -> oklch(0.350 0.011 248)
     * Section cream rgb(249,247,244) -> oklch(0.977 0.005 78)
     *
     * The secondary is an ANALOGOUS neighbour here, not a complement: the
     * practice pairs teal headings with deep-blue subheads, and a 150 deg
     * complement would have put orange on the page, which appears nowhere. */
    --palette-hue: 212;
    --palette-chroma: 0.09;
    --palette-secondary-shift: 32;

    --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;

    /* 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-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. */
    /* The gap between an eyebrow and the heading it labels. One value for
     * the whole site -- see the note on .band-eyebrow. */
    /* --- Band rhythm ---------------------------------------------------
     * The vertical spacing inside every band, as tokens rather than a value
     * per band. Before these, title->lede measured 15/30/35/65px and
     * lede->lede 15/20/25px across one page, because each band set its own
     * via a flex gap, a local margin, or both stacked.
     *
     * THE SIZES ARE SET AGAINST THE TEXT, not picked. .band-lede is 19px on
     * 1.6, so a wrapped line occupies a 30px line box. A paragraph break has
     * to clear that or the space between two paragraphs is smaller than the
     * leading inside them and they read as one cramped block -- which is
     * exactly what a first pass at 16px did. --lede-gap is 22px plus the
     * paragraph's own descender space, so consecutive paragraphs separate
     * cleanly while still reading as one voice. */
    --eyebrow-gap: 0.75rem;   /* eyebrow -> the heading it labels */
    --title-gap: 2rem;        /* heading -> the copy under it */
    --lede-gap: 1.375rem;     /* one paragraph -> the next */
    --copy-gap: 2.25rem;      /* last line of copy -> a button or list below */
    /* Half a band gap: adjacent bands each contribute this, so the step
     * between any two is 2x --band-pad and the page has one rhythm. */
    --band-pad: clamp(3rem, 5.5vw, 6.25rem);

    --grid-max-width: 93.75rem;
    --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 -- colour and other paint-only changes */
    --duration-hover: 260ms;  /* hover MOVEMENT. 120ms across 4px of travel reads
                                 as a snap rather than a lift; the eye needs
                                 longer to register displacement than colour. */
    --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: 'Asap', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-heading: 'Barlow', var(--font-body);
}

/*
 * 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.
 */
@media (prefers-color-scheme: dark) {
    :root {
        /* Flipping --scheme inverts every neutral at once, via the --l-*
         * dials. The three brand values below are NOT a polarity flip -- a
         * brand colour has to get lighter and less contrasty on a dark
         * ground to stay legible, which is a different adjustment. */
        --scheme: 0;

        --l-solid: 0.68;
        --l-hover: 0.75;
        --l-pale: 0.24;
        --l-on-solid: 0.18;

        --shadow-strength: 12%;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
 * 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; }

* { margin: 0; }

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

/* Media is block-level and never overflows its container. */

/* Form controls inherit typography rather than using browser chrome. */
input, button, textarea, select { font: inherit; }

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

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

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

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

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

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

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

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

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

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

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

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

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

/* A visible focus ring on every interactive element, always. */
:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

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(--divider);
}

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

/* Media ──────────────────────────────────────────────────────────────── */
img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }

/*
 * MIGRATION (high-street-family-practice): the template's default img rule
 * ( background: var(--surface)) is removed
 * rather than overridden.
 *
 * Both halves were wrong here. The live site squares every image, and the
 * surface plate is white, so it painted an opaque box behind any transparent
 * asset -- the header logo and the QPA accreditation mark both showed one.
 * The template's opt-out below keys off class NAMES containing logo or icon,
 * and neither matched (the header <img> has no class; the mark is
 * .accred-card__mark), so the hatch never fired.
 *
 * Deleting it leaves images with the browser default: square, transparent.
 * That is what this site wants, so there is nothing left to override.
 */

/*
 * Logos, icons and inline SVG are not photographs — a background plate and a
 * radius are wrong for them, and show up as a white box on a dark footer.
 */
img[class*="logo"], img[class*="icon"], .logo img, svg {
    border-radius: 0;
    background: none;
}

/* Opt out where a radius is wrong: full-bleed art, decorative crops. */
img.is-flush, .is-flush img { border-radius: 0; background: none; }

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(--divider);
    vertical-align: top;
}

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

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

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

input, select, textarea {
    width: 100%;
    padding: var(--space-2xs) var(--space-xs);
    font-family: inherit;
    font-size: var(--step-0);
    color: var(--ink);
    background: var(--surface);
    border: var(--border-thin) solid var(--border-strong);
    transition: border-color var(--duration-fast) var(--ease-out);
}

input:hover, select:hover, textarea:hover { border-color: var(--ink-soft); }
input:focus, select:focus, textarea:focus { border-color: var(--primary); }

textarea { min-height: 8rem; resize: vertical; }

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

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

::placeholder { color: var(--ink-soft); opacity: 1; }

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

/* 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 {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3xs);
    padding: var(--space-2xs) var(--space-s);
    font-family: inherit;
    font-size: var(--step--1);
    font-weight: var(--weight-600);
    line-height: var(--leading-s);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    border: var(--border-thin) solid transparent;
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

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

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

.btn--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;
    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);
}

/* 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;
    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: clamp(6rem, 14vw, 11rem) clamp(4rem, 9vw, 7rem);
    text-align: center;
}
.notfound__inner { max-width: 46rem; margin-inline: auto; }

/* Decorative, aria-hidden in the markup - the <h1> carries the meaning. Sized
 * large but kept at a legible contrast: a very pale numeral fails WCAG even
 * when it is ornamental. */
.notfound__code {
    font-size: clamp(4.5rem, 18vw, 9rem);
    font-weight: 700;
    line-height: 0.9;
    margin: 0 0 0.25em;
    opacity: 0.55;
}
.notfound h1 { margin-top: 0; }
.notfound__lede { font-size: 1.125rem; line-height: 1.5; margin-inline: auto; max-width: 40rem; }

.notfound__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: center;
    margin-top: 2rem;
}

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

/* ==========================================================================
   TRANSCRIBED FROM THE LIVE SITE — measured, not designed.
   --------------------------------------------------------------------------
   This is a migration. Every value below was read off the live WordPress page
   with scripts/banddetail.mjs + typescale.mjs rather than chosen. If a value
   here looks arbitrary, it is because the original is arbitrary; change it
   only against a fresh measurement.

   Live type scale (1440px):
     nav link      21px Barlow 600
     hero eyebrow  21px Barlow 600   <- the <h1>, deliberately small
     hero headline 74px Barlow 700   rgb(1,88,138)  (the secondary blue)
     section h2    21px Barlow 600
     card title    25px Barlow 700   (an <h5> — see the note below)
     buttons       19px Asap 700
     body          16px Asap 400

   HEADING LEVEL AND SIZE ARE INDEPENDENT. The live site uses <h5> for card
   titles at 25px. The tag carries document outline and accessibility; the SIZE
   comes from these rules. Do not "fix" the tags to match the sizes, and do not
   let the v2 template's small default h5 collapse them back to 14px.
   ========================================================================== */

/* The site ground is the kit's warm off-white, not white. */
:root {
    --ground: #F9F7F4;
    --ink-body: rgb(54, 59, 64);      /* kit "text" */
    --brand-teal: rgb(14, 116, 132);  /* kit primary  #0E7484 */
    --brand-blue: rgb(1, 88, 138);    /* kit secondary #01588A */
    --brand-pale: #CBF1FC;            /* kit accent */

    /* Every divider on the site -- table rules, the accordion borders, the
       footer rule, the policy-list separators. Teal, but mixed back toward
       the page so it stays a RULE rather than a coloured line: full-strength
       --brand-teal at 1px competes with the text it is separating. */
    --divider: color-mix(in srgb, var(--brand-teal) 55%, var(--ground));
    /* Site chrome -- the rule under the nav and the one above the footer
       columns. Those frame the page rather than separating content within it,
       so they stay neutral and let the teal rules read as part of the content. */
    --divider-chrome: rgb(199, 196, 191);

    /* Band title sizes, measured off live at 1440 (74 / 58 / 49 / 41px) and
       fluid below it. Defined once; every band reads these. */
    --title-hero: clamp(2.75rem, 1.6rem + 3.8vw, 74px);
    --title-xl:   clamp(2rem, 1.1rem + 2.6vw, 58px);
    --title-lg:   clamp(1.9rem, 1.1rem + 2.2vw, 49px);
    --title-md:   clamp(1.6rem, 1rem + 1.8vw, 41px);
}

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

/* Live section headings are 21px, not the template's 44px. */
.section h2 { font-size: 21px; font-weight: 600; line-height: 1.3; }
.section .lede { font-size: 19px; line-height: 1.55; }

/* ==========================================================================
   Site header — 21px Barlow 600 nav, on the ground colour
   ========================================================================== */
.skip-link {
    position: absolute; left: var(--space-s); top: var(--space-s);
    z-index: var(--layer-top);
    padding: var(--space-2xs) var(--space-s);
    background: var(--brand-teal); color: #fff;
    transform: translateY(-200%);
    transition: transform var(--duration-fast) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

.header-inner {
    max-width: var(--grid-max-width);
    margin-inline: auto;
    padding: var(--space-2xs) var(--grid-gutter);
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-s);
}

.brand { display: inline-flex; align-items: center; flex-shrink: 0; }

.primary-nav { display: flex; align-items: center; gap: var(--space-s); }
.nav-list {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav-list a, .sub-toggle {
    display: block;
    padding: var(--space-3xs) var(--space-2xs);
    font-family: var(--font-heading);
    color: var(--ink-body);
    background: none;
    border: 0;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
}

/* The same lift as the buttons, at the same duration and curve, so the whole
 * header moves with one vocabulary. Smaller travel: a nav item is text, not a
 * slab, so 4px on it reads as a jump. */
.nav-list a, .sub-toggle {
    transition: color var(--duration-hover) var(--ease-out),
                transform var(--duration-hover) var(--ease-out);
}
.nav-list a:hover, .sub-toggle:hover,
.nav-list a:focus-visible, .sub-toggle:focus-visible {
    color: var(--brand-teal);
    transform: translateY(-2px);
}
.nav-list a.active, .sub-toggle.active { color: var(--brand-teal); }

/* The drawer stacks the items in normal flow, where a lift just nudges text
 * around under the finger. Colour only below the drawer breakpoint. */
@media (max-width: 68.75rem) {
    .nav-list a:hover, .sub-toggle:hover,
    .nav-list a:focus-visible, .sub-toggle:focus-visible { transform: none; }
}

.has-sub { position: relative; }
.sub-toggle::after {
    content: ""; display: inline-block; margin-left: 0.35em;
    border: 0.28em solid transparent; border-top-color: currentColor;
    transform: translateY(0.15em);
}

.sub-list {
    position: absolute; top: calc(100% + 0.25rem); left: 0;
    min-width: 16rem; margin: 0; padding: var(--space-3xs);
    list-style: none;
    /* Sky blue panel, no outline -- the shadow alone separates it from the
     * page, which reads cleaner than a border on a tinted ground. */
    background: var(--brand-pale);
    border: 0;
    box-shadow: var(--shadow-m);
    opacity: 0; visibility: hidden; transform: translateY(-0.25rem);
    transition: opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out),
                visibility var(--duration-fast);
    z-index: var(--layer-overlay);
}

.has-sub:hover .sub-list, .has-sub.is-open .sub-list, .has-sub:focus-within .sub-list {
    opacity: 1; visibility: visible; transform: translateY(0);
}

.sub-list a {
    padding: var(--space-2xs) var(--space-xs);
    font-size: 18px; font-weight: 500;
    white-space: normal;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}
/* Hover fills the row with the dark blue. Contrast checked: ink on the pale
 * panel 9.4:1, white on the blue 7.6:1 -- both clear of AA. */
.sub-list a:hover,
.sub-list a:focus-visible {
    background: var(--brand-blue);
    color: #fff;
    text-decoration: none;
}

/* Header CTA — 19px Asap 700 on the teal, matching the live button. */
.nav-cta {
    flex-shrink: 0;
    font-family: var(--font-body);
    font-size: 19px;
    font-weight: 700;
    background: var(--brand-teal);
    color: #fff;
    padding: var(--space-2xs) var(--space-s);
    border: 0;
    text-decoration: none;
    white-space: nowrap;
}
.nav-cta:hover { background: var(--brand-blue); color: #fff; }

.nav-toggle {
    display: none; flex-direction: column; justify-content: center; gap: 0.28rem;
    width: 2.75rem; height: 2.75rem; padding: 0;
    background: none; border: var(--border-thin) solid var(--border); cursor: pointer;
}
.nav-toggle__bar {
    display: block; width: 1.25rem; height: 2px; margin-inline: auto;
    background: var(--ink-body);
    transition: transform var(--duration-base) var(--ease-out), opacity var(--duration-fast) var(--ease-out);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) { transform: translateY(0.5rem) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { transform: translateY(-0.5rem) rotate(-45deg); }

/*
 * Mobile drawer. Two measured constraints, both learned the hard way:
 *  - `display: none` when closed, NOT translateX(100%) — a transformed element
 *    still counts toward document scroll width, which made every viewport
 *    below the breakpoint exactly twice its own width.
 *  - an explicit dvh height, because .site-header carries a background and the
 *    drawer is a fixed descendant; a top/bottom inset pair resolves against the
 *    header, collapsing the drawer to its own padding.
 */
@media (max-width: 68.75rem) {
    .nav-toggle { display: flex; }

    /* The drawer is a panel sized to its own links, not a full-height screen:
     * `height: 100dvh - header` left a large empty field under the last item
     * with the CTA stranded at the foot. max-height keeps it scrollable on a
     * short screen without claiming the space when it does not need it. */
    .primary-nav {
    position: fixed;
    right: 0;
    left: 0;
    max-height: calc(100dvh - var(--header-height, 4.5rem));
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0;
    background: var(--brand-pale);
    border-top: 0;
    overflow-y: auto;
    display: none;
}
    .primary-nav.is-open { display: flex; }

    .nav-list { flex-direction: column; align-items: stretch; gap: 0; }
    /* Rows are separated by the panel itself, not by rules -- the reference
     * has no dividers between them. */
    .nav-list > li { border-bottom: 0; }
    .nav-list a, .sub-toggle {
        padding: var(--space-s) var(--grid-gutter);
        width: 100%;
        text-align: center;
        justify-content: center;
    }
    /* The current page is a solid teal row across the drawer. */
    .nav-list a.active, .sub-toggle.active {
        background: var(--brand-teal);
        color: #fff;
    }

    .sub-list {
        position: static; min-width: 0;
        padding: 0 0 var(--space-2xs) var(--space-s);
        background: none; border: 0; box-shadow: none;
        opacity: 1; visibility: visible; transform: none;
        display: none;
    }
    .has-sub.is-open .sub-list { display: block; }

    /* The CTA sits in the HEADER ROW beside the close button, as the
     * reference has it -- not as a bar under the menu. It is markup-wise
     * inside <nav>, so it is lifted out: fixed to the header's own band,
     * which the drawer then starts below. */
    /* Two centred rows, as the reference has it: the logo alone on the first,
     * the hamburger and Book Appointment as a centred pair on the second.
     * Measured off the reference (603px wide, scaled to 390): the pair sits
     * with equal 69px insets, the close button 43px and the CTA 190. */
    .header-inner {
        flex-wrap: wrap;
        justify-content: center;
        row-gap: var(--space-2xs);
    }
    .brand { flex: 0 0 100%; display: flex; justify-content: center; }
    .nav-cta {
        order: 2;
        margin: 0;
        text-align: center;
        /* Wide enough for "BOOK APPOINTMENT" on ONE line, as the reference
         * has it -- at 190px and 19px type it broke to three. The toggle
         * (44) plus a 20px gap plus this still leaves an even inset either
         * side of a 390 viewport. */
        flex: 0 0 auto;
    }
    /* Teal slab with white bars, as the reference has it -- it was a
     * transparent box with a hairline border and dark bars. */
    .nav-toggle {
        order: 1;
        margin-right: var(--space-2xs);
        background: var(--brand-teal);
        border-color: var(--brand-teal);
        width: 56px;
        height: 56px;
        align-items: center;
    }
    .nav-toggle .nav-toggle__bar { background: #fff; }

    /* `.btn, .booking-button { font-size: 19px; white-space: nowrap }`,
     * `.btn { white-space: normal }` and the `.btn` padding all sit outside
     * any media query, so they come later than this block and won -- which
     * is why a `width` plus `padding-inline: 0` set above still computed as
     * 45px of side padding squeezing 193px of text into a 226px box. Every
     * property that decides the button's size has to be stated HERE, at the
     * matching specificity. No width: the button sizes to its one line. */
    .header-inner .nav-cta.btn {
        font-size: 16px;
        white-space: nowrap;
        letter-spacing: 0.02em;
        padding: 15px 22px;
    }

    /* The three bars sat 20x2 with 4.5px between them inside a 56px slab --
     * a small cramped mark floating in a large square. Wider, thicker and
     * further apart so the icon fills its box. */
    .nav-toggle .nav-toggle__bar {
        width: 26px;
        height: 3px;
        margin-inline: auto;
    }
    .nav-toggle { gap: 6px; }

    /* The nav is the fixed drawer, so it contributes nothing to the header's
     * own flow -- but as a flex item it still claimed a row and pushed the
     * CTA onto a third one. Taken out of the flow entirely; the drawer's own
     * `position: fixed` places it. */
    .primary-nav { position: fixed; order: 3; }
    body.nav-open { overflow: hidden; }
}

/* ==========================================================================
   Hero — measured: 1136px tall, cover background, 250px/100px padding,
   600px text column on the left, NO scrim.
   ========================================================================== */
.hero {
    position: relative;
    isolation: isolate;
    background-image: url("/assets/images/hero-waiting-room.webp");
    background-size: cover;
    background-position: 50% 50%;
    background-repeat: no-repeat;
}

/*
 * The readability wash, copied verbatim from the live site's ::before overlay:
 *   linear-gradient(112deg, var(--ground) 49%, rgba(255,255,255,0) 61%) at opacity .76
 * It fades the ground colour across the left half so the copy stays legible,
 * then clears entirely so the photograph reads at full strength on the right.
 * This is a pseudo-element on the band, exactly as the original builds it —
 * inferring it from a screenshot was never going to produce these numbers.
 */
.hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: var(--layer-behind);
    opacity: 0.76;
    background-image: linear-gradient(112deg, var(--ground) 49%, rgba(255, 255, 255, 0) 61%);
}

/*
 * Measured: the live hero band pads 20px at the sides, not the 40px page
 * gutter, so its text starts at x=20 and the headline wraps at 472px into
 * four lines. Using the container's own gutter pushed the text to x=140 and
 * let the headline run to 600px, wrapping to three lines instead.
 */
.hero__inner { padding-block: 250px 100px; padding-inline: 20px; max-width: none; margin-inline: 0; }

/*
 * The copy column is roughly half the band, matching the scrim above it: the
 * wash is solid to 49% of the BAND and fully clear by 61%, so text past the
 * midpoint runs onto the bare photograph and stops being legible.
 *
 * 48% rather than a round 50 because this percentage resolves against the
 * CONTAINER (1420px inside the gutters) while the scrim is painted on the
 * full-width band. At 50% the box edge overran the solid part of the wash by
 * ~15px at every width -- harmless in practice, since a last line rarely
 * fills the box, but wrong by construction.
 *
 * Replaces a 600px cap measured against the old 1240px container, which left
 * the copy on ~42% of the wider page and read as cramped. The clamp holds a
 * 20rem floor on small desktops and a 46rem ceiling so the lede never runs
 * past a comfortable measure on a very wide monitor. Cancelled outright
 * below 56.25rem, where the wash goes full-width and the copy is one column.
 */
.hero__content { width: clamp(20rem, 48%, 46rem); max-width: 100%; }

.hero__eyebrow {
    font-family: var(--font-heading);
    font-size: 21px; font-weight: 600;
    letter-spacing: var(--tracking-xl);
    text-transform: uppercase;
    color: var(--ink-body);
    margin: 0 0 var(--eyebrow-gap);   /* the site-wide standard; see .band-eyebrow */
}

.hero__headline {
    max-width: 100%;
    font-family: var(--font-heading);
    font-size: var(--title-hero);
    font-weight: 700;
    line-height: 1.1;
    color: var(--brand-blue);
    margin: 0 0 var(--space-m);
}

/* The lede and the body are two halves of one thought -- "Finding a GP ..."
 * then "At <practice>, that's exactly what we do" -- so they are set at one
 * size. They were 19px and 16px, which read as a standfirst followed by
 * small print rather than as continuous speech. */
.hero__lede,
.hero__body { font-size: 19px; line-height: 1.55; color: var(--ink-body); max-width: 58ch; }
.hero__body { margin-top: var(--space-2xs); }

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--action-gap); margin-top: var(--space-m); }
/* Book Appointment and the Call link, everywhere they sit together. It was
 * 15px on the homepage, 10 on the inner heroes and 4 on /appointment, where
 * .band-actions was never given a flex gap at all and the pair were separated
 * by a word space. --space-s is 16-20px fluid. */
.band-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--action-gap); }

@media (max-width: 56.25rem) {
    /* The photo's subject sits right of centre; keep the text legible over it. */
    .hero { background-position: 70% 50%; }
    .hero__inner { padding-block: var(--space-xl) var(--space-l); }
    .hero__content { width: auto; max-width: none; }
    /* Narrow viewports need the wash across the whole band, not a diagonal. */
    .hero::before { background-image: linear-gradient(180deg, var(--ground) 55%, rgba(255,255,255,0) 100%); opacity: 0.88; }
}

/* Ticked feature list — the live site's check-mark items, not pills. */
.ticklist { list-style: none; margin: var(--space-m) 0 0; padding: 0; }
.ticklist li {
    position: relative; padding-left: 1.75rem;
    margin-bottom: var(--space-2xs);
    font-size: 19px; color: var(--ink-body);
}
/* The tick is two CSS borders rotated 45 degrees, not an SVG, so the stroke
 * rule that thickened the icon set does not reach it. 3px against the 2 it
 * had -- 4 put the tick's stroke heavier than the strokes of the 19px text
 * beside it, which is what read as unbalanced. */
.ticklist li::before {
    content: ""; position: absolute; left: 0; top: 0.39em;
    width: 0.78rem; height: 0.42rem;
    border-left: 3px solid var(--brand-teal);
    border-bottom: 3px solid var(--brand-teal);
    transform: rotate(-45deg);
}
.ticklist--inline { display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-m); }

/* Google rating — a real logo, as the live site has. */
/* Google rating.
 *
 * Laid out the way a rating conventionally reads: mark, then the score, then
 * the stars, then what it is out of. The stars were five identical filled
 * glyphs, which says 5.0 no matter what number sits beside them -- 4.7 has to
 * show four full stars and a partial fifth or it is just decoration.
 *
 * Done with two stacked rows of the same glyph: a grey row underneath, and a
 * gold row on top clipped by width to --rating (4.7/5 = 94%). One custom
 * property is the only thing to change if the score does. No images, no JS,
 * and it degrades to the grey row if the clip is unsupported.
 */
.rating {
    display: inline-flex;   /* shrink to the mark + stack, not the hero column */
    align-items: center;
    gap: 0.6rem;
    margin-top: var(--space-m);
    font-size: 16px;
}
/* The mark carries the block: it stands as tall as the two stacked rows
 * beside it, as on the reference. */
.rating__logo { display: block; width: 44px; height: 44px; flex-shrink: 0; }

/* Stars over the text, the pair sitting to the right of the mark. */
.rating__stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 0;
}

.rating__stars {
    position: relative;
    display: inline-block;
    color: rgb(190, 194, 198);          /* the empty row */
    font-size: 19px;
    letter-spacing: 0.08em;
    line-height: 1;
    white-space: nowrap;
}
.rating__stars-on {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--rating, 100%);
    overflow: hidden;
    color: #F4B400;                      /* Google's star gold */
    white-space: nowrap;
}
.rating__text { color: var(--ink-body); font-size: 17px; line-height: 1.2; }

/* ==========================================================================
   Buttons — 19px Asap 700, matching the live site
   ========================================================================== */

.btn--primary, .booking-button { background: var(--brand-teal); color: #fff; }
.btn--primary:hover, .booking-button:hover { background: var(--brand-blue); color: #fff; }

/*
 * The provider logo inside {{booking_button}}. Rails emits this markup and
 * neither _template nor _shared styles it, so without a rule the SVG renders
 * at its intrinsic ~250px and the label wraps under it.
 */
.booking-provider-logo { height: 1.15em; width: auto; flex-shrink: 0; filter: brightness(0) invert(1); }

/* ==========================================================================
   Benefits — live 645px band, H5 titles at 25px
   ========================================================================== */
.benefits__grid {
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    margin: 0; padding: 0; list-style: none;
}

/* ==========================================================================
   About — two 318px photos, H3 features in the secondary blue
   ========================================================================== */
.about__grid { grid-template-columns: minmax(0, 1fr); align-items: center; }



.feature-grid {
    grid-template-columns: minmax(0, 1fr);
    margin: var(--space-m) 0 0; padding: 0; list-style: none;
    border-top: 1px solid var(--divider);     /* the rule above the features, as on live */
    padding-top: var(--space-m);
}
@media (min-width: 40rem) { .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-m); } }

/* ==========================================================================
   Practice policies — a tinted panel beside the policy list
   ========================================================================== */
.policies__split { grid-template-columns: minmax(0, 1fr); align-items: start; }

.policies__panel {
    padding: var(--space-l);
    background: var(--brand-pale);
    /* The flex column and its gap, at EVERY width. The band-heading rules
     * below set `margin-bottom: calc(<gap> - var(--band-flow-gap))` on the
     * assumption that a 35px flex gap makes up the difference. That layout
     * was declared only inside the desktop query, so on a phone the panel
     * was a block box and those calcs resolved NEGATIVE -- the eyebrow
     * overlapped its own title by 23px. */
    display: flex;
    flex-direction: column;
    gap: var(--band-flow-gap, 35px);
}
.policies__panel > * { margin-block: 0; }
.policies__panel h2 { margin-top: var(--space-2xs); }
.policies__panel p { font-size: 16px; color: var(--ink-body); }

.policy-item:last-child { border-bottom: 0; }
.policy-item h5 {
    font-family: var(--font-heading);
    font-size: 25px; font-weight: 700; color: var(--ink-body);
    margin: 0 0 var(--space-2xs);
}
.policy-item p { margin: 0; font-size: 16px; line-height: 1.6; color: var(--ink-body); }

/* ==========================================================================
   Services — six photographic cards, H5 titles at 25px
   ========================================================================== */
.services__grid {
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    /*
     * The gap from the section head down to the cards is a BAND-level step,
     * not a paragraph one: the button ends the intro and the grid is a new
     * block of content. At --copy-gap (36px) the button sat sandwiched, with
     * the same distance above it as below and the cards crowding it.
     */
    margin: var(--band-pad) 0 0; padding: 0; list-style: none;
}

/* ==========================================================================
   FAQ — transparent ground on the live site, photo beside the questions
   ========================================================================== */
.faq__grid { grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 68.75rem) {
    .faq__media { position: sticky; top: 6rem; }
}
.faq__body h3 { font-family: var(--font-heading); font-size: 25px; font-weight: 700; margin-top: 0; }

/* Teal, via --divider. (This was a neutral grey: --border is built on the
 * palette hue and read as an unintended blue-green cast. --divider makes the
 * teal deliberate and consistent with every other rule on the site.) */
.faq__item { border-bottom: var(--border-thin) solid var(--divider); padding: var(--space-xs) 0; }
.faq__item summary {
    display: flex;
    align-items: center;
    cursor: pointer;
    list-style: none;
    font-family: var(--font-body);
    color: var(--ink-body);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after {
    flex-shrink: 0;
    font-size: 1.35em;
    line-height: 1;
    color: var(--brand-teal);
    transition: transform var(--duration-base) var(--ease-out);
}
.faq__item[open] summary::after { transform: rotate(45deg); }
.faq__item p { margin: var(--space-2xs) 0 var(--space-3xs); font-size: 16px; color: var(--ink-body); max-width: 62ch; }

/* ==========================================================================
   CTA — live 1051px, on the ground colour with a 680x651 photo
   ========================================================================== */
.cta__grid { grid-template-columns: minmax(0, 1fr); align-items: center; }
.cta__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-top: var(--space-m); }

/* ==========================================================================
   Contact information — four columns
   ========================================================================== */
.contact-card h5 {
    font-family: var(--font-heading);
    font-size: 25px; font-weight: 700; color: var(--ink-body);
    margin: 0 0 var(--space-2xs);
}
/* 14px was two steps below the body copy. --step--1 is the site's own small
 * step and still reads as a note against the details above it. Italic to set
 * it apart from the hours it qualifies, and nbsp-joined in the markup so
 * "8:30 am" cannot break across two lines. */
.contact-card__note {
    color: var(--ink-soft);
    font-size: var(--step--1);
    font-style: italic;
    /* The text comes from {{hours_notes}}, so the break cannot be fixed with
     * an nbsp in the markup; balance keeps "8:30 am" off its own line. */
    text-wrap: balance;
    margin-top: var(--space-2xs);
}
/* The day/time columns are handled where the table is centred, further down;
 * this rule pushed the time to the card edge when the table was full-width. */
.contact-card .hours-table td:last-child { text-align: left; }

/* ==========================================================================
   Site footer — MEASURED: light, not dark
   --------------------------------------------------------------------------
   The v2 template's footer is dark navy. The live footer is not: it sits on
   the same warm ground as the page (var(--ground)) with dark body text, 25px column
   headings in the secondary blue, and an 83px bottom bar in that same blue.
   This was the single largest remaining band difference in the visual diff.
   ========================================================================== */

.footer-inner { display: grid; }

.site-footer a { text-decoration: none; }

.site-footer .hours-table { width: 100%; border-collapse: collapse; font-size: 16px; }
.site-footer .hours-table td { padding: 0.2rem 0; }

/* The bottom bar: measured 83px, rgb(1,88,138). */
.footer-bar > * { max-width: var(--grid-max-width); }
.footer-bar a { text-decoration: none; }
.footer-bar a:hover { text-decoration: underline; }

.powered-by { display: inline-flex; align-items: center; gap: var(--space-3xs); }
/* Size only — never `display`, or it overrides the shared light/dark logo toggle. */
.powered-by img { width: 120px; height: auto; }

/* ==========================================================================
   Benefit cards — measured: 318x405, 50px padding, SQUARE corners, centred,
   40px icon, one tinted ground per card (the practice kit's accents).
   ========================================================================== */
.benefit {
    /*
     * Content centres in whatever height the row settles at. The cards are
     * stretched to a common min-height and the copy varies in length, so
     * packing to the top left 85-116px of dead space under the text -- uneven
     * card to card, and heavier at the bottom than the 120px above. Centring
     * makes the leftover space split evenly instead of all falling below.
     */
    padding: var(--space-l) var(--space-m);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-s);
    text-align: center;
    border-radius: 0;          /* live cards are square, not rounded */
}

.benefit__icon { display: block; width: 40px; height: 40px; flex-shrink: 0; color: var(--tint-ink, currentColor); }
.benefit__title {
    font-family: var(--font-heading);
    font-size: 25px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--ink-body);
    margin: 0 0 var(--space-2xs);
    text-align: center;
}
.benefit p {
    margin: 0;
    font-size: 16px;
    line-height: 1.6;
    color: var(--ink-body);
    text-align: center;
}

/* The live About band centres its intro above the two-column split. */
.about__body { text-align: left; }

/* ==========================================================================
   MEASURED BAND GEOMETRY — restored after a bad splice; do not remove.
   --------------------------------------------------------------------------
   Every value read off the live page at 1440px. The v2 `.section` clamp
   (40/80px) is nothing like the original's asymmetric padding, and using it
   made every band roughly half its live height.

     hero      250/100      benefits  120/0      about     0/220
     policies  130 all      services  120/1200   faq       0/200
     cta       120/0        contact   150/120
   ========================================================================== */
@media (min-width: 68.75rem) {
    /* The .section padding is zeroed on bands whose CONTAINER carries
     * --band-pad instead; otherwise the two stack and the band contributes
     * ~160px to each gap rather than one --band-pad. The inner-page bands
     * (.commitments, .twoup, .policy-grid-band, .hcc) were missing from this
     * list, which is why the-practice ran 245-355px between sections against
     * the homepage's ~200. */
    .benefits, .about, .policies, .services, .faq, .cta, .contact-info,
    .commitments, .twoup, .policy-grid-band, .hcc { padding-block: 0; }

    /* The live band does carry padding-bottom:1200px, but that space is NOT
       empty — the FAQ starts INSIDE it (y=5677 against a band running to 6677,
       a 1000px overlap). Reproducing the padding without the overlap turned it
       into a viewport of dead space, so the tail is normal here and the
       overlap is expressed on the FAQ band instead. */
    /*
     * Band padding, as ONE value rather than the transcribed live pairs.
     *
     * Those were 120/200, 0/200, 120/0, 150/120 and 90/220, which stack into
     * band-to-band gaps of 230, 264, 326, 332, 342 and 426px on one page --
     * no pattern, which is what reads as "some sections too close, some too
     * far". --band-pad is half a gap, so two adjacent bands always sum to
     * one consistent step. Bands that deliberately run flush to a neighbour
     * (the FAQ overlapping services, the CTA card riding up) still say so
     * below.
     */
    .services     > .u-container,
    .faq          > .u-container,
    .cta          > .u-container,
    /* These two were zeroed with the group above but never given the band
     * padding back, so the Healthcare Complaints heading sat flush against
     * the policy cards -- a 0px gap where every other section here has 82.5. */
    .policy-grid-band > .u-container,
    .hcc          > .u-container,
    .contact-info > .u-container { padding-block: var(--band-pad); }

    /* Content geometry, also measured. */
    .benefits__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 30px; }
    /* 405px was measured off live, but our copy is shorter than theirs and it
     * forced ~100px of empty card under every block of text. Let the row size
     * to its tallest card, with a floor well below what the content needs. */
    .benefit { min-height: 320px; }

    /* 50px between the rows and columns, matching .services--detail on the
     * services page, which already used 50. Was 30, which pulled the second
     * row up against the first. */
    .services__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 50px; }
    .service-card { min-height: 532px; }
    .service-card__media { flex-shrink: 0; }
    .service-card__body { display: flex; flex-direction: column; gap: var(--space-2xs); padding: var(--space-m); }

    /*
     * Measured: a 700px pale-blue panel flush to the LEFT EDGE (x=0) running
     * the band's full 1023px height with 130px padding, and plain ground to
     * its right. Not a centred card — and the photograph that appears beside
     * it in a screenshot belongs to the services band below, not this one.
     */
    /* Full bleed: the band runs edge to edge. Only the 50px down the middle
     * is space -- see .policies__split for how the two halves are sized. */
    .policies > .u-container { padding-block: var(--band-pad); max-width: none; margin-inline: 0; padding-inline: 0; }
    .policies__panel {
        background: rgb(221, 245, 248);
        padding: 130px;
        border-radius: 0;
        display: flex; flex-direction: column; gap: 35px;
        grid-row: 1;
    }
    .policies__panel > * { margin: 0; }
    .policies__body { display: flex; flex-direction: column; gap: 35px; padding: 130px 40px; }

    /* Two even halves. Was a pair of fixed 660px tracks measured off live's
     * 1360 container; at the wider container that left the pair floating in
     * the middle with a stray inset, so they share the width instead. */
    .faq__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; }
    .faq__body { padding-left: 30px; display: flex; flex-direction: column; gap: 35px; }

    .cta__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0; }
    .cta__body { padding: 90px; display: flex; flex-direction: column; gap: 35px; }
    /* (The two standfirst lines used a negative margin here to undo the
     * band's flex gap. The --lede-gap token and the flow correction below
     * handle it now, for every band rather than this one.) */
    .cta__body > * { margin: 0; }

}

/* ==========================================================================
   Service cards — MEASURED: tinted, square, with a circular icon badge
   --------------------------------------------------------------------------
   Live: 430x532 (top row) / 430x569 (bottom row), square corners, a 296px
   photo, and a 100px circular icon badge straddling the photo's lower edge.
   Each card carries its own tint from the practice kit's accent set, with the
   badge in a matching darker tone. Text is centred.
   ========================================================================== */
.service-card__media { height: 296px; border-radius: 0; }
.service-card__media img { width: 100%; height: 100%; object-fit: cover; }

.service-card__body { padding: 70px var(--space-m) var(--space-l); text-align: center; }
.service-card__body .service-card__title {
    font-family: var(--font-heading);
    font-size: 25px;
    font-weight: 700;
    color: var(--ink-body);
    margin: 0 0 var(--space-2xs);
    text-align: center;
}
.service-card__body p {
    margin: 0;
    font-size: 16px;
    line-height: 1.6;
    color: var(--ink-body);
    text-align: center;
}

/* Hover lift removed: the live cards are static. */
.service-card:hover { transform: none; box-shadow: none; }

/* ==========================================================================
   Below 900px — the measured geometry above is all min-width:56.25rem, so
   without this block every narrow viewport falls back to template defaults.
   --------------------------------------------------------------------------
   Measured band heights at 768px, live vs the fallback we were producing:
     services  4952 vs 2086     about  1488 vs 1119     total 14009 vs 8048

   The live site stacks to a single column and keeps its generous padding,
   which is why it grows taller rather than shorter on a narrow screen. The
   template's instinct is the opposite — collapse padding and keep columns —
   and that is what produced a page 43% short.
   ========================================================================== */
@media (max-width: 68.74rem) {
    .policies     > .u-container { padding-block: 0; }
    .services     > .u-container,
    .faq          > .u-container,
    .cta          > .u-container,
    .contact-info > .u-container { padding-block: var(--band-pad); }

    /* One column, full-width cards, padding retained. */
    .benefits__grid,
    .services__grid,
    .contact-info__grid { grid-template-columns: minmax(0, 1fr); gap: 30px; }

    .benefit { min-height: 0; padding: 40px; }
    .service-card { min-height: 0; }

    .about__grid,
    .faq__grid,
    .cta__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }

    .about__body,
    .faq__body { padding-left: 0; }

    .cta__body { padding: 40px; gap: 28px; }

    /* The policies panel goes full-width and keeps its tint. */
    /* Edge to edge, as .access, .feeinfo and .related already are at this
     * width. The desktop rule zeroes this inline padding but lives in a
     * min-width query, so on a phone the band kept the container's gutter and
     * was the only full-bleed section on the site sitting 19px in from both
     * edges. The panel's own 40px keeps the copy off the screen edge. */
    .policies > .u-container,
    .policies__split { padding-inline: 0; max-width: none; margin-inline: 0; }
    .policies__split { grid-template-columns: minmax(0, 1fr); }
    .policies__panel { padding: 60px 40px; }
    .policies__body { padding: 40px 40px 60px; }

}

/* ==========================================================================
   The band heading pattern — MEASURED, and shared by every band
   --------------------------------------------------------------------------
   Every content band on the live site opens the same way:

     eyebrow   21px Barlow 600, UPPERCASE, ink   (an <h2>/<h3> — the tag
               carries the outline, the class carries the appearance)
     title     58px Barlow 700, secondary blue   (a <div> on the original)
     lede      19px Asap 400, ink

   I previously rendered these as a small `.eyebrow` plus a 21px <h2>, which
   is why every section read as "formatting and copy totally different".
   ========================================================================== */
/*
 * THE EYEBROW GAP IS A SITE-WIDE STANDARD -- --eyebrow-gap, set on :root.
 *
 * It was not one before: the same eyebrow-to-heading step measured 10, 10,
 * 35, 10, 15, 45 and 15 across the seven bands of the homepage, because each
 * band had picked up its own spacing from a flex gap, a margin, or both
 * stacked. An eyebrow is a label ON its heading, so it should sit the same
 * distance from it everywhere; use the token rather than a local margin.
 */
.band-eyebrow {
    font-family: var(--font-heading);
    font-size: 21px; font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-l);
    color: var(--ink-body);
    margin: 0 0 var(--eyebrow-gap);
}

.band-title {
    font-family: var(--font-heading);
    font-size: var(--title-xl);
    font-weight: 700; line-height: 1.15;
    color: var(--brand-blue);
    margin: 0 0 var(--title-gap);
}

.band-lede { font-size: 19px; line-height: 1.6; color: var(--ink-body); margin: 0 0 var(--lede-gap); }
/* Consecutive paragraphs are one thought continuing, so they sit closer to
 * each other than the heading sits to the first of them. */
.band-lede + .band-lede { margin-top: 0; }

/* A button, a ticklist or an actions row is a different KIND of thing from
 * the copy above it, so it needs more air than one paragraph gives another.
 * Without this the CTA button sat hard against the last line of its lede. */
.band-lede + .btn,
.band-lede + .ticklist,
.band-lede + .cta__actions,
.band-lede + p > .btn,
.band-lede + p:last-child { margin-top: var(--copy-gap); }
.cta__actions, .band-lede + .hero__actions { margin-top: var(--copy-gap); }

/* ==========================================================================
   Buttons — MEASURED: SQUARE, uppercase, 16px/45px, 60px tall
   --------------------------------------------------------------------------
   The live buttons have border-radius 0. Rounded corners were the single most
   visible difference across the whole page.
   ========================================================================== */
.btn, .booking-button {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    font-family: var(--font-body);
    line-height: 1.3;
    text-decoration: none;
    white-space: nowrap;
    border: var(--border-medium) solid transparent;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
    border-radius: 0;
    padding: 16px 45px;
    font-size: 19px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--tracking-l);
    min-height: 60px;
    justify-content: center;
}

.nav-cta { border-radius: 0; text-transform: uppercase; }

/* Outlined variant — 1px, as measured on "view practice policies". */
.btn--outline { border-color: var(--brand-teal); background: transparent; color: var(--brand-teal); border: 1px solid var(--brand-teal); }
.btn--outline:hover { background: var(--brand-teal); color: #fff; }

/* "CALL ..." is a borderless uppercase text link on the live site. */
.btn--text {
    background: transparent;
    border: 0;
    color: var(--brand-teal);
    padding: 16px 45px 16px 0;
}
.btn--text:hover { color: var(--brand-blue); background: transparent; }

/* ==========================================================================
   About — images LEFT, copy RIGHT (measured x=40 / x=805)
   ========================================================================== */
@media (min-width: 68.75rem) {
    /* Equal halves, so the images' right edge lands at 725 -- exactly where
     * the policies panel below it ends. At 1.2fr:1fr the media finished at
     * 769, 44px past that panel edge, and the two bands read as misaligned.
     * The 50px gap is the same one the full-bleed bands use down the middle. */
    .about__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 50px; }
    .about__body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    order: 1;
    padding-left: 0;
}

}

/* ==========================================================================
   Policies — a single panel; all copy inside it
   ========================================================================== */
.policies__panel > * { margin-block: 0; }
.policy-item { padding-block: var(--space-s); border-bottom: var(--border-thin) solid var(--divider); border: 0; padding: 0; }
.policy-item__title {
    display: block;
    font-family: var(--font-heading);
    font-size: 25px; font-weight: 700;
    color: var(--ink-body);
    margin-bottom: var(--space-2xs);
}

/* ==========================================================================
   FAQ — 18px w800 questions, the open one in teal
   ========================================================================== */
/* 20px: these are the questions, the main thing to read in the band, and 18px
 * sat below the 19px band copy beside them. */
.faq__item summary { font-size: 20px; font-weight: 800; }
.faq__item[open] summary { color: var(--brand-teal); }
/* Hovering a question previews the open state: the text takes the teal and
 * the +/- follows it, so the whole row responds rather than just the cursor
 * changing. Focus included, so keyboard users get the same signal. */
.faq__item summary:hover,
.faq__item summary:focus-visible { color: var(--brand-teal); }
.faq__item summary:hover .faq__mark::before,
.faq__item summary:hover .faq__mark::after,
.faq__item summary:focus-visible .faq__mark::before,
.faq__item summary:focus-visible .faq__mark::after { background: var(--brand-teal); }
.faq__item summary { transition: color var(--duration-fast) var(--ease-out); }

/* ==========================================================================
   CTA — the left column sits on the page ground, not a tint
   ========================================================================== */
/* Same as .policies__panel: the flex column and its gap at every width, so
 * the band-heading calcs that subtract --band-flow-gap have the gap they
 * assume. Declared only for desktop before, so the eyebrow overlapped its
 * title by 23px on phone and tablet. */
.cta__body {
    background: transparent;
    display: flex;
    flex-direction: column;
    gap: var(--band-flow-gap, 35px);
}
.cta__body > * { margin-block: 0; }

/* ==========================================================================
   About media — MEASURED: two 318x318 photos STACKED at x=40
   --------------------------------------------------------------------------
   y=1782 and y=2175, both at x=40, 393px apart: a vertical stack, not the
   overlapping inset the v2 template offers and not a side-by-side pair.
   ========================================================================== */

/* ==========================================================================
   ROUND 2 CORRECTIONS — all measured off the live page
   ========================================================================== */

/* --- Links carry no underline anywhere on the live site ------------------ */
a { text-decoration: none; }
a:hover { text-decoration: none; }

/* --- About / trust bar: no band-level wash ------------------------------
 * These bands used to carry their own radial gradients, pulled up with a
 * negative margin so each arrived already faded. Every band doing that is
 * still N paint surfaces with N-1 joins to get wrong, and each join was
 * found and fixed one at a time. The wash is now painted ONCE per page on
 * <body> -- see PAGE WASH at the end of this file -- so there is one surface
 * and no join. The bands here are transparent and simply sit on it. */

/* Clearance above the full-bleed band that follows. The grid's own 82.5px
 * left the photos 87px off it, where the reference has ~158: a full-bleed
 * band runs edge to edge, so it needs more air above it than a bounded
 * section does or the photos read as sitting on top of it. */
@media (min-width: 68.75rem) {
    .about:has(+ .policies) > .u-container { padding-bottom: 158px; }
}


.about__media-tower {
    background-image: url("/assets/images/qv-tower.webp");
    background-size: cover;
    background-position: 50% 50%;
    min-height: 320px;
}

/* Feature icons — measured 30x30 in rgb(38,169,186). */

/* --- Policies: the title is 58px like every other band ------------------- */
.policies__panel .band-title { font-size: var(--title-xl); }

/* --- Services + FAQ: no wrapper wash --------------------------------------
 * The .wash--services-faq wrapper in index.html stays (harmless), but it no
 * longer paints. Its four gradients and the opaque ground they sat on moved
 * to the page canvas (PAGE WASH, end of file); the ground on the wrapper was
 * what produced the rectangle edge at its top, and clipping it off the
 * overhang turned out to be a fight with background-clip's layer truncation
 * that the canvas simply does not have. */

/* --- FAQ: an explicit +/- marker per question ---------------------------- */
.faq__item summary::after { content: none; }        /* replaced by .faq__mark */

.faq__mark {
    position: relative;
    flex-shrink: 0;
    /* Sized to the question text's own glyph box, measured at 21px for the
     * 18px/27px summary. 18px square sits level with the text rather than
     * overhanging it, and the 4px strokes keep the weight. */
    width: 18px; height: 18px;
    margin-right: var(--space-2xs);
}
.faq__mark::before,
.faq__mark::after {
    content: "";
    position: absolute;
    background: var(--brand-teal);
    transition: opacity var(--duration-fast) var(--ease-out);
}
/* 4px strokes. 2px was the original and 2.5px was indistinguishable from it;
 * the mark needs real weight to sit against 18px bold question text rather
 * than looking like a hairline. Centred: (18 - 4) / 2 = 7. */
.faq__mark::before { left: 0; top: 7px; width: 18px; height: 4px; }   /* the bar */
.faq__mark::after  { left: 7px; top: 0; width: 4px; height: 18px; }   /* the stem */
/* Open: the stem disappears, leaving a minus. */
.faq__item[open] .faq__mark::after { opacity: 0; }

/* --- CTA: a light blue card, offset down from the photo ------------------ */
.cta__card { background: rgb(203, 241, 252); --tint-ink: var(--tint-sky-ink); }

@media (min-width: 68.75rem) {
    .cta__grid { align-items: start; }
    .cta__card { margin-top: 90px; }     /* the measured downward offset */
    /* The photo was 533 tall beside a 976px card, so it stopped level with
     * the card's midpoint and left the right half of the band empty below
     * it. Taller, and it reads as a pair with the card rather than a
     * thumbnail parked at the top. */
    /*
     * Square less 50px off the bottom. Written as a height rather than a
     * ratio so the trim stays 50px at every width -- a ratio would scale the
     * crop with the column. object-fit: cover on the child means this crops
     * the photograph rather than squashing it.
     */
    /*
     * Square, then 50px off the bottom.
     *
     * The height has to derive from the frame's own WIDTH, so a plain
     * `calc(100% - 50px)` is wrong -- percentages resolve against the parent's
     * height, which is not the square, and that trimmed 30px rather than 50.
     * The parent becomes an inline-size container and the frame sizes from
     * 100cqw, which IS its width. object-fit: cover on the child crops the
     * photograph rather than squashing it.
     */
    .cta__media { container-type: inline-size; }
    .cta__media .media-frame {
        aspect-ratio: auto;
        height: calc(100cqw - 50px);
    }
    .cta__media img { width: 100%; height: 100%; object-fit: cover; }
}

/* --- Contact band: no band-level wash (see PAGE WASH, end of file) ------ */

/* --- Every ticklist item gets its check, including inline rows ----------- */
.ticklist--inline li { margin-bottom: 0; padding-left: 1.75rem; }
.ticklist--inline li::before { display: block; }

/* ==========================================================================
   ROUND 3 — contact cards, FAQ alignment, image radius, band spacing
   ========================================================================== */

/* --- FAQ: questions are LEFT aligned; the marker sits beside the text ---- *
 * `justify-content: space-between` pushed the question to the right edge and
 * left a large gap after the +/- marker. The live layout is a simple row.
 */
.faq__item summary { justify-content: flex-start; gap: var(--space-2xs); }

/*
 * FAQ: the gap BETWEEN items -- the one the arrows in the screenshot mark.
 *
 * Measured 50px from each divider to the next question: the .faq__body flex
 * gap (35px) plus the item's own 15px top padding, stacked. For a list of
 * one-line questions that is a lot of air, and it made the rules read as
 * belonging to the item above rather than separating a pair. The items lose
 * the stacked flex gap and set their own even padding instead, so the text
 * sits the same distance from the rule above and below it.
 */
.faq__body { row-gap: 0; }
.faq__item { padding-block: var(--space-xs); }
/* row-gap:0 also closed up the heading pair, which does want breathing room
 * between the eyebrow, the title and the start of the list. */
.faq__body .band-title { margin-bottom: var(--space-s); }
/*
 * A deliberate break in a heading is only right while the column is wide
 * enough to have been the one choosing the wrap. Below the two-column fold
 * the band is narrow and the heading should break on its own, so the <br>
 * collapses to nothing and the surrounding spaces do the work.
 */
.title-break { display: none; }
@media (min-width: 68.75rem) { .title-break { display: inline; } }

/*
 * Bands that lay their children out with a flex gap add that gap on top of
 * the eyebrow's own margin, which is where the 35px and 45px in the audit
 * came from. Each such band declares the gap it uses, and the eyebrow
 * subtracts it, so the rendered distance is --eyebrow-gap everywhere.
 */
.policies__panel  { --band-flow-gap: 35px; }
.cta__body        { --band-flow-gap: 35px; }
.contact-info__intro { --band-flow-gap: var(--space-xs); }
.faq__body        { --band-flow-gap: 0px; }

.policies__panel .band-eyebrow,
.cta__body .band-eyebrow,
.contact-info__intro .band-eyebrow,
.faq__body .band-eyebrow {
    margin-bottom: calc(var(--eyebrow-gap) - var(--band-flow-gap, 0px));
}
/* The same correction for the rest of the rhythm, so a band laid out with a
 * flex gap renders the same title->lede and lede->lede distances as one laid
 * out in normal flow. Without it these bands were 65px and 25px where the
 * tokens ask for 24px and 16px. */
.policies__panel .band-title,
.cta__body .band-title,
.contact-info__intro .band-title,
.faq__body .band-title {
    margin-bottom: calc(var(--title-gap) - var(--band-flow-gap, 0px));
}
.policies__panel .band-lede,
.cta__body .band-lede,
.contact-info__intro .band-lede,
.faq__body .band-lede {
    margin-bottom: calc(var(--lede-gap) - var(--band-flow-gap, 0px));
}

/*
 * And the gap above the first question.
 *
 * Measured in the browser at 65px -- the .faq__body flex gap (35px) plus the
 * item's own 15px top padding, stacked under the band title. The gap between
 * items is right; it is only the leading one that reads as a void, so the
 * first item loses its top padding and the run starts tighter to the title.
 */
.faq__body .faq__item:first-of-type { padding-top: 0; margin-top: 0; }

/* --- Contact: copy left, a 2x2 grid of tinted cards right --------------- */

@media (min-width: 68.75rem) {
    .contact-info__intro { display: flex; flex-direction: column; gap: var(--space-xs); align-items: flex-start; }
    .contact-info__intro > * { margin: 0; }
}

/* Centred: icon, heading and details share one axis down the middle of the
 * card. align-items centres the boxes (the icon and the hours table, which
 * are not full-width), text-align centres the copy inside them. */
.contact-card {
    /* 50px all round left a deep void under the content: grid stretches every
     * card to the tallest in the row, and packing to the top put all of that
     * slack at the bottom. Tighter block padding, and the content centres in
     * whatever height the card ends up with. */
    padding: var(--space-l) var(--space-m);
    border-radius: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: var(--space-3xs);
}

.contact-card__icon { width: 40px; height: 40px; margin-bottom: var(--space-2xs); color: var(--tint-ink, currentColor); }

.contact-card__title {
    display: block;
    font-family: var(--font-heading);
    font-size: 25px; font-weight: 700;
    color: var(--ink-body);
    /* Was inheriting a 40px heading margin-top, which stacked on the icon's
     * own 10px for 56px between them; the reference has 40. The icon's inline
     * SVG box also contributes a few px below it, so 20 here -- not 30 --
     * lands the measured gap on 40. */
    margin: 20px 0 20px;
}

/* These were pinned at 16px, the bottom of the body range, which read as
 * small print next to the 25px card titles. --step-0 is the site's body size
 * (16px on a phone, 18px on a desktop), which is what these details are. */
.contact-card p, .contact-card address {
    margin-bottom: var(--space-2xs);
    font-size: var(--step-0);
    line-height: var(--leading-m);
    font-weight: 500;
    margin: 0;
    color: var(--ink-body);
}
.contact-card address { font-style: normal; }
/*
 * Opening hours as centred LINES, not a two-column table.
 *
 * Rails emits a <table> of day/time pairs. Left as table-cells the two
 * columns align against each other, so "Mon-Thu  8:15am-5:30pm" reads as two
 * separate stacks with a gutter down the middle -- which is the line through
 * the hours, and why they looked off-centre in a card where everything else
 * is centred. Making the cells inline puts each row on one line that centres
 * as a whole.
 */
.contact-card .hours-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--step-0);
    font-weight: 500;
}
.contact-card .hours-table tbody { display: block; }
.contact-card .hours-table tr { display: block; text-align: center; }
.contact-card .hours-table td {
    display: inline;
    padding: 0;
    border: 0;
    text-align: center;
}
.contact-card .hours-table td:first-child::after { content: "\00a0\00a0"; }
/* Rails ships the table with a 30px bottom margin, which opened a 36px gap
 * before the note. The card sets its own rhythm via gap instead. */
.contact-card .hours-table { margin: 0; }

/* Measured tints, matching the live cards. */

/* ==========================================================================
   ROUND 4 — the policies photograph, and real space above the About band
   ========================================================================== */

/*
 * Policies: a 690x1043 photograph filling the right half. Measured y=2639
 * against a band top of 2759, so it starts 120px ABOVE the band and overlaps
 * the section above — hence the negative margin rather than plain alignment.
 */
/*
 * These side panels are half-width by design and every source is landscape
 * (~1.5), so `cover` always discards width -- here about half of it. That is
 * the layout, not a bug, but the CROP POSITION decides whether the subjects
 * survive it. Each of these is set from the photograph rather than left at
 * the 50% default:
 *
 *   policies       75% -- the centre cut the second clinician out of frame
 *   patient-care   80% -- an empty consulting room; the centre showed a desk
 *                         chair, the right shows the examination bed
 *
 * Checked against the source by rendering the candidate positions.
 */
.policies__media {
    background-image: url("/assets/images/policies-consult.webp");
    background-size: cover;
    background-position: 75% 50%;
    min-height: 320px;
}

@media (min-width: 68.75rem) {
    /*
     * The panel flexes, the photograph keeps its measured width, and the gap
     * between them is a real 50px rather than whatever is left over.
     *
     * This was two fixed tracks (700 + 690 = 1390) with justify-content:
     * space-between. The band is full-bleed on desktop, so that leftover is
     * measured against the VIEWPORT, not the container: it lands at exactly
     * 50px only at the 1440 it was transcribed at, and stretches everywhere
     * else -- 110px at 1500, 530px on a 1920 monitor -- while below 1390 the
     * two tracks overflowed the page instead. A 1fr panel has neither
     * failure mode.
     */
    /* The panel takes the slack up to a point, then stops: past ~1600 an
     * unbounded 1fr made it a 1180px field of cyan holding a 545px column of
     * text, which is the imbalance you see rather than any padding error.
     * Capped, the two halves stay in proportion and the band centres. */
    .policies__split {
        gap: 50px;
        /*
         * Full bleed, split down the middle: the band runs edge to edge and
         * the two halves are EQUAL, so the panel reaches the left edge and the
         * photograph the right by the same distance. The only space in the
         * band is the 50px between them.
         *
         * They were 1fr and a 690px cap, which is not a half each: at 1920
         * that gave the panel 1180 against the photo's 690, so one side ran
         * visibly further than the other.
         */
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }
    /* Padding scales with the panel rather than staying at the measured
     * 130px, which is only right when the panel is near full width. */
    .policies__panel { padding: clamp(2.5rem, 8.5vw, 130px); }
    /*
     * The panel is flush to the viewport edge and now flexes, so the copy is
     * capped at a readable measure. Centre the capped block rather than
     * letting it sit left: the panel's own padding is an even 130px, but a
     * left-aligned cap put 130px on the left and 314px on the right at 1920,
     * so the copy was not centred in its own panel.
     *
     * align-items on the flex column, not margin-inline on the children,
     * because the children are the ones carrying the cap.
     */
    .policies__panel {
        align-items: center;
        /* The panel is stretched to the photograph's 1043px, but the copy is
         * only ~756px of that, so packing to the top left ~270px of dead
         * space under the button and the block read as sitting high. Centre
         * it in the space it actually has. */
        justify-content: center;
    }
    .policies__panel > * { width: 100%; }
    /* The eyebrow's 10px top margin is what made the optical padding 166px at
     * the top against 130px at the bottom. The panel's own padding sets the
     * inset; the first child should not add to it. */
    .policies__panel > :first-child { margin-top: 0; }
    .policies__media { height: 1043px; margin-top: -120px; }
}

/*
 * Space above the About band. The live band carries no top padding of its own
 * — the gap comes from the BENEFITS band above it, whose inner padding is
 * 120px top and 0 bottom, and which then leaves its own trailing space. Ours
 * had benefits at `120px 0` too, so the two bands butted together with no
 * breathing room at all. Give the benefits band its bottom padding back.
 */
@media (min-width: 68.75rem) {
    .benefits > .u-container { padding-block: var(--band-pad); }
}

@media (max-width: 68.74rem) {
    .benefits > .u-container { padding-block: var(--band-pad); }
    .policies__media { height: 340px; margin-top: 0; }
}

/* ==========================================================================
   THE PRACTICE (about) — measured from /the-practice/
   --------------------------------------------------------------------------
   Band inner padding, live: hero 0/40 (full-bleed photo), about 0/40,
   patient-care 130 all round, commitments 120/0, two-up 120/0,
   feedback 250/120.
   ========================================================================== */
.hero--about {
    background-image: url("/assets/images/about-hero-consult.webp");
    min-height: 0;
}
.hero--about .hero__inner { padding-block: 220px 160px; }

/* Title variants — the live page uses 58px, 49px and 41px on different bands,
   and two of them sit in ink rather than the secondary blue. */
.band-title--md { font-size: var(--title-lg); }
.band-title--ink { color: var(--ink-body); }

.accreditation-mark { display: block; width: 336px; max-width: 100%; height: auto; margin-bottom: var(--space-s); }

/* The patient-care photograph sits in the same slot as the homepage's. */
/* Measured: the patient-care band uses new-website-consulting-room, not the
   homepage's policies photograph. */
.policies__media--care {
    background-image: url("/assets/images/patient-care-consult.webp");
    background-position: 80% 50%;   /* the examination bed, not the desk chair */
}

/* --- Practice commitments ------------------------------------------------ */
.commitments {
    background-image:
        radial-gradient(950px 720px at 22% 48%, rgba(214, 236, 190, 0.28), rgba(0, 0, 0, 0) 72%);
}
/*
 * The photograph fills the column to the same height as the cards beside it.
 *
 * The source is landscape (1400x933). The original frame was 491x751 -- so
 * narrow that `cover` kept only the middle 43% and sliced the doctor at one
 * edge and the child at the other. Widening the column to 620px is what makes
 * a matching height workable: the frame ratio is now gentle enough that the
 * crop keeps 61% of the width, which holds all three people intact (checked
 * against the source).
 *
 * align-items: stretch on the grid does the height matching; there is no
 * fixed pixel height here, so it tracks the cards at any width.
 */
.commitments__media {
    background-image: url("/assets/images/commitments-paediatric.webp");
    background-size: cover; background-position: 50% 50%;
    /* The ratio is the BASE so the div always has a height -- it is a
     * background, not an <img>, so without one it collapses to zero wherever
     * the grid is stacked. The two-column breakpoint overrides it with
     * `height: 100%` to match the cards. */
    aspect-ratio: 1400 / 933;
    min-height: 0;
}
.commitment__title {
    display: block;
    font-family: var(--font-heading);
    font-size: 25px; font-weight: 700; color: var(--ink-body);
    margin-bottom: var(--space-2xs);
}
.commitment p { margin: 0; font-size: 16px; line-height: 1.6; color: var(--ink-body); }

/* --- Two-up (privacy / fees) --------------------------------------------- */

/* An icon inside a button sits inline with its label. */
.btn__icon { width: 19px; height: 19px; flex-shrink: 0; }

@media (min-width: 68.75rem) {
    /* The photo column was 491px, sized when the image was cropped to a tall
     * portrait strip. At its true landscape ratio that made a 327px picture
     * beside a 630px block of cards. A wider column gives it presence without
     * squeezing the two card columns. */
    .commitments__grid { grid-template-columns: minmax(0, 620px) minmax(0, 1fr); gap: 50px; align-items: stretch; }
    /* (the stretch is set at 78rem, where the grid actually becomes two
       columns -- see .commitments__media below the stacked rule) */
    /* Column flow, as the reference has it: the cards read DOWN the left
     * column (Safety, Confidentiality, Ethics) and then down the right
     * (Communication, Professional Conduct), rather than across the rows.
     * Three explicit rows so the five cards split 3 + 2. The markup order is
     * unchanged, which is what a screen reader and the stacked phone layout
     * follow. */
    .commitments__list {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-rows: repeat(3, min-content);
        grid-auto-flow: column;
        gap: var(--space-l);
        align-items: start;
    }
    .commitments > .u-container,
    .twoup > .u-container,
    .policies--care > .u-container { padding-block: var(--band-pad); }
}

@media (max-width: 68.74rem) {
    .hero--about .hero__inner { padding-block: var(--space-xl) var(--space-l); }
    .commitments__media { height: auto; }
    .commitments > .u-container,
    .twoup > .u-container { padding-block: var(--band-pad); }
}

/* --- Two-up cards: measured 407px, 40px padding, square, offset ---------- *
 * Blue at x=40 y=4707, pink at x=993 y=4757 — the second sits 50px lower.
 * The photograph that appears between them in a screenshot belongs to the
 * commitments band above (hit-tested: only spacers live between the cards).
 */
.twoup__col {
    padding: 40px;
    border-radius: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
.twoup__col > * { margin-block: 0 var(--space-s); }
/* The blanket `> *` above also overrides the rhythm; point each at its token
 * so these columns match every other band. */
.twoup__col > .band-eyebrow { margin-bottom: var(--eyebrow-gap); }
.twoup__col > .band-title   { margin-bottom: var(--title-gap); }
.twoup__col > .band-lede    { margin-bottom: var(--lede-gap); }
.twoup__col > :last-child { margin-bottom: 0; }

.twoup__col:first-child { background: rgb(204, 227, 245); }
.twoup__col:last-child  { background: rgb(238, 211, 223); }

@media (min-width: 68.75rem) {
    .twoup__col:last-child { margin-top: 50px; }
}

/* ==========================================================================
   ROUND 5 — FAQ overlap, contact card alignment, collapsed service cards
   ========================================================================== */

/*
 * Contact: the four tinted cards are RIGHT aligned beside the copy, not
 * stacked under it. `.contact-info > .u-container { display: block }` was
 * overriding the two-column split, so the grid never applied.
 */
@media (min-width: 68.75rem) {
    .contact-info__split {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
        gap: 40px;
        align-items: start;
    }
    /* The four cards are a 2x2 block, right aligned beside the copy. */
    .contact-info__grid { grid-template-columns: repeat(2, minmax(0, 325px)); gap: 30px; justify-self: end; }
}

/* ==========================================================================
   Collapsed service cards (below the 3-column breakpoint)
   --------------------------------------------------------------------------
   Three faults when the grid collapses:
     - the photo kept its 296px desktop height against a much wider card, so
       the crop read as a letterbox strip;
     - the icon badge stayed pinned to `top: 296px`, so it drifted off the
       photo's edge instead of straddling it;
     - the body kept 70px of top padding meant to clear a badge that is no
       longer there.
   The badge is positioned from the MEDIA's own bottom edge instead, so it
   straddles the boundary at every width.
   ========================================================================== */
.service-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: transform var(--duration-base) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out);
    background: #fff;
    border: 0;
    border-radius: 0;
    text-align: center;
    position: relative;
}

/*
 * The badge must straddle the MEDIA's lower edge at every width. It is placed
 * inside the media box so `bottom: -50px` (half its 100px height) resolves
 * against the photo rather than the whole card — with it as a sibling, the
 * nearest positioned ancestor was .service-card and the badge sank to the
 * card's foot, landing on the body text.
 */
.service-card__media { position: relative; overflow: visible; }

/*
 * Services step down 3 -> 2 -> 1 rather than collapsing straight to a single
 * column: at tablet widths a full-width card gives the photo an extremely wide
 * crop and leaves the copy stranded on one short line.
 */
@media (max-width: 68.74rem) and (min-width: 40rem) {
    .services__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 68.74rem) {
    /* A 16:9 crop reads correctly on a wider card; a fixed 296px does not. */
    .service-card__media { height: auto; aspect-ratio: 16 / 9; }
    .service-card__body { padding-top: 70px; }
}

@media (max-width: 39.99rem) {
    .services__grid { grid-template-columns: minmax(0, 1fr); }
    /* A single-column card is wide, so a shallower crop keeps the subject. */
    .service-card__media { aspect-ratio: 3 / 2; }
}

/* ==========================================================================
   THE PRACTICE — accreditation card, commitments cards, mid-band photo
   ========================================================================== */

/*
 * Accreditation: a full-width white card overlapping the hero. Measured
 * 1360x193 at y=1095 against a hero photo ending at 1181 — an ~86px overlap,
 * spanning the content width rather than sitting in a column.
 */
.accred-band { position: relative; z-index: var(--layer-raised); }

.accred-card {
    background: #fff;
    padding: 50px;
    border-radius: 0;
    box-shadow: rgba(0, 0, 0, 0.04) 0 1px 20px 0;
    display: grid;
    gap: var(--space-m);
    align-items: center;
}

.accred-card__mark { width: 336px; max-width: 100%; height: auto; }
.accred-card__copy p { margin: 0 0 var(--space-2xs); font-size: 16px; line-height: 1.6; color: var(--ink-body); }
.accred-card__copy p:last-child { margin-bottom: 0; }

@media (min-width: 68.75rem) {
    .accred-card { grid-template-columns: minmax(0, 336px) minmax(0, 1fr); gap: 50px; }
    .accred-band { margin-top: -86px; }   /* the measured overlap into the hero */
    /* The About band then needs its own space below the card. */
    .about > .u-container { padding-block: var(--band-pad); }
}

@media (max-width: 68.74rem) {
    .accred-card { padding: 30px; }
    .accred-band { margin-top: -40px; }
    .about > .u-container { padding-block: var(--band-pad); }
}

/* --- Commitments: header spans the full width, items are tinted cards ----- */
.commitments__head { max-width: 46rem; margin-bottom: var(--space-l); }
.commitments__head > * { margin-block: 0 var(--space-2xs); }
.commitments__head > .band-eyebrow { margin-bottom: var(--eyebrow-gap); }
.commitments__head > .band-title   { margin-bottom: var(--title-gap); }
.commitments__head > .band-lede    { margin-bottom: var(--lede-gap); }

.commitment {
    padding: 20px;
    border-radius: 0;
}

/* --- The photograph between the privacy and fees cards ------------------- */
.twoup__media {
    background-image: url("/assets/images/privacy-mid-consult.webp");
    background-size: cover;
    background-position: 50% 50%;
    min-height: 280px;
}

@media (min-width: 68.75rem) {
    /* Measured: 407px cards either side of a 407x550 photograph, each column
     * starting at its own offset. The 550px was live's photo height against
     * live's copy; ours is longer, so a fixed height left the three columns
     * finishing 146px apart. The photo now stretches to the row, keeping the
     * staggered TOPS -- which is the part that makes the arrangement -- while
     * the feet come back into line. */
    .twoup__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; align-items: stretch; }
    .twoup__media { order: 2; margin-top: 30px; margin-bottom: 30px; }
    .twoup__col:first-of-type { order: 1; }
    .twoup__col:last-of-type { order: 3; margin-top: 50px; }
}

/* ==========================================================================
   DOCTORS AND STAFF — measured from the live page at 1440
   ==========================================================================
   The doctor entries are full-width ROWS, not cards in a grid: a square
   photo at x=40 and the copy starting at x=348. That 308px gap is the photo
   (218) plus a 90px gutter, so the row is a two-column grid rather than a
   float. Nurses and reception use the same row at a larger photo, two rows
   to a line.
   -------------------------------------------------------------------- */

.staff-band { background: transparent; }   /* transparent: the page wash runs under it */
.staff-band--plain { background: transparent; }

.staff-band .band-actions { margin-top: var(--space-s); }
.band-eyebrow--center, .band-title--center, .band-lede--center,
.band-actions--center { text-align: center; }
.band-lede--center { margin-inline: auto; max-width: 76ch; }

/* One person: photo left, copy right. */
.staff {
    display: grid;
    grid-template-columns: minmax(0, 218px) minmax(0, 1fr);
    gap: 90px;
    align-items: start;
    margin-top: 56px;
}

.staff__photo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
}

.staff__role { font-size: 21px; font-weight: 400; color: var(--ink-body); }

.staff__body p { font-size: 19px; color: var(--ink-body); }
.staff__body p + p { margin-top: 14px; }

/* Nurses and reception: two people to a line, each still a photo/copy row.
   The photo is larger (249 / 223) but the column stays square. */
.staff-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 40px 55px;
}

.staff-pair .staff {
    grid-template-columns: minmax(0, 249px) minmax(0, 1fr);
    gap: 50px;
}

/* Dr Henley's farewell: the portrait row, then the book copy beside two
   further photos. */

.staff-feature__copy p { font-size: 19px; color: var(--ink-body); }
.staff-feature__copy p + p { margin-top: 14px; }

/* --- Responsive ---------------------------------------------------------
   The pair grid collapses to one person per line before the row itself
   collapses, so a photo never sits alone above its own name at a width
   where there was still room beside it. */
@media (max-width: 68.75rem) {
    .staff-pair { grid-template-columns: minmax(0, 1fr); }
    .staff, .staff-pair .staff { grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: 40px; }
    .staff--feature { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); gap: 48px; }
    .staff-feature__book { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}

@media (max-width: 48rem) {
    .staff, .staff-pair .staff, .staff--feature,
    .staff-band--split .staff-pair .staff,
    .staff-band--reception .staff-pair .staff,
    .staff-band--plain .staff {
        grid-template-columns: minmax(0, 1fr);
        gap: 20px;
    }
    .staff__photo { max-width: 240px; }
}

/* ==========================================================================
   DOCTORS AND STAFF — round 3, every value measured off the live page
   ==========================================================================

   Hero
   ----
   The four pastel cards are 340x390 at x = 40 / 380 / 720 / 1060 -- exactly
   340 apart, so they are DIRECT NEIGHBOURS with no gap. The hero photo ends
   at y=1134 and the cards sit at y=917, so they straddle its lower edge by
   217px rather than sitting below it.

   Staff cards
   -----------
   Every staff member (all 13; Dr Henley is the exception) sits on a tinted
   card 1092 wide with 40px padding, holding the name, the qualification and
   the biography together -- not a tint behind the name alone. A 1px rule in
   the brand primary runs under the qualification, the full inner width.
   The tint cycles through six colours; live breaks its own cycle once, at
   Terri, and that is cloned rather than corrected.
   -------------------------------------------------------------------- */

/* --- Hero: cards straddle the photo, touching ---------------------------- */
/* Inner-page hero: cover photo pinned to the top edge, centred copy, and the
 * measured 140px above the eyebrow. Each page adds only its own photo and
 * wash (.hero--fees etc). The homepage hero is left-aligned and keeps the
 * base rules; the-practice is left-aligned too and keeps .hero--about. */
.hero--inner { background-size: cover; background-position: 50% 0%; }
.hero--inner .hero__inner {
    text-align: center;
    padding-top: calc(var(--header-h) + var(--space-3xl) + var(--space-s));
    padding-bottom: 0;
}
/* width:auto is load-bearing. The base .hero__content carries
 * `width: clamp(20rem, 48%, 46rem)` for the HOMEPAGE hero, whose copy has to
 * stay inside the photograph's readability wash. An inner-page hero is
 * centred with no subject to avoid, so that clamp was pinning this content to
 * 682px (48% of the container) and the 900px max-width below never applied --
 * which is what wrapped a two-line headline onto four. */
.hero--inner .hero__content { width: auto; max-width: 900px; margin-inline: auto; }
.hero--inner .hero__headline { max-width: 851px; margin-inline: auto; }
.hero--inner .hero__lede { max-width: 620px; margin-inline: auto; }
.hero--inner .ticklist--inline { justify-content: center; }
.hero--inner .hero__actions { display: flex; justify-content: center; gap: var(--action-gap); flex-wrap: wrap; }
.hero--staff .hero__lede { max-width: 555px; }      /* the one page with a narrower lede */
/* Asked for grey here specifically. Scoped to the page so the blue headline
 * stays the pattern on every other inner hero. */
/* "Your Rights and Responsibilities" needs 1045px at the 74px hero size; the
 * shared 851px cap wrapped it again and made the <br> produce three lines
 * rather than two. Widened for this page only, and only where there is room
 * for it -- below 1200px the break is off anyway (.title-break). */
.hero--rights .hero__headline { color: var(--ink-body); }
@media (min-width: 68.75rem) {
    /* Both caps have to move: .hero__content is the 900px one that was
     * actually clipping the line, the headline's own 851px sits inside it. */
    .hero--rights .hero__content  { max-width: 1100px; }
    .hero--rights .hero__headline { max-width: 1100px; }
}

/* The cards' own container is pulled up over the photo. 217 of the card's
   390 height sits below the image edge on the live page. */
.hero--staff .benefit { min-height: 390px; }

/* the band below must clear the overhang the cards now create */
.hero--staff + .staff-band { padding-top: 257px; }

/* --- Staff cards: the tint wraps the whole block ------------------------- */
.staff__body {
    padding: 40px;
    text-align: left;
}

.staff__name {
    font-family: var(--font-heading, Barlow), sans-serif;
    font-size: 25px;
    font-weight: 700;
    color: var(--brand-blue);
    margin: 0;
}

/* The 1px brand rule under the qualification, full inner width. */
.staff__role { margin: 4px 0 0; padding-bottom: 18px; border-bottom: 1px solid var(--brand-teal); }
.staff__body > p:not(.staff__role) { margin-top: 18px; }

/* Nurses sit in a taller card with roomier padding (measured 416x493,
   100px block / 50px inline). */

/* --- Dr Henley -----------------------------------------------------------
 * Left column: portrait, then name and qualifications beneath it.
 * Right column: the farewell copy, then the book photo, then the stroke
 * story with its image left and the copy right -- all inside the right
 * column rather than spanning the page. */
.staff--feature .staff__photo { display: block; }
.staff--feature .staff__body {
    background: none;
    padding: 0;
}
.staff--feature .staff__name { margin-top: 24px; }

/* the left column stacks photo over name/qualification */
.staff--feature { grid-template-columns: minmax(0, 300px) minmax(0, 1fr); gap: 60px; align-items: start; grid-template-areas: "photo copy"; }

.staff-feature__book {
    grid-template-columns: minmax(0, 480px) minmax(0, 1fr);
    gap: 60px;
    align-items: start;
    display: block;
    margin-top: 40px;
}
.staff-feature__media { gap: 20px; display: block; margin-bottom: 28px; }
.staff-feature__media img { display: block; width: 100%; height: auto; }

/* the stroke story: image left, copy right, within the right column */
.staff-feature__story {
    display: grid;
    grid-template-columns: minmax(0, 232px) minmax(0, 1fr);
    gap: 40px;
    align-items: start;
}

@media (max-width: 68.75rem) {
    .hero--staff .benefits__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: -120px; }
    .hero--staff + .staff-band { padding-top: 160px; }
    .staff-band--split .staff-pair .staff__body { padding: 50px 40px; }
    .staff-feature__story { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 48rem) {
    .hero--staff .benefits__grid { grid-template-columns: minmax(0, 1fr); margin-bottom: 0; }
    .hero--staff + .staff-band { padding-top: var(--space-l); }
    .staff__body { padding: 30px; }
}

/* Dr Henley: two columns, the left one stacking portrait over name. */
.henley {
    display: grid;
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    gap: 60px;
    align-items: start;
}
.henley__intro img { display: block; width: 100%; height: auto; }
.henley__intro .staff__name { margin-top: 22px; }
.henley__role { border-bottom: 0; padding-bottom: 0; }
.henley__main > p { font-size: 19px; color: var(--ink-body); }
.henley__main > p + p { margin-top: 14px; }
.henley__book { display: block; width: 100%; height: auto; margin: 32px 0; }
.staff-feature__story img { display: block; width: 100%; height: auto; }
.henley__story-copy p { font-size: 19px; color: var(--ink-body); }
.henley__story-copy p + p { margin-top: 14px; }

@media (max-width: 48rem) {
    .henley { grid-template-columns: minmax(0, 1fr); gap: 24px; }
    .henley__intro img { max-width: 240px; }
}

/* --- Nurses and reception: measured pair geometry ------------------------
 * Live at 1440, each pair occupies a 660px half of the container:
 *
 *   Nurses     photo 249 at x=40,  card 416 at x=289  -> touching, pad 100/50
 *   Reception  photo 223 at x=40,  card 372 at x=313  -> 50px gap, pad 40
 *
 * My even 1fr split gave the photo far too much room and squeezed the copy
 * into a ribbon. These are explicit tracks, not fractions. */
.staff-band--split .staff-pair { gap: 40px 55px; }

/* nurses */
.staff-band--split .staff-pair .staff { gap: 0; }
/* 100px top and bottom was transcribed from live, whose nurse bios are
 * longer. Against our copy it left ~100px of empty panel above the name and
 * 120-135px below the last line -- and Louise has no bio at all, so her panel
 * was mostly blank. Even padding all round, matching the reception row. */
.staff-band--split .staff-pair .staff__body { padding: 50px; }

/* reception -- narrower photo, a real gutter, tighter padding */
.staff-band--reception .staff-pair .staff { gap: 50px; }
.staff-band--reception .staff-pair .staff__body { padding: 40px; }

/* practice management: one wide card, 1125 beside a 225 photo */
.staff-band--plain .staff { gap: 50px; }

@media (max-width: 78rem) {
    .staff-band--split .staff-pair .staff,
    .staff-band--reception .staff-pair .staff,
    .staff-band--plain .staff { grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: 30px; }
    .staff-band--split .staff-pair .staff__body { padding: 40px; }
}

/* The straddle needs a stacking context, not just a negative margin.
 * The band below the hero has its own background and comes later in the
 * DOM, so with both at `position: static` it painted straight over the
 * overhanging cards -- they were behind the band, not on top of the photo. */
.hero--staff .benefits__grid {
    margin-top: 90px;
    margin-bottom: -217px;
    gap: 0;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    position: relative;
    z-index: var(--layer-raised);
}

/* Reception/nurse rows: the photo is a fixed track, the card takes the rest.
 *
 * Fixed 223px + 50 + 372px needs 645, but our container is 1160 (live's is
 * 1360), leaving 552 per column -- so the second card overflowed the page by
 * 185px. The photo keeps its measured size; the card flexes into whatever
 * remains, which holds the live proportions at 1440 without overflowing. */
.staff-band--reception .staff-pair .staff { grid-template-columns: minmax(0, 223px) minmax(0, 1fr); }
.staff-band--split .staff-pair .staff { grid-template-columns: minmax(0, 249px) minmax(0, 1fr); }
.staff-band--plain .staff { grid-template-columns: minmax(0, 225px) minmax(0, 1fr); }

/* ==========================================================================
   REMAINING PAGES — components measured off the live pages
   ==========================================================================
   Shared band grammar (eyebrow / title / lede) and the pastel card set are
   already defined above; only what is new to these pages lives here.
   -------------------------------------------------------------------- */

/* --- After hours: 630 copy column at x=40, contacts at x=810 ------------- */
.after-hours__photo { display: block; width: 100%; height: auto; margin-top: 40px; }

.ah-group__head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.ah-group__title { font-size: 25px; font-weight: 700; color: var(--ink); margin: 0; }
.ah-icon { flex-shrink: 0; }
.ah-group__head--pink  .ah-icon { color: var(--tint-pink-ink); }
.ah-group__head--blue  .ah-icon { color: var(--tint-sky-ink); }
.ah-group__head--green .ah-icon { color: var(--tint-green-ink); }

.ah-group__cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.ah-card p { font-size: var(--step-0); line-height: 1.7; margin: 0; }

/* --- Link cards (services, patient rights) ------------------------------- */
.linkcards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px; margin-top: 50px; }
.linkcard p { font-size: 16px; line-height: 1.7; }

/* --- Service cards on /services: image over a heading and ticked list ---- */
.services--detail .services__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 50px; margin-top: 60px; }
.services--detail .services__grid .service-card__media { height: auto; aspect-ratio: 420 / 263; }
.services--detail .services__grid .service-card__media img { width: 100%; height: 100%; object-fit: cover; }

/* --- Access / info cards ------------------------------------------------- */
.access__grid { gap: 30px; margin-top: 50px; }
.access-card { background: #fff; padding: 40px; }
.access-card__title { font-size: 25px; font-weight: 700; color: var(--brand-blue); margin: 0 0 12px; }
.access-card p { font-size: 16px; line-height: 1.7; }

/* --- Rights and responsibilities: two columns at x=55 and x=745 ---------- */
.rights__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 60px; }
.rights__title { font-size: 25px; font-weight: 700; color: var(--brand-blue);
                 text-transform: uppercase; letter-spacing: var(--tracking-l); margin: 0 0 24px; }
.rights__col .ticklist li { font-size: 19px; margin-bottom: 18px; }

/* --- Fees ---------------------------------------------------------------- */
.fees-intro__grid { display: grid; grid-template-columns: minmax(0, 520px) minmax(0, 1fr); gap: 60px; align-items: start; }
.fees-intro__title { font-size: 41px; font-weight: 700; color: var(--brand-blue); margin: 0 0 20px; }
.fees-intro__notes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }

.fees-table th, .fees-table td { padding: 22px 20px; text-align: left; font-size: 21px; }
.fees-table td { font-size: 19px; }

/* --- Practice policy ----------------------------------------------------- */

/* stretch, not start: this rule comes AFTER the desktop block above and was
 * resetting its align-items, so the photo kept its natural height and the
 * three columns finished up to 245px apart. */
/* Each panel sizes to its OWN content. Stretching them to a shared height
 * left 69px of empty panel under the shorter one's last line -- varying
 * heights are fine here, dead space inside a panel is not. The photo still
 * stretches, so it fills the row between them. */
.twoup__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 50px; align-items: start; }
.twoup__media { align-self: stretch; }
/* 58px sat between the last line of copy and the button: --copy-gap (36px)
 * from the generic `.band-lede + .btn` rule, plus the paragraph's own 22.
 * That gap is sized for a button closing a full-width band; inside a panel
 * this narrow it reads as a hole. */
.twoup__col .band-lede + .band-actions,
.twoup__col p.band-actions:last-child { margin-top: var(--space-2xs); }
.twoup__media { display: flex; }
.twoup__media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.band-title--sm { font-size: 41px; }

.hcc-card { background: #fff; padding: 30px; }
.hcc-card__title { font-size: 21px; font-weight: 700; color: var(--ink);
                   text-transform: uppercase; letter-spacing: var(--tracking-l); margin: 0 0 8px; }
.hcc-card p { font-size: 16px; margin: 0; }

/* --- Contact (/sample-page) ---------------------------------------------- */
.practice-info { display: grid; grid-template-columns: minmax(0, 560px) minmax(0, 1fr); gap: 70px;
                 align-items: start; margin-top: 60px; }
.practice-info__title { font-size: 33px; font-weight: 700; color: var(--brand-blue); margin: 0 0 14px; }
.practice-info__feats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.feature {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr);
    column-gap: 14px;
    align-items: start;
}
.feature__icon {
    grid-row: 1 / 3;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    color: var(--brand-teal);
}
.feature__title {
    display: block;
    font-family: var(--font-heading);
    color: var(--brand-blue);
    margin-bottom: 0.15rem;
    grid-column: 2;
    font-size: 21px;
    font-weight: 700;
    margin: 0 0 4px;
}
.feature p { color: var(--ink-body); grid-column: 2; font-size: 16px; margin: 0; }

.contact-info__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; margin-top: 50px; }

/* --- Responsive ---------------------------------------------------------- */
@media (max-width: 78rem) {
    .after-hours__grid, .fees-intro__grid, .scripts, .workers__grid, .practice-info {
        grid-template-columns: minmax(0, 1fr); gap: 40px;
    }
    .linkcards, .services__grid, .policy-grid, .twoup__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 48rem) {
    .linkcards, .services__grid, .access__grid, .rights__grid, .policy-grid,
    .twoup__grid, .commitments__list, .policy-grid__list, .hcc__grid,
    .ah-group__cards, .fees-intro__notes, .practice-info__feats,
    .contact-info__grid { grid-template-columns: minmax(0, 1fr); }
    /* Stacked, there is no neighbouring column to match, so the photo keeps
     * its own ratio instead of being cropped to a row height. */
    .twoup__media { margin-block: 0; }
    .twoup__media img { height: auto; }
    .fees-table th, .fees-table td { padding: 14px 10px; font-size: 16px; }
}

/* The link cards sat outside a constrained container on /services, so three
 * 425px cards ran 72px past the viewport. u-grid needs the container's
 * max-width, and the cards must be allowed to shrink below their content. */
.hero--services .linkcards,
.hero--staff .linkcards { max-width: var(--grid-max-width); margin-inline: auto; }
.linkcards .linkcard { min-width: 0; }

/* --- Services: tinted link cards and service bodies ----------------------
 * Sampled from the live PNG: the three link cards straddle the hero photo
 * and are tinted blue/green/lavender with an icon above the title; the six
 * service-card bodies carry the same six-colour cycle beneath their images.
 * Both sets touch -- no gap -- as everywhere else on this site. */
.hero--services .linkcards {
    gap: 0;                       /* live cards are direct neighbours */
    margin-bottom: -110px;        /* straddle the hero photo */
    position: relative;           /* ...and paint above the band below */
    z-index: var(--layer-raised);
}
.hero--services + .section { padding-top: 150px; }

.linkcard {
    background: #fff;
    padding: 40px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.linkcard__icon { display: block; color: var(--tint-ink, currentColor); }
/* The title reads as a heading for the card, in the same ink as the copy
 * beneath it rather than the brand blue -- the blue is the link colour and
 * made every title look actionable. 25px matches the other card headings on
 * the site (.contact-card__title, .policy-col__title). */
/* `.section h2 { font-size: 21px }` outranks a single class, and these titles
 * are h2s inside a .section -- hence .linkcard.linkcard rather than a bare
 * class, which matches the same element at higher specificity without
 * resorting to !important. */
.linkcard .linkcard__title,
.linkcard__title { font-weight: 700; color: var(--ink-body); margin: 0 0 12px; font-size: 25px; }

@media (max-width: 48rem) {
    .hero--services .linkcards { margin-bottom: 0; }
    .hero--services + .section { padding-top: var(--space-l); }
}

/* --- Patient rights: a two-column table -------------------------------
 * Measured off the live PNG: a lavender header row (218,227,255) over data
 * rows on the page ground, separated by thin rules. It was built as a
 * ticked list first, which is the wrong component and read nothing like it. */
/* This table's own divider: the lavender header makes the site's teal rule
 * read as a clash. --divider is a token, so the band can restate it rather
 * than the rules needing a bespoke colour. Same 55% mix against the ground,
 * so it matches the weight of every other rule on the site. */
.rights-table {
    --divider: color-mix(in srgb, rgb(150, 165, 232) 55%, var(--ground));
    width: 100%; border-collapse: collapse; margin-top: 40px;
}
.rights-table thead th {
    background: rgb(218, 227, 255);
    padding: 26px 30px;
    text-align: left;
    font-size: 25px;
    font-weight: 700;
    /* --ink is the near-black; every other heading of this kind on the site
     * is --ink-body. */
    color: var(--ink-body);
    text-transform: uppercase;
    letter-spacing: var(--tracking-l);
}
.rights-table td {
    padding: 26px 30px;
    font-size: 19px;
    line-height: 1.6;
    vertical-align: top;
    width: 50%;
    border-bottom: 1px solid var(--divider);
}
.rights__note { margin-top: 36px; font-size: 19px; font-style: italic; }

@media (max-width: 48rem) {
    .rights-table, .rights-table thead, .rights-table tbody,
    .rights-table tr, .rights-table th, .rights-table td { display: block; width: auto; }
    .rights-table thead th { margin-top: 20px; }
    .rights-table td { padding: 16px 20px; }
}

.info-card {
    background: #fff;
    padding: 70px;
    display: grid;
    grid-template-columns: minmax(0, 480px) minmax(0, 1fr);
    gap: 70px;
    align-items: start;
}
.info-card__title { font-size: 33px; font-weight: 700; color: var(--brand-blue); margin: 0 0 14px; }
.info-card__copy p { font-size: 19px; line-height: 1.7; margin: 0; }
.info-card__feats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 28px 40px;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* --- Get in touch: copy and cards left, map right ----------------------- */
.contact-info__left .contact-info__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 30px;
    margin-top: 40px;
    list-style: none;
    padding: 0;
}

@media (max-width: 78rem) {
    .info-card { grid-template-columns: minmax(0, 1fr); gap: 40px; padding: 50px; }
    .contact-info__map iframe { height: 500px; }
}

@media (max-width: 48rem) {
    .info-card { margin-bottom: 0; padding: 34px; }
    .info-card__feats, .contact-info__left .contact-info__grid { grid-template-columns: minmax(0, 1fr); }
}

/* The map is 630 fixed on live within a 1360 container, leaving 660 for the
 * copy column plus a 70 gutter. Our container is 1160, so a fixed 630 map
 * squeezed the cards to ~230 each. Let the map flex instead and keep the
 * left column at the live proportion (roughly 57/43). */
.contact-info__map iframe { display: block; width: 100%; border: 0; height: 815px; }

/* Get in Touch: the intro is its own row, constrained to a reading measure
 * rather than running the full 1160; the cards and the map share the row
 * below it. (First build stacked the intro inside the left column, which
 * confined the copy to half the page and squeezed the cards.) */
.contact-info__intro { max-width: var(--measure); }
/* 58px measured from the last line of copy: --copy-gap (36px) from the
 * generic `.band-lede + .btn` rule, plus the paragraph's own bottom margin.
 * That gap is sized for a button closing a full band; here the button sits
 * with the copy directly above it. The extra class matches that rule's
 * specificity and wins on source order. */
.contact-info .contact-info__intro .band-actions { margin-top: 0; }
/* The paragraph above keeps its own bottom margin (--lede-gap, 22px), which
 * is the whole of the remaining gap. Closing on it here rather than editing
 * --lede-gap, which every band on the site reads. */
.contact-info .contact-info__intro .band-lede + .band-actions { margin-top: -6px; }

.contact-info__split {
    display: grid;
    grid-template-columns: minmax(0, 1.32fr) minmax(0, 1fr);
    gap: 70px;
    align-items: start;
    margin-top: var(--space-l);
}
.contact-info__split .contact-info__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 30px;
    margin: 0;
    padding: 0;
    list-style: none;
}

@media (max-width: 78rem) {
    .contact-info__split { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}
@media (max-width: 48rem) {
    .contact-info__split .contact-info__grid { grid-template-columns: minmax(0, 1fr); }
}

/* --- Contact page: its own band names -----------------------------------
 * These were .contact-info__split / __grid, which the HOMEPAGE contact band
 * also uses. Six competing definitions had accumulated across the sheet and
 * the homepage's single-column rule won here, dropping the map below the
 * cards. Distinct names, so neither page can decide the other's layout. */
/* Tracks from the reference: the four cards come out 312px each, which sets
 * the left column at 655 and leaves the map the larger share.
 *
 * "Get in Touch" and its copy are INSIDE this grid's left column, above the
 * cards -- not a band of their own above the whole row. That is what lets the
 * map start level with the heading and run the full height beside both, which
 * is how the reference is built. align-items: stretch (the default) does the
 * rest; `start` would leave the map only as tall as its own content. */
.ct-row {
    display: grid;
    grid-template-columns: minmax(0, 0.923fr) minmax(0, 1fr);
    gap: 55px;
    margin-top: var(--space-l);
}
.ct-left { display: flex; flex-direction: column; }
/* The cards take the space left under the copy, so the column's foot lines up
 * with the map's rather than floating short of it. */
.ct-left .ct-cards { flex: 1 1 auto; align-content: start; margin-top: var(--space-l); }
.ct-cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 30px;
    margin: 0;
    padding: 0;
    list-style: none;
}
/* Full height of the left column -- heading top to the foot of the last card.
 * A max-height was capping this at 815px and leaving the map ~250px short of
 * the cards; the grid's default stretch does the work, so the iframe only
 * needs to fill what it is given. */
.ct-map { display: flex; }
.ct-map iframe { display: block; width: 100%; height: 100%; min-height: 0; border: 0; }

@media (max-width: 78rem) {
    .ct-row { grid-template-columns: minmax(0, 1fr); gap: 40px; }
    .ct-map iframe { min-height: 500px; }
}
@media (max-width: 48rem) {
    .ct-cards { grid-template-columns: minmax(0, 1fr); }
}

/* Card centred on the photo's lower edge: half above, half below. */
.info-card {
    margin-top: 0;
    margin-bottom: 0;
    position: relative;
    z-index: var(--layer-raised);
    top: 50%;
}

@media (max-width: 48rem) {
    .info-card { top: 0; margin-top: var(--space-m); }
}

.fees-card {
    background: #fff;
    padding: 50px;
    display: grid;
    grid-template-columns: minmax(0, 520px) minmax(0, 1fr);
    gap: 60px;
    align-items: start;
    position: relative;
    z-index: var(--layer-raised);
}
/* 41px, not the 33px this shipped with -- the live title is the band size. */
.fees-card__title { font-size: 41px; font-weight: 700; color: var(--brand-blue); margin: 0 0 18px; }
.fees-card__copy p { font-size: 19px; line-height: 1.7; }
.fees-card__copy p + p { margin-top: 16px; }
.fees-card__notes {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 40px;
    list-style: none; margin: 0; padding: 0;
}
/* Measured rgb(1,88,138) -- these were rendering in the ink colour. */
.notecard__title { font-size: 21px; font-weight: 700; color: var(--brand-blue); margin: 0 0 8px; }
.notecard { background: none; padding: 0; }
.notecard p { font-size: 16px; line-height: 1.6; margin: 0; }

/* --- Price table --------------------------------------------------------
 * Header band cyan (221,245,248) with teal 21px bold headings; the
 * consultation types are 21px BOLD ink -- they shipped at 400 weight and
 * were barely visible. Row rules in the brand teal at low alpha. */
.fees-table thead th {
    background: rgb(221, 245, 248);
    padding: 22px 24px;
    text-align: left;
    font-size: 21px;
    font-weight: 700;
    border-bottom: 0;
}
.fees-table tbody th {
    padding: 24px;
    text-align: left;
    font-size: 21px;
    font-weight: 700;
    color: var(--ink);
}
.fees-table tbody td {
    padding: 24px;
    font-size: 19px;
    color: var(--ink);
}

/* --- Script-only: pastel blue panel -------------------------------------- */
.scripts {
    background: rgb(204, 227, 245);
    padding: 50px;
    display: grid;
    grid-template-columns: minmax(0, 650px) minmax(0, 1fr);
    gap: 50px;
    align-items: center;
    margin-top: 60px;
}
.scripts__media img { display: block; width: 100%; height: auto; }
.scripts__title { font-size: 33px; font-weight: 700; color: var(--brand-blue); margin: 0 0 16px; }
.scripts__copy p { font-size: 16px; line-height: 1.7; }
.scripts__copy p + p { margin-top: 12px; }

/* --- Fees information: pink panel left, photo right ----------------------
 * The four items are plain copy ON the panel, not white cards. The photo
 * is 690x1138 and full-bleed to the right edge. */
/* Clear of its neighbours the way the other full-bleed bands are: .related
 * carries the same --space-2xl below it, .access 50px above. Without this the
 * band butted straight into the sections either side of it. */
.feeinfo { padding-block: 0; margin-block: var(--space-2xl); }
/* Equal halves with 50px down the middle, as on the policies and access
 * bands. 1fr beside a 690px cap is not a half each. */
.feeinfo__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 50px; align-items: stretch; }
/* Staggered, like every other full-bleed band here: the panel starts lower
 * than the photo and the photo finishes higher than the panel. This one sat
 * flush at 0/0, the only one of the four squared off top and bottom, which
 * read as a plain two-up rather than part of the same family.
 *   policies  photo -120 top / -50 foot
 *   access            -50       / -100
 *   related           -90       / -90
 * 90/90 here, the middle of that range. */
.feeinfo__panel { background: rgb(238, 211, 223); display: flex; align-items: center; margin-top: 90px; }
.feeinfo__media { margin-bottom: 90px; }
/* 110px top and bottom was more air than the copy needs -- the panel ran
 * well past its photograph. 70 keeps it generous without the slack. */
.feeinfo__inner { padding: 70px var(--space-xl) 70px clamp(2.5rem, 6vw, 7rem); }
.feeinfo__items {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px;
    list-style: none; margin: 50px 0 0; padding: 0;
}
.feeinfo-item { background: none; padding: 0; }
.feeinfo-item__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 0 0 12px; }
.feeinfo-item p { font-size: 16px; line-height: 1.7; margin: 0; }
.feeinfo__media {
    background-image: url("/assets/images/fees-caregiver.webp");
    background-size: cover; background-position: 50% 50%;
    min-height: 1138px;
}

/* --- Payment policy: three pastel columns -------------------------------- */
.policy-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px; margin-top: 50px; }
.policy-col { padding: 44px 38px; }
.policy-col__title { font-size: 25px; font-weight: 700; color: var(--brand-blue); margin: 0 0 18px; }
.policy-col p { font-size: 19px; line-height: 1.7; }
.policy-col p + p { margin-top: 18px; }

@media (max-width: 78rem) {
    .fees-card { grid-template-columns: minmax(0, 1fr); gap: 36px; padding: 40px; }
    .scripts { grid-template-columns: minmax(0, 1fr); gap: 32px; }
    .feeinfo__grid { grid-template-columns: minmax(0, 1fr); }
    .feeinfo__inner { margin-inline: auto; padding: 60px var(--space-m); }
    /* Stacked, there is no column alongside to stagger against. */
    .feeinfo__panel { margin-top: 0; }
    .feeinfo__media { min-height: 520px; margin-bottom: 0; }
    .policy-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 48rem) {
    .fees-card { margin-top: var(--space-m); padding: 30px; }
    .fees-card__notes, .feeinfo__items { grid-template-columns: minmax(0, 1fr); }
    .fees-table thead th, .fees-table tbody th, .fees-table tbody td { padding: 14px 12px; font-size: 16px; }
}

/* --- Table text: the DARK INK, and blue-tinted row rules -----------------
 * Correcting an over-correction. Told the consultation types were washed
 * out, I changed their WEIGHT and left every cell in the same pale colour --
 * so the whole table stayed low-contrast instead of just the labels being
 * fixed. Live is rgb(54,59,64) throughout; set it once, on the table. */
.fees-table { width: 100%; border-collapse: collapse; margin-top: 50px; color: var(--ink-body); }
.fees-table tbody th,
.fees-table tbody td { color: var(--ink-body); }
.fees-table tbody tr { border-bottom: 1px solid var(--divider); }

/* --- Workers compensation -----------------------------------------------
 * Three columns: 407 photo, the work-related copy, then three pastel cards.
 *
 * The band title was being written as a bare <p class="band-title"> inside a
 * column, where nothing sized it -- hence the wrong font size. It carries the
 * band-title class now, which is where the 58px lives, so it inherits the
 * size every other band title on the site uses instead of being restyled. */
.workers__grid { display: grid; gap: 50px; align-items: start; }
.workers__media img { display: block; width: 100%; height: auto; }

/* Live is rgb(54,59,64) -- the dark ink -- not the muted body colour. */
.workers__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 28px 0 12px; }
/* The body rule for this column lives further down, scoped :not([class]) --
 * see the note there. An unscoped `.workers__copy p` was here and still won
 * on specificity, which is what stretched the band title's leading to 96px. */

.workers__cards { display: grid; gap: 24px; list-style: none; margin: 0; padding: 0; }
.workers-card { padding: 32px; }
.workers-card__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 0 0 10px; }
.workers-card p { font-size: 16px; line-height: 1.7; color: var(--ink-body); margin: 0; }

@media (max-width: 78rem) {
    .workers__grid { gap: 36px; }
}

/* `.workers__copy p` (0,1,1) outranks `.band-title` (0,1,0), so my own body
 * rule was overriding the band title and rendering it at 16px. Scope the body
 * rule so it cannot claim a titled paragraph -- the class then supplies the
 * size, as it does in every other band. */
.workers__copy p:not([class]) { font-size: 16px; line-height: 1.7; color: var(--ink-body); }

/* The reference sets this heading at the full band size -- its longest line,
 * "Motor Accident", measures 632px there -- and lets it run to four lines.
 * Our middle column was 490, too narrow for that, which is why the heading
 * had been shrunk to --title-md instead. Widening the copy column to 640 (at
 * the photo's expense, 407 -> 340) gives the type its room back. */
/* Measured off the reference. Two hard constraints: the cards run x
 * 1036..1460, so 424 wide; and the eyebrow "WORKERS COMPENSATION CLAIMS"
 * sits on ONE line there, which needs 426. A 320px copy column wrapped that
 * eyebrow to two lines. 456/440/424 satisfies both, and the heading still
 * wraps to four lines as the reference shows -- its longest line needs ~447
 * at 41px against a 440 column. */
.workers__grid { grid-template-columns: minmax(0, 456fr) minmax(0, 440fr) minmax(0, 424fr); }
/* Full band size, as the reference has it. It still tracks its COLUMN rather
 * than the viewport -- the column is one of three tracks and narrows faster
 * than a vw clamp shrinks type -- but the ceiling is now --title-xl instead
 * of --title-md. */
.workers__copy { container-type: inline-size; }
.workers__copy .band-title {
    /* ~12.8cqw sets the longest line at about 96% of this narrower column,
     * so it wraps to four lines as the reference does while staying large. */
    font-size: clamp(1.75rem, 12.8cqw, 49px);
    color: var(--brand-blue);
    overflow-wrap: normal;
    word-break: normal;
    hyphens: none;
}

/* Row rules: one SOLID colour, so every divider including the last is
 * identical. rgba(1,88,138,.22) composited to rgb(216,223,225) -- a pale
 * grey-blue -- and the final rule differed again where it met the band
 * ground rather than another row. A solid value cannot vary with what is
 * behind it. Blue-tinted, from the brand secondary. */
.fees-table tbody tr,
.fees-table tbody tr:last-child { border-bottom: 1px solid var(--divider); }
.policy-hero-cards { position: relative; z-index: var(--layer-raised); gap: 0; }

/* --- Commitments: photo left, five DIFFERENT pastel cards right ---------- */
.commitments__grid {
    display: grid;
    grid-template-columns: minmax(0, 620px) minmax(0, 1fr);
    gap: 50px;
    align-items: stretch;   /* the photo matches the cards' height */
    margin-top: 50px;
}
.commitments__media img { display: block; width: 100%; height: auto; }
.commitments__list {
    margin-top: 50px;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 30px;
    list-style: none;
    margin: 0;
    padding: 0;
}
.commit-card { padding: 34px; background: none; }
.commit-card__title { font-size: 25px; font-weight: 700; color: var(--brand-blue); margin: 0 0 10px; }
.commit-card p { font-size: 16px; line-height: 1.7; margin: 0; color: var(--ink-body); }

/* Two columns: the photo stretches to the row, matching the cards. Tied to
 * the same 78rem breakpoint that creates the columns -- when it sat at
 * 68.75rem instead, the 1101-1248 range got `height: 100%` with no row to
 * fill and the photograph collapsed to zero height. */
@media (min-width: 78.01rem) {
    .commitments__media { aspect-ratio: auto; height: 100%; }
}

@media (max-width: 78rem) {
    .commitments__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }

}
@media (max-width: 48rem) {
    .commitments__list { grid-template-columns: minmax(0, 1fr); }
}

/* --- Four policy cards: distinct pastels, not four of one colour --------- */
.policy-grid__list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; }
.policy-card { padding: 44px; }
.policy-card__title { font-size: 41px; font-weight: 700; color: var(--brand-blue); margin: 0 0 14px; }
.policy-card p { font-size: 19px; line-height: 1.7; margin: 0; color: var(--ink-body); }

/* --- Healthcare Complaints Commission ------------------------------------
 * Title left 50%, description right 50%; four items on the plain ground with
 * a teal icon above a dark title, separated by vertical rules in the
 * secondary blue. They were white cards with no icons. */
.hcc__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 60px; align-items: start; }
.hcc__lede { font-size: 16px; line-height: 1.7; font-style: italic; color: var(--ink-body); margin: 12px 0 0; }
.hcc__grid {
    gap: 30px;
    margin-top: 40px;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    list-style: none;
    margin: 50px 0 0;
    padding: 0;
}
.hcc-item {
    background: none;
    padding: 10px 30px;
    text-align: center;
    border-left: 1px solid rgb(9, 93, 142);
}
.hcc-item:first-child { border-left: 0; }
/* The item is text-align: center, but a block-level SVG ignores that and sat
 * hard left -- icon centred at 47px against the title's 166. An auto inline
 * margin centres the box itself. */
/* 34px in the markup read small against the 21px titles beneath them; 48
 * gives the row some presence. Set here rather than on each of the four
 * SVGs, so the width/height attributes stay as the intrinsic size. */
.hcc-item__icon { color: var(--brand-teal); display: block; width: 48px; height: 48px; margin: 0 auto 26px; }
/* The four icons have different viewBox aspects, so an equal BOX does not
 * give equal presence: the signal icon is 384x512 (0.75), which
 * preserveAspectRatio fits to 36px of the 48px width against 48 for the two
 * square ones -- it read noticeably smaller in the row. Letting it fill the
 * box (rather than fitting inside it) paints it at the full 48 wide; its
 * viewBox has enough side padding that the shape is not clipped. */
/* Matched by AREA, not by one dimension. The four icons have different
 * viewBox aspects and preserveAspectRatio fits the glyph to the smaller one,
 * so an equal 48px box painted the signal icon (384x512, a 0.75 aspect) at
 * 36px wide against 48 for the square ones -- visibly smaller in the row.
 * Since its aspect is fixed, no box makes both its width and height match;
 * 54px puts its painted area within 1% of theirs, which is what the eye
 * actually reads. 64 matched the width but stood 61px tall against 47. */
.hcc-item:nth-child(2) .hcc-item__icon { width: 54px; height: 54px; margin-bottom: 20px; }
.hcc-item__title { font-size: 21px; font-weight: 700; color: var(--ink-body); margin: 0 0 8px; }
.hcc-item p { font-size: 16px; margin: 0; color: var(--ink-body); }

/* --- Patient feedback: pink, and the photo is square-cornered ------------
 * The image sat in .media-frame, which carries border-radius: var(--radius-m)
 * from the template -- the rounded corners. This site squares every image, so
 * the photo is no longer wrapped in a frame at all. */
.cta__media-plain img { display: block; width: 100%; height: auto; border-radius: 0; }

@media (max-width: 78rem) {
    .hcc__head { grid-template-columns: minmax(0, 1fr); gap: 20px; }
    .hcc__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 40px; }
    .hcc-item:nth-child(3) { border-left: 0; }
}
@media (max-width: 48rem) {
    .policy-grid__list { grid-template-columns: minmax(0, 1fr); }
    .hcc__grid { grid-template-columns: minmax(0, 1fr); }
    .hcc-item { border-left: 0; }
}
.rights-hero-cards { position: relative; z-index: var(--layer-raised); gap: 0; }
.rights-hero-cards .benefit { min-height: 390px; }

/* --- Related practice policies: blue panel left, offset photo right ------ */
/* Equal halves with 50px down the middle, as on the policies and access
 * bands. 1fr beside a 690px cap is not a half each. */
/* The photo and the panel are the SAME height, offset vertically -- in the
 * reference the panel is 700 and the photo 702, with the photo starting 43px
 * higher. Stretch does that: the row is as tall as the panel plus its offset,
 * and the photo fills it. It was `start`, so the photo sat at its natural 484
 * against a 725 panel and finished 331px short. */
.related__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 50px; align-items: stretch; }
.related__panel { background: rgb(204, 227, 245); margin-top: 90px; }
.related__media { margin-bottom: 90px; }
.related__inner { padding: 90px var(--space-xl) 90px clamp(2.5rem, 6vw, 7rem); }
.related__items {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px;
    list-style: none; margin: 40px 0 0; padding: 0;
}
.rel-item { background: none; padding: 0; }
.rel-item__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 0 0 12px; }
.rel-item p { font-size: 16px; line-height: 1.7; }
/* Fills the stretched cell. An earlier attempt set an arbitrary 620px here,
 * which cropped the sides to reach a height nothing was aligned to; this
 * matches the PANEL, which is the relationship the design actually has. */
.related__media { display: flex; }
.related__media img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 35%; }

@media (max-width: 78rem) {
    .related__grid { grid-template-columns: minmax(0, 1fr); }
    .related__panel { margin-top: 0; }
    .related__inner { margin-inline: auto; padding: 50px var(--space-m); }
    /* Stacked: nothing to align to, so the photo keeps its own ratio rather
     * than being cropped to a height that no longer means anything. */
    .related__media { margin-bottom: 0; }
    .related__media img { height: auto; }
}
@media (max-width: 48rem) {
    .related__items { grid-template-columns: minmax(0, 1fr); }
}

/* --- Ghost buttons ------------------------------------------------------
 * The live "Learn more" is a 1px outline in the darker secondary blue on the
 * pastel panel, not the muted grey the template gives .btn--ghost. */
.btn--ghost { background: transparent; border: 1px solid var(--brand-blue); color: var(--brand-blue); }

/*
 * Ghost buttons ON A TINTED PANEL take that panel's own colour, so each card's
 * button belongs to its card rather than all three being the same blue.
 *
 * --tint-ink is the tint's matching tone, but it is a 2.4-2.65:1 against its
 * own tint -- fine for the icons it was made for, nowhere near the 4.5:1 text
 * needs. Mixing 40% of it into the body ink keeps the hue recognisable and
 * clears AA on every tint in the set (worst case 4.71:1 on pink).
 *
 * Hover fills with the teal-to-blue gradient and flips the text to white
 * (5.45:1 on the teal end, 7.6:1 on the blue).
 */
/* These panel buttons are plain .btn now: the shared ::after supplies the
 * gradient and the shared rule supplies the lift, so everything that used to
 * live here is gone.
 *
 * What was here: a gradient painted at `background-size: 0 100%` and grown to
 * full width on hover, with its OWN 300ms transition list that carried
 * background-size but not transform. So these buttons animated a
 * background-size that no longer changes anything -- most visible on hover
 * OFF, where the pointer had left but the button kept animating for 300ms.
 * They also kept a flat-blue `.btn--ghost:hover` that fought the gradient. */

/* --- Space between the related-policies band and the footer ------------- */
.related { padding-block: 0; margin-bottom: var(--space-2xl); }

/* ==========================================================================
   OVERLAP — a block that rides up into the section before it
   ==========================================================================
   The one pattern for any card, panel or grid that straddles a boundary:

       <section class="hero">…</section>
       <div class="overlap overlap--m">   <!-- s / m / l, or set --overlap -->
           <div class="u-container">…</div>
       </div>
       <section class="section">…</section>

   The block stays IN NORMAL FLOW. It takes real layout space, so whatever
   follows is pushed down by the part that does not overlap -- nothing is
   ever painted over. The preceding section grows by the same amount, so its
   background continues down behind the block. One token sets the depth and
   both sides read it, via :has(), so the HTML never has to agree with a
   number.

   Depth is on the Utopia scale, so it breathes with the viewport:
     --overlap-s   a lip, about a quarter of a typical card
     --overlap-m   about half a card (the usual look)      <- default
     --overlap-l   most of the card sits on the photo

   Absolute positioning is NOT how this works, and it never was: an
   absolutely-positioned block occupies no space, so the next section butts
   straight up against the hero and the block paints over its content. That
   is what every "cards overlay the table" report was.
   -------------------------------------------------------------------- */
:root {
    --overlap-s: var(--space-2xl);
    --overlap-m: calc(var(--space-3xl) * 1.5);
    --overlap-l: calc(var(--space-3xl) * 2);
}

.overlap {
    --overlap: var(--overlap-m);
    position: relative;           /* stacks above the section it rides into */
    z-index: var(--layer-raised);
    margin-top: calc(-1 * var(--overlap));
}
.overlap--s { --overlap: var(--overlap-s); }
/*
 * Services: the hero photograph runs down to the CENTRE of the link cards,
 * with the cards above it. The shared --overlap-s straddled only 80px of a
 * 385px card, so the photo stopped at their top edge and the effect read as
 * two stacked bands rather than one riding into the other.
 *
 * z-index is on .overlap already (--layer-raised); the hero needs an explicit
 * stacking position or its background paints over the cards.
 */
.hero--services + .overlap--s { --overlap: 192px; }
.hero--services { position: relative; z-index: 0; }
/*
 * The straddling band must not paint its own ground over the photograph.
 * .services--detail carries the page wash (an opaque #F9F7F4 plus the
 * gradients), which covered the hero across the whole overlap -- the cards
 * measured as straddling 192px but nothing showed behind them. The wash moves
 * to a pseudo-element that starts BELOW the overlap, so the photo shows in the
 * straddle and the wash still runs under the rest of the band.
 */
.services--detail.overlap { background: none; }
/* (The ::before that carried an opaque ground plus the wash below the
 * straddle is gone: the page canvas runs under the whole band, and the
 * hero photograph ends exactly at the straddle's foot by construction --
 * section top = hero bottom - overlap -- so there is nothing to cover.) */

.overlap--l { --overlap: var(--overlap-l); }

/* The section before an overlap makes room for it. Same token, read from
   the block's modifier, so there is exactly one place the depth is set. */

/* The section after one needs no special-casing: normal top padding, the
   block's remainder simply sits above it. */

@media (max-width: 48rem) {
    /* Stacked: the block follows the section rather than riding into it. */
    .overlap { margin-top: var(--space-m); }
    :is(.hero, .section):has(+ .overlap) { padding-bottom: var(--space-l); }
}

/* --- The four straddle heroes: just a background each, nothing else ------ */
.hero--staff   { background-image: url("/assets/images/doctors-hero.webp"); }
.hero--fees    { background-image: url("/assets/images/fees-hero.webp"); }
.hero--policy  { background-image: url("/assets/images/policy-hero.webp"); }
.hero--rights  { background-image: url("/assets/images/rights-hero.webp"); }
.hero--contact { background-image: url("/assets/images/contact-hero.webp"); }

/* --- Overlap: room below the hero copy --------------------------------------
 * The preceding section's extra padding is the overlap depth PLUS a normal
 * gap, so the copy never sits hard against the block. */
:is(.hero, .section):has(+ .overlap) { padding-bottom: calc(var(--overlap-m) + var(--space-xl)); }
:is(.hero, .section):has(+ .overlap--s) { padding-bottom: calc(var(--overlap-s) + var(--space-xl)); }
:is(.hero, .section):has(+ .overlap--l) { padding-bottom: calc(var(--overlap-l) + var(--space-xl)); }

/* Services uses a deeper straddle than --overlap-s (the cards sit at their own
 * midpoint), so it needs matching clearance or they ride up over the Book
 * Appointment button -- they covered it by 22px. Must come AFTER the generic
 * rules above: same specificity, so source order decides. */
.hero--services:has(+ .overlap--s) { padding-bottom: calc(192px + var(--space-xl)); }

/* Blocks that used to live inside a hero, now inside .overlap. Their
 * hero-scoped rules (gap: 0, the negative margin) no longer match. */
.overlap .benefits__grid { gap: 0; margin: 0; }
.overlap .benefit { min-height: 390px; }
.overlap .linkcards { gap: 0; margin: 0; }
.overlap.accred-band { margin-top: calc(-1 * var(--overlap)); }

/* --- Hero washes: one per page, each copied from the live overlay's ::before
 * (angle, stops and opacity as measured). Most inner pages fade top-to-bottom;
 * only the homepage and the-practice are diagonal. ------------------------- */
.hero--about::before    { background-image: linear-gradient(107deg, var(--ground) 51%, rgba(255,255,255,0) 70%); opacity: .91; }
.hero--staff::before    { background-image: linear-gradient(187deg, #fff 68%, rgba(255,255,255,0) 87%); opacity: .86; }
.hero--services::before { background-image: linear-gradient(187deg, #fff 62%, rgba(249,247,244,0) 100%); opacity: .86; }
.hero--fees::before     { background-image: linear-gradient(178deg, rgba(255,255,255,.81) 66%, rgba(249,247,244,0) 100%); opacity: 1; }
.hero--policy::before   { background-image: linear-gradient(178deg, #fff 31%, rgba(255,255,255,0) 87%); opacity: .76; }
.hero--rights::before   { background-image: linear-gradient(243deg, #fff 36%, rgba(255,255,255,0) 87%); opacity: .95; }
.hero--contact::before  { background-image: linear-gradient(181deg, rgba(255,255,255,.81) 38%, rgba(249,247,244,0) 100%); opacity: .51; }
.hero--privacy::before  { background-image: linear-gradient(180deg, var(--ground) var(--header-h), rgba(255,255,255,.15) calc(var(--header-h) + 120px)); opacity: .94; } /* wash held solid to the header divider so the nav stays legible; live stops at 9% */
.hero--terms::before    { background-image: linear-gradient(180deg, #fff 12%, rgba(255,255,255,0) 40%); opacity: .86; }
@media (max-width: 56.25rem) {
    .hero--about::before { background-image: linear-gradient(180deg, var(--ground) 55%, rgba(255,255,255,0) 100%); opacity: .88; }
}

/* --- Fees table header: dark ink, not teal-on-cyan ------------------------- */
.fees-table thead th { color: var(--ink-body); }

/* --- Services ---------------------------------------------------------------- */
.service-card__body .ticklist li::before { border-color: var(--tint-ink, var(--brand-teal)); }
.hero--services { background-image: url("/assets/images/services-hero.webp"); }
.services--detail .services__grid .service-card__title { font-size: 25px; font-weight: 700; margin: 0 0 16px; color: var(--ink-body); }     /* measured live */
.services--detail .services__grid .service-card {
    background: transparent;
    text-align: left;
    display: flex;
    flex-direction: column;
    height: 100%;
}
.services--detail .services__grid .service-card__body { padding-top: 24px; padding: 30px; flex: 1; }

/* --- Header: let the nav wrap, as the live header does ---------------------
 * .nav-list could not shrink below its content, so at 1440 the nav ran 27px
 * past the viewport on every page. Live wraps ("Contact" drops to a second
 * line); so do we. */
.primary-nav { min-width: 0; flex: 1 1 auto; justify-content: center; }
.nav-list { flex: 1 1 auto; flex-wrap: wrap; justify-content: center; row-gap: 0; }   /* wrapped rows centre in the column */

/* The-practice hero is left-aligned, so it is not .hero--inner, but it
 * shares the inner-page vertical rhythm. */
.hero--about .hero__inner { padding-bottom: 0; }

/* --- Services: how to access -- cyan panel left, photo right --------------- */
/* Only the SPLIT form is flush: there the panel and photo supply their own
 * padding. On practice-policy `.access` is a plain copy section with no such
 * panel, and zeroing it there left the band with no vertical space at all --
 * it sat 30px off its neighbour where every other section has 82.5px. */
.access:has(.access__grid) { padding-block: 0; }
/* Equal halves with 50px down the middle, as on the policies band. It was
 * 1fr beside a 740px cap, which is not a half each -- the panel came out
 * 730 against the photo's 740 and they sat only 30px apart. */
.access__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 50px; }
/*
 * The copy fills the panel rather than sitting in a capped column inside it.
 *
 * The 830px cap and `margin-left: auto` are left over from when this panel
 * was an unbounded 1fr track that could grow past a readable measure. Now
 * that it is a fixed half of the band, they push the text 105px off the
 * panel's left edge at 1920 and hold it hard against the right -- the panel
 * is full-bleed but its content is not.
 *
 * Even padding instead: generous on the outer (viewport) edge, and the band's
 * own gutter on the inner one so the copy does not run into the photograph.
 */
.access__inner { padding: 110px var(--space-xl) 110px clamp(2.5rem, 6vw, 7rem); }
.access__inner .band-title--ink { color: var(--ink-body); }      /* measured: dark ink, not blue */
.access__items { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 50px 40px; list-style: none; margin: 40px 0 0; padding: 0; }
.access-item__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 0 0 12px; }
.access-item p { font-size: 16px; line-height: 1.7; margin: 0; color: var(--ink-body); }
.access__media { background-image: url("/assets/images/fees-tablet.webp"); background-size: cover; background-position: 50% 50%; min-height: 900px; }

/* --- After-hours contacts: one tinted card per group, icon inside --------- */
/* Equal-height cards. They are rows in a single-column grid, so their
 * heights track their content -- 245/252/279 for the three here. A uniform
 * row size makes them match, and the content centres in whatever that
 * settles at rather than leaving the shorter ones short. */
.after-hours__contacts { display: grid; gap: 30px; grid-auto-rows: 1fr; }
.ah-card { display: flex; flex-direction: column; justify-content: center; }
.ah-card { padding: 34px 40px; }
.ah-card__head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.ah-card__title { font-size: 25px; font-weight: 700; color: var(--ink-body); margin: 0; }
.ah-card__icon { flex-shrink: 0; color: var(--tint-ink, currentColor); }
.ah-card__cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 30px; }
.ah-card--single .ah-card__cols { grid-template-columns: minmax(0, 1fr); }     /* live: single column */
/* --step-0 is the site body size (16px phone, 18px desktop). A flat 16px sat
 * small against the 25px titles and below the body copy everywhere else. */
.ah-card__cols p { font-size: var(--step-0); line-height: 1.7; margin: 0; color: var(--ink-body); }
.ah-card__cols a { color: var(--tint-ink, inherit); font-weight: 700; }

/* --- Practice-policy feedback card: inset and offset like the live band ---- */
/* The CTA runs wider than the site container: its two halves are a photo and
 * a tinted card that butt together with no gutter, so it carries the extra
 * width without the copy reaching an awkward measure. Owner's call at 1750. */
.cta .u-container { padding-inline: var(--grid-gutter); max-width: 109.375rem; }
/* The band's own padding sat ON TOP of its container's --band-pad, so the
 * CTA contributed ~200px to the gap below it where every other band gives
 * --band-pad. The container does the spacing; the band just paints. */
.cta { background: transparent; padding-block: 0; }   /* transparent: the page wash runs under it */
.cta__card--low { margin-top: 100px; }        /* live: card starts 100px below the photo's top */

@media (max-width: 78rem) {
    .access__grid { grid-template-columns: minmax(0, 1fr); }
    .access__inner { margin-inline: auto; padding: 60px var(--space-m); }
    .access__media { min-height: 480px; }
    .ah-card__cols { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 48rem) { .access__items { grid-template-columns: minmax(0, 1fr); } .cta__card--low { margin-top: 0; } }

/* After-hours: live splits the row evenly (630 copy / 630 cards); a fixed
   630px copy column left the cards only 450 wide in our 1160 container. */
.after-hours__grid { display: grid; align-items: start; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 70px; }

/* ==========================================================================
   NO CORNER RADIUS, ANYWHERE. Every border-radius token use was stripped
   from this sheet above; this is the backstop so nothing new brings one
   back. --radius-round (the circular badge) is the only shape that stays.
   ========================================================================== */
img, .media-frame, .card, [class*="__media"], [class*="-card"], [class*="__card"] { border-radius: 0; }

/* --- Services cards: left-aligned, ticks matched to the tint --------------- */
.services--detail .services__grid .service-card,
.services--detail .services__grid .service-card__body,
.services--detail .services__grid .service-card__title,
.services--detail .services__grid .ticklist { text-align: left; }
.services--detail .services__grid .ticklist { display: block; }
.services--detail .services__grid .ticklist li { display: block; }

/* --- How to access: panel sits 50px lower than the photo, ends 100 lower --- */
.access__grid { align-items: stretch; }
.access__panel { background: rgb(221, 245, 248); display: flex; align-items: center; margin-top: 50px; }
.access__media { margin-bottom: 100px; }

/* --- FAQ on services: same layout as the homepage, photo right-aligned ---- */
.faq__media { justify-self: end; }
/* Square rather than the frame's default 4/3: at 660px wide the photo was
 * 495 tall against a 533px copy column beside it, so it finished well short
 * of the text. 1:1 fills the column and balances the pair. object-fit is
 * already cover on .media-frame children, so the 800x600 source crops rather
 * than distorting. */
.faq__media .media-frame { max-width: 660px; aspect-ratio: 1; }

/* ==========================================================================
   Tints — the practice kit's pastel set, sampled from the live site.
   --------------------------------------------------------------------------
   One system for every tinted block on the site: staff cards, service
   bodies, link cards, policy columns, benefit and contact cards, CTAs.
   `.tint-<name>` paints the background and exposes `--tint-ink`, the
   matching darker tone that icons, ticks and links inside it pick up.
   Each name is a colour, not a component, so "blue" means the same thing
   everywhere. Two near-duplicates exist because the live site has them:
   rose beside pink, sky beside cyan, sand beside amber.
   ========================================================================== */
:root {
    --tint-pink: rgb(238, 211, 223);  --tint-pink-ink: rgb(198, 108, 149);
    --tint-rose: rgb(237, 200, 213);  --tint-rose-ink: rgb(198, 108, 149);
    --tint-blue: rgb(204, 227, 245);  --tint-blue-ink: rgb(96, 141, 183);
    --tint-sky: rgb(203, 241, 252);  --tint-sky-ink: rgb(38, 169, 186);
    --tint-cyan: rgb(221, 245, 248);  --tint-cyan-ink: rgb(38, 169, 186);
    --tint-green: rgb(213, 234, 188);  --tint-green-ink: rgb(123, 158, 66);
    --tint-lavender: rgb(218, 227, 255);  --tint-lavender-ink: rgb(97, 98, 166);
    --tint-amber: rgb(252, 234, 197);  --tint-amber-ink: rgb(174, 119, 9);
    --tint-sand: rgb(239, 220, 172);  --tint-sand-ink: rgb(174, 119, 9);
}
.tint-pink { --tint-bg: var(--tint-pink); --tint-ink: var(--tint-pink-ink); }
.tint-rose { --tint-bg: var(--tint-rose); --tint-ink: var(--tint-rose-ink); }
.tint-blue { --tint-bg: var(--tint-blue); --tint-ink: var(--tint-blue-ink); }
.tint-sky { --tint-bg: var(--tint-sky); --tint-ink: var(--tint-sky-ink); }
.tint-cyan { --tint-bg: var(--tint-cyan); --tint-ink: var(--tint-cyan-ink); }
.tint-green { --tint-bg: var(--tint-green); --tint-ink: var(--tint-green-ink); }
.tint-lavender { --tint-bg: var(--tint-lavender); --tint-ink: var(--tint-lavender-ink); }
.tint-amber { --tint-bg: var(--tint-amber); --tint-ink: var(--tint-amber-ink); }
.tint-sand { --tint-bg: var(--tint-sand); --tint-ink: var(--tint-sand-ink); }
.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green, .tint-lavender, .tint-amber, .tint-sand { background-color: var(--tint-bg); }

/* --- Ready to get started: blue pastel card on the right ------------------- */

/* --- After-hours: columns fold. The evening-out rule was appended after the
   breakpoint and overrode it at every width, so the two columns only ever
   narrowed. Same for the access band. ------------------------------------ */
@media (max-width: 78rem) {
    .after-hours__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
    .access__panel { margin-top: 0; }
    .access__media { margin-bottom: 0; }
}

/* --- Contact information band (the-practice, homepage) -------------------
 * The 2x2 cards were auto-sized columns -- 252px and 195px side by side --
 * and the copy column took 620 of the 1160, because a rule written for the
 * contact page's band (same class names) was winning here. Live: copy on the
 * narrower side, four EQUAL cards (2 x 325) on the wider. */
@media (min-width: 75rem) {
    .contact-info__split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 50px; }
    .contact-info__split .contact-info__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; width: 100%; justify-self: stretch; }
}

/* Contact band cards: live rows are equal height (325x309 each). */
.contact-info__split .contact-info__grid { grid-auto-rows: 1fr; }

/* ==========================================================================
   HEADER — overlaid on the hero, as on the live site
   ==========================================================================
   Live: 161px tall, transparent, static (not sticky), a 1px rule in
   rgba(54,59,64,.45) beneath it, logo ~100px tall, links 21px/600 -- and
   the hero photo runs up behind it from y=0. Ours was an opaque 106px bar
   that pushed the hero down beneath it.
   -------------------------------------------------------------------- */
:root { --header-h: 161px; }

.site-header {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: var(--layer-nav);
    background: transparent;
    min-height: var(--header-h);
    display: flex;
    align-items: center;
}
/* The rule belongs to the container, not the viewport: it should run from the
 * logo to the Book Appointment button and stop, matching every other band on
 * the site. It was on .site-header, which is pinned left:0/right:0 and so drew
 * edge to edge. */
.site-header .header-inner {
    width: 100%;
    border-bottom: 1px solid rgba(84, 88, 94, .45);
}
/* On a phone the rule stays on the inner container too, so it holds the same
 * gutter as every band below it rather than running edge to edge -- a
 * full-bleed rule under an inset header reads as a seam in the page, not as
 * the close of the band. It also needs real air beneath the button: at the
 * header's own padding the rule sat 9px off the CTA and crowded it. */
@media (max-width: 68.75rem) {
    .site-header .header-inner {
        border-bottom: 0;
        padding-bottom: var(--space-s);
        position: relative;
    }
    /* The gutter on .header-inner is PADDING, so a border-bottom on it still
     * draws the full width of the box. The rule is a pseudo-element inset by
     * that same padding instead, which is what puts it in line with the bands
     * below. */
    .site-header .header-inner::after {
        content: "";
        position: absolute;
        left: var(--grid-gutter);
        right: var(--grid-gutter);
        bottom: 0;
        border-bottom: 1px solid var(--divider-chrome);
    }
    /* Open, the drawer's own panel edge does that work. */
    body.nav-open .site-header .header-inner::after { border-bottom-color: transparent; }
}
.brand img { display: block; width: auto; height: 100px; }
/* 21px was the transcribed live size, but live has no Book Appointment button
 * in the bar. With one, seven items at 21px plus a 249px logo need ~1470px
 * against the 1420px container, so the CTA wrapped. 19px is the largest that
 * fits at full width. */
.nav-list a, .sub-toggle { font-size: 19px; font-weight: 600; }

/* ==========================================================================
   Laptop nav — one row, never two
   --------------------------------------------------------------------------
   Seven items plus two dropdown carets, a 249px logo and the Book Appointment
   button need roughly 1400px at the desktop sizes below. The hamburger does
   not take over until 1100px, so across the whole 1100-1500 laptop range the
   bar was short of room and dropped the CTA onto a second row.

   Rather than one hard step, the header tightens in two stages: the type and
   gaps come in first, then the logo. The first tier starts at 93.74rem, just
   under the 1500px container cap -- above that the container has stopped
   growing, so there is nothing to be gained by shrinking the type further and
   the full-size header stands.
   ========================================================================== */
@media (max-width: 93.74rem) and (min-width: 68.76rem) {
    .nav-list a, .sub-toggle { font-size: 18px; padding: var(--space-3xs) 6px; }
    .nav-list { gap: 10px; }
    .primary-nav { gap: var(--space-2xs); }
    .brand img { height: 84px; }
    .nav-cta { font-size: 16px; padding: 12px 18px; }
}

@media (max-width: 82.5rem) and (min-width: 68.76rem) {
    .nav-list a, .sub-toggle { font-size: 15px; padding: var(--space-3xs) 3px; }
    .nav-list { gap: 4px; }
    .brand img { height: 68px; }
    .nav-cta { font-size: 14px; padding: 9px 12px; }
}

/*
 * The structural guarantee, independent of the sizes above: nothing in the
 * bar may wrap, and the nav-list is the only thing allowed to give, so a
 * miscalculation shows up as slightly tighter items rather than the CTA
 * dropping to a second row. The sizes are what keep it comfortable; these
 * rules are what keep it a single row regardless.
 */
@media (min-width: 68.76rem) {
    .header-inner { flex-wrap: nowrap; }
    .primary-nav { flex-wrap: nowrap; min-width: 0; }
    .nav-list { flex-wrap: nowrap; min-width: 0; }
    .brand { flex-shrink: 0; }
    .nav-cta { white-space: nowrap; flex-shrink: 0; }
    .nav-list a, .sub-toggle { white-space: nowrap; }
}

/* Every page starts under the header ... */
main, .hub-main { padding-top: var(--header-h); }   /* .hub-main: the hub page wraps in a div, the widget brings its own <main> */
/* ... except a hero, which runs up behind it and carries the offset itself. */
.hero { margin-top: calc(-1 * var(--header-h)); }
.hero__inner { padding-top: calc(var(--header-h) + 90px); }                 /* homepage: eyebrow at live's y≈250 */
.hero--about .hero__inner { padding-top: calc(var(--header-h) + var(--space-3xl) + var(--space-s)); }   /* 140 below the header, as measured */

/* Mobile: a shorter header, and the drawer opens beneath it. */
@media (max-width: 68.75rem) {
    /* --header-height is measured by the header script; do NOT pin it here.
     * The mobile header is two rows now, so a hard 5rem put the drawer's top
     * under the button row and the drawer covered it. --header-h keeps a
     * static fallback for rules that need one before the script runs. */
    :root { --header-h: 10.5rem; }
    /* The logo leads the header, so it reads larger than the button beneath
     * it. At 72px it was 179 wide against a 226px CTA -- the button looked
     * like the bigger element. 104px renders it 259 wide (the file is
     * 461x185), clearly ahead of the button with room either side of a 390
     * viewport. */
    .brand img { height: 104px; }
    .nav-list a, .sub-toggle { font-size: 18px; }
    .primary-nav { top: var(--header-height, var(--header-h)); }
    /* Clear the REAL header, not the fallback. --header-h is a static 10.5rem
     * while the measured header is ~196px, so every hero was clearing a box
     * 28px shorter than the one actually above it and the eyebrow sat 21px
     * under the divider on all nine pages. --header-height is what the header
     * script measures; --header-h stays the fallback for the first paint.
     * The 40px on top of it is the gap the hero is meant to have. */
    .hero__inner, .hero--inner .hero__inner, .hero--about .hero__inner {
        padding-top: calc(var(--header-height, var(--header-h)) + 40px);
    }
    /* The pages with no hero (after-hours, appointment, patient hub) pad
     * `main` by the same stale fallback, so they were tighter still -- 8px
     * and 4px of clear air. `main` clears the real header; the extra 40 goes
     * on :first-child so it does NOT stack with a hero, which cancels main's
     * padding with a negative margin and brings its own 40 above. */
    main, .hub-main { padding-top: var(--header-height, var(--header-h)); }
    .hero { margin-top: calc(-1 * var(--header-height, var(--header-h))); }
    main > :first-child:not(.hero),
    .hub-main > :first-child:not(.hero) { margin-top: 40px; }
}

/* --- Privacy / terms: a full-bleed photo (no copy) with the document riding
 * up into it as a card, as on live. Same overlap block as everywhere else. */
.hero--legal { height: 900px; box-sizing: border-box; background-size: cover; background-position: 50% 0%; }
.hero--privacy { background-image: url("/assets/images/legal-privacy-hero.webp"); }
.hero--terms   { background-image: url("/assets/images/contact-hero.webp"); background-position: 50% 50%; }
/* Opaque only where it rides the photograph (the --overlap-l straddle is
 * 12-15rem; 26rem covers it at every width), then fading to nothing, so the
 * card's foot is not a ground-coloured edge cutting the page wash. */
.legal-card {
    background: linear-gradient(var(--ground) 0, var(--ground) 26rem, rgba(0, 0, 0, 0) 60rem);
    padding: 100px;
}
.legal-card > :first-child { margin-top: 0; }
.legal-card h2 { font-family: var(--font-heading); font-size: var(--title-md); font-weight: 700; color: var(--ink-body); margin: var(--space-l) 0 var(--space-xs); }
.legal-card p, .legal-card li { font-size: 19px; line-height: 1.55; color: var(--ink-body); }
.overlap--legal { margin-bottom: var(--space-2xl); }
@media (max-width: 48rem) {
    .hero--legal { height: 60vw; min-height: 280px; }
    .legal-card { padding: var(--space-l) var(--space-m); }
}

/* --- Patient hub intro: live's teal welcome heading and 21px lede -------- */
.hub-intro { padding-block: var(--space-l) var(--space-m); }
.hub-intro__title { font-family: var(--font-heading); font-size: var(--title-lg); font-weight: 700; line-height: 1.15; color: var(--brand-teal); margin: 0 0 var(--space-xs); }
.hub-intro__lede { font-size: 21px; line-height: 1.5; color: rgb(51, 51, 51); margin: 0; }

/* --- Buttons wrap rather than overflow a narrow viewport ------------------ */
.btn { white-space: normal; max-width: 100%; }

/* --- Workers-comp grid: the column-ratio rule was appended after its fold
 * and won at every width (same trap as after-hours). Fold restated last. -- */
@media (max-width: 78rem) { .workers__grid { grid-template-columns: minmax(0, 1fr); } }

/* ==========================================================================
   Footer — matched to live (measured at 1440)
   --------------------------------------------------------------------------
   A 1px rule inset 40px, 50px above four columns at x=40/493/812/1131
   (403 + 3 x 269, 50 gaps); Barlow 24.75px headings in the secondary blue;
   Asap 16.5px body; links in the secondary blue. Hours are stacked
   label / time blocks, not a two-column table. 83px bar: copyright and
   "Powered by" on the left, legal links on the right.
   ========================================================================== */
.footer-inner {
    max-width: var(--grid-max-width);
    margin-inline: auto;
    padding: 50px 0 60px;
    border-top: 1px solid var(--divider-chrome);
    gap: 50px;
    /* Live's tracks are 403 + 3x269 at a 1360 container. Held as the same
     * ratio rather than fixed px so the columns fill the wider container
     * instead of leaving a ragged gap at the right edge. */
    grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
}
@media (max-width: 78rem) { .footer-inner { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); } }
@media (max-width: 48rem) { .site-footer { padding-inline: var(--grid-gutter); } }

.site-footer h2 {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--brand-blue);
    font-size: 24.75px;
    margin: 0 0 24px;
}
.site-footer p { margin: 0 0 17px; }
/*
 * Footer links.
 *
 * Nav links are ink and pick up the teal on hover. Contact details (phone,
 * fax, email) stay blue and UNDERLINED never -- they read as information
 * rather than navigation -- and turn teal on hover like everything else.
 * The Facebook mark is the reverse: teal at rest, blue on hover.
 *
 * Contrast on the footer ground: teal 5.1:1, blue 7.1:1, ink 10.6:1.
 */
.site-footer a { color: var(--brand-blue); text-decoration: none; }
.site-footer a:hover,
.site-footer a:focus-visible { color: var(--brand-teal); text-decoration: none; }
.footer-mission { color: var(--ink-body); font-size: inherit; line-height: 1.5; max-width: 339px; }
.footer-social {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3xs);
    font-size: 16px;
    margin-top: 30px;
    color: var(--brand-teal);
}   /* live icon is teal */
.site-footer .footer-social { color: var(--brand-teal); }
.site-footer .footer-social:hover,
.site-footer .footer-social:focus-visible { color: var(--brand-blue); }
.footer-social svg { width: 25px; height: 25px; }
.footer-social span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.footer-links {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 10px;
}

/* Hours: the grouped table from Rails, laid out as live's stacked blocks --
 * bold day label, time beneath, a paragraph gap between groups. */
.site-footer .hours-table, .site-footer .hours-table tbody,
.site-footer .hours-table tr, .site-footer .hours-table td {
    display: block; width: auto; text-align: left; padding: 0; font-size: inherit;
}
.site-footer .hours-table tr { margin-bottom: 17px; }
.site-footer .hours-table td:first-child { font-weight: 700; }
.site-footer .hours-table td:first-child::after { content: ":"; }
/* "Weekends and Public Holidays / Closed" is a statement rather than a
 * label + value, so it takes no colon -- as in the reference. */
.site-footer .hours-table tr:last-child td:first-child::after { content: ""; }
.footer-note { font-size: inherit; font-style: italic; color: var(--ink-body); margin-top: 0; }

.footer-address { font-style: normal; margin-bottom: var(--space-2xs); margin: 0; }

.footer-bar {
    background: var(--brand-blue);
    color: #fff;
    padding: 0;
    border-top: 0;
    min-height: 83px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-s);
    padding-inline: 20px;
    font-size: 14.4px;
}
.footer-bar__left { display: flex; align-items: center; gap: 20px; }
.footer-bar__left .powered-by { border-left: 1px solid rgba(255, 255, 255, .5); padding-left: 20px; font-size: 12.4px; line-height: 1.4; gap: 12px; }
.footer-bar__left .powered-by img { width: 75px; }
.footer-bar__legal { font-weight: 500; }
.footer-bar__legal span { color: rgb(203, 241, 252); margin-inline: 20px; }

/* Footer fix-ups after the first pass: the bar is full-bleed on live (the
 * inset belongs to the columns, not the footer); nav links are ink and bar
 * links white, both of which the blue link rule above had claimed; and the
 * hours cells still carried the table's right-alignment and row rules. */
.site-footer {
    margin-top: 0;
    /* Transparent, not ground: the page wash (PAGE WASH, end of file) runs
     * on under the footer, so its top is not an edge. The footer keeps its
     * own small blobs on top -- they sit well inside the band and never
     * reach its edges. */
    background: transparent;
    color: var(--ink-body);
    background-image: radial-gradient(520px 220px at 18% 32%, rgba(241, 221, 170, 0.3), rgba(0, 0, 0, 0) 75%),
        radial-gradient(560px 240px at 26% 56%, rgba(214, 236, 190, 0.26), rgba(0, 0, 0, 0) 78%),
        radial-gradient(600px 260px at 82% 44%, rgba(242, 209, 221, 0.34), rgba(0, 0, 0, 0) 75%),
        radial-gradient(680px 280px at 72% 62%, rgba(206, 234, 245, 0.32), rgba(0, 0, 0, 0) 80%);
    border-top: 0;
    font-size: 16.5px;
    line-height: 1.5;
    padding: 40px 0 0;
}
.footer-inner { width: calc(100% - 80px); }
@media (max-width: 48rem) { .footer-inner { width: calc(100% - 2 * var(--grid-gutter)); } }
.footer-links a { font-size: inherit; color: var(--ink-body); }
.footer-links a:hover, .footer-links a:focus-visible { color: var(--brand-teal); }
.footer-bar a { color: #fff; }
.site-footer .hours-table tr, .site-footer .hours-table td { border: 0; }
.site-footer .hours-table td:last-child { text-align: left; padding-left: 0; }

/* The bar is blue, so it takes the white (inverted) Mediflare mark even
 * though the footer as a whole is light. */
.footer-light .footer-bar .mf-logo-light { display: none; }
.footer-light .footer-bar .mf-logo-dark { display: inline; }

/* HotDoc's injected lightbox container ends in an empty <p>, whose page
 * paragraph margin left 30px of ground below the footer bar. */
body > .hotdoc-lightbox > p { margin: 0; }

/* Bar text sits centred: the footer's paragraph margin was pushing it 17px
 * off-centre (22 above the text, 39 below). */
.footer-bar p { padding-inline: 0; margin: 0; }

/* Reception rows in the two-up layout: a smaller portrait and a wider card.
 * The section carries both band classes and the nurses' 249px track came
 * later, so the reception track never applied. Scoped to the wide layout;
 * the fold below 78rem is unchanged. */
@media (min-width: 78rem) {
    .staff-band--reception .staff-pair .staff { grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: 40px; }
}

/* Footer logo: a fixed height plus the global max-width:100% squashed it once
 * the column was narrower than the mark. Width-led, so the ratio holds. */
.footer-logo { margin-bottom: var(--space-s); margin: 0 0 30px; width: min(100%, 320px); height: auto; }

/* --- Homepage service cards: the badge is a 72px circle straddling the
 * photo's lower edge, title 32px beneath it (measured live). The radius
 * backstop above matches [class*="-card"], which squared it. */
.service-card__icon {
    background: var(--tint-ink);
    display: grid;
    place-items: center;
    color: #fff;
    z-index: var(--layer-raised);
    position: absolute;
    top: auto;
    left: 50%;
    transform: translateX(-50%);
    width: 72px;
    height: 72px;
    bottom: -36px;
    border-radius: 50%;
}

/* ==========================================================================
   About media — one layout at every width, as on live
   --------------------------------------------------------------------------
   Two square photos stacked beside the QV Tower shot, the tower as tall as
   the stack. Live: 318px squares 75px apart and a 318px tower at 1440; at
   1024–768 the same block runs full width above the copy (457/395/329px
   squares); below 600 the copy comes first. Squares are aspect-ratio 1, so
   the block just reflows with its container instead of needing a size per
   breakpoint.
   ========================================================================== */
.about__media { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; align-items: stretch; }
.about__media-stack { display: grid; grid-template-rows: 1fr 1fr; gap: 75px; }
.about__media-main, .about__media-inset { display: block; aspect-ratio: 1; margin: 0; transform: none; border: 0; box-shadow: none; }
.about__media-main img, .about__media-inset img { width: 100%; height: 100%; object-fit: cover; }
.about__media-tower { min-height: 0; height: 100%; }
.about__body .btn { align-self: flex-start; }
@media (max-width: 48rem) {
    .about__media-stack { gap: 30px; }
}

/* --- One column: the copy always comes before its photo ------------------
 * These bands put the media first in the markup because that is the order
 * they sit in on a wide screen. Once the grid folds to one column that
 * source order is what you get, so five of them opened with a photo and
 * pushed the heading below the fold.
 *
 * The breakpoint is 78rem, not the 48rem the .about rule used: that is where
 * these grids actually collapse, so between 768 and 1248 they were still
 * leading with the image on a tablet. */
/* Two breakpoints, because these grids do not all fold at the same width.
 * .cta and .faq become two columns at 68.75rem; reordering them above that
 * swapped their COLUMNS rather than their stacking order, putting the copy
 * on the wrong side at 1100-1248. */
@media (max-width: 78rem) {
    .about__body,
    .scripts__copy,
    .workers__copy,
    .commitments__body { order: -1; }
}
@media (max-width: 68.74rem) {
    .faq__body,
    .cta__body { order: -1; }
}

/* --- Services hero cards: an odd last card centres when the row folds ---- */
@media (max-width: 78rem) {
    .linkcards .linkcard:last-child:nth-child(odd) { grid-column: 1 / -1; width: 50%; justify-self: center; }
}
@media (max-width: 48rem) {
    .linkcards .linkcard:last-child:nth-child(odd) { width: auto; }
}

/* Mid-size button: live's card buttons are 16.5px/700 on 15x20 padding
 * (55px tall), one step down from the 19px/16x45 site default. */
.btn--md { font-size: 16.5px; padding: 15px 20px; }

/* About: live has 65px between the last feature and the button. */
.about__body .btn { margin-top: var(--space-l); }

/* Services page grid: scoping its rules to .services--detail outranked the
 * shared 2/1-column fold, so it stayed at three columns. Fold restated. */
@media (max-width: 78rem) { .services--detail .services__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 48rem) { .services--detail .services__grid { grid-template-columns: minmax(0, 1fr); } }

/* ==========================================================================
   WCAG 2.2 AA — link contrast and identification (axe: color-contrast,
   link-in-text-block). The template link teal is 4.39:1 on the page ground;
   the tint inks are 2–3.6:1 on their tints and stay for icons only.
   ========================================================================== */
main a:not([class]) { color: var(--brand-teal); text-decoration: underline; text-underline-offset: .15em; }   /* 5.1:1 on ground */
:is(.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green, .tint-lavender, .tint-amber, .tint-sand) a:not(.btn),
.ah-card__cols a { color: var(--brand-blue); text-decoration: underline; text-underline-offset: .15em; }     /* >= 5.0:1 on every tint */
/*
 * The footer contact details are NOT underlined, by owner's request.
 *
 * The underline here was for WCAG's link-in-text-block criterion, which
 * applies to links embedded in a block of prose, where colour alone would be
 * the only thing marking them. These are not that: each is preceded by its
 * own bold label ("Phone:", "Fax:") on its own line, so the link is
 * identified structurally rather than by decoration, and it changes colour
 * on hover and focus. Links that ARE inside footer prose keep the underline.
 */
.site-footer p a { text-decoration: underline; text-underline-offset: .15em; }
.site-footer p:has(> strong) a { text-decoration: none; }
.band-title--center { text-align: center; }

/* Heading levels follow the document outline (axe: heading-order); the size
 * comes from the class. .section h2 sizes bare h2s as eyebrows by tag, and
 * would otherwise beat these classes on any title re-levelled to h2. */
.section :is(h1, h2, h3, h4, h5, h6).band-title    { font-size: var(--title-xl); font-weight: 700; line-height: 1.15; letter-spacing: 0; }
.section :is(h1, h2, h3, h4, h5, h6).benefit__title { font-size: 25px; font-weight: 700; line-height: 1.25; }
.section :is(h1, h2, h3, h4, h5, h6).ah-card__title { font-size: 25px; font-weight: 700; line-height: 1.18; }
.service-card__body .service-card__title { line-height: 1.3; }   /* the h5 it replaced used --leading-m */

/* --- Services: the three link cards are the first thing in the Expert Care
 * section, which is itself the overlap block (as the accreditation band is
 * on the-practice). The section's wash then runs behind the cards and the
 * strip beneath them; it starts at the hero photo's bottom edge -- shifted
 * down by the overlap depth -- so the photo still shows behind the cards'
 * top half. Live: one washed section holding both. */
.services--detail.overlap { padding-top: 0; background-repeat: no-repeat; background-position: 0 var(--overlap); }
.services--detail.overlap > .u-container { padding-top: 0; }   /* the band container carried 120px; the cards must sit at the section edge */
.services--detail .linkcards { gap: 0; margin: 0 0 150px; }

/* Hero copy sits inside the page container on every page (owner's call over
 * live's 20px inset on the homepage and the-practice). */
.hero__inner { max-width: var(--grid-max-width); margin-inline: auto; padding-inline: var(--grid-gutter); }

/* ── Scroll entrance (one-way) ──────────────────────────────────────────
 * Elements opt in with data-reveal="up|down|left|right|fade" and an optional
 * data-reveal-stagger="<ms>"; the script in partials/header.html reveals them
 * once, the first time they enter the viewport. Deliberately NOT the
 * template's scroll-driven .reveal (animation-timeline: view()): that one is
 * linked to scroll position, so it plays backwards as you scroll up, which is
 * the thing that makes entrance effects feel gimmicky. One-way was the call
 * on Hinchinbrook and is the house style since.
 *
 * Travel is short on purpose — 26px vertical, 18px horizontal — a nudge
 * rather than a slide. Directions are chosen from the layout: a two-column
 * pair arrives from its own side (left column from the left, right from the
 * right), grid rows sweep left-to-right on an 80ms stagger, headings and
 * stacked content come up.
 *
 * The hiding rule is gated on .js so a visitor without JavaScript, or a
 * script that fails, sees the page fully rather than blank. */
.js [data-reveal] {
    opacity: 0;
    transition:
        opacity 760ms cubic-bezier(0.22, 0.61, 0.36, 1),
        transform 760ms cubic-bezier(0.22, 0.61, 0.36, 1);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
}
.js [data-reveal="up"]    { transform: translateY(26px); }
.js [data-reveal="down"]  { transform: translateY(-26px); }
.js [data-reveal="left"]  { transform: translateX(-18px); }
.js [data-reveal="right"] { transform: translateX(18px); }
.js [data-reveal="fade"]  { transform: none; }
.js [data-reveal][data-revealed] { opacity: 1; transform: none; }
/* Once played, drop the compositor hint and the transition so the element
 * cannot interfere with sticky or fixed descendants, and so a later layout
 * change does not animate. */
.js [data-reveal][data-reveal-done] { will-change: auto; transition: none; }
/* Motion is decoration — honour the OS setting exactly. */
@media (prefers-reduced-motion: reduce) {
    .js [data-reveal] { opacity: 1; transform: none; transition: none; }
}


/* Staff rows collapse to one column on phones.
 *
 * This sits at the END of the file deliberately. The band-scoped rules
 * (.staff-band--split .staff-pair .staff and friends) are 0,3,0 and appear in
 * several later @media blocks, so a collapse written earlier -- even inside
 * (max-width: 48rem) -- lost to them on specificity AND on source order. The
 * result was a 249px photo track beside a 72px text track at 390, where the
 * staff names rendered one character per line. Last word wins; keep it last. */
@media (max-width: 48rem) {
    .staff,
    .staff-pair .staff,
    .staff--feature,
    .staff-band--split .staff-pair .staff,
    .staff-band--reception .staff-pair .staff,
    .staff-band--plain .staff {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-s);
    }
}

/* ==========================================================================
   Button hover — one treatment for every variant
   --------------------------------------------------------------------------
   A teal-to-blue gradient with a small lift and a shadow under it, per the
   reference. Buttons previously swapped one flat colour for another with no
   movement, and each variant did its own thing.

   Mechanics worth knowing before editing:

   - A background-image cannot be transitioned, so the gradient sits on a
     full-size ::after that fades in via opacity. Growing it with
     background-size was tried first and rejected: that squeezes the gradient
     into a sliver and stretches it, so the stops visibly slide sideways
     mid-transition.
   - The lift is `translateY(-4px)`, no shadow. Only transform and opacity are
     animated, so this stays on the compositor -- animating `top` or `margin`
     here would repaint every frame.
   - This block sits at the END of the stylesheet because several variants set
     a flat background on :hover at the same specificity, and source order is
     what decides between them.
   - The ghost buttons on tinted panels keep their own per-tint resting
     colour (see the .tint-* block above); they inherit the same gradient and
     lift on hover, so the treatment is consistent.
   ========================================================================== */
/* The resting colour is set with background-color, NOT the `background`
 * shorthand: the shorthand resets background-image, which would wipe the
 * gradient these rules paint. That is exactly what
 * `.btn--primary:hover { background: var(--brand-blue) }` was doing -- it is
 * more specific than .btn:hover, so the hover rendered as a flat blue swap
 * with no gradient at all. Those variant rules are neutralised below. */
.btn,
.booking-button {
    position: relative;
    isolation: isolate;          /* keeps the ::after under the label */
    transition: transform var(--duration-hover) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}

/*
 * The gradient is a full-size pseudo-element that FADES IN, rather than a
 * background-image scaled from zero width.
 *
 * The scaling version looked wrong in motion: a gradient squeezed into a
 * narrow strip and stretched as it grew, so its colours slid across the
 * button mid-animation -- at 60ms the right-hand edge was still teal. Fading
 * a correctly-sized gradient keeps every stop where it belongs throughout.
 */
.btn::after,
.booking-button::after {
    content: "";
    position: absolute;
    /* Negative by the border width: `inset: 0` is the PADDING box, so on the
     * bordered variants the transparent border showed as a pale ring around
     * the gradient. This pushes the layer out under the border itself. */
    inset: calc(-1 * var(--btn-border, 0px));
    z-index: -1;
    border-radius: inherit;
    background-image: linear-gradient(90deg, var(--brand-teal), var(--brand-blue));
    opacity: 0;
    transition: opacity var(--duration-hover) var(--ease-out);
}
.btn:hover::after,
.btn:focus-visible::after,
.booking-button:hover::after,
.booking-button:focus-visible::after { opacity: 1; }

/* The variant :hover rules set a flat background using the `background`
 * SHORTHAND, which resets background-image -- which is why the gradient rides
 * on ::after, out of their reach, rather than on the element itself. */

.btn:hover,
.btn:focus-visible,
.booking-button:hover,
.booking-button:focus-visible {
    color: #fff;
    border-color: transparent;
    transform: translateY(-4px);
}

/* The borderless "Call ..." link is text, not a filled control: it takes the
 * lift and the colour shift but no gradient slab behind it. */
.btn--text::after { content: none; }
.btn--text:hover,
.btn--text:focus-visible {
    color: var(--brand-blue);
    transform: translateY(-4px);
}

/* Someone who has asked for less motion gets the colour change without the
 * movement -- the hover still reads, it just does not travel. */
@media (prefers-reduced-motion: reduce) {
    .btn:hover, .btn:focus-visible,
    .booking-button:hover, .booking-button:focus-visible,
    .btn--text:hover, .btn--text:focus-visible,
    .nav-list a:hover, .nav-list a:focus-visible,
    .sub-toggle:hover, .sub-toggle:focus-visible { transform: none; }
}


/* --- Every button reads as a button at rest ------------------------------
 * .btn--outline, .btn--ghost and .btn--secondary were outlines: transparent
 * fill, a visible 1-2px rule, coloured label. Beside a solid .btn--primary
 * they read as a different kind of control rather than a quieter one -- and
 * .btn--ghost's rule was a desaturated grey that was not a brand colour at
 * all. They now carry the same teal fill and white label as the primary; the
 * gradient hover then lands on all of them identically.
 *
 * --btn-border tells the ::after how far to spill so it covers the border
 * box; without it a transparent border shows as a pale ring on hover. */
.btn--primary, .btn--primary.nav-cta { --btn-border: 2px; }
.btn--secondary                      { --btn-border: 2px; }
.btn--outline, .btn--ghost           { --btn-border: 1px; }

.btn--outline,
.btn--ghost,
.btn--secondary {
    background: var(--brand-teal);
    border-color: var(--brand-teal);
    color: #fff;
}

/* A button INSIDE a tinted panel is an outline, not a slab: a 1px rule and a
 * label in a darker tone of the panel's own colour. A teal slab on a pastel
 * card read as a foreign object dropped onto it.
 *
 * The colour is --tint-btn, not --tint-ink itself: the raw tint inks were
 * picked for icons and sit at 2.5-4.3:1 against their own panel. Mixing 40%
 * of the body ink into them keeps the hue recognisable and lifts every one
 * past 4.5 (5.0 on pink, 5.2 on blue, 6.6 on lavender).
 *
 * The ::after gradient still supplies the hover, so these keep the same
 * teal-to-blue fill and lift as every other button. */
/* A darker step of each icon colour, for button text and its 1px rule. Same
 * hue and saturation as --tint-ink; only the lightness drops, so the button
 * still reads as the panel's own pink/blue/sky rather than a grey. Each
 * clears 4.5:1 against its panel, where the raw ink sits at 2.4-2.7. */
.tint-pink, .tint-rose   { --tint-btn: rgb(157, 61, 104); }
.tint-blue               { --tint-btn: rgb(63, 102, 138); }
.tint-sky, .tint-cyan    { --tint-btn: rgb(26, 115, 126); }
.tint-lavender           { --tint-btn: rgb(92, 93, 163); }
.tint-green              { --tint-btn: rgb(81, 110, 44); }
.tint-amber, .tint-sand  { --tint-btn: rgb(140, 97, 16); }
.cta__card               { --tint-btn: rgb(26, 115, 126); }
.access--care .access__panel { --tint-btn: rgb(92, 93, 163); }
.twoup__col:first-child  { --tint-btn: rgb(63, 102, 138); }
.twoup__col:last-child   { --tint-btn: rgb(157, 61, 104); }
.related__panel          { --tint-btn: rgb(63, 102, 138); }

/* These three panels carry a hard-coded pastel rather than a .tint-* class,
 * so they declare the ink their buttons should read. */
.access--care .access__panel   { --tint-ink: var(--tint-lavender-ink); }
.twoup__col:first-child        { --tint-ink: var(--tint-blue-ink); }
.twoup__col:last-child         { --tint-ink: var(--tint-pink-ink); }
.related__panel                { --tint-ink: var(--tint-blue-ink); }

:is(.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green,
    .tint-lavender, .tint-amber, .tint-sand,
    .access--care .access__panel, .twoup__col, .related__panel, .cta__card)
    :is(.btn--ghost, .btn--secondary, .btn--primary) {
    /* Falls back to a mix, but each panel below sets an explicit value:
     * the icon's own hue at a darker lightness. */
    --tint-btn: var(--tint-ink);
    --btn-border: 1px;
    background: transparent;
    background-image: none;
    border: 1px solid var(--tint-btn);
    color: var(--tint-btn);
}
/* Hover is the shared gradient, so the label turns white over it. */
:is(.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green,
    .tint-lavender, .tint-amber, .tint-sand,
    .access--care .access__panel, .twoup__col, .related__panel, .cta__card)
    :is(.btn--ghost, .btn--secondary, .btn--primary):is(:hover, :focus-visible) {
    color: #fff;
    border-color: transparent;
}


/* --- Inside a tinted card: heading grey, ticks in the panel's own ink -----
 * The band title was brand blue on every card regardless of its tint, and
 * the ticklist checks were teal everywhere. On the pink feedback card the
 * reference has a grey heading and dark-pink checks -- the tint already
 * carries that colour as --tint-ink, so the marks just read it. */
:is(.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green,
    .tint-lavender, .tint-amber, .tint-sand) :is(.band-title, .cta__title) {
    color: var(--ink-body);
}

/* The same, for the panels that carry a hard-coded pastel rather than a
 * .tint-* class. On practice-policy eleven headings sat blue on a coloured
 * panel while the benefit cards and the feedback card beside them were
 * already grey -- the same page disagreeing with itself. A heading on a
 * tinted panel is grey; blue is for headings on the page ground. */
.commit-card__title,
.policy-card__title,
.access--care .access__inner .band-title,
.twoup__col .band-title,
.feeinfo__inner .band-title,
.related__inner .band-title,
.policies__panel .band-title,
.cta__card .band-title,
.scripts__title,
.policy-col__title {
    color: var(--ink-body);
}
:is(.tint-pink, .tint-rose, .tint-blue, .tint-sky, .tint-cyan, .tint-green,
    .tint-lavender, .tint-amber, .tint-sand) .ticklist li::before {
    border-left-color: var(--tint-ink);
    border-bottom-color: var(--tint-ink);
}


/* --- Patient care band (practice policy) ---------------------------------
 * The same full-bleed shape as .access on the services page, but its own
 * photo and a LIGHTER version of the blue used by the privacy and
 * confidentiality panel below it (rgb(204,227,245) mixed 55% toward the page
 * ground), so the two read as related rather than identical. */
/* The light purple of the "Feedback and Complaints" card (--tint-lavender),
 * not the blue: that read as a near-copy of the privacy panel below it. */
.access--care .access__panel { background: var(--tint-lavender); }
.access__media--care {
    background-image: url("/assets/images/policy-privacy.webp");
    background-size: cover;
    background-position: 50% 45%;
    /* The base .access__media carries min-height: 900px for the services
     * band, whose copy column is that tall. This one's copy is shorter, so it
     * sizes to the panel beside it instead. */
    min-height: 620px;
}


/* --- Icon weight ---------------------------------------------------------
 * Every icon on the site is a solid fill="currentColor" path with no stroke,
 * so there is no stroke-width to raise. Painting a stroke in the same colour
 * thickens the shape from its own outline outward -- the glyph gains weight
 * without changing its drawing. paint-order keeps the stroke behind the fill
 * so thin interior details do not close up.
 *
 * The HCC row is measured at 48px and the rest at 30-40, so the stroke is
 * proportional rather than one value everywhere. */
.hcc-item__icon,
.benefit__icon,
.contact-card__icon,
.feature__icon,
.linkcard__icon,
.ah-card__icon,
.workers-card__icon,
.commit-card__icon {
    stroke: currentColor;
    /* In viewBox units, so it scales with the icon rather than being a fixed
     * number of screen pixels. These viewBoxes are ~512 wide against a 30-48px
     * box, so 12 units lands near 1px of extra weight per edge. */
    stroke-width: 12;
    stroke-linejoin: round;
    paint-order: stroke fill;
}

/* ==========================================================================
   PAGE WASH — one canvas per page
   --------------------------------------------------------------------------
   The soft colour washes are painted ONCE, on <body>, as a stack of large
   translucent radial gradients over a transparent body on a ground-coloured
   <html>. One paint surface means the wash cannot seam: there is no band
   edge for it to start or stop at, so it flows under every section, under
   the header and on through the footer as one field. Each page places its
   own blobs (data-wash on <body>) so no two pages carry the same tint in the
   same place, but the palette and the technique are shared.

   Sizing. Widths are in vw with a rem floor so a blob still spans a phone.
   Heights are a plain PERCENTAGE of the page, so a blob covers the same
   share of it whether it is 5,000px or 18,000px tall; short pages set a
   larger --wash-h so they still get a real blob rather than a dot. A rem
   floor would be nicer, but Chromium REJECTS a math function that mixes a
   percentage with a length in a gradient size -- max(9%, 36rem) computed
   to background-image: none on every page, verified in 151 -- while a bare
   percentage and max(64vw, 36rem) are both fine.

   Placement. Positions are percentages of the page height, so a blob keeps
   its place relative to the content at every viewport. Nothing is placed
   in the top ~12%: that is the hero, which covers the canvas anyway, and a
   tint ending exactly at a hero scrim's foot would read as a step.

   What had to give way. Every section that painted its own opaque ground
   (.cta, .staff-band, the services straddle's ::before, the footer) is now
   transparent, and .legal-card fades its ground out below the photo it
   rides. A section that paints ground over the canvas produces a
   rectangle edge at its top and foot -- which is the defect this replaces.
   Tinted cards and panels (.policies__panel, .cta__card, .feeinfo__panel,
   the tint-* cards) are deliberately opaque: their edges are edges.
   ========================================================================== */
html { background: var(--ground); }
body {
    --wash-y: 241 221 170;   /* warm yellow */
    --wash-g: 214 236 190;   /* soft green  */
    --wash-p: 242 209 221;   /* pink        */
    --wash-b: 206 234 245;   /* sky blue    */
    --wash-l: 164 178 219;   /* lavender    */
    --wash-w: max(64vw, 36rem);
    --wash-h: 9%;
    background-color: transparent;
    background-repeat: no-repeat;
    /* default, for any page without a data-wash */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 14%, rgb(var(--wash-y) / .22), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 40%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 64%, rgb(var(--wash-p) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 14% 88%, rgb(var(--wash-g) / .24), rgba(0, 0, 0, 0) 70%);
}

/* Home: the blobs sit where the band washes used to -- a faint warm one
 * behind the trust bar, blue/pink behind About, the four-colour cluster
 * behind Services and the FAQ, lavender behind the contact band. */
body[data-wash="home"] {
    background-image:
        radial-gradient(max(90vw, 40rem) 9%  at 90% 14%, rgb(var(--wash-y) / .16), rgba(0, 0, 0, 0) 70%),
        radial-gradient(max(60vw, 34rem) 6%  at 10% 24%, rgb(var(--wash-b) / .25), rgba(0, 0, 0, 0) 70%),
        radial-gradient(max(64vw, 36rem) 7%  at 88% 29%, rgb(var(--wash-p) / .28), rgba(0, 0, 0, 0) 72%),
        radial-gradient(max(62vw, 36rem) 7%  at 16% 44%, rgb(var(--wash-y) / .34), rgba(0, 0, 0, 0) 70%),
        radial-gradient(max(64vw, 36rem) 8%  at 22% 52%, rgb(var(--wash-g) / .30), rgba(0, 0, 0, 0) 72%),
        radial-gradient(max(70vw, 40rem) 8%  at 84% 49%, rgb(var(--wash-p) / .38), rgba(0, 0, 0, 0) 70%),
        radial-gradient(max(90vw, 46rem) 10% at 72% 58%, rgb(var(--wash-b) / .36), rgba(0, 0, 0, 0) 78%),
        radial-gradient(max(76vw, 40rem) 8%  at -8% 84%, rgb(var(--wash-l) / .35), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="practice"] {
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 84% 19%, rgb(var(--wash-g) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 44%, rgb(var(--wash-b) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 45%, rgb(var(--wash-p) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 16% 58%, rgb(var(--wash-y) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 70%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  4% 84%, rgb(var(--wash-l) / .32), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="services"] {
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 14% 23%, rgb(var(--wash-y) / .32), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 28%, rgb(var(--wash-p) / .34), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 24% 35%, rgb(var(--wash-g) / .28), rgba(0, 0, 0, 0) 72%),
        radial-gradient(var(--wash-w) var(--wash-h) at 82% 58%, rgb(var(--wash-b) / .30), rgba(0, 0, 0, 0) 74%),
        radial-gradient(var(--wash-w) var(--wash-h) at 12% 70%, rgb(var(--wash-y) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 94% 90%, rgb(var(--wash-l) / .28), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="contact"] {
    --wash-h: 16%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(max(76vw, 40rem) 18% at -8% 46%, rgb(var(--wash-l) / .35), rgba(0, 0, 0, 0) 72%),
        radial-gradient(var(--wash-w) 14%    at 92% 36%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) 14%    at 86% 74%, rgb(var(--wash-g) / .22), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) 14%    at 26% 88%, rgb(var(--wash-p) / .26), rgba(0, 0, 0, 0) 70%);
}
body[data-wash="fees"] {
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 88% 26%, rgb(var(--wash-b) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 32%, rgb(var(--wash-y) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 62%, rgb(var(--wash-g) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  6% 70%, rgb(var(--wash-l) / .24), rgba(0, 0, 0, 0) 72%),
        radial-gradient(var(--wash-w) var(--wash-h) at 82% 87%, rgb(var(--wash-p) / .30), rgba(0, 0, 0, 0) 70%);
}
body[data-wash="policy"] {
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 88% 22%, rgb(var(--wash-p) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 28%, rgb(var(--wash-g) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 49%, rgb(var(--wash-y) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  8% 60%, rgb(var(--wash-b) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 69%, rgb(var(--wash-p) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  4% 90%, rgb(var(--wash-l) / .30), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="doctors"] {
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 17%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  8% 29%, rgb(var(--wash-y) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 92% 46%, rgb(var(--wash-p) / .32), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 60%, rgb(var(--wash-g) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 88% 74%, rgb(var(--wash-b) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at -6% 92%, rgb(var(--wash-l) / .30), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="rights"] {
    --wash-h: 12%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 88% 34%, rgb(var(--wash-p) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  8% 50%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 67%, rgb(var(--wash-y) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  6% 88%, rgb(var(--wash-l) / .30), rgba(0, 0, 0, 0) 72%);
}
body[data-wash="legal"] {
    --wash-h: 13%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 92% 42%, rgb(var(--wash-b) / .26), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  6% 62%, rgb(var(--wash-y) / .24), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 94% 86%, rgb(var(--wash-p) / .26), rgba(0, 0, 0, 0) 70%);
}
body[data-wash="after-hours"] {
    --wash-h: 24%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 28%, rgb(var(--wash-y) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 62%, rgb(var(--wash-b) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 90% 88%, rgb(var(--wash-p) / .24), rgba(0, 0, 0, 0) 70%);
}
body[data-wash="appointment"] {
    --wash-h: 32%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 88% 32%, rgb(var(--wash-b) / .30), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at 10% 70%, rgb(var(--wash-y) / .26), rgba(0, 0, 0, 0) 70%);
}
body[data-wash="hub"] {
    --wash-h: 36%;   /* short page: a larger share so the blob is still a blob */
    background-image:
        radial-gradient(var(--wash-w) var(--wash-h) at 86% 30%, rgb(var(--wash-g) / .28), rgba(0, 0, 0, 0) 70%),
        radial-gradient(var(--wash-w) var(--wash-h) at  8% 76%, rgb(var(--wash-l) / .26), rgba(0, 0, 0, 0) 70%);
}
